MsEwsTokenProvider.class
package com.maildata;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.time.Duration;
import java.util.concurrent.*;
import javax.enterprise.concurrent.ManagedExecutorService;
import microsoft.exchange.webservices.data.misc.ITraceListener;
import org.apache.log4j.Logger;
import com.microsoft.aad.adal4j.AuthenticationCallback;
import com.microsoft.aad.adal4j.AuthenticationContext;
import com.microsoft.aad.adal4j.AuthenticationResult;
import com.microsoft.aad.adal4j.ClientCredential;
import microsoft.exchange.webservices.data.core.ExchangeService;
import microsoft.exchange.webservices.data.core.WebProxy;
import microsoft.exchange.webservices.data.core.enumeration.misc.ConnectingIdType;
import microsoft.exchange.webservices.data.core.enumeration.misc.ExchangeVersion;
import microsoft.exchange.webservices.data.misc.ImpersonatedUserId;
/**
* Used to obtain an access token for use in an EWS application. Caches the
* token and refreshes it 5mins prior to expiration.
*
* @author Stephen O'Hair
*
*/
public final class MsEwsTokenProvider {
private static final Logger log = Logger.getLogger(MsEwsTokenProvider.class);
private static final String EWS_URL = "https://outlook.office365.com/EWS/Exchange.asmx";
private static final String RESOUCE = "https://outlook.office365.com";
private static final String TENANT_NAME = "lihr.online";
private static final String AUTHORITY = "https://login.microsoftonline.com/" + TENANT_NAME;
private static final long REFRESH_BEFORE_EXPIRY_MS = Duration.ofMinutes(5).toMillis();
private static long expiryTimeMs;
private static String accessToken;
/**
* Takes an OAuth2 token and configures an {@link ExchangeService}.
*
* @param token
* @param senderAddr
* @param traceListener
* @return a configured and authenticated {@link ExchangeService}
* @throws URISyntaxException
* @throws Exception
*/
public static ExchangeService getAuthenticatedService(String token, String senderAddr,
ITraceListener traceListener) throws URISyntaxException, Exception {
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2010_SP2);
service.setTraceListener(traceListener);
service.getHttpHeaders().put("Authorization", "Bearer " + token);
service.getHttpHeaders().put("X-AnchorMailbox", senderAddr);
//service.setWebProxy(new WebProxy(proxyHost, proxyPort));
service.setUrl(new URI(EWS_URL));
service.setImpersonatedUserId(new ImpersonatedUserId(ConnectingIdType.PrincipalName, senderAddr));
return service;
}
/**
* Simple way to get an access token using the Azure Active Directory Library.
*
* Authenticates at : https://login.microsoftonline.com/
*
* @param clientId
* - client id of the AzureAD application
* @param clientSecret
* - client secret of the AzureAD application
* @param service
* - managed executor service
*
* @return provisioned access token
* @throws MalformedURLException
* @throws InterruptedException
* @throws ExecutionException
* @throws TimeoutException
*/
public static synchronized String getAccesToken(String clientId, String clientSecret, ExecutorService service)
throws MalformedURLException, InterruptedException, ExecutionException, TimeoutException {
long now = System.currentTimeMillis();
if (accessToken == null || now - expiryTimeMs > REFRESH_BEFORE_EXPIRY_MS) {
AuthenticationContext context = new AuthenticationContext(AUTHORITY, false, service);
AuthenticationCallback<AuthenticationResult> callback = new AuthenticationCallback<AuthenticationResult>() {
@Override
public void onSuccess(AuthenticationResult result) {
log.info("received token");
}
@Override
public void onFailure(Throwable exc) {
throw new RuntimeException(exc);
}
};
log.info("requesting token");
Future<AuthenticationResult> future = context.acquireToken(RESOUCE,
new ClientCredential(clientId, clientSecret), callback);
// wait for access token
AuthenticationResult result = future.get(30, TimeUnit.SECONDS);
// cache token and expiration
accessToken = result.getAccessToken();
expiryTimeMs = result.getExpiresAfter();
System.out.println("expiryTimeMs: " + expiryTimeMs);
}
return accessToken;
}
}
SendMail.class
package com.maildata;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import com.microsoft.aad.adal4j.AuthenticationCallback;
import com.microsoft.aad.adal4j.AuthenticationContext;
import com.microsoft.aad.adal4j.AuthenticationResult;
import com.microsoft.aad.adal4j.ClientCredential;
import microsoft.exchange.webservices.data.core.ExchangeService;
import microsoft.exchange.webservices.data.core.WebProxy;
import microsoft.exchange.webservices.data.core.enumeration.misc.ConnectingIdType;
import microsoft.exchange.webservices.data.core.enumeration.misc.ExchangeVersion;
import microsoft.exchange.webservices.data.core.enumeration.property.WellKnownFolderName;
import microsoft.exchange.webservices.data.core.exception.service.local.ServiceLocalException;
import microsoft.exchange.webservices.data.core.service.item.EmailMessage;
import microsoft.exchange.webservices.data.core.service.item.Item;
import microsoft.exchange.webservices.data.misc.ITraceListener;
import microsoft.exchange.webservices.data.misc.ImpersonatedUserId;
import microsoft.exchange.webservices.data.property.complex.EmailAddress;
import microsoft.exchange.webservices.data.property.complex.FolderId;
import microsoft.exchange.webservices.data.property.complex.Mailbox;
import microsoft.exchange.webservices.data.property.complex.MessageBody;
import microsoft.exchange.webservices.data.search.FindItemsResults;
import microsoft.exchange.webservices.data.search.ItemView;
import javax.enterprise.concurrent.ManagedExecutorService;
import static com.maildata.MsEwsTokenProvider.getAuthenticatedService;
public class SendMail {
public static ExecutorService service;
/**
* Entry point.
*
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
// Pro tip: make sure to set your proxy configuration here if needed
// and exclude outlook.office365.com from proxy SSL inspection.
String clientId = "dd32d56f-5ce3-4bca-99ea-23d66af6bdfc";
String clientSecret = "OCp8Q~fpLHJi~DGFhh~7XDrR56HoL5LGsCcbOaAF";
String tenantName = "btcm.group";
String recipientAddr = "recipient@yourdomain.com";
String senderAddress = "wangyt@lihr.online";
ITraceListener traceListener = new ITraceListener() {
@Override
public void trace(String traceType, String traceMessage) {
// TODO log it, do whatever...
}
};
service = Executors.newFixedThreadPool(1);
// I used a ManagedExecutorService provided by glassfish but you can
// use an ExecutorService and manage it yourself.
String token = MsEwsTokenProvider.getAccesToken(clientId, clientSecret, service);
// don't log this in production!
System.out.println("token=" + token);
// test mailbox read access
System.out.println("geting emails");
try (ExchangeService service = getAuthenticatedService(token, senderAddress, traceListener)) {
listInboxMessages(service, senderAddress);
}
// send a message
System.out.println("sending a message");
try (ExchangeService service = getAuthenticatedService(token, senderAddress, traceListener)) {
sendTestMessage(service, recipientAddr, senderAddress);
}
System.out.println("finished");
}
public static void sendTestMessage(ExchangeService service, String recipientAddr, String senderAddr)
throws Exception {
EmailMessage msg = new EmailMessage(service);
msg.setSubject("Hello world!");
msg.setBody(MessageBody.getMessageBodyFromText("Sent using the EWS Java API."));
msg.getToRecipients().add(recipientAddr);
msg.send();
msg.setSender(new EmailAddress(senderAddr));
}
public static void listInboxMessages(ExchangeService service, String mailboxAddr) throws Exception {
System.out.println(mailboxAddr);
service.setImpersonatedUserId(new ImpersonatedUserId(ConnectingIdType.PrincipalName, mailboxAddr));
ItemView view = new ItemView(50);
Mailbox mb = new Mailbox(mailboxAddr);
FolderId folder = new FolderId(WellKnownFolderName.SentItems, mb);
FindItemsResults<Item> result = service.findItems(folder, view);
result.forEach(i -> {
try {
System.out.println("subject=" + i.getSubject());
} catch (ServiceLocalException e) {
e.printStackTrace();
}
});
}
}