07. HelloMailGAE - AntonioDiaz/examples.spring.mvc GitHub Wiki

Send mail from GAE

Proof of concept of send mail from GAE from a Spring MVC project using 2 methods: JavaMail and SendGrid.


Front-end view


Project structure


Java API code

...
import javax.mail.BodyPart;
import javax.mail.Message;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
...
@RequestMapping(value = "/sendMail", method = RequestMethod.GET)
public ModelAndView sendMailPage(
		@RequestParam(value = "from") String sendFrom, 
		@RequestParam(value = "to") String sendTo) {
	logger.info("sendMailPage");
	ModelAndView modelAndView = new ModelAndView("mail_form");
	Properties properties = new Properties();
	Session session = Session.getDefaultInstance(properties, null);
	try {
		Message msg = new MimeMessage(session);
		msg.setFrom(new InternetAddress(sendFrom, "Example.com Admin"));
		msg.addRecipient(Message.RecipientType.TO, new InternetAddress(sendTo, "Mr. User"));
		msg.setSubject("mail sent on " + new Date());

		BodyPart messageBodyPart = new MimeBodyPart();
		messageBodyPart.setText("This is message body<br>otra lineallll");
		Multipart multipart = new MimeMultipart();
		multipart.addBodyPart(messageBodyPart);

		MimeBodyPart attachment = new MimeBodyPart();
		InputStream attachmentDataStream = new ByteArrayInputStream("toma toma toma".getBytes());
		attachment.setFileName("manual.pdf");
		attachment.setContent(attachmentDataStream, "application/pdf");
		multipart.addBodyPart(attachment);
		msg.setContent(multipart);
		Transport.send(msg);
		modelAndView.addObject("result", "ok");
	} catch (Exception ex) {
		logger.log(Level.SEVERE, ex.toString(), ex);
	}
	return modelAndView;
}
...

SendGrid code

  • Controller.java
@Autowired Sendgrid sendGrid
...
@RequestMapping(value="/sendMailGrid", method=RequestMethod.POST)
public ModelAndView sendMail( 
		@RequestParam(value = "from")String from, 
		@RequestParam(value = "to")String to, 
		@RequestParam(value = "text")String text) {
	ModelAndView modelAndView = new ModelAndView("index");
	// set email data
	sendGrid.setTo(to);
	sendGrid.setFrom(from);
	sendGrid.setSubject("Subject goes here");
	sendGrid.setText("Hello World!");
	sendGrid.setHtml(text);
	// send your message
	try {
		sendGrid.send();
		modelAndView.addObject("result", "ok");
	} catch (JSONException e) {
		// TODO Auto-generated catch block
		modelAndView.addObject("result", "fail");
		e.printStackTrace();
	}
    return modelAndView;
} 
  • Sendgrid.java
package com;

import java.net.HttpURLConnection;
import java.util.*;
import java.io.IOException;
import java.util.Iterator;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.google.appengine.labs.repackaged.org.json.JSONException;
import com.google.appengine.labs.repackaged.org.json.JSONObject;
import com.google.appengine.labs.repackaged.org.json.JSONArray;

@Service
public class Sendgrid {
    
	@Value("${user_mail}")
	protected String username;

	@Value("${password_mail}")
	protected String password;
    
	private String from;
	private String from_name;
	private String reply_to;
	private String subject;
	private String text;
	private String html;
	
    protected Boolean use_headers = true;
    private String serverResponse = "";
    private ArrayList<String> to_list  = new ArrayList<String>();
    private ArrayList<String> to_name_list  = new ArrayList<String>();
    private ArrayList<String> bcc_list = new ArrayList<String>();
    private JSONObject header_list = new JSONObject();

    protected String domain = "https://sendgrid.com/";
    
    protected String endpoint= "api/mail.send.json";
    

    public Sendgrid(){
        try {
            this.setCategory("google_sendgrid_java_lib");
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }
    
    public Sendgrid(String username, String password) {
        this.username = username;
        this.password = password;
        try {
            this.setCategory("google_sendgrid_java_lib");
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }

    /**
     * getTos - Return the list of recipients
     *
     * @return  List of recipients
     */
    public ArrayList<String> getTos() {
        return this.to_list;
    }

    /**
     * setTo - Initialize a single email for the recipient 'to' field
     * Destroy previous recipient 'to' data.
     *
     * @param    email   A list of email addresses
     * @return           The SendGrid object.
     */
    public Sendgrid setTo(String email) {
        this.to_list = new ArrayList<String>();
        this.addTo(email);

        return this;
    }

    /**
     * addTo - Append an email address to the existing list of addresses
     * Preserve previous recipient 'to' data.
     *
     * @param    email   Recipient email address
     * @param    name    Recipient name
     * @return           The SendGrid object.
     */
    public Sendgrid addTo(String email, String name) {
        if (this._useHeaders() == true){
            String toAddress = (name.length() > 0) ? name + "<" + email + ">" : email;
            this.to_list.add(toAddress);
        } else {
            if (name.length() > 0){
              this._addToName(name);
            } else {
                this._addToName("");
            }
            this.to_list.add(email);
        }

        return this;
    }

    /**
     * addTo - Make the second parameter("name") of "addTo" method optional
     *
     * @param   email   A single email address 
     * @return          The SendGrid object.  
     */
    public Sendgrid addTo(String email) {
        return addTo(email, "");
    }

    /**
     * getTos - Return the list of names for recipients
     *
     * @return  List of names
     */
    public ArrayList<String> getToNames() {
        return this.to_name_list;
    }

    /**
     * getFrom - Get the from email address
     *
     * @return  The from email address
     */
    public String getFrom() {
        return this.from;
    }

    /**
     * setFrom - Set the from email
     *
     * @param    email   An email address
     * @return           The SendGrid object.
     */
    public Sendgrid setFrom(String email) {
        this.from = email;

        return this;
    }

    /**
     * getFromName - Get the from name
     *
     * @return  The from name
     */
    public String getFromName() {
        return this.from_name;
    }

    /**
     * setFromName - Set the from name
     *
     * @param    name    The name
     * @return           The SendGrid object.
     */
    public Sendgrid setFromName(String name) {
        this.from_name = name;

        return this;
    }

    /**
     * getReplyTo - Get reply to address
     *
     * @return the reply to address
     */
    public String getReplyTo() {
        return this.reply_to;
    }

    /**
     * setReplyTo - set the reply-to address
     *
     * @param  email   the email to reply to
     * @return         the SendGrid object.
     */
    public Sendgrid setReplyTo(String email) {
      this.reply_to = email;

      return this;
    }

    /**
     * getBccs - return the list of Blind Carbon Copy recipients
     *
     * @return ArrayList - the list of Blind Carbon Copy recipients
     */
    public ArrayList<String> getBccs() {
        return this.bcc_list;
    }

    /**
     * setBcc - Initialize the list of Carbon Copy recipients
     * destroy previous recipient Blind Carbon Copy data
     *
     * @param  email   an email address
     * @return         the SendGrid object.
     * @throws JSONException
     */
    public Sendgrid setBcc(String email) throws JSONException {
        this.bcc_list = new ArrayList<String>();
        this.bcc_list.add(email);
        if (this._useHeaders() == true)
        {
            this.addFilterSetting("bcc", "enable", "1");
            this.addFilterSetting("bcc", "email", email);
        }

        return this;
    }

    /** 
     * getSubject - Get the email subject
     * 
     * @return  The email subject
     */
    public String getSubject() {
        return this.subject;
    }

    /** 
     * setSubject - Set the email subject
     * 
     * @param    subject   The email subject
     * @return             The SendGrid object
     */
    public Sendgrid setSubject(String subject) {
        this.subject = subject;
      
        return this;
    }

    /** 
     * getText - Get the plain text part of the email
     * 
     * @return   the plain text part of the email
     */
    public String getText() {
        return this.text;
    }

    /** 
     * setText - Set the plain text part of the email
     * 
     * @param   text   The plain text of the email
     * @return         The SendGrid object.
     */
    public Sendgrid setText(String text) {
        this.text = text;

        return this;
    }
    
    /** 
     * getHtml - Get the HTML part of the email
     * 
     * @return   The HTML part of the email.
     */
    public String getHtml() {
        return this.html;
    }

    /** 
     * setHTML - Set the HTML part of the email
     * 
     * @param   html   The HTML part of the email
     * @return         The SendGrid object.
     */
    public Sendgrid setHtml(String html) {
        this.html = html;

        return this;
    }

    /**
     * setCategories - Set the list of category headers
     * destroys previous category header data
     *
     * @param  category_list   the list of category values
     * @return                 the SendGrid object.
     * @throws JSONException
     */
    public Sendgrid setCategories(String[] category_list) throws JSONException {
        JSONArray categories_json = new JSONArray(category_list);
        this.header_list.put("category", categories_json);
        this.addCategory("google_sendgrid_java_lib");

        return this;
    }

    /**
     * setCategory - Clears the category list and adds the given category
     *
     * @param  category   the new category to append
     * @return            the SendGrid object.
     * @throws JSONException
     */
    public Sendgrid setCategory(String category) throws JSONException {
        JSONArray json_category = new JSONArray(new String[]{category});
        this.header_list.put("category", json_category);
        this.addCategory("google_sendgrid_java_lib");

        return this;
    }

    /**
     * addCategory - Append a category to the list of categories
     *
     * @param  category   the new category to append
     * @return            the SendGrid object.
     * @throws JSONException
     */
    public Sendgrid addCategory(String category) throws JSONException {
        if (true == this.header_list.has("category")) {
            ((JSONArray) this.header_list.get("category")).put(category);
        } else {
            this.setCategory(category);
        }

        return this;
    }

    /**
     * setSubstitutions - Substitute a value for list of values, where each value corresponds
     * to the list emails in a one to one relationship. (IE, value[0] = email[0],
     * value[1] = email[1])
     *
     * @param  key_value_pairs   key/value pairs where the value is an array of values
     * @return                   the SendGrid object.
     */
    public Sendgrid setSubstitutions(JSONObject key_value_pairs) {
        try {
            this.header_list.put("sub", key_value_pairs);
        } catch (JSONException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return this;
    }

    /**
     * addSubstitution - Substitute a value for list of values, where each value corresponds
     * to the list emails in a one to one relationship. (IE, value[0] = email[0],
     * value[1] = email[1])
     *
     * @param  from_key    the value to be replaced
     * @param  to_values   an array of values to replace the from_value
     * @return             the SendGrid object.
     * @throws JSONException
     * @throws IOException
     * @throws JsonMappingException
     * @throws JsonParseException
     */
    public Sendgrid addSubstitution(String from_value, String[] to_values) throws JSONException {
      if (false == this.header_list.has("sub")) {
        this.header_list.put("sub", new JSONObject());
      }
      JSONArray json_values = new JSONArray(to_values);
      ((JSONObject) this.header_list.get("sub")).put(from_value, json_values);

      return this;
    }

    /**
     * setSection - Set a list of section values
     *
     * @param  key_value_pairs   key/value pairs
     * @return                   the SendGrid object.
     * @throws JSONException
     */
    public Sendgrid setSections(JSONObject key_value_pairs) throws JSONException {
        this.header_list.put("section", key_value_pairs);

        return this;
    }

    /**
     * addSection - append a section value to the list of section values
     *
     * @param  from_value  the value to be replaced
     * @param  to_value    the value to replace
     * @return             the SendGrid object.
     * @throws JSONException
     */
    public Sendgrid addSection(String from_value, String to_value) throws JSONException {
        if (false == this.header_list.has("section")) {
          this.header_list.put("section", new JSONObject() );
        }
        ((JSONObject) this.header_list.get("section")).put(from_value, to_value);

        return this;
    }

    /**
     * setUniqueArguments - Set a list of unique arguments, to be used for tracking purposes
     *
     * @param key_value_pairs - list of unique arguments
     */
    public Sendgrid setUniqueArguments(JSONObject key_value_pairs) {
        try {
            this.header_list.put("unique_args", key_value_pairs);
        } catch (JSONException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return this;
    }

    /**
     * addUniqueArgument - Set a key/value pair of unique arguments, to be used for tracking purposes
     *
     * @param key     the key
     * @param value   the value
     */
    public Sendgrid addUniqueArgument(String key, String value) {
        if (false == this.header_list.has("unique_args")) {
          try {
              this.header_list.put("unique_args", new JSONObject());
          } catch (JSONException e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
          }
        }
        try {
            ((JSONObject) this.header_list.get("unique_args")).put(key, value);
        } catch (JSONException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return this;
    }

    /**
     * setFilterSettings - Set filter/app settings
     *
     * @param filter_settings - JSONObject of fiter settings
     */
    public Sendgrid setFilterSettings(JSONObject filter_settings) {
        try {
            this.header_list.put("filters", filter_settings);
        } catch (JSONException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return this;
    }

    /** 
     * addFilterSetting - Append a filter setting to the list of filter settings
     *
     * @param  filter_name       filter name
     * @param  parameter_name    parameter name
     * @param  parameter_value   setting value
     * @throws JSONException
     */
    public Sendgrid addFilterSetting(String filter_name, String parameter_name, String parameter_value) throws JSONException {
        if (false == this.header_list.has("filters")) {
            this.header_list.put("filters", new JSONObject());
        }
        if (false == ((JSONObject) this.header_list.get("filters")).has(filter_name)) {
            ((JSONObject) this.header_list.get("filters")).put(filter_name, new JSONObject());
        }
        if (false == ((JSONObject) ((JSONObject) this.header_list.get("filters")).get(filter_name)).has("settings")) {
            ((JSONObject) ((JSONObject) this.header_list.get("filters")).get(filter_name)).put("settings", new JSONObject());
        }
        ((JSONObject) ((JSONObject) ((JSONObject) this.header_list.get("filters")).get(filter_name)).get("settings"))
            .put(parameter_name, parameter_value);

        return this;
    }

    /**
     * getHeaders - return the list of headers
     *
     * @return JSONObject with headers
     */
    public JSONObject getHeaders() {
        return this.header_list;
    }

    /**
     * setHeaders - Sets the list headers
     * destroys previous header data
     *
     * @param  key_value_pairs   the list of header data
     * @return                   the SendGrid object.
     */
    public Sendgrid setHeaders(JSONObject key_value_pairs) {
        this.header_list = key_value_pairs;

        return this;
    }

    /**
     * getServerResponse - Get the server response message
     *
     * @return  The server response message
     */
    public String getServerResponse() {
        return this.serverResponse;
    }

    /**
     * _arrayToUrlPart - Converts an ArrayList to a url friendly string
     *
     * @param  array   the array to convert
     * @param  token   the name of parameter
     * @return         a url part that can be concatenated to a url request
     */
    protected String _arrayToUrlPart(ArrayList<String> array, String token) {
        String string = "";
        for(int i = 0;i < array.size();i++)
        {
            try {
                string += "&" + token + "[]=" + URLEncoder.encode(array.get(i), "UTF-8");
            } catch (UnsupportedEncodingException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        return string;
    }

    /**
     * _prepMessageData - Takes the mail message and returns a url friendly querystring
     *
     * @return the data query string to be posted
     * @throws JSONException 
     */
    protected Map<String, String> _prepMessageData() throws JSONException {
        Map<String,String> params = new HashMap<String, String>();

        params.put("api_user", this.username);
        params.put("api_key", this.password);
        params.put("subject", this.getSubject());
        if(this.getHtml() != null) {
            params.put("html", this.getHtml());
        }
        if(this.getFromName() != null) {
            params.put("fromname", this.getFromName());
        }
        params.put("text",this.getText());
        params.put("from", this.getFrom());

        if (this.getReplyTo() != null) {
            params.put("replyto", this.getReplyTo());
        }

        if (this._useHeaders() == true) {
            JSONObject headers = this.getHeaders();
            params.put("to", this.getFrom());
            JSONArray tos_json = new JSONArray(this.getTos());
            headers.put("to", tos_json);
            this.setHeaders(headers);
            params.put("x-smtpapi", _escapeUnicode(this.getHeaders().toString()));
        } else {
            params.put("to", this.getTos().toString());
            if (this.getToNames().size() > 0) {
                params.put("toname", this.getToNames().toString());
            }
        }

        return params;
    }

    /**
     * Invoked when a warning is returned from the server that
     * isn't critical
     */
    public static interface WarningListener {
        public void warning(String serverResponse, Throwable t);
    }

    /**
     * send - Send an email
     *
     * @throws JSONException
     */
    public void send() throws JSONException {
        send(new WarningListener() {
            public void warning(String w, Throwable t) {
                serverResponse = w;
            }
        });
    }

    /**
     * send - Send an email
     *
     * @param w callback that will receive warnings
     * @throws JSONException
     */
    public void send(WarningListener w) throws JSONException {
        Map<String,String> data = new HashMap<String, String>();

        data = this._prepMessageData();
        StringBuffer requestParams = new StringBuffer();
        Iterator<String> paramIterator = data.keySet().iterator();
        while (paramIterator.hasNext()) {
            final String key = paramIterator.next();
            final String value = data.get(key);
            if (key.equals("to") && this.getTos().size() > 0) {
                if (this._useHeaders() == true){
                    try {
                        requestParams.append("to=" + URLEncoder.encode(value, "UTF-8") + "&");
                    } catch (UnsupportedEncodingException e) {
                        e.printStackTrace();
                    }
                } else{
                    requestParams.append(this._arrayToUrlPart(this.getTos(), "to")+"&");
                }
            } else {
                if (key.equals("toname") && this.getToNames().size() > 0) {
                    requestParams.append(this._arrayToUrlPart(this.getToNames(), "toname").substring(1)+"&");
                } else {
                    try {
                        requestParams.append(URLEncoder.encode(key, "UTF-8"));
                    } catch (UnsupportedEncodingException e) {
                        w.warning("Unsupported Encoding Exception", e);
                    }
                    requestParams.append("=");
                    try {
                        requestParams.append(URLEncoder.encode(value, "UTF-8"));
                    } catch (UnsupportedEncodingException e) {
                        w.warning("Unsupported Encoding Exception", e);
                    }
                    requestParams.append("&");
                }
            }
        }
        String request = this.domain + this.endpoint;

        if (this.getBccs().size() > 0){
            request += "?" +this._arrayToUrlPart(this.getBccs(), "bcc").substring(1);
        }
        try {
            URL url = new URL(request);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setDoOutput(true);
            connection.setRequestMethod("POST");

            OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream());
            writer.write(requestParams.toString());
            // Get the response
            writer.flush();

            BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            String line, response = "";

            while ((line = reader.readLine()) != null) {
                // Process line...
                response += line;
            }
            reader.close();
            writer.close();

            if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
                // OK
                serverResponse = "success";
            } else {
                // Server returned HTTP error code.
                JSONObject apiResponse = new JSONObject(response);
                JSONArray errorsObj = (JSONArray) apiResponse.get("errors");
                for (int i = 0; i < errorsObj.length(); i++) {
                    if (i != 0) {
                        serverResponse += ", ";
                    }
                    serverResponse += errorsObj.get(i);
                }
                w.warning(serverResponse, null);
            }
        } catch (MalformedURLException e) {
            w.warning("Malformed URL Exception", e);
        } catch (IOException e) {
            w.warning("IO Exception", e);
        }
    }

    /**
     * _addToName - Append an recipient name to the existing list of names
     *
     * @param    email   Recipient email address
     * @param    name    Recipient name
     * @return           The SendGrid object.
     */
    private Sendgrid _addToName(String name) {
        this.to_name_list.add(name);

        return this;
    }

    /**
     * useHeaders - Checks to see whether or not we can or should you headers. In most cases,
     * we prefer to send our recipients through the headers, but in some cases,
     * we actually don't want to. However, there are certain circumstances in
     * which we have to.
     */
    private Boolean _useHeaders() {
        if ((this._preferNotToUseHeaders() == true) && (this._isHeadersRequired() == false)) {
            return false;
        } else {
            return true;
        }
    }

    /**
     * _preferNotToUseHeaders - There are certain cases in which headers are not a preferred choice
     * to send email, as it limits some basic email functionality. Here, we
     * check for any of those rules, and add them in to decide whether or
     * not to use headers
     *
     * @return if true we don't
     */
    private Boolean _preferNotToUseHeaders() {
        if (this.getBccs().size() == 0)
        {
            return true;
        }
        if (this.use_headers != null && this.use_headers == false)
        {
            return true;
        }

        return false;
    }

    /**
     * isHeaderRequired - determines whether or not we need to force recipients through the smtpapi headers
     *
     * @return if true headers are required
     */
    private Boolean _isHeadersRequired() {
        if (this.use_headers == true)
        {
            return true;
        }

        return false;
    }
    
    private String _escapeUnicode(String input) {
        StringBuilder sb = new StringBuilder();
        int len = input.length();
        for (int i = 0; i < len; i++) {
          int code = Character.codePointAt(input, i);
          if (code > 127) {
            sb.append(String.format("\\u%x", code));
          } else {
            sb.append(String.format("%c", code));
          }
        }
        return sb.toString();
      }
}
⚠️ **GitHub.com Fallback** ⚠️