4

I am trying to get "html": "... entire contents of HTML page goes here ..." in Java using Unirest HTTP communication with MailGun API.

So far I have verified my domain, successfully sent an email via the API, and successfully sent an HTML (basic) email via the API in Java. I am having trouble sending emails (via the API, in Java), whenever I am trying to use any email templates. I was trying to use the suggested email templates present here: https://github.com/mailgun/transactional-email-templates/tree/master/templates .

My code for sending the email is in line with the documentation

public static JsonNode sendSimpleMessage() throws UnirestException {
        HttpResponse<com.mashape.unirest.http.JsonNode> request = Unirest.post("https://api.mailgun.net/v3/sub.domain.com" + "/messages")
                .basicAuth("api",  API_KEY)
                .queryString("from", "name <test@domain.com>")
                .queryString("to", "myemail@gmail.com")
                .queryString("subject", "Great")
                .queryString("html", returnEmail())
                .asJson();

        return request.getBody();
    }

public static String returnEmail() {
        try {
            return StreamUtils.copyToString(new ClassPathResource("email.html").getInputStream(), Charset.defaultCharset());
        } catch (IOException e) {
            e.printStackTrace();
        }

        return "<html><strong>not today</strong></html>";
    }

And email.html contains an example from the emails mentioned above (https://github.com/mailgun/transactional-email-templates/tree/master/templates) or any HTML emails, as none work.

The error that I am get is:

com.mashape.unirest.http.exceptions.UnirestException: java.lang.RuntimeException: java.lang.RuntimeException: org.json.JSONException: A JSONArray text must start with '[' at 1 [character 2 line 1].

Stacktrace:

com.mashape.unirest.http.exceptions.UnirestException: java.lang.RuntimeException: java.lang.RuntimeException: org.json.JSONException: A JSONArray text must start with '[' at 1 [character 2 line 1]
at com.mashape.unirest.http.HttpClientHelper.request(HttpClientHelper.java:143)
at com.mashape.unirest.request.BaseRequest.asJson(BaseRequest.java:68)
at com..application.ApplicationController.sendSimpleMessage(ApplicationController.java:78)
at com.application.ApplicationController.create(ApplicationController.java:61)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:133)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:661)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:105)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:478)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:80)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:799)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1455)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.RuntimeException: java.lang.RuntimeException: org.json.JSONException: A JSONArray text must start with '[' at 1 [character 2 line 1]
    at com.mashape.unirest.http.HttpResponse.<init>(HttpResponse.java:106)
    at com.mashape.unirest.http.HttpClientHelper.request(HttpClientHelper.java:139)
    ... 57 more
Caused by: java.lang.RuntimeException: org.json.JSONException: A JSONArray text must start with '[' at 1 [character 2 line 1]
    at com.mashape.unirest.http.JsonNode.<init>(JsonNode.java:51)
    at com.mashape.unirest.http.HttpResponse.<init>(HttpResponse.java:95)
    ... 58 more
Caused by: org.json.JSONException: A JSONArray text must start with '[' at 1 [character 2 line 1]
    at org.json.JSONTokener.syntaxError(JSONTokener.java:433)
    at org.json.JSONArray.<init>(JSONArray.java:105)
    at org.json.JSONArray.<init>(JSONArray.java:144)
    at com.mashape.unirest.http.JsonNode.<init>(JsonNode.java:48)
    ... 59 more

I also get the same error if I simply paste all the HTML as a string instead of returnEmail(). I also get the same error message if I escape the special characters and/or minify the HTML before sending.

Now I assume that as Unirest is an http library it builds a JSON from the queryString keys and values. While it appears to work with simpel html like "HTML version whatever , it certainly does not like any lengthy/'normal' html, such as an email template.

Can you please suggest any working examples for html emails using the Java API or any hint on how to send the html and have a valid json?

Thanks a ton

flashjpr
  • 470
  • 6
  • 12
  • Do you have any way of inspecting the network request? Maybe the outgoing request is valid and you are expecting (but not receiving) a JSON response? – Matt Clark Jun 27 '18 at 01:32
  • Yep, ignore the response bit for now and ensure that the sending bit is working – Scary Wombat Jun 27 '18 at 01:38
  • the problem is isolated to the content of HERE (String) `.queryString("html", HERE)`, as if the value is `whatever`, it is being sent and received as html in any mail client. The question is, still, how do you populate HERE with an html page? Just tried `StringEscapeUtils.escapeHtml4(returnEmail()))` and still have the exact same issue – flashjpr Jun 27 '18 at 10:05
  • @MG supp: Unfortunately, our documentation doesn't have a Java example in the docs that assigns a file to the HTML variable. However, there are examples of this on GitHub such as the below: - https://github.com/ezshipp-code/ezshipp-api/blob/343922e84c5dee67eb1d92a4726a8ced1a6c09cf/src/main/java/com/ezshipp/api/service/MailGunEmailService.java - https://github.com/Szelemetko/mail-service-demo/blob/b464f932df43c19ddf041de33806336590ecc3b6/src/main/java/pl/szelemekto/emailservicedemo/connector/MailgunConnector.java - https://github.com/search?l=Java&q=mailgun+unirest+java+message&type=Code – flashjpr Jun 27 '18 at 10:12
  • 2
    If you are trying to use mailgun, I would suggest you to look at [this project on GitHub](https://github.com/sargue/mailgun) I used this project before, and it worked fine for me for sending email templates. – Raghuram Kasyap Jun 27 '18 at 14:43
  • @RaghuramKasyap tried it, doesn't work with complex html as mentioned [here](https://github.com/sargue/mailgun#advanced-content-using-content-helpers) – flashjpr Jun 27 '18 at 15:41
  • 1
    For complex html, I used this: `response= Mail.using(configuration) .from(fromName, fromEmail) .to("To Address") .subject("some subject") .html("Content String With Html Tags") .build() .send();` – Raghuram Kasyap Jun 27 '18 at 15:46

3 Answers3

8

From the error message reported:

org.json.JSONException: A JSONArray text must start with '[' at 1 [character 2 line 1].

I'm not sure what Unirest is trying to do but it seems to be trying to encode something to JSON. But the Mailgun API doesn't require JSON encoding, you are fine using good old application/x-www-form-urlencoded encoding. Check with Unirest documentation to see if you can force other encoding types.

Alternatively you can use this other library (disclaimer: I am the developer) as noted in the comments of the question. The OP complains that doesn't work with complex html as mentioned here. What I meant there is the content helper mechanism where you build the HTML content using chained method calls (DSL style). If you already have the HTML you just need to pass it to the html() method call as noted also on the comments.

Your code would look a bit like this:

// somewhere else
Configuration configuration = new Configuration()
    .domain("sub.domain.com")
    .apiKey(API_KEY)
    .from("name", "test@domain.com");

public static void sendSimpleMessage() {
    Mail.using(configuration)
        .to("myemail@gmail.com")
        .subject("Great")
        .html(returnEmail())
        .build()
        .send();
}
sargue
  • 5,695
  • 3
  • 28
  • 43
  • I'm giving it a try as we speak and got this error: javax.net.ssl.SSLHandshakeException: sun.security.…find valid certification path to requested target . What certificate am I missing? Mailgun? Or my domain's? – flashjpr Jul 05 '18 at 06:33
  • 2
    That could be related to this: https://github.com/sargue/mailgun#important-note-january-2018 – sargue Jul 05 '18 at 06:52
  • This is indeed the correct answer! Thank you so much for your work and effort! – flashjpr Jul 05 '18 at 16:05
  • If anyone is having an issue getting the code to work, order to use @sargue 's library, he mentions you need Jersey Client (https://mvnrepository.com/artifact/org.glassfish.jersey.core/jersey-client) but you also need the Injection manager (https://mvnrepository.com/artifact/org.glassfish.jersey.inject/jersey-hk2) as well. So make sure to install both before attempting to run it or exceptions will ensue. – PGMacDesign Aug 14 '18 at 21:40
  • @Silmarilos that's only if you are using Jersey version >= 2.26 which has endured several changes (https://jersey.github.io/release-notes/2.26.html). The library has a dependency on 2.25.1 so that might arise only if you explicit the Jersey dependency. – sargue Aug 15 '18 at 07:19
1

I was getting the same error trying to post a large HTML document via Unirest.post(...).queryString("html", massiveStringHere)..asJson();

and found that "com.mashape.unirest.http.exceptions.UnirestException: java.lang.RuntimeException: java.lang.RuntimeException: org.json.JSONException: A JSONArray text must start with '[' at 1 [character 2 line 1]"

Actually results from trying to parse the response into JSON. Change the asJson() to asString() and you'll see the actual error Mailgun is giving you:

<html>
<head><title>414 Request-URI Too Large</title></head>
<body bgcolor="white">
<center><h1>414 Request-URI Too Large</h1></center>
<hr><center>nginx</center>
</body>
</html>

This is because Unirest has you adding the html body as a queryString, which exceeds the allowable query string length configured in Nginx. I tried changing the Content to form url-encoded and using .field("html", massiveStringHere), but this didn't work as I had expected. Why all fields of a POST must be specified in the URL is a design flaw IMO. I've not found a workaround yet, but will try the suggestions given here.. hoping not to resort to going the MIME route :-/

UPDATE: Confirmed https://github.com/sargue/mailgun is the way to go for Mailgun integration and does not suffer from HTML size limitations that apparently plague Unirest. Easily integrated in a few mins.

0

From the html files you shared, use the html from within body, remove all other tags, it should work fine post that.

Eg. from file - https://github.com/mailgun/transactional-email-templates/blob/master/templates/alert.html

Use only the below mentioned html.

<table class="body-wrap">
    <tr>
        <td></td>
        <td class="container" width="600">
            <div class="content">
                <table class="main" width="100%" cellpadding="0" cellspacing="0">
                    <tr>
                        <td class="alert alert-warning">
                            Warning: You're approaching your limit. Please upgrade.
                        </td>
                    </tr>
                    <tr>
                        <td class="content-wrap">
                            <table width="100%" cellpadding="0" cellspacing="0">
                                <tr>
                                    <td class="content-block">
                                        You have <strong>1 free report</strong> remaining.
                                    </td>
                                </tr>
                                <tr>
                                    <td class="content-block">
                                        Add your credit card now to upgrade your account to a premium plan to ensure you don't miss out on any reports.
                                    </td>
                                </tr>
                                <tr>
                                    <td class="content-block">
                                        <a href="http://www.mailgun.com" class="btn-primary">Upgrade my account</a>
                                    </td>
                                </tr>
                                <tr>
                                    <td class="content-block">
                                        Thanks for choosing Acme Inc.
                                    </td>
                                </tr>
                            </table>
                        </td>
                    </tr>
                </table>
                <div class="footer">
                    <table width="100%">
                        <tr>
                            <td class="aligncenter content-block"><a href="http://www.mailgun.com">Unsubscribe</a> from these alerts.</td>
                        </tr>
                    </table>
                </div></div>
        </td>
        <td></td>
    </tr>
</table>

Update - You can use all html tags in the email, just don't include any external relative css or javascript link or script tags. Ideally your entire html including css should be in the single HTML string.

mdeora
  • 4,152
  • 2
  • 19
  • 29
  • and how would that email message be styled at all? – flashjpr Jun 27 '18 at 15:58
  • you would have to give inline css or add – mdeora Jun 27 '18 at 15:59
  • anyway, that does not work. as mentioned in the original answer, the problem lies down to: how do you send an HTML content as a string JSON value. Cutting from HTML is not a solution, as those tags are important and target specific email clients – flashjpr Jun 27 '18 at 16:03
  • As per my understanding all email clients doesn't fetch external css and than apply, it needs to be sent along in the mail body in – mdeora Jun 27 '18 at 16:07
  • From the issue it looks like you need to just escape double quotes in the html. – mdeora Jun 27 '18 at 16:16