Java mail API works on localhost bu not on google app engine - 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.

Related

Using OAuth SMTP to send mails without losing access to Google App Engine Mail

Google App Engine's Email service uses JavaMail JARs included in the AppEngine SDK. It specifically asks us not to use Oracle's JARs.
But in order to use Google OAuth 2.0 for sending emails using SMTP, we need Oracle's JavaMail JARs.
Any idea on how to achieve this? I'm stuck. Our software's internal features use AppEngine mails to send emails within our team, and our clients need Google OAuth SMTP to send mails on behalf of their email address.
After including Oracle's JARs, I get this exception while trying to use normal AppEngine Mail:
javax.mail.SendFailedException: Send failure (com.sun.mail.util.MailConnectException: Couldn't connect to host, port: localhost, 25; timeout -1 (java.net.SocketException: Permission denied: Attempt to access a blocked recipient without permission.))
at javax.mail.Transport.send(Transport.java:163)
at javax.mail.Transport.send(Transport.java:48)
at app.util.EmailUtil.sendMail_AppEngine(EmailUtil.java:948)
Update: Here is the code I use to send mails using OAuth
Properties props = new Properties();
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.starttls.required", "true");
props.put("mail.smtp.sasl.enable", "false");
Session session = Session.getInstance(props);
session.setDebug(false);
final URLName unusedUrlName = null;
SMTPTransport transport = new SMTPTransport(session, unusedUrlName);
transport.connect("smtp.gmail.com", 587, fromAddress, null);
byte[] response = String.format("user=%s\1auth=Bearer %s\1\1", lFrom, oauthToken).getBytes();
response = BASE64EncoderStream.encode(response);
transport.issueCommand("AUTH XOAUTH2 " + new String(response),
235);
MimeMessage msg = new MimeMessage( session );
msg.setFrom( new InternetAddress( fromAddress , fromName ) );
msg.setRecipients( Message.RecipientType.TO , InternetAddress.parse( toAddress , false ) );
msg.setSubject( emailSubject );
MimeBodyPart htmlPart = new MimeBodyPart();
Multipart multiPart = new MimeMultipart();
htmlPart.setContent( "<html>email content</html>" , "text/html; charset=UTF-8" );
multiPart.addBodyPart( htmlPart );
msg.setContent( multiPart );
msg.saveChanges();
transport.sendMessage(msg, msg.getAllRecipients());
I hope someone helps me out here.
This code works for me :
import java.security.Provider;
import java.security.Security;
import java.util.Properties;
import java.util.logging.Logger;
import javax.mail.Session;
import javax.mail.URLName;
import com.sun.mail.gimap.GmailSSLStore;
import com.sun.mail.smtp.SMTPTransport;
/**
* Performs OAuth2 authentication.
*
* <p>
* Before using this class, you must call {#code initialize} to install the
* OAuth2 SASL provider.
*/
public class XOAuth2Authenticator {
private static final Logger logger = Logger.getLogger(XOAuth2Authenticator.class.getName());
public static final class OAuth2Provider extends Provider {
private static final long serialVersionUID = 1L;
public OAuth2Provider() {
super("Google OAuth2 Provider", 1.0, "Provides the XOAUTH2 SASL Mechanism");
put("SaslClientFactory.XOAUTH2", "be.vsko.davidgadget.server.gmail.OAuth2SaslClientFactory");
}
}
/**
* Installs the OAuth2 SASL provider. This must be called exactly once
* before calling other methods on this class.
*/
public static void initialize() {
Security.addProvider(new OAuth2Provider());
}
/**
* Connects and authenticates to an IMAP server with OAuth2. You must have
* called {#code initialize}.
*
* #param host
* Hostname of the imap server, for example
* {#code imap.googlemail.com}.
* #param port
* Port of the imap server, for example 993.
* #param userEmail
* Email address of the user to authenticate, for example
* {#code oauth#gmail.com}.
* #param oauthToken
* The user's OAuth token.
* #param debug
* Whether to enable debug logging on the IMAP connection.
*
* #return An authenticated GmailSSLStore that can be used for IMAP operations.
*/
public static GmailSSLStore connectToImap(String host, int port, String userEmail, String oauthToken, boolean debug)
throws Exception {
logger.info("connecting to IMAP for user " + userEmail);
Properties props = new Properties();
// props.put("mail.imaps.sasl.enable", "true");
// props.put("mail.imaps.sasl.mechanisms", "XOAUTH2");
props.put("mail.gimaps.sasl.enable", "true");
props.put("mail.gimaps.sasl.mechanisms", "XOAUTH2");
props.put(OAuth2SaslClientFactory.OAUTH_TOKEN_PROP, oauthToken);
props.put("mail.store.protocol", "gimaps");
Session session = Session.getInstance(props);
props = session.getProperties();
// props.put("mail.imaps.sasl.enable", "true");
// props.put("mail.imaps.sasl.mechanisms", "XOAUTH2");
props.put("mail.gimaps.sasl.enable", "true");
props.put("mail.gimaps.sasl.mechanisms", "XOAUTH2");
props.put(OAuth2SaslClientFactory.OAUTH_TOKEN_PROP, oauthToken);
props.put("mail.store.protocol", "gimaps");
session.setDebug(debug);
final URLName unusedUrlName = null;
final String emptyPassword = "";
GmailSSLStore gmailStore = new GmailSSLStore(session, unusedUrlName);
gmailStore.connect(host, port, userEmail, emptyPassword);
logger.info("SUCCESS connecting to IMAP for user " + userEmail);
return gmailStore;
// IMAPSSLStore store = new IMAPSSLStore(session, unusedUrlName);
// store.connect(host, port, userEmail, emptyPassword);
// return store;
}
/**
* Connects and authenticates to an SMTP server with OAuth2. You must have
* called {#code initialize}.
*
* #param host
* Hostname of the smtp server, for example
* {#code smtp.googlemail.com}.
* #param port
* Port of the smtp server, for example 587.
* #param userEmail
* Email address of the user to authenticate, for example
* {#code oauth#gmail.com}.
* #param oauthToken
* The user's OAuth token.
* #param debug
* Whether to enable debug logging on the connection.
*
* #return An authenticated SMTPTransport that can be used for SMTP
* operations.
*/
public static SMTPTransport connectToSmtp(Session session, String host, int port, String userEmail) throws Exception {
logger.info("connecting to SMTP for user " + userEmail);
final URLName unusedUrlName = null;
SMTPTransport transport = new SMTPTransport(session, unusedUrlName);
// If the password is non-null, SMTP tries to do AUTH LOGIN.
final String emptyPassword = "";
transport.connect(host, port, userEmail, emptyPassword);
logger.info("SUCCESS connecting to SMTP for user " + userEmail);
return transport;
}
public static Session getSmtpSession(String oauthToken, boolean debug) {
Properties props = new Properties();
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.starttls.required", "true");
props.put("mail.smtp.sasl.enable", "true");
props.put("mail.smtp.sasl.mechanisms", "XOAUTH2");
props.put(OAuth2SaslClientFactory.OAUTH_TOKEN_PROP, oauthToken);
Session session = Session.getInstance(props);
props = session.getProperties();
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.starttls.required", "true");
props.put("mail.smtp.sasl.enable", "true");
props.put("mail.smtp.sasl.mechanisms", "XOAUTH2");
props.put(OAuth2SaslClientFactory.OAUTH_TOKEN_PROP, oauthToken);
session.setDebug(debug);
return session;
}
}
With this in place, you can :
XOAuth2Authenticator.initialize();
Session smtpSession = XOAuth2Authenticator.getSmtpSession(oauthToken, true);
SMTPTransport transport = XOAuth2Authenticator.connectToSmtp(smtpSession, "smtp.gmail.com", 587, email);
and now use transport as usual.
PS: The easier way round IMO, is to use the new GMAIL api.

How do I make a call to the Yahoo hourly weather forecast API?

I have found yahoo weather forcast most helpful.
I'm able to get an hourly weather request here from Yahoo.
How can I make an API request for the above hourly weather report using an Yahoo API call to http://weather.yahooapis.com/forecastrss?w=2502265?
This is the documentation I found.
Yahoo Weather Api does not seem to support hourly forecasts, there are just a few parameters you have control over like the location in (woeid) or (lat, long) and the temperature-unit (u or f), refer here for yahoo query language.
You can use other api's AccuWeather for hourly details.
You can do using the REST API's of the programming language you want to use.. I will give Java example. (Similar thing applies to other languages too.. )'
package tests;
import org.apache.http.*;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
/**
* A simple Java REST GET example using the Apache HTTP library.
* This executes a call against the Yahoo Weather API service, which is
* actually an RSS service (http://developer.yahoo.com/weather/).
*
* Try this Twitter API URL for another example (it returns JSON results):
* http://search.twitter.com/search.json?q=%40apple
* (see this url for more twitter info: https://dev.twitter.com/docs/using-search)
*
* Apache HttpClient: http://hc.apache.org/httpclient-3.x/
*
*/
public class ApacheHttpRestClient1 {
public static void main(String[] args) {
DefaultHttpClient httpclient = new DefaultHttpClient();
try {
// specify the host, protocol, and port
HttpHost target = new HttpHost("weather.yahooapis.com", 80, "http");
// specify the get request
HttpGet getRequest = new HttpGet("/forecastrss?p=80020&u=f");
System.out.println("executing request to " + target);
HttpResponse httpResponse = httpclient.execute(target, getRequest);
HttpEntity entity = httpResponse.getEntity();
System.out.println("----------------------------------------");
System.out.println(httpResponse.getStatusLine());
Header[] headers = httpResponse.getAllHeaders();
for (int i = 0; i < headers.length; i++) {
System.out.println(headers[i]);
}
System.out.println("----------------------------------------");
if (entity != null) {
System.out.println(EntityUtils.toString(entity));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// When HttpClient instance is no longer needed,
// shut down the connection manager to ensure
// immediate deallocation of all system resources
httpclient.getConnectionManager().shutdown();
}
}
There are more ways to do the same... you can find many other alternate ways at http://alvinalexander.com/java/java-apache-httpclient-restful-client-examples

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

Sending email using Javamail

I'm trying to send a mail using the GoDaddy email host I have registered couple of days ago using Java mail api, however it turns out that its not that easy to implement,
and I am getting, this error:
Could not connect to SMTP host: smtpout.asia.secureserver.net, port: 80, response: -1
I have tried ports 3535, 465, 587, 25 but still get the same error. The same code below has been tested to work with sending out email using Gmail, with the addition of this code (which I have omitted in this case):
props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
MailSender.java:
public class MailSender {
private static String HOST = "smtpout.asia.secureserver.net";
private static String PORT = "80";
public static void sendMail(final Mail mail) throws MailException {
EmailValidator validtor = new EmailValidator();
if (validtor.validate(mail.getReceipient())) {
Properties props = new Properties();
props.put("mail.smtp.host", HOST);
props.put("mail.smtp.socketFactory.port", PORT);
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.port", PORT);
Session session = Session.getDefaultInstance(props,
new javax.mail.Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(mail.getUsername(),mail.getPassword());
}
});
try {
Message message = new MimeMessage(session);
message.setFrom(new InternetAddress(mail.getSender()));
message.setRecipients(Message.RecipientType.TO,
InternetAddress.parse(mail.getReceipient()));
message.setSubject(mail.getSubject());
message.setText(mail.getBody());
Transport.send(message);
System.out.println("OK");
} catch (MessagingException e) {
throw new MailException(e.getMessage());
}
} else {
throw new MailException("Email address not valid.");
}
}
}
The Mail parameter in this class holds all other mail information, the username/password, sender and recipient email address string, which is tested to work with email clients like Outlook & Thunderbird.
Port 80 is used for HTTP.
Change it to 465 or 587.
(Consult the GoDaddy documentation for the correct port)
Apparently, the problem was not with Java mail api, but was with GoDaddy server, I have consulted their tech support and work fine now.

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