Getting exception while sending email with attachment using AWS SES java SDK - jakarta-mail

I am trying to send email with an excel attachment using AWS SES java SDK. I am following the code template provided by AWS.
https://docs.aws.amazon.com/ses/latest/DeveloperGuide/examples-send-raw-using-sdk.html
But I am getting javax.mail.internet.ParseException. Any idea what's going on here ?
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Properties;
//JavaMail libraries. Download the JavaMail API
//from https://javaee.github.io/javamail/
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.util.ByteArrayDataSource;
import org.apache.log4j.Logger;
//AWS SDK libraries. Download the AWS SDK for Java
//from https://aws.amazon.com/sdk-for-java
import com.amazonaws.regions.Regions;
import com.amazonaws.services.simpleemail.AmazonSimpleEmailService;
import com.amazonaws.services.simpleemail.AmazonSimpleEmailServiceClientBuilder;
import com.amazonaws.services.simpleemail.model.RawMessage;
import com.amazonaws.services.simpleemail.model.SendRawEmailRequest;
public class AmazonSESSample {
private final Logger logger = Logger.getLogger(getClass());
// Replace sender#example.com with your "From" address.
// This address must be verified with Amazon SES.
private static String SENDER = "US CWB INTDEV <uscwbintdev#gmail.com>";
// Replace recipient#example.com with a "To" address. If your account
// is still in the sandbox, this address must be verified.
private static String RECIPIENT = "bhapanda#acme.com";
// Specify a configuration set. If you do not want to use a configuration
// set, comment the following variable, and the
// ConfigurationSetName=CONFIGURATION_SET argument below.
private static String CONFIGURATION_SET = "ConfigSet";
// The subject line for the email.
private static String SUBJECT = "Weekly users and projects report";
// The email body for recipients with non-HTML email clients.
private static String BODY_TEXT = "Hello,\r\n" + "Please see the attached file for a list "
+ "of customers to contact.";
// The HTML body of the email.
private static String BODY_HTML = "<html>" + "<head></head>" + "<body>" + "<h1>Hello!</h1>"
+ "<p>Please see the attached file for a " + "list of customers to contact.</p>" + "</body>" + "</html>";
public void send(byte[] attachment) throws AddressException, MessagingException, IOException {
Session session = Session.getDefaultInstance(new Properties());
// Create a new MimeMessage object.
MimeMessage message = new MimeMessage(session);
// Add subject, from and to lines.
message.setSubject(SUBJECT, "UTF-8");
message.setFrom(new InternetAddress(SENDER));
message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(RECIPIENT));
// Create a multipart/alternative child container.
MimeMultipart msg_body = new MimeMultipart("alternative");
// Create a wrapper for the HTML and text parts.
MimeBodyPart wrap = new MimeBodyPart();
// Define the text part.
MimeBodyPart textPart = new MimeBodyPart();
textPart.setContent(BODY_TEXT, "text/plain; charset=UTF-8");
// Define the HTML part.
MimeBodyPart htmlPart = new MimeBodyPart();
htmlPart.setContent(BODY_HTML, "text/html; charset=UTF-8");
// Add the text and HTML parts to the child container.
msg_body.addBodyPart(textPart);
msg_body.addBodyPart(htmlPart);
// Add the child container to the wrapper object.
wrap.setContent(msg_body);
// Create a multipart/mixed parent container.
MimeMultipart msg = new MimeMultipart("mixed");
// Add the parent container to the message.
message.setContent(msg);
// Add the multipart/alternative part to the message.
msg.addBodyPart(wrap);
// Define the attachment
MimeBodyPart att = new MimeBodyPart();
DataSource fds = new ByteArrayDataSource(attachment, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
att.setDataHandler(new DataHandler(fds));
att.setFileName(fds.getName());
// Add the attachment to the message.
msg.addBodyPart(att);
// Try to send the email.
try {
System.out.println("Attempting to send an email through Amazon SES " + "using the AWS SDK for Java...");
// Instantiate an Amazon SES client, which will make the service
// call with the supplied AWS credentials.
AmazonSimpleEmailService client = AmazonSimpleEmailServiceClientBuilder.standard()
// Replace US_WEST_2 with the AWS Region you're using for
// Amazon SES.
.withRegion(Regions.US_WEST_2).build();
// Print the raw email content on the console
// Send the email.
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
message.writeTo(outputStream);
RawMessage rawMessage = new RawMessage(ByteBuffer.wrap(outputStream.toByteArray()));
SendRawEmailRequest rawEmailRequest = new SendRawEmailRequest(rawMessage)
.withConfigurationSetName(CONFIGURATION_SET);
client.sendRawEmail(rawEmailRequest);
System.out.println("Email sent!");
// Display an error if something goes wrong.
} catch (Exception ex) {
System.out.println("Email Failed");
System.err.println("Error message: " + ex.getMessage());
ex.printStackTrace();
}
}
}
Here is the stack trace.
2019-01-10 09:57:52 <2a7abff3-14be-11e9-9b09-dbd922fa0afd> DEBUG AmazonWebServiceClient:79 - Internal logging successfully configured to commons logger: true
Email Failed
Error message: Expected parameter value, got "null"
javax.mail.internet.ParseException: Expected parameter value, got "null"
at javax.mail.internet.ParameterList.<init>(ParameterList.java:169)
at javax.mail.internet.ContentDisposition.<init>(ContentDisposition.java:87)
at javax.mail.internet.MimeBodyPart.updateHeaders(MimeBodyPart.java:1307)
at javax.mail.internet.MimeBodyPart.updateHeaders(MimeBodyPart.java:1001)
at javax.mail.internet.MimeMultipart.updateHeaders(MimeMultipart.java:333)
at javax.mail.internet.MimeBodyPart.updateHeaders(MimeBodyPart.java:1255)
at javax.mail.internet.MimeMessage.updateHeaders(MimeMessage.java:2012)
at javax.mail.internet.MimeMessage.saveChanges(MimeMessage.java:1980)
at javax.mail.internet.MimeMessage.writeTo(MimeMessage.java:1680)
at javax.mail.internet.MimeMessage.writeTo(MimeMessage.java:1659)
at com.acme.workbench.project.list.email.service.AmazonSESSample.send(AmazonSESSample.java:131)
at com.acme.workbench.project.list.email.service.ProjectListEmailService.execute(ProjectListEmailService.java:60)
at com.acme.lambda.handler.LambdaFunctionForScheduledEvent.execute(LambdaFunctionForScheduledEvent.java:68)
at com.acme.lambda.handler.LambdaFunctionForScheduledEvent.handleRequest(LambdaFunctionForScheduledEvent.java:55)
at com.acme.lambda.handler.LambdaFunctionForScheduledEvent.handleRequest(LambdaFunctionForScheduledEvent.java:13)
at lambdainternal.EventHandlerLoader$PojoHandlerAsStreamHandler.handleRequest(EventHandlerLoader.java:178)
at lambdainternal.EventHandlerLoader$2.call(EventHandlerLoader.java:888)
at lambdainternal.AWSLambda.startRuntime(AWSLambda.java:293)
at lambdainternal.AWSLambda.<clinit>(AWSLambda.java:64)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:348)
at lambdainternal.LambdaRTEntry.main(LambdaRTEntry.java:104)

You copied code that set the attachment from a file and changed it to set the attachment from a byte array, but the ByteArrayDataSource has no name so when you set the file name for the attachment you set it to null, which is what causes the problem. Change the call to att.setFileName to set a non-null file name.

Related

Unexpected Character(m~) In $SkipToken

I am using ms graph api for java. At the beginning of the skipToken i have received inside #odata.nextLink, there is an unexpected character (m~) before actual skip token string (Can be seen below). Skip token string works fine after i get rid of m~.
But i am confused why this has happened and can other unexpected characters effect skipToken in the future? And what can i do to prevent that?
I am using msgraph java sdk version 2.4.0.
https://graph.microsoft.com/v1.0/users?$select=givenName%2csurname%2cuserPrincipalName%2cbusinessPhones%2cassignedPlans&$count=true&$orderby=displayName&$filter=&$top=2&$skiptoken=m~X%270100B7013B3B33303030343530303330303033323030333030303435303033313030343130303330303034353030333230303331303033303030343530303334303033383030333030303435303033323030333130303330303033373030333030303332303033303030343530303431303033323030333030303435303033303030333230303330303034353030333730303330303033303030343530303330303034313030333030303435303033323030333130303B313B303B%27
I'm not clear how did you get the m~ in skiptoken, but I can get the pages of users success by microsoft graph api sdk for java with below code:
package com.graph;
import java.util.List;
import com.azure.identity.ClientSecretCredential;
import com.azure.identity.ClientSecretCredentialBuilder;
import com.microsoft.graph.authentication.TokenCredentialAuthProvider;
import com.microsoft.graph.models.User;
import com.microsoft.graph.requests.GraphServiceClient;
import com.microsoft.graph.requests.UserCollectionPage;
public class Testgraph {
public static void main(String[] args) {
final ClientSecretCredential clientSecretCredential = new ClientSecretCredentialBuilder()
.clientId("clientId")
.clientSecret("clientSecret")
.tenantId("tenantId")
.build();
final TokenCredentialAuthProvider tokenCredentialAuthProvider = new TokenCredentialAuthProvider(clientSecretCredential);
final GraphServiceClient graphClient = GraphServiceClient
.builder()
.authenticationProvider(tokenCredentialAuthProvider)
.buildClient();
//You can use the code below to get current page users
UserCollectionPage users = graphClient.users()
.buildRequest()
.get();
List<User> userList=users.getCurrentPage();
for (User user:userList) {
System.out.println(user.displayName);
}
//If you want to get nextpage, you can use below code
UserCollectionPage users1 = users.getNextPage().buildRequest().get();
List<User> userList1=users1.getCurrentPage();
for (User user:userList1) {
System.out.println(user.displayName);
}
}
}

Java mail API works on localhost bu not on google app engine

I have a sending email logic using Java Mail API and it works fine when I am on localhost but once deployed on google cloud platform, the email goes to my servlet but never gets delivered.
I bought the email from GoDaddy so it's: xxx#mydomain.com.
After reading docs on google cloud platform and some comments here on StackOverflow, I have configured firewall rules but nothing seems to work to allow ingress and egress on port 25, 465 and 587 (I know google doesn't allow traffic on port 25).
I don't wanna use 3rd party email senders like sendbird... because I was using elastic before and I didn't need a 3rd party email sender, JavaMail was enough.
So I think Java mail should be enough for GCP.
Can anyone help me out?
Here is my sending email logic
import java.io.UnsupportedEncodingException;
import java.util.Properties;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
/**
*
* #author sidibe ibrahim
* Sending email logic
*/
public class EmailSender {
static MessagingException me;
public static boolean sendMail(String from, String password, String message, String to[], String title) throws UnsupportedEncodingException {
String host = "smtpout.secureserver.net";
Properties props = System.getProperties();
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.host", host);
props.put("mail.smtp.user", from);
props.put("mail.smtp.password", password);
props.put("mail.smtp.host", 465);
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.ssl.trust", "*");
Session session = Session.getDefaultInstance(props, null);
MimeMessage mimeMessage = new MimeMessage(session);
try {
mimeMessage.setFrom(new InternetAddress(from, "xxx"));
InternetAddress[] toAddress = new InternetAddress[to.length];
for (int i = 0; i < to.length; i++) {
toAddress[i] = new InternetAddress(to[i]);
}
for (int i = 0; i < toAddress.length; i++) {
mimeMessage.addRecipient(Message.RecipientType.TO, toAddress[i]);
}
//sdd subject
mimeMessage.setSubject(title);
//set message to mimeMessage
mimeMessage.setText(message, "UTF-8", "html");
Transport transport = session.getTransport("smtp");
transport.connect(host, from, password);
transport.sendMessage(mimeMessage, mimeMessage.getAllRecipients());
transport.close();
return true;
} catch (MessagingException m) {
me.printStackTrace();
}
return false;
}
}
The App Engine Mail API (which also supports JavaMail) has already been deprecated.
Instead, GCP recommends to use a third-party mail provider such as:
SendGrid
Mailgun
Mailjet
EDIT
If you would still like to continue with the outdated solution, however, see the article on sending emails with the Mail API.

Send a mail with attachment in java

I want to send mail with attachment in java with the following criteria -
The file which I want to attach is a downloadable URL
(like : http://berkeleycollege.edu/browser_check/samples/excel.xls)
Code is deployed on appengine, so Write to File, Create File, Save File is prohibited by google.
I don't even have the permission to download the file first and attach it as an attachment.
I have tried InputStream input = new URL(url).openStream(); to read the file content pass that to a datahandler. Where url is mentioned above. Sample Code:
Message msg = new MimeMessage(session);
Multipart multiPart = new MimeMultipart();
MimeBodyPart attachment = new MimeBodyPart();
DataHandler handler;
InputStream input = new URL(url).openStream();
handler = new DataHandler(new ByteArrayDataSource(input,"xls");
attachment.setDataHandler(handler);
multiPart.addBodyPart(attachment);
msg.setContent(multiPart);
Transport.send(msg);
Above code is giving Invalid Content error.
Is there anyway, I can read the content of that downloadable link and that content can be send as attachment ?
Please share the code snippet for the reference.
Map model = new HashMap();
String text = null;
Properties properties_mail = new Properties();
InputStream iStream_mail = null;
String propFileName_mail = "properties/mail.properties";
InputStream stream_mail = getClass().getClassLoader().getResourceAsStream(propFileName_mail);
properties_mail.load(stream_mail);
MimeMessage message = this.javamailsenderImpl.createMimeMessage();
MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(message, MimeMessageHelper.MULTIPART_MODE_RELATED, "UTF-8");
mimeMessageHelper.setFrom(properties_mail.getProperty("javaMailSender.username"));
mimeMessageHelper.setTo(user.getUserEmail());
mimeMessageHelper.setSubject("Bpa Qa Product - New Organisation User");
/*String mailBody = "Welcome New User!!! <br /> Your Login Id is : "+user.getUserEmail()+"Your Password is:"+user.getUserPassword();
mimeMessageHelper.setText(mailBody, true);*/
model.put("firstName", user.getFirstName());
model.put("userEmail", user.getUserEmail());
model.put("userPassword", user.getUserPassword());
model.put("organizationName", user.getOrganization().getOrganizationName());
model.put("heading", "Thank You for Registering with us .!! Here is your Login credentials.");
text = VelocityEngineUtils.mergeTemplateIntoString(velocityEngine, "UserDetails.vm", "UTF-8", model);
mimeMessageHelper.setText(new String(text.getBytes(), "UTF-8"), true);
this.javamailsenderImpl.send(message);

email address on GAE

I have an application on google app engine like abc.appspot.com can I have an email address to send/receive emails like admin#abc.appspot.com kindly help me.
Edit
here is my SendMail class
public class SendMail {
private static String fromAddress = "abc#gmail.com";
private static Logger log = Logger.getLogger(SendMail.class.getCanonicalName());
// Send the Mail
public void send(String toAddress, String subject, String msgBody)
throws IOException {
Properties props = new Properties();
Session session = Session.getDefaultInstance(props, null);
try {
Message msg = new MimeMessage(session);
msg.setFrom(new InternetAddress(fromAddress));
InternetAddress to = new InternetAddress(toAddress);
msg.addRecipient(Message.RecipientType.TO, to);
msg.setSubject(subject);
msg.setText(msgBody);
Transport.send(msg, new InternetAddress[] { to });
} catch (AddressException addressException) {
log.log(Level.SEVERE, "Address Exception , mail could not be sent", addressException);
} catch (MessagingException messageException) {
log.log(Level.SEVERE, "Messaging Exception , mail could not be sent", messageException);
}
}
}
So it sends an email regarding abc#gmail.com but I want that it should send from email#abc.appspot.com.
You can only receive emails in the form of #abc.appspotmail.com. AFAIK there is no way to have #abc.appspot.com as receiving address.
If you wan to receive emails from your custom domain, e.g. #abc.com, than the only way is to have external email service forward emails to your #abc.appspotmail.com. Most domain registrars offer free limited email service with forwarding (we use GoDaddy and get limited forwarding free).
Yes you can: https://developers.google.com/appengine/docs/java/mail/usingjavamail#Senders_and_Recipients

Testing server deployed on Google App Engine

I want to test a server I have deployed on GAE to see if a connection can be made via a HTTP POST request. The end client will run on Android but for now I would like to run a simple test on my laptop.
I send different "action" params as part of the request to the server and based on the action it will look for and handle other params to complete the request. Below is an example of how a command is handled. One param is the action and the other a username. It will in the end return a JSON object with the groups this user is a member of but for now I want to just get the test string "Just a test" back to see everything is working.
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException {
.
.
.
.
/*
* GET GROUPS
*
* #param action == getGroups
* #param user == the username of the user making the request
*/
else if(request.getParameter("action").equals("getGroups")) {
/* Query for the User by username */
User user = queryUser(request.getParameter("user"), pm);
/* Generate the list of groups this user belongs to */
ArrayList<Group> groups = null;
if(user != null) {
groups = new ArrayList<Group>(user.groups().size());
for(Group group : user.groups())
groups.add(group);
}
/* Send response back to the client */
response.setContentType("text/plain");
response.getWriter().write("Just a test");
}
A side question, do I send HTTP POST requests to http://myapp.appspot.com/myapplink
or just to http://myapp.appspot.com/?
I have low experience writing client-server code so I was looking for help and examples of a simple POST request using supplied params and then reading the response back (with in my example the test string) and display it to the terminal.
Here is a sample of a test I was running:
public static void main(String[] args) throws IOException {
String urlParameters = "action=getGroups&username=homer.simpson";
String request = "http://myapp.appspot.com/myapplink";
URL url = new URL(request);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setInstanceFollowRedirects(false);
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
connection.setRequestProperty("charset", "utf-8");
connection.setRequestProperty("Content-Length", "" + Integer.toString(urlParameters.getBytes().length));
connection.setUseCaches (false);
DataOutputStream wr = new DataOutputStream(connection.getOutputStream ());
wr.writeBytes(urlParameters);
wr.flush();
wr.close();
if ( connection.getResponseCode() == HttpURLConnection.HTTP_OK ){
System.out.println("Posted ok!");
System.out.println("Res" + connection.getResponseMessage()); //OK read
System.out.println("Size: "+connection.getContentLength()); // is 0
System.out.println("RespCode: "+connection.getResponseCode()); // is 200
System.out.println("RespMsg: "+connection.getResponseMessage()); // is 'OK'
}
else {
System.out.println("Bad post...");
}
}
When executing however, I get that it's a "bad post"
Usually you will want to send it to a particular link, so you have a way of separating the different servlet classes. Assuming that the doPost() method is inside MyAppLinkServlet class in the package myapp, you will need a web.xml file like the one below to describe how you will respond to the link. BTW, the code is only slightly modified from the GAE/J example at http://code.google.com/appengine/docs/java/gettingstarted/creating.html
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app xmlns="http://java.sun.com/xml/ns/javaee" version="2.5">
<servlet>
<servlet-name>myapplink</servlet-name>
<servlet-class>myapp.MyAppLinkServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>myapplink</servlet-name>
<url-pattern>/myapplink</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>
On the server, try adding the line
response.setStatus(200);
(which effectively sets the status as "OK").
On the client side, try something simple to start, such as:
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
public class TestRequest {
public static void main(String[] args) throws IOException {
String urlParameters = "action=getGroups&username=homer.simpson";
String request = "http://myapp.appspot.com/myapplink";
URL postUrl = new URL (request+"?"+urlParameters);
System.out.println(readFromUrl(postUrl));
}
private static String readFromUrl (URL url) throws IOException {
FetchOptions opt = FetchOptions.Builder.doNotValidateCertificate(); //depending on how did you install GAE, you might not need this anymore
HTTPRequest request = new HTTPRequest (url, HTTPMethod.POST, opt);
URLFetchService service = URLFetchServiceFactory.getURLFetchService();
HTTPResponse response = service.fetch(request);
if (response.getResponseCode() == HttpURLConnection.HTTP_OK) {
byte[] content = response.getContent();
return new String(content);
} else {
return null;
}
}
}
Good luck!

Resources