- Products
- Solutions Use casesBy industry
- Developers
- Resources Connect
- Pricing
Webhooks is ideal in situations where you need to get notified of certain events like an email being opened, a contact getting updated or a calendar event getting deleted. While you need an external server to host your webhook-enabled application, thanks to the Nylas Java SDK, you can easily test webhooks locally on your machine.
As we know, we can use a web server to test our webhooks as we demonstrated using Ruby, Python, and PHP , we can test webhooks locally by using the Nylas CLI.
In Episode 26 of Coding with Nylas, Exploring Nylas’ webhook utilities we saw a preview of how to use the NodeJS SDK to test webhooks locally.
This of course brings us to our main point of showing how to use another one of our SDKs to test webhooks locally, the Java SDK.
Well, anything that we can test on our local machine is better, because if anything goes wrong, we can always quickly fix it. Having to test on a web server makes it more complex as we need sometimes to repeat a long workflow to submit all the needed changes.
By testing locally, we can make sure everything is working properly before we move it to an external web server.
We’re going to create a SparkJava application, which is a web application but also we’re going to use Twilio to be able to send ourselves an SMS message when a certain event happens, like when someone opens a link that we sent.
Let’s zoom in to showcase that we’re getting information on who’s sending the email, who is receiving the email, and what type or kind of Webhook we’re getting.
When the recipient opens the link that we send inside an email, we will get notified via SMS:
Let’s recap the information we’re getting:
If you already have the Nylas Java SDK installed and your Java environment is configured, then continue along with the blog.
Otherwise, I would recommend that you read the post How to Send Emails with the Nylas Java SDK where the basic setup is clearly explained.
In order to get SMS delivered to our cell phones, we need to create a Twilio account:
After that, you will need to create a Twilio Phone number from where the SMS will be sent:
Don’t forget to copy the Account SID, Auth Token and Twilio phone number and store them in your .env file:
Our Webhooks application is going to deal with two different kinds of emails, one tracked and the other untracked. The first one needs to be sent using some extra commands, so we’re going to create a small program for that, the second one can be sent like any regular email.
Let’s create a new project called Email_Tracking with a main class called emailtracking.java.
Keep in mind that Email Tracking is a paid customer feature, so with a trail account, you will not be able to use it.
Let’s start with the pom.xml file:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>Nylas</groupId> <artifactId>Email_Tracking</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>18</maven.compiler.source> <maven.compiler.target>18</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>com.nylas.sdk</groupId> <artifactId>nylas-java-sdk</artifactId> <version>1.21.0</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>2.0.6</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>2.0.6</version> </dependency> <dependency> <groupId>io.github.cdimascio</groupId> <artifactId>dotenv-java</artifactId> <version>2.3.2</version> </dependency> </dependencies> <build> <pluginManagement> <plugins> <plugin> <!-- Build an executable JAR --> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <version>3.1.0</version> <configuration> <archive> <manifest> <addClasspath>true</addClasspath> <mainClass>emailtracking</mainClass> </manifest> </archive> </configuration> </plugin> <plugin> <artifactId>maven-assembly-plugin</artifactId> <configuration> <archive> <manifest> <mainClass>emailtracking</mainClass> </manifest> </archive> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> </configuration> </plugin> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> <version>1.2.1</version> <executions> <execution> <phase>package</phase> <goals> <goal>java</goal> </goals> </execution> </executions> <configuration> <mainClass>emailtracking</mainClass> <cleanupDaemonThreads>false</cleanupDaemonThreads> </configuration> </plugin> </plugins> </pluginManagement> </build> </project>
And then the emailtracking.java file:
// Import Java Utilities import java.util.Arrays; // Import Nylas Packages import com.nylas.NylasClient; import com.nylas.NylasAccount; import com.nylas.Tracking; import com.nylas.Draft; import com.nylas.NameEmail; //Import DotEnv to handle .env files import io.github.cdimascio.dotenv.Dotenv; public class emailtracking { public static void main(String[] args) throws Exception { Dotenv dotenv = Dotenv.load(); // Create the Nylas client object NylasClient client = new NylasClient(); NylasAccount account = client.account(dotenv.get("ACCESS_TOKEN")); // Create the draft email Draft draft = new Draft(); draft.setSubject("With Love, From Nylas"); draft.setTo(Arrays.asList(new NameEmail("YourName", "YourEmail"))); draft.setBody("This email was sent using the Nylas email API. Visit <a href='https://nylas.com'>Nylas</a> for details."); // Create the tracking object and add it to the draft Tracking tracking = new Tracking(); tracking.setOpens(true); tracking.setLinks(true); tracking.setThreadReplies(true); tracking.setPayload("payload"); draft.setTracking(tracking); // Send the draft account.drafts().send(draft); } }
Let’s compile and run our application:
$ mvn package && mvn exec:java -Dexec.mainClass="emailtracking"
We should get an email in our mailbox:
Our project is going to be called Local_Webhooks, and it will have a main class called localwebhooks. We will need to change the default pom.xml file and create one Mustache template called main.
As we mentioned, we want to use a web framework, and a very light and fast option is SparkJava.
We’re going to include some useful libraries:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>Nylas</groupId> <artifactId>Local_Webhooks</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>18</maven.compiler.source> <maven.compiler.target>18</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>com.nylas.sdk</groupId> <artifactId>nylas-java-sdk</artifactId> <version>1.21.0</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>2.0.6</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>2.0.6</version> </dependency> <dependency> <groupId>io.github.cdimascio</groupId> <artifactId>dotenv-java</artifactId> <version>2.3.2</version> </dependency> <dependency> <groupId>com.sparkjava</groupId> <artifactId>spark-core</artifactId> <version>2.9.4</version> </dependency> <dependency> <groupId>com.sparkjava</groupId> <artifactId>spark-template-mustache</artifactId> <version>2.7.1</version> </dependency> <dependency> <groupId>com.twilio.sdk</groupId> <artifactId>twilio</artifactId> <version>9.2.5</version> </dependency> </dependencies> </project>
With our pom.xml file ready, we can move on to the source code.
Here’s the source code of our localwebhooks.java class:
//Import Java Utilities import java.net.URISyntaxException; import java.time.LocalDateTime; import java.time.ZoneOffset; import java.util.*; import java.io.IOException; //Import Nylas Packages import com.nylas.*; import com.nylas.services.Tunnel; //Import DotEnv to handle .env files import io.github.cdimascio.dotenv.Dotenv; // Import Spark and Mustache libraries import spark.ModelAndView; import static spark.Spark.*; import spark.template.mustache.MustacheTemplateEngine; // Import Twilio libraries import com.twilio.Twilio; import com.twilio.rest.api.v2010.account.Message; public class localwebhooks { static Dotenv dotenv = Dotenv.load(); private static String id; private static String type; // Get Twilio keys public static final String ACCOUNT_SID = dotenv.get("TWILIO_SID"); public static final String AUTH_TOKEN = dotenv.get("TWILIO_TOKEN"); public static void main(String[] args) throws RequestFailedException, IOException, URISyntaxException { // Initialize Twilio client Twilio.init(ACCOUNT_SID, AUTH_TOKEN); // List to hold webhooks notifications List<WebhookInfo> webhook_list = new ArrayList<>(); // Create the Nylas client object NylasClient client = new NylasClient(); // Create the Nylas account object NylasAccount account = client.account(dotenv.get("ACCESS_TOKEN")); // Connect it to Nylas using the Access Token from the .env file NylasApplication application = client.application(dotenv.get("CLIENT_ID"), dotenv.get("CLIENT_SECRET")); // Create a Webhook tunnel connection, passing the required triggers Tunnel webhookTunnel = new Tunnel.Builder(application, new HandleNotifications()). triggers(Webhook.Trigger.MessageCreated, Webhook.Trigger.MessageOpened, Webhook.Trigger.MessageUpdated, Webhook.Trigger.MessageLinkClicked).build(); // Establish the Webhook connection webhookTunnel.connect(); // Default path when we load our web application get("/", (request, response) -> { // Create the Nylas messages endpoint Messages messages = account.messages(); // If we get the message id if(id != "" && id != null){ // Create a new webhookinfo structure object WebhookInfo info = new WebhookInfo(); // Retrive the information from the message com.nylas.Message message = messages.get(id); // Convert the date into something readable LocalDateTime ldt = LocalDateTime.ofInstant(message.getDate(), ZoneOffset.UTC); String date = ldt.getYear() + "/" + ldt.getMonthValue() + "/" + ldt.getDayOfMonth(); String time = ldt.getHour() + ":" + ldt.getMinute() + ":" + ldt.getSecond(); // Fill up the webhookinfo structure info.id = message.getId(); info.date = date + " on " + time; info.emailFrom = message.getFrom().get(0).getEmail(); info.emailTo = message.getTo().get(0).getEmail(); info.title = message.getSubject(); info.type = type; // Add the structure to the list of webhooks notifications webhook_list.add(info); // If someone click on the link in the email if(type.equals("message.link_clicked")){ // Send a Twilio SMS Message twilio_message = Message.creator( new com.twilio.type.PhoneNumber("+13439969736"), new com.twilio.type.PhoneNumber(dotenv.get("TWILIO_PHONE")), "The link you sent to " + message.getTo().get(0).getEmail() + " was opened").create(); // Trigger the SMS System.out.println(twilio_message.getSid()); } } id = ""; // Create a model to pass information to the mustache template Map<String, Object> model = new HashMap<>(); model.put("webhook_list", webhook_list); // Call the mustache template return new ModelAndView(model, "main.mustache"); }, new MustacheTemplateEngine()); } // Setters and Getters for Id and Type public static void set_Id(String sId){ id = sId; } public static String get_Id(){ return id; } public static void set_Type(String sType){ type = sType; } public static String get_Type(){ return type; } } // Structure to hold the Webhooks notification information class WebhookInfo { String id; String date; String emailFrom; String emailTo; String title; String type; } // Every time there's a new Webhook, we're going to deal with it here class HandleNotifications implements Tunnel.WebhookHandler { @Override public void onMessage(Notification.Delta delta) { Dotenv dotenv = Dotenv.load(); localwebhooks local = new localwebhooks(); String type = delta.getTrigger(); // Get the Id for either message.created or message.updated if(type.equals("message.created") || type.equals("message.updated")) { local.set_Id(delta.getObjectData().getId()); // Get the Id for either message.opened or message.link_clicked }else{ local.set_Id(delta.getObjectData().getMessageTrackingData().getMessageId()); } local.set_Type(type); } // When the connection to the Webhook tunnel is made @Override public void onOpen(short httpStatusCode) { System.out.println("Webhook tunnel is open"); } }
Inside the resources folder, we need to create a templates folder and inside we need to create one file called main.mustache:
<html> <head> <script src="https://cdn.tailwindcss.com"></script> <title>Webhooks Monitor</title> <meta http-equiv="refresh" content="5"> <style> td { text-align: center; } </style> </head> <body> <div class="bg-green-600 border-green-600 border-b p-4 m-4 rounded grid place-items-center"> <p class="text-6xl text-center">Webhooks Monitor</p> <br> <table border="1" style="width:100%"> <tr> <th>Id</th> <th>Date</th> <th>Email From</th> <th>Email To</th> <th>Title</th> <th>Type</th> </tr> {{#webhook_list}} <tr> <td><p class="text-white"><b>{{id}}</b></p></td> <td><p class="text-white"><b>{{date}}</b></p></td> <td><p class="text-white"><b>{{emailFrom}}</b></p></td> <td><p class="text-white"><b>{{emailTo}}</b></p></td> <td><p class="text-white"><b>{{title}}</b></p></td> <td><p class="text-white"><b>{{type}}</b></p></td> </tr> {{/webhook_list}} </table> </div> </body> </html>
And that’s it. We’re ready to roll.
In order to run our application, we need to compile it by typing the following on the terminal window:
$ mvn package
To actually run the application, we need to type the following:
$ mvn exec:java -Dexec.mainClass="localwebhooks"
Our application will be running on port 4567 of localhost, so we just need to open our favourite browser and go to the following address:
http://localhost:4567
Locally testing your webhooks with Java saves you a lot of time and helps you to prevent errors.
If you want to learn more about Webhooks, go to our documentation.
You can sign up Nylas for free and start building!
Don’t miss the action, join our LiveStream Coding with Nylas:
Blag aka Alvaro Tejada Galindo is a Senior Developer Advocate at Nylas. He loves learning about programming and sharing knowledge with the community. When he’s not coding, he’s spending time with his wife, daughter and son. He loves Punk Music and reading all sorts of books.