I'm trying to send a text message over Apache Camel using the camel-twilio component. Since I never used the Twilio API (neither natively nor using Apache Camel), I'm not sure if I got the parameters right. Here's the method I wrote:
/**
* Sends a text message to the given recipient's number (parameter to)
*
* #param username:
* Twilio username (email)
* #param password:
* Twilio password (in plain text)
* #param accountSid:
* Twilio account sid (from the dashboard)
* #param from:
* registered phone number (starting with country prefix +XX)
* #param to:
* the recipient's phone number (starting with country prefix +XX)
* #param message:
* the message to be sent (plain text)
* #throws Exception
*/
public static void sendTextMessage(String username, String password, String accountSid, String from, String to,
String message) throws Exception {
String route = String.format("twilio:message/creator?username=%s&password=%s&accountSid=%s&from=%s&to=%s",
username, password, accountSid, from, to);
CamelContext context = new DefaultCamelContext();
context.addRoutes(new RouteBuilder() {
#Override
public void configure() throws Exception {
from("direct:message").to(route);
}
});
context.start();
ProducerTemplate producer = context.createProducerTemplate();
producer.sendBody("direct:message", message);
context.stop();
}
The most important line is the creation of the route, the first of the method. When I'm running this method with parameters according to the JavaDoc, I get this error:
Caused by: org.apache.camel.RuntimeCamelException: Missing properties for creator, need one or more from [pathAccountSid, mediaUrl, messagingServiceSid, body]
So I thought to add the parameter messagingServiceSid, providing my accountSid again:
String route = String.format("twilio:message/creator?username=%s&password=%s&accountSid=%s&from=%s&to=%s&messagingServiceSid=%s",
username, password, accountSid, from, to, accountSid);
Now I get this error message:
Caused by: java.lang.IllegalArgumentException: No matching method for message/creator, with arguments [messagingServiceSid, from, to]
What am I doing wrong?
EDIT: These are my Maven dependencies:
<dependencies>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-core</artifactId>
<version>2.20.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-twilio</artifactId>
<version>2.20.1</version>
</dependency>
</dependencies>
EDIT 2: Here's the modified and working version of the method:
/**
* Sends a text message to the given recipient's number (parameter to)
*
* #param accountSid:
* Twilio account sid (from the dashboard)
* #param authToken:
* Twilio auth token (from the dashboard)
* #param from:
* registered phone number (starting with country prefix +XX)
* #param to:
* the recipient's phone number (starting with country prefix +XX)
* #param message:
* the message to be sent (plain text)
* #throws Exception
*/
public static void sendTextMessage(String accountSid, String authToken, String from, String to, String message)
throws Exception {
CamelContext context = new DefaultCamelContext();
TwilioComponent twilio = context.getComponent("twilio", TwilioComponent.class);
twilio.getConfiguration().setUsername(accountSid);
twilio.getConfiguration().setPassword(authToken);
context.addRoutes(new RouteBuilder() {
#Override
public void configure() throws Exception {
from("direct:message")
.setHeader("CamelTwilioTo", constant(new PhoneNumber(to)))
.setHeader("CamelTwilioFrom", constant(new PhoneNumber(from)))
.setHeader("CamelTwilioBody", constant(message))
.to("twilio://message/creator");
}
});
context.start();
ProducerTemplate producer = context.createProducerTemplate();
producer.sendBody("direct:message", message);
context.stop();
}
I have to say that to work efficiently with camel-twilio, you need to have a good understanding on Twilio Java API. In your case, let's get familiar with the MessageCreator API here:
https://www.twilio.com/docs/libraries/reference/twilio-java/7.17.0/com/twilio/rest/api/v2010/account/MessageCreator.html
With that said, first of all, since the username (i.e. accountSid) and password should be something shared in a camel-twilio component, let's set them at the component:
TwilioComponent twilio = context.getComponent("twilio", TwilioComponent.class);
twilio.getConfiguration().setUsername(username);
twilio.getConfiguration().setPassword(password);
(Note most of the time the twilio username and accoundSid refer to the same thing, so you can use only one of them.)
Once the username/password are set up, let's use the MessageCreator. The simplest constructor you can use would be MessageCreator(PhoneNumber to, PhoneNumber from, String body), but since to and from have to be PhoneNumber instances, it's easier to pass them to the endpoint as Camel message headers instead of embedding them to the endpoint URI as endpoint parameters. (NOTE: Any of the camel-twilio endpoint options can be provided in a message header with CamelTwilio prefix.)
This would look something like the following:
public void configure() throws Exception {
from("direct:message")
.setHeader("CamelTwilioTo", constant(new PhoneNumber(to)))
.setHeader("CamelTwilioFrom", constant(new PhoneNumber(from)))
.setHeader("CamelTwilioBody", constant(message))
.to("twilio://message/creator");
}
Note, at this moment, the endpoint URI can be as simple as twilio://message/creator.
Now you should be able to send texts to Twilio.
FYI, there is a working example of camel-twilio with Spring Boot:
https://github.com/tadayosi/demo-camel-hawtio-springboot
Related
I have a piece of Java code (see below), which has not been changed in a while and worked two weeks ago. When I run it now, I suddenly get an "AUTHENTICATE FAILED." I get the error on two different PCs, and I have validated that the credentials used still work when I log into may office365 mailbox using the browser.
Has something changed on the office365 side I should know of?
The error I get is:
javax.mail.AuthenticationFailedException: AUTHENTICATE failed.
at com.sun.mail.imap.IMAPStore.protocolConnect(IMAPStore.java:732)
at javax.mail.Service.connect(Service.java:366)
at javax.mail.Service.connect(Service.java:246)
at my.application.input.imap.ImapMailBoxReader.processOnMessages(ImapMailBoxReader.java:69)
Digging deeper, the cause seems to be an A3 NO AUTHENTICATE failed. response (line 730 of javax.mail.IMAPStore).
The code I use is the following (using javax.mail version 1.6.2):
package my.application.input.imap;
import my.application.dao.PhysicalTransactionDao;
import com.sun.mail.util.MailSSLSocketFactory;
import javax.mail.*;
import javax.mail.search.AndTerm;
import javax.mail.search.ComparisonTerm;
import javax.mail.search.ReceivedDateTerm;
import javax.mail.search.SearchTerm;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.*;
import java.io.*;
import java.util.function.Consumer;
public class ImapMailBoxReader {
private String host;
private String username;
private String password;
public static void main(String[] args) {
ImapMailBoxReader imapReader = new ImapMailBoxReader(
"outlook.office365.com",
"myemail",
"mypassword");
LocalDate startDate = LocalDate.of(2022,4,1);
LocalDate endDate = LocalDate.of(2022,7,1);
imapReader.processOnMessages("Inbox", startDate, endDate, SomeClass::processMessage);
}
public ImapMailBoxReader(String host, String username, String password) {
this.host = host;
this.username = username;
this.password = password;
}
/**
* Returns all messages on or after the given since date, until today. If the given since date is null, all messages
* are returned
* #param folder the folder to search through
* #param since the given since date
* #param mailConsumer the consumer that will process the messages retrieved
*/
public void processOnMessages(String folder, LocalDate since, Consumer<Message> mailConsumer) {
processOnMessages(folder, since, null, mailConsumer);
}
/**
* Runs a given mailconsumer on all messages in the given imap folder that have been received on, or after, the given
* since date and before the given until date. If since is null, all messages are returned up to the until date.
* If until is null, all messages are returned from the since date until now. If both are null, all messages are
* returned.
* #param folder the folder to search through
* #param since if specified, only messages from this date on are returned
* #param mailconsumer the consumer that will be executed on the messages
*/
public void processOnMessages(String folder, LocalDate since, LocalDate until, Consumer<Message> mailconsumer) {
try {
Properties prop = new Properties();
MailSSLSocketFactory sf = new MailSSLSocketFactory();
sf.setTrustAllHosts(true);
prop.setProperty("mail.imap.starttls.enable", "true");
prop.put("mail.imap.starttls.enable", "true");
prop.put("mail.imap.ssl.socketFactory", sf);
//Connect to the server
Session session = Session.getDefaultInstance(prop, null);
Store store = session.getStore("imap");
store.connect(host, username, password);
//open the inbox folder
Folder inbox = store.getFolder(folder);
inbox.open(Folder.READ_ONLY);
Message[] messages;
if (since != null) {
Date startDate = Date.from(since.atStartOfDay(ZoneId.systemDefault()).toInstant());
SearchTerm newerThan = new ReceivedDateTerm(ComparisonTerm.GE, startDate);
if (until != null) {
Date endDate = Date.from(until.plusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant());
SearchTerm olderThan = new ReceivedDateTerm(ComparisonTerm.LT, endDate);
SearchTerm both = new AndTerm(olderThan, newerThan);
messages = inbox.search(both);
} else {
messages = inbox.search(newerThan);
}
} else if (until != null) {
Date endDate = Date.from(until.plusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant());
SearchTerm olderThan = new ReceivedDateTerm(ComparisonTerm.LT, endDate);
messages = inbox.search(olderThan);
} else {
messages = inbox.getMessages();
}
for (Message m: messages) {
mailconsumer.accept(m);
}
inbox.close(false);
store.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Will search through all attachments of the message, and will pass those with the given extension (if provided)
* to the consumer. Note that the connection to the imap should be open for all this magic to work. this method
* is intended to be called from a messageconsumer during the processOnMessages method from this class.
* #param message the message for which the attachments are needed.
* #param extension if provided, only attachments with this extension will be provided
* #param attachmentConsumer the consumer that will process the attachments
* #throws IOException if for some reason the attachments can't be accessed
* #throws MessagingException for other messaging errors
*/
public static void processOnAttachments(Message message, String extension, Consumer<InputStream> attachmentConsumer)
throws IOException, MessagingException {
Multipart multipart = (Multipart) message.getContent();
for (int i = 0; i < multipart.getCount(); i++) {
BodyPart bodyPart = multipart.getBodyPart(i);
if (bodyPart.getFileName() != null && bodyPart.getFileName().endsWith(extension)) {
attachmentConsumer.accept(bodyPart.getInputStream());
}
}
}
}
Again, this code worked perfectly two weeks ago, nothing was changed on my side and the credentials still work...
All suggestions are appreciated.
You must use OAuth2, legacy security may have been deprecated.
Works with Thunderbird for example.
Just see how to reactivate legacy auth or use OAuth2 with your java client.
#see https://learn.microsoft.com/fr-fr/exchange/troubleshoot/administration/cannot-connect-mailbox-pop-imap-outlook to reactivate legacy
PS : To use shared mailbox, you must user mailbox name as user, and OAuth2 user + password and MFA if needed during Auth part, all that instead of old way (user#domain\sharedmailbox + password)
I have a piece of Java code (see below), which has not been changed in a while and worked two weeks ago. When I run it now, I suddenly get an "AUTHENTICATE FAILED." I get the error on two different PCs, and I have validated that the credentials used still work when I log into may office365 mailbox using the browser.
Has something changed on the office365 side I should know of?
The error I get is:
javax.mail.AuthenticationFailedException: AUTHENTICATE failed.
at com.sun.mail.imap.IMAPStore.protocolConnect(IMAPStore.java:732)
at javax.mail.Service.connect(Service.java:366)
at javax.mail.Service.connect(Service.java:246)
at my.application.input.imap.ImapMailBoxReader.processOnMessages(ImapMailBoxReader.java:69)
Digging deeper, the cause seems to be an A3 NO AUTHENTICATE failed. response (line 730 of javax.mail.IMAPStore).
The code I use is the following (using javax.mail version 1.6.2):
package my.application.input.imap;
import my.application.dao.PhysicalTransactionDao;
import com.sun.mail.util.MailSSLSocketFactory;
import javax.mail.*;
import javax.mail.search.AndTerm;
import javax.mail.search.ComparisonTerm;
import javax.mail.search.ReceivedDateTerm;
import javax.mail.search.SearchTerm;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.*;
import java.io.*;
import java.util.function.Consumer;
public class ImapMailBoxReader {
private String host;
private String username;
private String password;
public static void main(String[] args) {
ImapMailBoxReader imapReader = new ImapMailBoxReader(
"outlook.office365.com",
"myemail",
"mypassword");
LocalDate startDate = LocalDate.of(2022,4,1);
LocalDate endDate = LocalDate.of(2022,7,1);
imapReader.processOnMessages("Inbox", startDate, endDate, SomeClass::processMessage);
}
public ImapMailBoxReader(String host, String username, String password) {
this.host = host;
this.username = username;
this.password = password;
}
/**
* Returns all messages on or after the given since date, until today. If the given since date is null, all messages
* are returned
* #param folder the folder to search through
* #param since the given since date
* #param mailConsumer the consumer that will process the messages retrieved
*/
public void processOnMessages(String folder, LocalDate since, Consumer<Message> mailConsumer) {
processOnMessages(folder, since, null, mailConsumer);
}
/**
* Runs a given mailconsumer on all messages in the given imap folder that have been received on, or after, the given
* since date and before the given until date. If since is null, all messages are returned up to the until date.
* If until is null, all messages are returned from the since date until now. If both are null, all messages are
* returned.
* #param folder the folder to search through
* #param since if specified, only messages from this date on are returned
* #param mailconsumer the consumer that will be executed on the messages
*/
public void processOnMessages(String folder, LocalDate since, LocalDate until, Consumer<Message> mailconsumer) {
try {
Properties prop = new Properties();
MailSSLSocketFactory sf = new MailSSLSocketFactory();
sf.setTrustAllHosts(true);
prop.setProperty("mail.imap.starttls.enable", "true");
prop.put("mail.imap.starttls.enable", "true");
prop.put("mail.imap.ssl.socketFactory", sf);
//Connect to the server
Session session = Session.getDefaultInstance(prop, null);
Store store = session.getStore("imap");
store.connect(host, username, password);
//open the inbox folder
Folder inbox = store.getFolder(folder);
inbox.open(Folder.READ_ONLY);
Message[] messages;
if (since != null) {
Date startDate = Date.from(since.atStartOfDay(ZoneId.systemDefault()).toInstant());
SearchTerm newerThan = new ReceivedDateTerm(ComparisonTerm.GE, startDate);
if (until != null) {
Date endDate = Date.from(until.plusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant());
SearchTerm olderThan = new ReceivedDateTerm(ComparisonTerm.LT, endDate);
SearchTerm both = new AndTerm(olderThan, newerThan);
messages = inbox.search(both);
} else {
messages = inbox.search(newerThan);
}
} else if (until != null) {
Date endDate = Date.from(until.plusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant());
SearchTerm olderThan = new ReceivedDateTerm(ComparisonTerm.LT, endDate);
messages = inbox.search(olderThan);
} else {
messages = inbox.getMessages();
}
for (Message m: messages) {
mailconsumer.accept(m);
}
inbox.close(false);
store.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Will search through all attachments of the message, and will pass those with the given extension (if provided)
* to the consumer. Note that the connection to the imap should be open for all this magic to work. this method
* is intended to be called from a messageconsumer during the processOnMessages method from this class.
* #param message the message for which the attachments are needed.
* #param extension if provided, only attachments with this extension will be provided
* #param attachmentConsumer the consumer that will process the attachments
* #throws IOException if for some reason the attachments can't be accessed
* #throws MessagingException for other messaging errors
*/
public static void processOnAttachments(Message message, String extension, Consumer<InputStream> attachmentConsumer)
throws IOException, MessagingException {
Multipart multipart = (Multipart) message.getContent();
for (int i = 0; i < multipart.getCount(); i++) {
BodyPart bodyPart = multipart.getBodyPart(i);
if (bodyPart.getFileName() != null && bodyPart.getFileName().endsWith(extension)) {
attachmentConsumer.accept(bodyPart.getInputStream());
}
}
}
}
Again, this code worked perfectly two weeks ago, nothing was changed on my side and the credentials still work...
All suggestions are appreciated.
You must use OAuth2, legacy security may have been deprecated.
Works with Thunderbird for example.
Just see how to reactivate legacy auth or use OAuth2 with your java client.
#see https://learn.microsoft.com/fr-fr/exchange/troubleshoot/administration/cannot-connect-mailbox-pop-imap-outlook to reactivate legacy
PS : To use shared mailbox, you must user mailbox name as user, and OAuth2 user + password and MFA if needed during Auth part, all that instead of old way (user#domain\sharedmailbox + password)
My SockJs client in webpage, sends message with a frame size of 16K. The message size limit is what determines the max size of the file that I can transfer.
Below is what I found in the doc.
/**
* Configure the maximum size for an incoming sub-protocol message.
* For example a STOMP message may be received as multiple WebSocket messages
* or multiple HTTP POST requests when SockJS fallback options are in use.
*
* <p>In theory a WebSocket message can be almost unlimited in size.
* In practice WebSocket servers impose limits on incoming message size.
* STOMP clients for example tend to split large messages around 16K
* boundaries. Therefore a server must be able to buffer partial content
* and decode when enough data is received. Use this property to configure
* the max size of the buffer to use.
*
* <p>The default value is 64K (i.e. 64 * 1024).
*
* <p><strong>NOTE</strong> that the current version 1.2 of the STOMP spec
* does not specifically discuss how to send STOMP messages over WebSocket.
* Version 2 of the spec will but in the mean time existing client libraries
* have already established a practice that servers must handle.
*/
public WebSocketTransportRegistration setMessageSizeLimit(int messageSizeLimit) {
this.messageSizeLimit = messageSizeLimit;
return this;
}
MY QUESTION:
Can I setup a partial messaging so that a file is transferred part by part and is not getting transferred as a single message as it is been done now?
Update:
Still looking for a solution with partial messaging
Meanwhile using HTTP now for large messages (which is file uploads/downloads in my application).
Can I setup a partial messaging so that a file is transferred part by part and is not getting transferred as a single message as it is been done now?
Yes. Here is the relevant Config from my Spring boot experimental project - basically UploadWSHandler is registered and WebSocketTransportRegistration.setMessageSizeLimit is set.
#Configuration
#EnableWebSocket
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer implements WebSocketConfigurer {
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new UploadWSHandler(), "/binary");
}
#Override
public void configureWebSocketTransport(WebSocketTransportRegistration registration) {
registration.setMessageSizeLimit(50 * 1024 * 1024);
}
}
The UploadWShandler is as follows. Sorry too much code here - Key points
supportsPartialMessage which returns true.
handleBinaryMessage will be called multiple times with partial message so we need to assemble the bytes. So afterConnectionEstablished establishes an identity using the websocket URL query. But you don't have to use this mechanism. The reason why I choose this mechanism is to keep the client side simple so that I call webSocket.send(files[0]) only once i.e. I am not slicing the file blob object on the javascript side. (side point: I want to use plain websocket on client side - no stomp/socks)
The iternal client side chunking mechanism provides message.isLast() last message
For demo purposes I am writing this to file system and accumulating those bytes in memory with FileUploadInFlight but you don't have to do this and can stream somewhere else as you go.
public class UploadWSHandler extends BinaryWebSocketHandler {
Map<WebSocketSession, FileUploadInFlight> sessionToFileMap = new WeakHashMap<>();
#Override
public boolean supportsPartialMessages() {
return true;
}
#Override
protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) throws Exception {
ByteBuffer payload = message.getPayload();
FileUploadInFlight inflightUpload = sessionToFileMap.get(session);
if (inflightUpload == null) {
throw new IllegalStateException("This is not expected");
}
inflightUpload.append(payload);
if (message.isLast()) {
Path basePath = Paths.get(".", "uploads", UUID.randomUUID().toString());
Files.createDirectories(basePath);
FileChannel channel = new FileOutputStream(
Paths.get(basePath.toString() ,inflightUpload.name).toFile(), false).getChannel();
channel.write(ByteBuffer.wrap(inflightUpload.bos.toByteArray()));
channel.close();
session.sendMessage(new TextMessage("UPLOAD "+inflightUpload.name));
session.close();
sessionToFileMap.remove(session);
}
String response = "Upload Chunk: size "+ payload.array().length;
System.out.println(response);
}
#Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
sessionToFileMap.put(session, new FileUploadInFlight(session));
}
static class FileUploadInFlight {
String name;
String uniqueUploadId;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
/**
* Fragile constructor - beware not prod ready
* #param session
*/
FileUploadInFlight(WebSocketSession session) {
String query = session.getUri().getQuery();
String uploadSessionIdBase64 = query.split("=")[1];
String uploadSessionId = new String(Base64Utils.decodeUrlSafe(uploadSessionIdBase64.getBytes()));
System.out.println(uploadSessionId);
List<String> sessionIdentifiers = Splitter.on("\\").splitToList(uploadSessionId);
String uniqueUploadId = session.getRemoteAddress().toString()+sessionIdentifiers.get(0);
String fileName = sessionIdentifiers.get(1);
this.name = fileName;
this.uniqueUploadId = uniqueUploadId;
}
public void append(ByteBuffer byteBuffer) throws IOException{
bos.write(byteBuffer.array());
}
}
}
BTW a working project is also sprint-boot-with-websocked-chunking-assembly-and-fetch in with-websocked-chunking-assembly-and-fetch branch
I have implemented Apache CXF WebService and trying to access WebService through Swings Application.
The problem that I am facing is CXF has created a class named Exception_Exception in client side from the WSDL. When an exception occurs in server side, the client Exception_Exception gets invokes but I don't receive the same stack trace of server in Exception_Exception stack trace.
Below is the class generated by CXF:
package com.abc.it.vcare.service;
import javax.xml.ws.WebFault;
/**
* This class was generated by the JAX-WS RI.
* JAX-WS RI 2.2-hudson-752-
* Generated source version: 2.2
*
*/
#WebFault(name = "Exception", targetNamespace = "http://service.vcare.it.abc.com/")
public class Exception_Exception
extends java.lang.Exception
{
/**
* Java type that goes as soapenv:Fault detail element.
*
*/
private com.axiom.it.vcare.service.Exception faultInfo;
/**
*
* #param message
* #param faultInfo
*/
public Exception_Exception(String message, com.axiom.it.vcare.service.Exception faultInfo) {
super(message);
this.faultInfo = faultInfo;
}
/**
*
* #param message
* #param faultInfo
* #param cause
*/
public Exception_Exception(String message, com.axiom.it.vcare.service.Exception faultInfo, Throwable cause) {
super(message, cause);
this.faultInfo = faultInfo;
}
/**
*
* #return
* returns fault bean: com.axiom.it.vcare.service.Exception
*/
public com.axiom.it.vcare.service.Exception getFaultInfo() {
return faultInfo;
}
}
Please help, what should I do to get the same StackTrace given in Server Exception in my client side Exception_Exception.
Thanks,
Jai G.
What version of CXF are you using? This is an old CXF issue that has been addressed in the most recent versions. Try using version 2.7.0 of CXF, that should solve the problem.
I am trying to sent an email using my company's mail server. But I am getting the following exception
Caused by: com.sun.mail.smtp.SMTPSendFailedException: 530 5.7.1 Client was not authenticated
at com.sun.mail.smtp.SMTPTransport.issueSendCommand(SMTPTransport.java:1388)
at com.sun.mail.smtp.SMTPTransport.mailFrom(SMTPTransport.java:959)
at com.sun.mail.smtp.SMTPTransport.sendMessage(SMTPTransport.java:583)
at javax.mail.Transport.send0(Transport.java:169)
at javax.mail.Transport.send(Transport.java:98)
Here is my sample code,
Properties props = System.getProperties();
// Setup mail server
props.put("mail.smtp.host", "example.server.com");
props.put("mail.smtp.auth", "true");
props.put("mail.debug", "true");
props.put("mail.smtp.port", "25");
// Get session
//Session session = Session.getDefaultInstance(props, null);
Session session = Session.getDefaultInstance(props,
new javax.mail.Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("username", "password");
}
});
// Define message
MimeMessage message = new MimeMessage(session);
// Set the from address
message.setFrom(new InternetAddress(from));
// Set the to address
message.addRecipient(Message.RecipientType.TO, new InternetAddress(to));
// Set the subject
message.setSubject("Hello JavaMail");
// Set the content
message.setText("Welcome to JavaMail");
// Send message
Transport.send(message);
What piece of code is wrong?
As as username and password , I am using my company's email address and password.
The 5.7.1 is probably caused by exchange and not your code. You may just need to enable relaying on the server. Either for anonymous users or from a certain IP address. I'm not an expert on Exchange but I have got this working before. Here is the last solution I tested that works:
If a 5.7.1 error is encountered when trying to send an email via SMTP on an exchange server when the user has been authenticated..
For ref the issue you just had was caused by a setting on the Exchange 2007 server – this would not normally be a problem on 2003 server
Fixed by doing below...
You can set this authentication setting via the GUI
In Server configuration / Hub Transport / Default <ServerName>
Right click, properties, Permission Groups
Check "Anonymous users" and then click OK
Obviously anon users is not too secure but you could see if this solves the problem.
When I will use a MS Exhange SMTP Server to send an email, I use the above maven dependency.
<dependency>
<groupId>com.microsoft.ews-java-api</groupId>
<artifactId>ews-java-api</artifactId>
<version>2.0</version>
</dependency>
For that reason I created a class that represents an email client for MS Exchange Servers. I use log4j for logging.
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
Below the MS Exchange client class (I use the builder pattern for the construction of the object for thread safety),
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import microsoft.exchange.webservices.data.core.ExchangeService;
import microsoft.exchange.webservices.data.core.enumeration.misc.ExchangeVersion;
import microsoft.exchange.webservices.data.core.exception.service.local.ServiceLocalException;
import microsoft.exchange.webservices.data.core.service.item.EmailMessage;
import microsoft.exchange.webservices.data.credential.ExchangeCredentials;
import microsoft.exchange.webservices.data.credential.WebCredentials;
import microsoft.exchange.webservices.data.property.complex.MessageBody;
import org.apache.log4j.Logger;
/**
* A client to connect to a MS Exchange SMTP Server.
*/
public final class ExchangeClient {
private static final Logger LOGGER = Logger.getLogger(ExchangeClient.class);
private final String hostname;
private final ExchangeVersion exchangeVersion;
private final String domain;
private final String username;
private final String password;
private final String subject;
private final String recipientTo;
private final List<String> recipientCc;
private final List<String> recipientBcc;
private final List<String> attachments;
private final String message;
private ExchangeClient(ExchangeClientBuilder builder) {
this.hostname = builder.hostname;
this.exchangeVersion = builder.exchangeVersion;
this.domain = builder.domain;
this.username = builder.username;
this.password = builder.password;
this.subject = builder.subject;
this.recipientTo = builder.recipientTo;
this.recipientCc = builder.recipientCc;
this.recipientBcc = builder.recipientBcc;
this.attachments = builder.attachments;
this.message = builder.message;
}
public static class ExchangeClientBuilder {
private String hostname;
private ExchangeVersion exchangeVersion;
private String domain;
private String username;
private String password;
private String subject;
private String recipientTo;
private List<String> recipientCc;
private List<String> recipientBcc;
private List<String> attachments;
private String message;
public ExchangeClientBuilder() {
this.exchangeVersion = ExchangeVersion.Exchange2010_SP1;
this.hostname = "";
this.username = "";
this.password = "";
this.subject = "";
this.recipientTo = "";
this.recipientCc = new ArrayList<>(0);
this.recipientBcc = new ArrayList<>(0);
this.attachments = new ArrayList<>(0);
this.message = "";
}
/**
* The hostname of the Exchange Web Service. It will be used for
* connecting with URI https://hostname/ews/exchange.asmx
*
* #param hostname the hostname of the MS Exchange Smtp Server.
* #return the builder for chain usage.
*/
public ExchangeClientBuilder hostname(String hostname) {
this.hostname = hostname;
return this;
}
/**
* The Exchange Web Server version.
*
* #param exchangeVersion the Exchange Web Server version.
* #return the builder for chain usage.
*/
public ExchangeClientBuilder exchangeVersion(ExchangeVersion exchangeVersion) {
this.exchangeVersion = exchangeVersion;
return this;
}
/**
* The domain of the MS Exchange Smtp Server.
*
* #param domain the domain of the Active Directory. The first part of
* the username. For example: MYDOMAIN\\username, set the MYDOMAIN.
* #return the builder for chain usage.
*/
public ExchangeClientBuilder domain(String domain) {
this.domain = domain;
return this;
}
/**
* The username of the MS Exchange Smtp Server. The second part of the
* username. For example: MYDOMAIN\\username, set the username.
*
* #param username the username of the MS Exchange Smtp Server.
* #return the builder for chain usage.
*/
public ExchangeClientBuilder username(String username) {
this.username = username;
return this;
}
/**
* The password of the MS Exchange Smtp Server.
*
* #param password the password of the MS Exchange Smtp Server.
* #return the builder for chain usage.
*/
public ExchangeClientBuilder password(String password) {
this.password = password;
return this;
}
/**
* The subject for this send.
*
* #param subject the subject for this send.
* #return the builder for chain usage.
*/
public ExchangeClientBuilder subject(String subject) {
this.subject = subject;
return this;
}
/**
* The recipient for this send.
*
* #param recipientTo the recipient for this send.
* #return the builder for chain usage.
*/
public ExchangeClientBuilder recipientTo(String recipientTo) {
this.recipientTo = recipientTo;
return this;
}
/**
* You can specify one or more email address that will be used as cc
* recipients.
*
* #param recipientCc the first cc email address.
* #param recipientsCc the other cc email address for this send.
* #return the builder for chain usage.
*/
public ExchangeClientBuilder recipientCc(String recipientCc, String... recipientsCc) {
// Prepare the list.
List<String> recipients = new ArrayList<>(1 + recipientsCc.length);
recipients.add(recipientCc);
recipients.addAll(Arrays.asList(recipientsCc));
// Set the list.
this.recipientCc = recipients;
return this;
}
/**
* You can specify a list with email addresses that will be used as cc
* for this email send.
*
* #param recipientCc the list with email addresses that will be used as
* cc for this email send.
* #return the builder for chain usage.
*/
public ExchangeClientBuilder recipientCc(List<String> recipientCc) {
this.recipientCc = recipientCc;
return this;
}
/**
* You can specify one or more email address that will be used as bcc
* recipients.
*
* #param recipientBcc the first bcc email address.
* #param recipientsBcc the other bcc email address for this send.
* #return the builder for chain usage.
*/
public ExchangeClientBuilder recipientBcc(String recipientBcc, String... recipientsBcc) {
// Prepare the list.
List<String> recipients = new ArrayList<>(1 + recipientsBcc.length);
recipients.add(recipientBcc);
recipients.addAll(Arrays.asList(recipientsBcc));
// Set the list.
this.recipientBcc = recipients;
return this;
}
/**
* You can specify a list with email addresses that will be used as bcc
* for this email send.
*
* #param recipientBcc the list with email addresses that will be used
* as bcc for this email send.
* #return the builder for chain usage.
*/
public ExchangeClientBuilder recipientBcc(List<String> recipientBcc) {
this.recipientBcc = recipientBcc;
return this;
}
/**
* You can specify one or more email address that will be used as cc
* recipients.
*
* #param attachment the first attachment.
* #param attachments the other attachments for this send.
* #return the builder for chain usage.
*/
public ExchangeClientBuilder attachments(String attachment, String... attachments) {
// Prepare the list.
List<String> attachmentsToUse = new ArrayList<>(1 + attachments.length);
attachmentsToUse.add(attachment);
attachmentsToUse.addAll(Arrays.asList(attachments));
// Set the list.
this.attachments = attachmentsToUse;
return this;
}
/**
* You can specify a list with email attachments that will be used for
* this email send.
*
* #param attachments the list with email attachments that will be used
* for this email send.
* #return the builder for chain usage.
*/
public ExchangeClientBuilder attachments(List<String> attachments) {
this.attachments = attachments;
return this;
}
/**
* The body of the email message.
*
* #param message the body of the email message.
* #return the builder for chain usage.
*/
public ExchangeClientBuilder message(String message) {
this.message = message;
return this;
}
/**
* Build a mail.
*
* #return an EmailApacheUtils object.
*/
public ExchangeClient build() {
return new ExchangeClient(this);
}
}
public boolean sendExchange() {
// The Exchange Server Version.
ExchangeService exchangeService = new ExchangeService(exchangeVersion);
// Credentials to sign in the MS Exchange Server.
ExchangeCredentials exchangeCredentials = new WebCredentials(username, password, domain);
exchangeService.setCredentials(exchangeCredentials);
// URL of exchange web service for the mailbox.
try {
exchangeService.setUrl(new URI("https://" + hostname + "/ews/Exchange.asmx"));
} catch (URISyntaxException ex) {
LOGGER.error("An exception occured while creating the uri for exchange service.", ex);
return false;
}
// The email.
EmailMessage emailMessage;
try {
emailMessage = new EmailMessage(exchangeService);
emailMessage.setSubject(subject);
emailMessage.setBody(MessageBody.getMessageBodyFromText(message));
} catch (Exception ex) {
LOGGER.error("An exception occured while setting the email message.", ex);
return false;
}
// TO recipient.
try {
emailMessage.getToRecipients().add(recipientTo);
} catch (ServiceLocalException ex) {
LOGGER.error("An exception occured while sstting the TO recipient(" + recipientTo + ").", ex);
return false;
}
// CC recipient.
for (String recipient : recipientCc) {
try {
emailMessage.getCcRecipients().add(recipient);
} catch (ServiceLocalException ex) {
LOGGER.error("An exception occured while sstting the CC recipient(" + recipient + ").", ex);
return false;
}
}
// BCC recipient
for (String recipient : recipientBcc) {
try {
emailMessage.getBccRecipients().add(recipient);
} catch (ServiceLocalException ex) {
LOGGER.error("An exception occured while sstting the BCC recipient(" + recipient + ").", ex);
return false;
}
}
// Attachements.
for (String attachmentPath : attachments) {
try {
emailMessage.getAttachments().addFileAttachment(attachmentPath);
} catch (ServiceLocalException ex) {
LOGGER.error("An exception occured while setting the attachment.", ex);
return false;
}
}
try {
emailMessage.send();
LOGGER.debug("An email is send.");
} catch (Exception ex) {
LOGGER.error("An exception occured while sending an email.", ex);
return false;
}
return true;
}
}
A working example,
// import microsoft.exchange.webservices.data.core.enumeration.misc.ExchangeVersion;
ExchangeClient client = new ExchangeClient.ExchangeClientBuilder()
.hostname("webmail.domainOfWeb.com")
.exchangeVersion(ExchangeVersion.Exchange2010)
.domain("ActiveDirectoryDomain")
.username("ActiveDirectoryUsername")
.password("ActiveDirectoryPassword")
.recipientTo("recipient#whatever.com")
.recipientCc("recipient#whatever.com") // Ignore it in case you will not use Cc recipients.
.recipientBcc("recipient#whatever.com") // Ignore it in case you will not use Bcc recipients.
.attachments("/home/username/image.png") // Ignore it in case you will not use attachements.
.subject("Test Subject")
.message("Test Message")
.build();
client.sendExchange();
Mail.jar (Version 1.4.0) has a compatibility issue with MS Exchange Server and throws 530 5.7.1 Client was not authenticated, even when Username and password are configured.
Upgrading the mail API to 1.4.4 OR 1.4.7 should resolve the issue.
Mail API's 1.4.7 can be download from the following URL: http://www.oracle.com/technetwork/java/javamail/index.html
In some companies, the Exchange server SMTP support is disable and you cannot ask them to enable it. In these cases, a reasonable solution is this one:
http://davmail.sourceforge.net/
Simple Java Mail worked for me. The only thing you have to check is for correct hostname, username, port and password TransportStrategy.SMTP_TLS:
new Mailer(host, port, username, password, TransportStrategy.SMTP_TLS).sendMail(email);
I had to use javamail + exchange. The messages returned were helpless.
Thanks to the stack, I got some hints.
Add this to your code
props.put("mail.smtp.starttls.enable","true");
Think of adding the certificates of the machines used too.
To find them, just go to your browser, export them and import to the cacerts file in use.
Please use the following code parts instead of Transport.send(message);
MimeMessage message = new MimeMessage(session);
message.saveChanges();
Transport transport = session.getTransport("smtp");
transport.connect(host, "user", "pwd");
transport.sendMessage(message, message.getAllRecipients());
transport.close();
I have tested in the local and it is working
The ews-java-api package is at end of life.
From https://github.com/OfficeDev/ews-java-api
Starting July 19th 2018, Exchange Web Services (EWS) will no longer receive feature updates. While the service will continue to receive security updates and certain non-security updates, product design and features will remain unchanged. This change also applies to the EWS SDKs for Java and .NET. More information here: https://developer.microsoft.com/en-us/graph/blogs/upcoming-changes-to-exchange-web-services-ews-api-for-office-365/