DEV Community

Cover image for Cloud Functions Using the New Java Runtime
Christy Jacob for Appwrite

Posted on • Edited on

Cloud Functions Using the New Java Runtime

One of the highlights of Appwrite’s latest release is the addition of four new Cloud Function runtimes! Java, Kotlin, .NET and C++ are now a part of our ever growing list of runtimes! 🤯

In this article, we’ll take a look at writing Cloud Functions using the Java runtime. If you’re an Android developer, you could essentially build your app and write any necessary Cloud Functions without the need to learn any new language! 🤩

🤔 New to Appwrite?

Appwrite is open source backend-as-a-service that abstracts all the complexity involved in building a modern application by providing you with a set of REST APIs for your core backend needs. Appwrite handles user authentication and authorization, realtime databases, file storage, cloud functions, webhooks and much more! If there is anything missing, you can extend Appwrite using your favorite backend language.

📝 Prerequisites

Before we get started, there are a couple of prerequisites. If you have the prerequisites setup already, you can skip to the following section.

In order to follow along, you’ll need a few things beforehand.

  • 🖥 An Appwrite instance

If you haven’t set up an Appwrite instance yet, you can follow the getting started guides to get up and running quickly. You can choose between One-click installations on DigitalOcean or Manual installations with Docker.

TL;DR - It's just a single command to install Appwrite

docker run -it --rm \
    --volume /var/run/docker.sock:/var/run/docker.sock \
    --volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
    --entrypoint="install" \
    appwrite/appwrite:0.14.2
Enter fullscreen mode Exit fullscreen mode

Once your server is up and running, head over to the Appwrite Dashboard on your server’s public IP address ( or localhost if you installed locally ) and create a new user account.

Create User

  • 🧑‍💻 The Appwrite CLI

We’ll use the Appwrite CLI during this exercise as it makes the process super simple. If you have Node.js installed, the installation command is a simple

npm install -g appwrite-cli
Enter fullscreen mode Exit fullscreen mode

If npm is not your thing, we have numerous installation options you can find in the getting started guide for the CLI

🏁 Getting Started

With everything set up, we can now begin! Login to the Appwrite CLI using the following command and use the credentials we used when setting up the Appwrite server to login.

appwrite login
? Enter your email test@test.com
? Enter your password ********
✓ Success
Enter fullscreen mode Exit fullscreen mode

Next, we need to create a new Appwrite project to work with. We can use the following command to set it up

appwrite init project
? How would you like to start? Create a new Appwrite project
? What would you like to name your project? My Awesome Project
✓ Success
Enter fullscreen mode Exit fullscreen mode

You can give your project any name of your choice. I’m going to stick to the defaults in this case. You’ll notice a new appwrite.json file in the current directory which stores all the information about your project.

It’s time to start writing our function! But wait, we don't need to start from scratch! The CLI can setup all the boilerplate for us using the following command

appwrite init function
? What would you like to name your function? java-example
? What runtime would you like to use? Java (java-17.0)
✓ Success 
Enter fullscreen mode Exit fullscreen mode

Give your function a name and choose the Java 17.0 runtime. This will create a new Appwrite Function in your project and set up all the boilerplate code necessary. Feel free to examine the files created in the functions/java-example directory. You’ll find the CLI created a simple Java function that returns a very important JSON message

{
    "areDevelopersAwesome":true
}
Enter fullscreen mode Exit fullscreen mode

Before we go ahead and modify the function, let’s deploy it to get a feel of the end-to-end workflow from initializing the function to deploying it.

appwrite deploy function
? Which functions would you like to deploy? java-example (6286f6905af1e032e934)
ℹ Info Deploying function java-example ( 6286f6905af1e032e934 )
ℹ Info Ignoring files using configuration from appwrite.json
✓ Success Deployed java-example ( 6286f6905af1e032e934 )
Enter fullscreen mode Exit fullscreen mode

The appwrite deploy function command packages your source code, uploads it to the Appwrite server and initiates the build process. During this time, all the function’s dependencies are installed and a .jar file is generated for your function.

You can head over to your Appwrite Dashboard to track the progress of your deployment.

Deployment

Once the deployment completes, you can execute your function by clicking the Execute Now button.

Execution Logs

The first execution results in what we call a Cold Start. Essentially this is the first time a runtime is created, so it takes a bit longer. As you can see, the first execution took about 331ms and subsequent executions were around 4ms. Now that we have an idea of the complete workflow, we can start tinkering with the code and write some cool functions.

🧮 Answering the Most Fundamental Question in Mathematics

As the title suggests we’re going to write a cloud function to answer one of the most fundamental questions in Mathematics.

Is the given number odd or even? I know what you’re thinking… It’s so easy! Why do I need a Cloud Function for it? I could just write

class Main {
    public static void main(String[ ] args) {
        int x = 12;
        System.out.println( x%2 == 0 ); // true
        System.out.println( (x/2) * 2 == x ); // true
        System.out.println( (x & 1) == 0 ); // true
        System.out.println( ( x >> 1) << 1  == x ); // true
    }
}
Enter fullscreen mode Exit fullscreen mode

But wait! We’re taking this to the next level. We’re going to decide if our number is even or odd using one of the most sophisticated and well written APIs out there! The isEven API.

Jokes aside, this example aims to illustrate how you can make API calls from your Cloud Function which will enable you to build anything you’d like.

Firstly, open the java-example folder in your favorite IDE. Let’s start with a blank canvas and clean up our src/Index.java file to

import java.util.Map;
import java.util.HashMap;

public RuntimeResponse main(RuntimeRequest req, RuntimeResponse res) throws Exception {
    Map<String, Object> data = new HashMap<>();
    data.put("message", "Hello World!");
    return res.json(data);
}
Enter fullscreen mode Exit fullscreen mode

In order to deal with JSON objects more easily, we’ll make use of the popular gson library. Include the following dependency in the deps.gradle file.

dependencies {
    implementation 'com.google.code.gson:gson:2.9.0'
}
Enter fullscreen mode Exit fullscreen mode

Next, let’s write some code to read and parse a number sent as an argument to this function.

import java.util.Map;
import java.util.HashMap;
import com.google.gson.Gson;

final Gson gson = new Gson();

public RuntimeResponse main(RuntimeRequest req, RuntimeResponse res) throws Exception {

    String payloadString = req.getPayload() == null || req.getPayload().isEmpty() 
        ? "{}" 
        : req.getPayload();

    Map<String, Object> payload = gson.fromJson(payloadString, Map.class);

    String number = "2";
    if (payload.containsKey("number") && payload.get("number") != null) {
        number = payload.get("number").toString();
    }

    Map<String, Object> data = new HashMap<>();
    data.put("message", "Hello World!");
    return res.json(data);
}
Enter fullscreen mode Exit fullscreen mode

Let’s go over the code we just wrote. From the functions documentation, we see that the payload is available through the request object and is a JSON string that needs to be converted to a JSON object to parse further. If the payload is empty, we replace it with an empty JSON object.

We then retrieve the number that was passed to the function and default to the number 2 in case it’s empty. We also import the gson library and create an instance of it.

Next, let’s make the call to the API using the native HTTP client.

import java.util.Map;
import java.util.HashMap;
import java.net.HttpURLConnection;
import java.net.URL;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import com.google.gson.Gson;

final Gson gson = new Gson();

public RuntimeResponse main(RuntimeRequest req, RuntimeResponse res) throws Exception {

    String payloadString = req.getPayload() == null || req.getPayload().isEmpty() 
        ? "{}" 
        : req.getPayload();

    Map<String, Object> payload = gson.fromJson(payloadString, Map.class);

    String number = "2";
    if (payload.containsKey("number") && payload.get("number") != null) {
        number = payload.get("number").toString();
    }

    URL url = new URL("https://api.isevenapi.xyz/api/iseven/" + number);
    HttpURLConnection con = (HttpURLConnection) url.openConnection();
    con.setRequestMethod("GET");
    con.getResponseCode();

    BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
    String inputLine;
    StringBuilder responseString = new StringBuilder();
    while ((inputLine = in.readLine()) != null) {
        responseString.append(inputLine);
    }
    in.close();
    con.disconnect();

    Map<String, Object> response = gson.fromJson(responseString.toString(), Map.class);

    return res.json(response);
}
Enter fullscreen mode Exit fullscreen mode

Awesome! We’re all set to test our function! If you remember the drill, all we need to do is deploy our function.

appwrite deploy function
? Which functions would you like to deploy? java-example (6286f6905af1e032e934)
ℹ Info Deploying function java-example ( 6286f6905af1e032e934 )
ℹ Info Ignoring files using configuration from appwrite.json
✓ Success Deployed java-example ( 6286f6905af1e032e934 )
Enter fullscreen mode Exit fullscreen mode

You can now head over to the Appwrite console and execute the function with the following payload.

Payload

Head over to the Logs tab to check the status and response from the function.

Execution Logs

Perfect! Looks like we’ve managed to solve one of the most pressing problems in Mathematics after all! Not to mention, the API also comes with a host of quirky Ads!

And that brings us to the end of this tutorial. Feel free to modify the function to your liking and play around with the API. If you’re stuck or need any help, we’re always here to help. Simply head over to the #support channels on our Discord Server 😊

📚 Resources

Here are some handy links for more information

Top comments (2)

Collapse
 
fuadefendi profile image
FuadEfendi

Trying to follow this guide; and the issues:

  • how I suppose to design non-trivial function if Kotlin library doesn't provide RuntimeRequest and RuntimeResponse classes?
  • why generated Index.java doesn't even have correct import and even public class Index {...} statements?
  • what about this exception,
Docker Error: /usr/local/src/src/main/java/io/openruntimes/java/Wrapper.java:53: error: cannot find symbol
    if (env == null
        ^
  symbol:   variable env
  location: class Wrapper
/usr/local/src/src/main/java/io/openruntimes/java/Wrapper.java:54: error: cannot find symbol
        || !env.containsKey("APPWRITE_FUNCTION_ENDPOINT")
            ^
  symbol:   variable env
  location: class Wrapper
/usr/local/src/src/main/java/io/openruntimes/java/Wrapper.java:55: error: cannot find symbol
        || !env.containsKey("APPWRITE_FUNCTION_API_KEY")
            ^
  symbol:   variable env
  location: class Wrapper
Note: /usr/local/src/src/main/java/io/openruntimes/java/RuntimeRequest.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
 errors

FAILURE: Build failed with an exception.
Enter fullscreen mode Exit fullscreen mode

Appwrite v.1.0.1

Collapse
 
fuadefendi profile image
FuadEfendi

I resolved all these issues; details here: github.com/appwrite/functions-star...