All Products
Search
Document Center

Java Interface

Last Updated: Aug 22, 2019

When you use Java to compile code in Function Compute, you must first define a Java function handler. The function handlers can be generic handlers or HTTP trigger handlers based on whether the Java runtime environment supports HTTP triggers. HTTP trigger handlers are different from generic handlers because they process HTTP requests and return responses with custom HTTP headers. Generic handlers include function handlers and initializers.

This topic introduces the generic handlers and HTTP trigger handlers.

Generic handlers

Function handlers

When you use Java to compile code, you must first implement the predefined interfaces provided by Function Compute. Function Compute currently provides two predefined interfaces:

  1. StreamRequestHandler uses the stream type to process the input event data and returns the execution result. The input data is bound to inputStream and the execution result is bound to outputStream.

  2. PojoRequestHandler uses the POJO type, allowing 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 function:

  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. }
1. Package and class names

You can assign any name to a package or class. However, these names must be referenced in the “handler” field of the function to be created. In the preceding example, the package name is “example” and the class name is “HelloFC”. Therefore, the “Handler” field must be set to example.HelloFC:handleRequest. The format of the “Handler” field is {package}.{class}::{method}.

2. Interface implementation

You must implement an interface that is predefined by Function Compute in your code. In the preceding example, the StreamRequestHandler interface is implemented. The inputStream parameter of the handler is bound to the input data and the outputStream parameter is bound to the execution result.

3. Context

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

4. Returned values

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

5. Reference the interface repository

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

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

You can click Maven repository to obtain the latest fc-java-core version.

Before you create a function, you must package code and its dependency fc-java-core into a JAR file. For more information about Java packaging, see Java code packaging. After you package the code, use Fun or the Function Compute console to upload the package. The following example shows how to use Fun to upload the package:

Create a template.yml file in the root directory of the project:

  1. ROSTemplateFormatVersion: '2015-09-01'
  2. Transform: 'Aliyun::Serverless-2018-04-03'
  3. Resources:
  4. FunDemo:
  5. Type: 'Aliyun::Serverless::Service'
  6. javademo:
  7. Type: 'Aliyun::Serverless::Function'
  8. Properties:
  9. Handler: example.HelloFC::handleRequest
  10. Runtime: java8
  11. CodeUri: './example.jar'

The description of the template.yml file is as follows: Declare a FunDemo service and a javademo function for the FunDemo service. Then, configure the function handler as example.HelloFC::handleRequest and specify the function runtime environment as java8. In addition, specify the property CodeUri as ./Example. jar. Fun packages and uploads the folder or files that the CodeUri property specifies during the deployment. For more configuration rules, see here.

The hello world code in the example is zipped into a JAR package. You can use this sample code package for testing purposes.

Use Fun to deploy:

  1. fun deploy

Related logs appear as follows when you successfully run the fun deploy command:

  1. using region: cn-hangzhou
  2. using accountId: ***********3557
  3. using accessKeyId: ***********r3Ra
  4. using timeout: 300
  5. Waiting for service FunDemo to be deployed...
  6. Waiting for function javademo to be deployed...
  7. Waiting for packaging function javademo code...
  8. package function javademo code done
  9. function javademo deploy success
  10. service FunDemo deploy success

Then, you can log on to the Function Compute console to view or call functions.

PojoRequestHandler

The following code defines a simple function:

  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 the function:

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

Use the fcli to call the function:

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

Initializers

The function uses the stream type for the input and output data, or uses POJO types to customize the input and output data. When you add an initializer to the Java runtime environment, you must first implement the predefined initializer.

The predefined initializer is as follows:

  1. package com.aliyun.fc.runtime;
  2. import java.io.IOException;
  3. public interface FunctionInitializer {
  4. /**
  5. * The interface to handle a function compute initialize request
  6. *
  7. * @param context The function compute initialize environment context object.
  8. * @throws IOException IOException during I/O handling
  9. */
  10. void initialize(Context context) throws IOException;
  11. }

The following demo contains an initializer and a simple function that uses the stream type:

  1. package aliyun.serverless.test.example;
  2. import com.aliyun.fc.runtime.Context;
  3. import com.aliyun.fc.runtime.FunctionComputeLogger;
  4. import com.aliyun.fc.runtime.StreamRequestHandler;
  5. import com.aliyun.fc.runtime.FunctionInitializer;
  6. import java.io.IOException;
  7. import java.io.InputStream;
  8. import java.io.OutputStream;
  9. public class InitializerAndStreamRequest implements StreamRequestHandler, FunctionInitializer {
  10. @Override
  11. public void initialize(Context context) {
  12. FunctionComputeLogger logger = context.getLogger();
  13. logger.debug(String.format("RequestID is %s %n", context.getRequestId()));
  14. }
  15. @Override
  16. public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException {
  17. FunctionComputeLogger logger = context.getLogger();
  18. logger.debug(String.format("RequestID is %s %n", context.getRequestId()));
  19. output.write(new String("hello world!").getBytes());
  20. output.flush();
  21. }
  22. }

The newly added initialize method for InitializerAndStreamRequest is an initializer. It has the following features:

1. Package and class names

In the same way as a function handler, you can assign any name to a package or a class of an initializer. The format of an initializer is {package}.{class}::{method}. The method in a function handler is handleRequest, while the method in an initializer is initialize. According to the definition, the initializer in this example is aliyun.serverless.test.example.InitializerAndStreamRequest::initialize.

2. Interface implementation

You must implement an interface that is predefined by Function Compute in your code. In the preceding example, the FunctionInitializer initializer is implemented. The initializer has only one context.

3. Context

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

4. Return values

A function that implements the FunctionInitializer initializer does not return any values.

The following demo contains an initializer and a function that uses the POJO types for the input data:

  1. package aliyun.serverless.test.example;
  2. import com.aliyun.fc.runtime.Context;
  3. import com.aliyun.fc.runtime.PojoRequestHandler;
  4. import com.aliyun.fc.runtime.FunctionInitializer;
  5. import com.aliyun.fc.runtime.FunctionComputeLogger;
  6. public class InitializerAndPojoRequest implements FunctionInitializer,PojoRequestHandler<SimpleRequest, SimpleResponse> {
  7. @Override
  8. public void initialize(Context context) {
  9. FunctionComputeLogger logger = context.getLogger();
  10. logger.debug(String.format("RequestID is %s %n", context.getRequestId()));
  11. }
  12. @Override
  13. public SimpleResponse handleRequest(SimpleRequest request, Context context) {
  14. FunctionComputeLogger logger = context.getLogger();
  15. logger.debug(String.format("RequestID is %s %n", context.getRequestId()));
  16. String message = "Hello, " + request.getFirstName() + " " + request.getLastName();
  17. return new SimpleResponse(message);
  18. }
  19. }

HTTP trigger handlers

For more information about HTTP triggers, see HTTP triggers.

HTTP trigger handler interface

Function Compute provides a servlet-based HTTP trigger handler. Its interface is as follows:

  1. Public interface HttpRequestHandler {
  2. /**
  3. * The entrance function of fc http trigger
  4. * @param request The servlet request
  5. * @param response The servlet response
  6. * @param context The fc context
  7. * @throws IOException If IO exception happened
  8. * @throws ServletException If servlet exception happened
  9. */
  10. public void handleRequest(HttpServletRequest request, HttpServletResponse response, Context context) throws IOException, ServletException;
  11. }

To use the HTTP trigger, you must update the version of the fc-java-core repository to 1.3.0 or later.

HTTP trigger demo

  1. package com.aliyun.fc.example;
  2. import java.io.IOException;
  3. import java.io.OutputStream;
  4. import javax.servlet.ServletException;
  5. import javax.servlet.http.HttpServletRequest;
  6. import javax.servlet.http.HttpServletResponse;
  7. import com.aliyun.fc.runtime.Context;
  8. import com.aliyun.fc.runtime.HttpRequestHandler;
  9. public class Hello implements HttpRequestHandler {
  10. public void handleRequest(HttpServletRequest request, HttpServletResponse response, Context context)
  11. throws IOException, ServletException {
  12. String requestPath = (String) request.getAttribute("FC_REQUEST_PATH");
  13. String requestURI = (String) request.getAttribute("FC_REQUEST_URI");
  14. String requestClientIP = (String) request.getAttribute("FC_REQUEST_CLIENT_IP");
  15. response.setStatus(200);
  16. response.setHeader("header1", "value1");
  17. response.setHeader("header2", "value2");
  18. String body = String.format("Path: %s\n Uri: %s\n IP: %s\n", requestPath, requestURI, requestClientIP);
  19. OutputStream out = response.getOutputStream();
  20. out.write((body).getBytes());
  21. out.flush();
  22. out.close();
  23. }
  24. }
1. HttpServletRequest

The HTTP trigger handler interface of Function Compute uses the standard servlet protocol. A user request is encapsulated into an HttpServletRequest object. User request parameters and the request header can be retrieved through this object. In addition, Function Compute encapsulates some attributes into the HttpServletRequest object. You can call the getAttribute method to query these attributes. These attributes include the following:

  • FC_REQUEST_PATH

    The path of the request.

  • FC_REQUEST_URI

    The URI of the request.

  • FC_REQUEST_CLIENT_IP

    The client IP address of the request.

2. HttpServletResponse

You can use a standard HttpServletResponse object to return the response header and body.

3. Context

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

HTTP triggers support traditional web applications

Traditional web applications based on the servlet protocol can be easily migrated to the Function Compute platform. Currently, mainstream frameworks including Spring, SpringBoot, and Struts2 are supported. The following example shows how to use the fc-java-common repository provided by Function Compute to load web applications.

  1. Package your web project and generate the demo.war package.

  2. Upload the demo.war package to an OSS bucket, for example, demo-bucket.

  3. Create a function. Then, create an initializer and a function handler.

    • The sample code of the function is as follows:

      1. package com.aliyun.fc.example;
      2. import java.io.IOException;
      3. import javax.servlet.ServletException;
      4. import javax.servlet.http.HttpServletRequest;
      5. import javax.servlet.http.HttpServletResponse;
      6. import com.aliyun.fc.runtime.Context;
      7. import com.aliyun.fc.runtime.FcAppLoader;
      8. import com.aliyun.fc.runtime.FunctionComputeLogger;
      9. import com.aliyun.fc.runtime.FunctionInitializer;
      10. import com.aliyun.fc.runtime.HttpRequestHandler;
      11. public class HelloWeb implements FunctionInitializer, HttpRequestHandler {
      12. private FcAppLoader fcAppLoader = new FcAppLoader();
      13. private String ossEndPoint = "YourOSSEndPoint";
      14. private String bucket = "YourOSSBucket";
      15. private String key = "YourWarName";
      16. private String userContextPath = "/2016-08-15/proxy/{YourServideName}/{YourFunctionName}";
      17. @Override
      18. public void initialize(Context context) throws IOException {
      19. FunctionComputeLogger fcLogger = context.getLogger();
      20. fcAppLoader.setFCContext(context);
      21. // Load code from OSS
      22. fcAppLoader.loadCodeFromOSS(ossEndPoint, bucket, key);
      23. // Init webapp from code
      24. fcAppLoader.initApp(userContextPath, HelloWeb.class.getClassLoader());
      25. }
      26. @Override
      27. public void handleRequest(HttpServletRequest request, HttpServletResponse response, Context context)
      28. throws IOException, ServletException {
      29. try {
      30. fcAppLoader.forward(request, response);
      31. } catch (Exception e) {
      32. e.printStackTrace();
      33. }
      34. }
      35. }
    • Reference the Maven repository

      1. <dependency>
      2. <groupId>com.aliyun.fc.runtime</groupId>
      3. <artifactId>fc-java-core</artifactId>
      4. <version>1.3.0</version>
      5. </dependency>
      6. <dependency>
      7. <groupId>com.aliyun.fc.runtime</groupId>
      8. <artifactId>fc-java-common</artifactId>
      9. <version>1.0.0</version>
      10. </dependency>

Java code packaging

Use Maven to package code

  1. Add the maven-assembly-plugin plug-in 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>
  2. Package the code

    1. mvn package

The JAR package is stored in the target folder.

Use Maven to package code in a jar file, and store the dependencies in the /lib folder as a JAR package.

As the dependencies of the project increase, the JAR package also becomes larger. The uploaded JAR or ZIP package is extracted and then the code in the file is loaded and executed. Therefore, the JAR package that you created in the preceding examples may contain a large number of class files. Consequently, the extraction process is time-consuming. It takes a long time to call the function for the first time.

We recommend that you store the third-party dependencies in the /lib folder as a JAR package.

A solution using the maven-dependency-plugin plug-in is as follows:

  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 you run mvn package, the folder structure of the packaged JAR file is as follows:

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

Use IDEA to package code

  1. Configure parameters for exporting the JAR file.

    java1

    java2

    java3

  2. Verify packaging results.

    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/