When you use Java for programming in Function Compute, you must define a Java function as a handler. This topic describes the structure and features of Java event functions.

Background information

Function Compute supports the Java 8 runtime environment. Unlike scripting languages such as Python and Node.js, Java code must be compiled before it can be run on a Java virtual machine (JVM). Therefore, Java has the following limits:

  • No support for code upload: You can upload only developed and compiled ZIP packages or JAR packages. Function Compute does not support Java-based compilation.
  • No support for online editing: You cannot edit code online because code cannot be uploaded. You can submit code only by uploading JAR packages or using Object Storage Service (OSS).

Event function handlers

When you use Java for programming, you must implement the predefined handlers provided by Function Compute. For event handler functions, the following two predefined handlers can be used:

  • StreamRequestHandler

    This handler uses the Stream type to process the input event data and return the execution result. You must read the input data from input streams and then write the execution result to output streams.

  • PojoRequestHandler

    This handler uses the plain old Java object (POJO) type, which allows you to customize the input and output of the handler. Both the input and output must be of the POJO type.

StreamRequestHandler

The following code defines a simple handler function, which is an event function:

package example;

import com.aliyun.fc.runtime.Context;
import com.aliyun.fc.runtime.StreamRequestHandler;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class HelloFC implements StreamRequestHandler {

    @Override
    public void handleRequest(
            InputStream inputStream, OutputStream outputStream, Context context) throws IOException {
        outputStream.write(new String("hello world").getBytes());
    }
}            
  • Package and class names

    Java is different from other languages in execution methods due to its packages. The execution method in the sample code is example.HelloFC::mainHandler, where example specifies the Java package, HelloFC specifies the class, and mainHandler specifies the class method.

    This method can be assigned arbitrary package and class names. However, the package and class names must match the Function Handler parameter that is used to create the function. In the preceding example, the package name is example, and the class name is HelloFC. Therefore, the value of the handler parameter that is specified during function creation is example.HelloFC::handleRequest, which is in the format of {package}.{class}::{method}.

  • Implemented handler

    Your code must implement the predefined handlers provided by Function Compute. In the preceding example, the StreamRequestHandler is implemented. The inputStream parameter of the handler is the input parameter when you invoke a function and the outputStream parameter is used to return the execution result.

  • context parameter

    The context parameter contains the runtime information of a function, such as the request ID and the temporary AccessKey pair. This parameter is of the com.aliyun.fc.runtime.Context type.

  • Return value

    A function that implements the StreamRequestHandler returns the execution result by using the outputStream parameter.

  • Handler repository
    The dependency of the com.aliyun.fc.runtime package can be referenced in the following pom.xml file:
    <dependency>
        <groupId>com.aliyun.fc.runtime</groupId>
        <artifactId>fc-java-core</artifactId>
        <version>1.4.0</version>
    </dependency>           

    You can visit the Maven repository to obtain the latest version of the fc-java-core package.

Before you create a function, you must compress the code and its dependency fc-java-core into a JAR package. For more information about how to compress the code and its dependency, see Use custom modules.
Note The hello world code in the example is compressed into a JAR package. You can use the sample code package to conduct the test.

You can run the related commands provided by Serverless Devs to initialize, build, and deploy a project. You can perform the following steps:

  1. Run the following command to initialize a project:
    s init start-fc-event-java8 -d start-fc-event-java8
    Sample command output:
     Serverless Awesome: https://github.com/Serverless-Devs/Serverless-Devs/blob/master/docs/zh/awesome.md
    
     file decompression completed
    
         ____  _     _ ___  _ _     _        _____ ____
        /  _ \/ \   / \\  \/// \ /\/ \  /|  /    //   _\
        | / \|| |   | | \  / | | ||| |\ ||  |  __\|  /
        | |-||| |_/\| | / /  | \_/|| | \||  | |   |  \__
        \_/ \|\____/\_//_/   \____/\_/  \|  \_/   \____/
      please select credential alias default
    
        Welcome to the Aliyun FC start application
         This application requires to open these services:
             FC : https://fc.console.aliyun.com/
    
         * Note: Actions are declared in the s.yaml file.
            Run the following command before project deployment: s build --use-docker --dockerfile ./code/Dockerfile.
           If you do not need to build the project every time or before deployment, or if you have built the project, you can comment out this part.
         * After the project is initialized, you can go to the project directory and run the s deploy command to deploy the project. 
    
    
    
     Thanks for using Serverless-Devs
     You could [cd /test/start-fc-event-java8] and enjoy your serverless journey!
     If you need help for this example, you can use [s -h] after you enter folder.
     Document Star: https://github.com/Serverless-Devs/Serverless-Devs
  2. Run the following command to go to the project directory:
    cd start-fc-event-java8
  3. Run the following command to install dependencies:
    s build
    Sample command output:
    [2021-12-19 12:07:05] [INFO] [S-CLI] - Start ...
    [2021-12-19 12:07:06] [INFO] [FC-BUILD] - Build artifact start...
    builder begin to build, runtime is: java8, sourceDir:  /test/start-fc-event-java8/
    running task: flow MavenTaskFlow
    running task: MavenCompileTask
    [INFO] Scanning for projects...
    [INFO]
    [INFO] -------------------< example:ServerlessToolProject >--------------------
    [INFO] Building ServerlessToolProject 1.0-SNAPSHOT
    [INFO] --------------------------------[ jar ]---------------------------------
    ......
    [INFO] BUILD SUCCESS
    [INFO] ------------------------------------------------------------------------
    [INFO] Total time: 01:39 min
    [INFO] Finished at: 2021-12-19T20:10:10+08:00
    [INFO] ------------------------------------------------------------------------
    running task: CopyMavenArtifacts
    copy maven artifacts from /test/start-fc-event-java8/target/dependency
    [2021-12-19 12:10:10] [INFO] [FC-BUILD] - Build artifact successfully.
    
    Tips for next step
    ======================
    * Invoke Event Function: s local invoke
    * Invoke Http Function: s local start
    * Deploy Resources: s deploy
    End of method: build
  4. Run the following command to deploy the project:
    s deploy
    Sample command output:
    [2021-12-19 12:17:21] [INFO] [S-CLI] - Start ...
    [2021-12-19 12:17:21] [INFO] [S-CLI] - Start the pre-action
    [2021-12-19 12:17:21] [INFO] [S-CLI] - Action: s build --use-docker
    [2021-12-19 12:17:21] [INFO] [S-CLI] - Start ...
    [2021-12-19 12:17:22] [INFO] [FC-BUILD] - Build artifact start...
    [2021-12-19 12:17:22] [INFO] [FC-BUILD] - Use docker for building.
    [2021-12-19 12:17:22] [INFO] [FC-BUILD] - Build function using image: registry.cn-beijing.aliyuncs.com/aliyunfc/runtime-java8:build-1.9.21
    [2021-12-19 12:17:22] [INFO] [FC-BUILD] - skip pulling image registry.cn-beijing.aliyuncs.com/aliyunfc/runtime-java8:build-1.9.21...
    [2021-12-19 12:17:49] [INFO] [FC-BUILD] - Build artifact successfully.
    
    Tips for next step
    ======================
    * Invoke Event Function: s local invoke
    * Invoke Http Function: s local start
    * Deploy Resources: s deploy
    End of method: build
    [2021-12-19 12:17:49] [INFO] [S-CLI] - End the pre-action
    [2021-12-19 12:17:50] [INFO] [FC-DEPLOY] - Using region: cn-hangzhou
    [2021-12-19 12:17:50] [INFO] [FC-DEPLOY] - Using access alias: default
    [2021-12-19 12:17:50] [INFO] [FC-DEPLOY] - Using accessKeyID: LTAI4G4cwJkK4Rza6xd9****
    [2021-12-19 12:17:50] [INFO] [FC-DEPLOY] - Using accessKeySecret: eCc0GxSpzfq1DVspnqqd6nmYNN****
     Using fc deploy type: sdk, If you want to deploy with pulumi, you can [s cli fc-default set deploy-type pulumi] to switch.
    [2021-12-19 12:17:50] [INFO] [FC-DEPLOY] - Checking Service hello-world-service exists
    [2021-12-19 12:17:51] [INFO] [FC-DEPLOY] - Checking Function event-java8 exists
    [2021-12-19 12:17:51] [INFO] [FC-DEPLOY] - Fc detects that you have run build command for function: event-java8.
    [2021-12-19 12:17:51] [INFO] [FC-DEPLOY] - Using codeUri: /test/start-fc-event-java8/.s/build/artifacts/hello-world-service/event-java8
    
    
    Detail:
    
    added: {}
    deleted: {}
    updated:
      LD_LIBRARY_PATH: >-
        /code/.s/root/usr/local/lib:/code/.s/root/usr/lib:/code/.s/root/usr/lib/x86_64-linux-gnu:/code/.s/root/usr/lib64:/code/.s/root/lib:/code/.s/root/lib/x86_64-linux-gnu:/code/.s/root/python/lib/python2.7/site-packages:/code/.s/root/python/lib/python3.6/site-packages:/code:/code/lib:/usr/local/lib
      PATH: >-
        /code/.s/root/usr/local/bin:/code/.s/root/usr/local/sbin:/code/.s/root/usr/bin:/code/.s/root/usr/sbin:/code/.s/root/sbin:/code/.s/root/bin:/code:/code/node_modules/.bin:/code/.s/python/bin:/code/.s/node_modules/.bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/sbin:/bin
      NODE_PATH: /code/node_modules:/usr/local/lib/node_modules
      PYTHONUSERBASE: /code/.s/python
    
     Fc want to add/append some content to your origin environment variables for finding dependencies generated by build comman
    d.
    Do you agree with the behavior? yes
     Make service hello-world-service success.
     Make function hello-world-service/event-java8 success.
    [2021-12-19 12:17:56] [INFO] [FC-DEPLOY] - Checking Service hello-world-service exists
    [2021-12-19 12:17:56] [INFO] [FC-DEPLOY] - Checking Function event-java8 exists
    
    ......
    
    helloworld:
      region:   cn-hangzhou
      service:
        name: hello-world-service
      function:
        name:       event-java8
        runtime:    java8
        handler:    example.App::handleRequest
        memorySize: 128
        timeout:    60

Log on to the Function Compute console to view the status of the function and invoke the function.

PojoRequestHandler

The following code defines a simple handler function, which is a processing function: The object of SimpleRequest is an object that can be serialized into a JSON string, such as an object of the POJO type.

// HelloFC.java
package example;

import com.aliyun.fc.runtime.Context;
import com.aliyun.fc.runtime.PojoRequestHandler;

public class HelloFC implements PojoRequestHandler<SimpleRequest, SimpleResponse> {

    @Override
    public SimpleResponse handleRequest(SimpleRequest request, Context context) {
        String message = "Hello, " + request.getFirstName() + " " + request.getLastName();
        return new SimpleResponse(message);
    }
}            
// SimpleRequest.java
package example;

public class SimpleRequest {
    String firstName;
    String lastName;

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public SimpleRequest() {}
    public SimpleRequest(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
}            
// SimpleResponse.java
package example;

public class SimpleResponse {
    String message;

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public SimpleResponse() {}
    public SimpleResponse(String message) {
        this.message = message;
    }
}            

Prepare an input file for invoking the function:

{
  "firstName": "FC",
  "lastName": "aliyun"
}            

Use the context parameter

The context parameter is an object that is generated when Function Compute is running. It contains runtime information. You can use the information in your code. For more information about how to implement the context parameter, see fc-java-libs. The following example shows the definition of the context parameter:

package com.aliyun.fc.runtime;

public interface Context {

    public String getRequestId();

    public Credentials getExecutionCredentials();

    public FunctionParam getFunctionParam();

    public FunctionComputeLogger getLogger();

    public Service getService();
}           

The following table describes the fields contained in the context parameter.

Parameter Description
RequestId The unique ID of the request for invoking the function. You can record the ID for troubleshooting if an error occurs.
Function The basic information of the invoked function, such as the name, handler, memory, and timeout period of the function.
Credentials The temporary AccessKey pair that Function Compute obtains by assuming your service-linked role. The temporary AccessKey pair is valid for 5 minutes. You can use credentials in your code to access the related service such as OSS. This allows you not to write your AccessKey pair in the function code. For more information, see Grant permissions to a RAM user by using an Alibaba Cloud account.
Logger The logger encapsulated by Function Compute.
Service The basic information of the called service.

The following sample code provides an example on how to upload an object to OSS by using a temporary AccessKey pair:

package example;

import com.aliyun.fc.runtime.Context;
import com.aliyun.fc.runtime.Credentials;
import com.aliyun.fc.runtime.StreamRequestHandler;
import com.aliyun.oss.OSSClient;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class HelloFC implements StreamRequestHandler {

    @Override
    public void handleRequest(
            InputStream inputStream, OutputStream outputStream, Context context) throws IOException {

        String endpoint = "oss-cn-shanghai.aliyuncs.com";
        String bucketName = "my-bucket";

        Credentials creds = context.getExecutionCredentials();
        OSSClient client = new OSSClient(
                endpoint, creds.getAccessKeyId(), creds.getAccessKeySecret(), creds.getSecurityToken());
        client.putObject(bucketName, "my-object", new ByteArrayInputStream(new String("hello").getBytes()));
        outputStream.write(new String("done").getBytes());
    }
}