This topic covers the JavaCallout interface exposed by the API Platform message processing pipeline. The API Platform enables you to write custom Java that executes as part of an API proxy Flow. Such Java code can simply modify message content, or it can execute complex asynchronous logic, depending on your requirements.
Java samples, JavaDocs, and required dependencies are available in the API Platform Samples repository on Github.
Overview
To execute Java on the API Platform:
- Write Java code to the interface described in the JavaCallout JavaDocs.
- Compile your Java code against the dependent JAR files.
- Create a Java resource (JAR file) under
/apiproxy/resources/java - Create a matching policy of type
JavaCalloutunder/apiproxy/policies - Attach the policy at the appropriate point in the processing Flow
- Import and deploy your API proxy to the API Platform to debug your Java
Compiling, packaging, and importing
To compile JavaCallout code, you require two JAR files that are available on GitHub:
The sample commands below assume that you are working in the java-callout directory under sample-proxies in the API Platform samples distribution on GitHub. If not, modify your paths appropriately to reflect your environment.
$ javac -d bin -sourcepath src -classpath ../lib/expressions-1.0.0.jar:../lib/message-flow-1.0.0.jar src/com/apigee/{MyClass}.java
Generate a JAR file containing your compiled JavaCallout. For example:
$ jar -cvf {MyClass}.jar /com
Save your JAR files under the /apiproxy/resources/java.
Reference the class file and the JAR that you created in a policy of type JavaCallout. For example:
<JavaCallout name="cityLookUp">
<ClassName>com.apigee.CityLookup</ClassName>
<ResourceURL>java://CityLookup.jar</ResourceURL>
</JavaCallout>
Attach the policy to an Endpoint Flow. In the example below, the JavaCallout executes for any request where the request URI path suffix matches /forecastrss, and the HTTP verb of the request is GET. The name of the step must match the name of the policy.
<Flows>
<Flow name="forecast">
<Condition>(proxy.pathsuffix MatchesPath "/forecastrss") and (request.verb = "GET")</Condition>
<Request>
<Step>
<Name>cityLookUp</Name>
</Step>
</Request>
</Flow>
</Flows>
Upload your API proxy to the API platform. To do so, you can use the deploy tool available on GitHub.
In the example command below, modifymyname, mypass, and myorg to match the settings for your account on enterprise.apigee.com:
$ python tools/deploy.py -n weatherapi -u myname:mypass -o myorg -e test -p / -d simpleProxy
Execution interface
The API Platform JavaCallout API exposes two basic execution interfaces that provide access to Flow variables:
messageContextprovides access to the request/response messageexecutionContextprovides control over flow processing, along with utility methods for asynchronous processing
Note on @IOIntensive annotation
On the API Platform, I/O and other processes that will never block the CPU execute in a core thread pool. The core thread pool is, by default, configured to one thread per CPU. Any process that might block a thread is dispatched to a separate asynchronous thread pool.
This is controlled using an annotation called @IOIntensive. By placing the annotation on the policy class, the Java code executes in the asynchronous thread pool. Where the annotation is not present, Java code executes in an I/O thread.
As a best practice, all JavaCallouts should have @IOIntensive annotation, unless there is a high degree of certainty that no blocking network I/O, no file I/O, no sleeps, etc. are used by the code.
JavaCallout patterns
Implement JavaCallouts according to the patterns described in the following section.
Pattern
Simple blocking
Scenario
Modification and mediation of message content
Example
@IOIntensive
public class SimpleBlockingExecution implements Execution {
public ExecutionResult execute(MessageContext messageContext, ExecutionContext executionContext) {
// message modification, etc
messageContext.getMessage().setHeader("Thread", Thread.currentThread().getName());
messageContext.getMessage().setHeader("SimpleBlockingExecution", "aye");
return ExecutionResult.SUCCESS; // flow continues to next execution
}
}
Pattern
Simple non-blocking
Scenario
For reading a custom response from a file, looking up cache, calling over the network to the other services.
Examples
Example 1: SimpleNonBlockingExecutionReturnError
@IOIntensive
public class SimpleNonBlockingExecutionReturnError implements Execution {
@Override
public ExecutionResult execute(MessageContext messageContext, ExecutionContext executionContext) {
try {
// message modification, IO operations, etc
// do something here that might fail
return ExecutionResult.SUCCESS;
} catch (Exception ex) {
ExecutionResult executionResult = new ExecutionResult(false, ExecutionResult.Action.ABORT);
executionResult.setErrorResponse(ex.getMessage());
executionResult.addErrorResponseHeader("ExceptionClass", ex.getClass().getName());
return new ExecutionResult(false, ExecutionResult.Action.ABORT);
}
}
}
Example 2: SimpleNonBlockingExecutionReturnSuccess
public class SimpleNonBlockingExecutionReturnSuccess implements Execution {
@Override
public ExecutionResult execute(MessageContext messageContext, ExecutionContext executionContext) {
// message modification, IO operations, etc
messageContext.getMessage().setHeader("Thread", Thread.currentThread().getName());
messageContext.getMessage().setHeader("SimpleNonBlockingExecutionReturnSuccess", "aye");
return ExecutionResult.SUCCESS;
}
}
Pattern
Non-blocking and irrelevant to the flow
Scenario
Logging, forwarding data to an analytics server
Examples
Example 1: SubmitTaskAndContinue
public class SubmitTaskAndContinue implements Execution {
private static final Logger logger = LoggerFactory.getLogger("SubmitTaskAndContinue");
public ExecutionResult execute(final MessageContext messageContext, final ExecutionContext executionContext) {
executionContext.submitTask(new Task());
// here onwards, the execution can return success or failure as in Simple Blocking Execution
return ExecutionResult.SUCCESS;
}
private static class Task implements Runnable {
public void run() {
// report to analytics agent
// log to disk
// aggregate statistics
logger.debug("SubmitTaskAndContinue.Task.run()");
}
}
}
Example 2: SubmitTaskAndContinueCallbackHandling
public class SubmitTaskAndContinueCallbackHandling implements Execution {
private static final Logger logger = LoggerFactory.getLogger("SubmitTaskAndContinueCallbackHandling");
public ExecutionResult execute(final MessageContext messageContext, final ExecutionContext executionContext) {
executionContext.submitTask(new Task(), new CallbackTask(), "handback");
return ExecutionResult.SUCCESS;
}
private static class Task implements Runnable {
public void run() {
// report to analytics server
// log to disk
logger.debug("SubmitTaskAndContinueCallbackHandling.Task.run()");
}
}
private static class CallbackTask implements Callback {
public void callback(Runnable task, Object handback) {
// log completion results to disk
logger.debug("SubmitTaskAndContinueCallbackHandling.CallbackTask.run()");
}
}
}
Pattern
Non-blocking and influences of the flow.
Scenario
For callbacks or events based on notificationss
Example
SubmitTaskAndPauseForCompletion
public class SubmitTaskAndPauseForCompletion implements Execution {
private static final Logger logger = LoggerFactory.getLogger("SubmitTaskAndPauseForCompletion");
@Override
public ExecutionResult execute(MessageContext messageContext, ExecutionContext executionContext) {
Message message = messageContext.getMessage();
// Blocking code here
// ...
//
executionContext.submitTask(new AuthenticationTask(), new PostAuthenticationCallback(), executionContext);
// This line resumes after the submitTask is done.
//...
return ExecutionResult.PAUSE; // causes the flow to pause until resumed explicitly again
}
class AuthenticationTask implements Runnable {
Subject subject;
@Override
public void run() {
// Lookup LDAP server and invoke the server
// do service callout
logger.debug("SubmitTaskAndPauseForCompletion.AuthenticationTask.run()");
}
public Subject getSubject() {
return subject;
}
}
class PostAuthenticationCallback implements Callback {
@Override
/**
* Post this method invocation, resume of flow is invoked by the framework
*/
public void callback(Runnable task, Object handback) {
AuthenticationTask authTask = (AuthenticationTask) task;
Subject subject = authTask.getSubject();
logger.debug("SubmitTaskAndPauseForCompletion.PostAuthenticationCallback.callback()");
}
}
}
Post questions to the Apigee Developer Forum.
Back to API Platform Developer Guide.