edit-icon download-icon

Java

Last Updated: Nov 08, 2018

Function Compute currently supports the following Java running environment:

  • OpenJDK 1.8.0 (runtime = java8)

When using Function Compute in Java, a class must be defined and a pre-defined Function Compute interface must be implemented. A simplest function is defined as follows:

  1. package example;
  2. import com.aliyun.fc.runtime.Context;
  3. import com.aliyun.fc.runtime.StreamRequestHandler;
  4. import java.io.IOException;
  5. import java.io.InputStream;
  6. import java.io.OutputStream;
  7. public class HelloFC implements StreamRequestHandler {
  8. @Override
  9. public void handleRequest(
  10. InputStream inputStream, OutputStream outputStream, Context context) throws IOException {
  11. outputStream.write(new String("hello world").getBytes());
  12. }
  13. }
  • Package name/Class name

    A package and a class can have random names, but their names must correspond to the “handler” field of the created function. In the previous example, the package name is “example” and the class name is “HelloFC”. Therefore, the handler specified during function creation is example.HelloFC::handleRequest. The format of “handler” is {package}.{class}::{method}.

  • Implement interface

    The pre-defined Function Compute interface must be implemented in your code. In the previous example, StreamRequestHandler is implemented, inputStream is the data imported when the function is called, and outputStream is used to return the function execution result. For more information about the function interfaces, see Function interfaces.

  • context parameter

    The context parameter contains the operation information of the function (such as the request ID and temporary AccessKey). The parameter type is com.aliyun.fc.runtime.Context. The Use context section introduces the function structure and usage.

  • Return value

    The function that implements the StreamRequestHandler interface returns execution results by using the outputStream parameter.

The dependency of the com.aliyun.fc.runtime package can be referenced in the following pom.xml:

  1. <dependency>
  2. <groupId>com.aliyun.fc.runtime</groupId>
  3. <artifactId>fc-java-core</artifactId>
  4. <version>1.0.0</version>
  5. </dependency>

Before a function is created, you must package project with its dependency fc-java-core into a JAR file. You can learn how to package a JAR file in Use a custom module. After the JAR package is created, use the fcli or console to upload the package. The following uses fcli as an example:

  1. rockuw-MBP:hello-java (master) $ ls -lrt
  2. total 16
  3. -rw-r--r-- 1 rockuw staff 7690 Aug 31 19:45 hellofc.jar
  4. >>> mkf hello-java -t java8 -h example.HelloFC::handleRequest -d ./functions/hello-java
  5. >>> invk hello-java
  6. hello world
  7. >>>

Advanced usage

Use context

context is an object generated during the Function Compute operation, and it contains the operation information. You can use the information in code. The type of context is object, and its definition is as follows. You can click here to learn about its detailed:

  1. package com.aliyun.fc.runtime;
  2. public interface Context {
  3. public String getRequestId();
  4. public Credentials getExecutionCredentials();
  5. public FunctionParam getFunctionParam();
  6. public FunctionComputeLogger getLogger();
  7. }

In this definition, context contains four elements:

  • RequestId: The unique ID of this execution request. It can be recorded for reference in case any exception occurs in future.
  • FunctionParam: Some basic information about the function, such as function name, function entry, function memory, and time-out period.
  • ExecutionCredentials: A group of temporary keys acquired when Function Compute plays a service role you provide. Its valid interval is 5 minutes. You can use it in code to access related service (such as OSS). This prevents you from permanently adding your own AccessKey information in function code.
  • Logger: A logger encapsulated by Function Compute. For details, see Use logging below.

For example, the following code uses a temporary key to upload a file to OSS:

  1. package example;
  2. import com.aliyun.fc.runtime.Context;
  3. import com.aliyun.fc.runtime.Credentials;
  4. import com.aliyun.fc.runtime.StreamRequestHandler;
  5. import com.aliyun.oss.OSSClient;
  6. import java.io.ByteArrayInputStream;
  7. import java.io.IOException;
  8. import java.io.InputStream;
  9. import java.io.OutputStream;
  10. public class HelloFC implements StreamRequestHandler {
  11. @Override
  12. public void handleRequest(
  13. InputStream inputStream, OutputStream outputStream, Context context) throws IOException {
  14. String endpoint = "oss-cn-shanghai.aliyuncs.com";
  15. String bucketName = "my-bucket";
  16. Credentials creds = context.getExecutionCredentials();
  17. OSSClient client = new OSSClient(
  18. endpoint, creds.getAccessKeyId(), creds.getAccessKeySecret(), creds.getSecurityToken());
  19. client.putObject(bucketName, "my-object", new ByteArrayInputStream(new String("hello").getBytes()));
  20. outputStream.write(new String("done").getBytes());
  21. }
  22. }

Use logging

The information about your function that has been printed by context.getLogger() is collected into the logstore that was assigned by you when creating a service:

  1. package example;
  2. import com.aliyun.fc.runtime.Context;
  3. import com.aliyun.fc.runtime.StreamRequestHandler;
  4. import java.io.IOException;
  5. import java.io.InputStream;
  6. import java.io.OutputStream;
  7. public class HelloFC implements StreamRequestHandler {
  8. @Override
  9. public void handleRequest(
  10. InputStream inputStream, OutputStream outputStream, Context context) throws IOException {
  11. context.getLogger().info("hello world");
  12. outputStream.write(new String("hello world").getBytes());
  13. }
  14. }

The logs output by the previous code are:

  1. message:2017-07-05T05:13:35.920Z a72df088-f738-cee3-e0fe-323ad89118e5 [INFO] hello world

context.getLogger().warn and context.getLogger().error can be respectively used to pack logs at WARN and ERROR levels.

Function interfaces

When you use the Java programming, a class must be implemented, which must implement a pre-defined Function Compute interface. Currently, two pre-defined interfaces can be implemented:

This interface uses the stream request handler to receive the information (events) input when calling a function and return execution results. You need to read the input information from inputStream and to write the function execution result into outputStream after the read operation is completed. The first example in this document uses this interface.

This interface uses the generic method to allow you to customize the input and output types, but note that the types must be POJO. The following gives an example on how to use this interface.

  1. // HelloFC.java
  2. package example;
  3. import com.aliyun.fc.runtime.Context;
  4. import com.aliyun.fc.runtime.PojoRequestHandler;
  5. public class HelloFC implements PojoRequestHandler<SimpleRequest, SimpleResponse> {
  6. @Override
  7. public SimpleResponse handleRequest(SimpleRequest request, Context context) {
  8. String message = "Hello, " + request.getFirstName() + " " + request.getLastName();
  9. return new SimpleResponse(message);
  10. }
  11. }
  1. // SimpleRequest.java
  2. package example;
  3. public class SimpleRequest {
  4. String firstName;
  5. String lastName;
  6. public String getFirstName() {
  7. return firstName;
  8. }
  9. public void setFirstName(String firstName) {
  10. this.firstName = firstName;
  11. }
  12. public String getLastName() {
  13. return lastName;
  14. }
  15. public void setLastName(String lastName) {
  16. this.lastName = lastName;
  17. }
  18. public SimpleRequest() {}
  19. public SimpleRequest(String firstName, String lastName) {
  20. this.firstName = firstName;
  21. this.lastName = lastName;
  22. }
  23. }
  1. // SimpleResponse.java
  2. package example;
  3. public class SimpleResponse {
  4. String message;
  5. public String getMessage() {
  6. return message;
  7. }
  8. public void setMessage(String message) {
  9. this.message = message;
  10. }
  11. public SimpleResponse() {}
  12. public SimpleResponse(String message) {
  13. this.message = message;
  14. }
  15. }

Prepare an input file for calling:

  1. {
  2. "firstName": "FC",
  3. "lastName": "aliyun"
  4. }

Use fcli to call the result:

  1. >>> invk hello-java -f /tmp/a.json
  2. {"message":"Hello, FC aliyun"}
  3. >>>

Use a custom module

If you want to use custom modules, you must pack the modules with code when packaging a JAR file. Maven and IDEA are used here to demonstrate how to package the OSS Java SDK into a jar.

Use maven to package the jar file

  1. Add OSS Java SDK dependency to pom.xml:

    1. <dependencies>
    2. <dependency>
    3. <groupId>com.aliyun.fc.runtime</groupId>
    4. <artifactId>fc-java-core</artifactId>
    5. <version>1.0.0</version>
    6. </dependency>
    7. <dependency>
    8. <groupId>com.aliyun.oss</groupId>
    9. <artifactId>aliyun-sdk-oss</artifactId>
    10. <version>2.6.1</version>
    11. </dependency>
    12. </dependencies>
  2. Add maven-assembly-plugin to pom.xml

    1. <build>
    2. <plugins>
    3. <plugin>
    4. <artifactId>maven-assembly-plugin</artifactId>
    5. <version>3.1.0</version>
    6. <configuration>
    7. <descriptorRefs>
    8. <descriptorRef>jar-with-dependencies</descriptorRef>
    9. </descriptorRefs>
    10. <appendAssemblyId>false</appendAssemblyId> <!-- this is used for not append id to the jar name -->
    11. </configuration>
    12. <executions>
    13. <execution>
    14. <id>make-assembly</id> <!-- this is used for inheritance merges -->
    15. <phase>package</phase> <!-- bind to the packaging phase -->
    16. <goals>
    17. <goal>single</goal>
    18. </goals>
    19. </execution>
    20. </executions>
    21. </plugin>
    22. <plugin>
    23. <groupId>org.apache.maven.plugins</groupId>
    24. <artifactId>maven-compiler-plugin</artifactId>
    25. <configuration>
    26. <source>1.8</source>
    27. <target>1.8</target>
    28. </configuration>
    29. </plugin>
    30. </plugins>
    31. </build>
  3. Package the java file

    1. mvn package

After completing the above steps, the dependent third-party jars are also packaged together into the jar. The generated jar will be stored in the target directory.

Use IDEA to package the jar file

  1. Add OSS Java SDK dependency to pom.xml:

    1. <dependencies>
    2. <dependency>
    3. <groupId>com.aliyun.fc.runtime</groupId>
    4. <artifactId>fc-java-core</artifactId>
    5. <version>1.2.0</version>
    6. </dependency>
    7. <dependency>
    8. <groupId>com.aliyun.oss</groupId>
    9. <artifactId>aliyun-sdk-oss</artifactId>
    10. <version>2.6.1</version>
    11. </dependency>
    12. </dependencies>
  2. Configure JAR package export options:

    java1

    java2

    java3

  3. Verify the jar file

    1. rockuw-MBP:hello-java (master) $ ls -lrth
    2. total 6520
    3. -rw-r--r-- 1 rockuw staff 3.2M Aug 31 21:03 hellofc.jar
    4. rockuw-MBP:hello-java (master) $ jar -tf hellofc.jar | head
    5. Picked up _JAVA_OPTIONS: -Duser.language=en
    6. META-INF/MANIFEST.MF
    7. example/
    8. example/HelloFC.class
    9. example/SimpleRequest.class
    10. example/SimpleResponse.class
    11. META-INF/
    12. META-INF//
    13. org/
    14. org//
    15. org/apache/

Use maven to package the jar file and put dependency .jar files in a separate /lib directory

As project dependencies increase, the size of the jar becomes larger. The user-uploaded jar or zip code will be decompressed before execution. Therefore, in the two previous implementations, there is a problem that our packaged jar contains a large number of class files, which will undoubtedly increase the decompression time and thus increase the first startup time of the function.

A better practice is to put your dependency .jar files in a separate /lib directory.

Here is an example using maven-dependency-plugin:

  1. <plugin>
  2. <groupId>org.apache.maven.plugins</groupId>
  3. <artifactId>maven-dependency-plugin</artifactId>
  4. <executions>
  5. <execution>
  6. <id>copy-dependencies</id>
  7. <phase>prepare-package</phase>
  8. <goals>
  9. <goal>copy-dependencies</goal>
  10. </goals>
  11. <configuration>
  12. <outputDirectory>${project.build.directory}/classes/lib</outputDirectory>
  13. <includeScope>runtime</includeScope>
  14. </configuration>
  15. </execution>
  16. </executions>
  17. </plugin>

After mvn package, the packaged jar’s directory structure is:

  1. */*.class
  2. lib/*.jar

Handle exception

If an exception occurs when your function is executed, Function Compute captures the exception and returns its information. The following codes are used as an example:

  1. package example;
  2. import com.aliyun.fc.runtime.Context;
  3. import com.aliyun.fc.runtime.StreamRequestHandler;
  4. import java.io.IOException;
  5. import java.io.InputStream;
  6. import java.io.OutputStream;
  7. public class HelloFC implements StreamRequestHandler {
  8. @Override
  9. public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException {
  10. throw new IOException("oops");
  11. }
  12. }

The response returned upon calling is:

  1. >>> invk hello-java -f /tmp/a.json
  2. {
  3. "errorMessage" : "oops",
  4. "errorType" : "java.io.IOException",
  5. "errorCause" : "oops",
  6. "stackTrace" : [ "example.HelloFC.handleRequest(HelloFC.java:15)" ]
  7. }
  8. Error: Request id: 45dd8d90-6b78-cce3-087c-8bf4ebc6c9af. Error type: UnhandledInvocationError

When an exception occurs, the HTTP header in the function calling response includes X-Fc-Error-Type: UnhandledInvocationError.

Thank you! We've received your feedback.