When you use Java to write the code in Function Compute, you must first define a Java function handler. You can use 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 need to process the input HTTP requests and return responses with custom HTTP headers. Generic handlers include function handlers and initialize handlers.
This topic introduces the generic handlers and HTTP trigger handlers.
Generic handlers
Function handlers
When you use Java to compile, you must first implement the predefined interfaces provided by Function Compute. Function Compute currently provides two predefined interfaces:
StreamRequestHandler
This interface uses the stream type to process the inputevent
data and return the execution result. The input data is bound toinputStream
and the execution result is bound tooutputStream
.PojoRequestHandler
This interface uses the POJO types, allowing you to customize the input and output of the handler. Both the input and output must be the POJO type.
StreamRequestHandler
The following code defines a simple 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());
}
}
1. Package and class names
You can assign any name to a package or class. However, the 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 the interface predefined by Function Compute in your code. In the preceding example, the interface StreamRequestHandler
is implemented. The parameter inputStream
of the handler is bound to the input data and the parameter outputStream
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 set. The type of the context is com.aliyun.fc.runtime.Context
.
4. Returned values
A function that implements the StreamRequestHandler
interface returns execution results through 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:
<dependency>
<groupId>com.aliyun.fc.runtime</groupId>
<artifactId>fc-java-core</artifactId>
<version>1.2.1</version>
</dependency>
You can click maven repository to obtain the latest fc-java-core version.
Before you create a function, you must package the code and its dependency fc-java-core
into a JAR file. For more information, see Java code packaging. After you package the code, use fcli or the Function Compute console to upload the package. The following example shows how to use fcli to upload the package:
rockuw-MBP:hello-java (master) $ ls -lrt
total 16
-rw-r--r-- 1 rockuw staff 7690 Aug 31 19:45 hellofc.jar
>>> mkf hello-java -t java8 -h example.HelloFC::handleRequest -d ./functions/hello-java
>>> invk hello-java
hello world
>>>
The hello world code in the preceding example is packaged into a JAR sample package. You can use this sample code package for testing purpose.
PojoRequestHandler
The following code defines a simple function:
//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 fcli to invoke the function. The following information is returned:
>>> invk hello-java -f /tmp/a.json
{"message":"Hello, FC aliyun"}
>>>
Initialize handlers
Whether your function uses the stream type for input and output or uses POJO types to customize the input and output, when you add an initializer interface to the Java runtime, you must first implement the predefined initializer interface.
The predefined initializer interface is as follows:
package com.aliyun.fc.runtime;
import java.io.IOException;
public interface FunctionInitializer {
/**
* The interface to handle a function compute initialize request
*
* @param context The function compute initialize environment context object.
* @throws IOException IOException during I/O handling
*/
void initialize(Context context) throws IOException;
}
The following demo contains an initialize handler and a simple function that uses the stream type:
package aliyun.serverless.test.example;
import com.aliyun.fc.runtime.Context;
import com.aliyun.fc.runtime.FunctionComputeLogger;
import com.aliyun.fc.runtime.StreamRequestHandler;
import com.aliyun.fc.runtime.FunctionInitializer;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class InitializerAndStreamRequest implements StreamRequestHandler, FunctionInitializer {
@Override
public void initialize(Context context) {
FunctionComputeLogger logger = context.getLogger();
logger.debug(String.format("RequestID is %s %n", context.getRequestId()));
}
@Override
public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException {
FunctionComputeLogger logger = context.getLogger();
logger.debug(String.format("RequestID is %s %n", context.getRequestId()));
output.write(new String("hello world!").getBytes());
output.flush();
}
}
The newly added initialize method for InitializerAndStreamRequest is the initializer interface. It has the following features:
1. Package and class names
Same as a function handler, you can assign any name to the package and class of the initialize handler. The format of the initialize handler is {package}.{class}::{method}
. However, the method called in the function handler is handleRequest
, while the method called in the initialize handler is initialize
. According to the definition, the initialize handler in this example is aliyun.serverless.test.example.InitializerAndStreamRequest::initialize
.
2. Interface implementation
You must implement the interface predefined by Function Compute in your code. In the preceding example, the FunctionInitializer
interface is implemented. The initializer interface has only one Context
parameter.
3 . Context
A context contains the runtime information of the function, such as the request ID and the temporary AccessKey set. The type of the context is com.aliyun.fc.runtime.Context
.
4. Returned values
A function that implements the FunctionInitializer
interface does not return any values.
The following demo contains an initialize handler and a function that uses the custom POJO types for input:
package aliyun.serverless.test.example;
import com.aliyun.fc.runtime.Context;
import com.aliyun.fc.runtime.PojoRequestHandler;
import com.aliyun.fc.runtime.FunctionInitializer;
import com.aliyun.fc.runtime.FunctionComputeLogger;
public class InitializerAndPojoRequest implements FunctionInitializer,PojoRequestHandler<SimpleRequest, SimpleResponse> {
@Override
public void initialize(Context context) {
FunctionComputeLogger logger = context.getLogger();
logger.debug(String.format("RequestID is %s %n", context.getRequestId()));
}
@Override
public SimpleResponse handleRequest(SimpleRequest request, Context context) {
FunctionComputeLogger logger = context.getLogger();
logger.debug(String.format("RequestID is %s %n", context.getRequestId()));
String message = "Hello, " + request.getFirstName() + " " + request.getLastName();
return new SimpleResponse(message);
}
}
HTTP trigger handlers
For more about HTTP triggers, see HTTP triggers.
HTTP trigger handler interface
Function Compute provides a Servlet-based HTTP trigger handler. Its interface is as follows:
Public interface HttpRequestHandler {
/**
* The entrance function of fc http trigger
* @param request The servlet request
* @param response The servlet response
* @param context The fc context
* @throws IOException If IO exception happened
* @throws ServletException If servlet exception happened
*/
public void handleRequest(HttpServletRequest request, HttpServletResponse response, Context context) throws IOException, ServletException;
}
To use the HTTP trigger, you must update the version of the fc-java-core
repository to 1.3.0
and later.
HTTP trigger demo
package com.aliyun.fc.example;
import java.io.IOException;
import java.io.OutputStream;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.aliyun.fc.runtime.Context;
import com.aliyun.fc.runtime.HttpRequestHandler;
public class Hello implements HttpRequestHandler {
public void handleRequest(HttpServletRequest request, HttpServletResponse response, Context context)
throws IOException, ServletException {
String requestPath = (String) request.getAttribute("FC_REQUEST_PATH");
String requestURI = (String) request.getAttribute("FC_REQUEST_URI");
String requestClientIP = (String) request.getAttribute("FC_REQUEST_CLIENT_IP");
response.setStatus(200);
response.setHeader("header1", "value1");
response.setHeader("header2", "value2");
String body = String.format("Path: %s\n Uri: %s\n IP: %s\n", requestPath, requestURI, requestClientIP);
OutputStream out = response.getOutputStream();
out.write((body).getBytes());
out.flush();
out.close();
}
}
1. HttpServletRequest
The HTTP trigger handler interface of Function Compute uses the standard Servlet protocol. A user request is encapsulated into the HttpServletRequest
object. User request parameters and the request header can be obtained through this object. In addition, Function Compute encapsulates some attributes into the HttpServletRequest
object. You can call the GetAttribute
method to query these attributes. The 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 the 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 set. 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.
Package your Web project and generate the demo.war package
Upload the demo.war package to an OSS bucket, for example, demo-bucket.
Create a function, and then create an initialize handler and a function handler.
The sample code of the function is as follows:
package com.aliyun.fc.example;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.aliyun.fc.runtime.Context;
import com.aliyun.fc.runtime.FcAppLoader;
import com.aliyun.fc.runtime.FunctionComputeLogger;
import com.aliyun.fc.runtime.FunctionInitializer;
import com.aliyun.fc.runtime.HttpRequestHandler;
public class HelloWeb implements FunctionInitializer, HttpRequestHandler {
private FcAppLoader fcAppLoader = new FcAppLoader();
private String ossEndPoint = "YourOSSEndPoint";
private String bucket = "YourOSSBucket";
private String key = "YourWarName";
private String userContextPath = "/2016-08-15/proxy/{YourServideName}/{YourFunctionName}";
@Override
public void initialize(Context context) throws IOException {
FunctionComputeLogger fcLogger = context.getLogger();
fcAppLoader.setFCContext(context);
// Load code from OSS
fcAppLoader.loadCodeFromOSS(ossEndPoint, bucket, key);
// Init webapp from code
fcAppLoader.initApp(userContextPath, HelloWeb.class.getClassLoader());
}
@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response, Context context)
throws IOException, ServletException {
try {
fcAppLoader.forward(request, response);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Reference the Maven repository
<dependency>
<groupId>com.aliyun.fc.runtime</groupId>
<artifactId>fc-java-core</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>com.aliyun.fc.runtime</groupId>
<artifactId>fc-java-common</artifactId>
<version>1.0.0</version>
</dependency>
Java code packaging
Use Maven to package the code
Add the maven-assembly-plugin plug-in to pom.xml
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<appendAssemblyId>false</appendAssemblyId> <! -- this is used for not append id to the jar name -->
</configuration>
<executions>
<execution>
<id>make-assembly</id> <! -- this is used for inheritance merges -->
<phase>package</phase> <! -- bind to the packaging phase -->
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
Package the code
mvn package
The JAR package is stored in the target directory.
Use Maven to package the code, and store the dependencies in the/lib directory as JAR packages.
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 period of time to invoke the function for the first time.
We recommend to store the third-party dependencies in the /lib directory as JAR packages.
A solution using the maven-dependency-plugin plug-in is as follows:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/classes/lib</outputDirectory>
<includeScope>runtime</includeScope>
</configuration>
</execution>
</executions>
</plugin>
After you run mvn package
, the directory of the packaged JAR file is as follows:
*/*.class
lib/*.jar
Use IDEA to package the code
Configure parameters for exporting the JAR file:
Verify packaging results
rockuw-MBP:hello-java (master) $ ls -lrth
total 6520
-rw-r--r-- 1 rockuw staff 3.2M Aug 31 21:03 hellofc.jar
rockuw-MBP:hello-java (master) $ jar -tf hellofc.jar | head
Picked up _JAVA_OPTIONS: -Duser.language=en
META-INF/MANIFEST.MF
example/
example/HelloFC.class
example/SimpleRequest.class
example/SimpleResponse.class
META-INF/
META-INF//
org/
org//
org/apache/