Was this helpful?

Note: Java Callout is not supported in free trial accounts.

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:

  1. Write Java code to the interface described in the JavaCallout JavaDocs.
  2. Compile your Java code against the dependent JAR files.
  3. Create a Java resource (JAR file) under /apiproxy/resources/java
  4. Create a matching policy of type JavaCallout under /apiproxy/policies
  5. Attach the policy at the appropriate point in the processing Flow
  6. 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 &quot;/forecastrss&quot;) and (request.verb = &quot;GET&quot;)</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, modify myname, 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:

  • messageContext provides access to the request/response message
  • executionContext provides 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.