This cookbook topic shows how to implement a policy that executes Java code in the context of an API proxy flow.

Download and try out the sample code

Sample code: We recommend that you download and try out this topic's sample code from Github. The sample is called java-cookbook, which you can find in the doc-samples folder in the Apigee Edge API Services samples repository in Github. For information on downloading Apigee Edge samples, see Using the samples

Instructions for deploying and calling the API are provided in the README file that comes with the sample. You can also find brief deployment instructions here: Deploying and testing samples.

About this cookbook example

Apigee Edge provides a range of out-of-the-box policy capabilities that address common API management requirements. However, there are some cases where your API requires custom behavior that is not implemented in the out-of-the-box policy palette. In these cases, Apigee provides several options that enable developers to script or code customized API behavior. One approach is to implement the desired behavior in Java.

This topic illustrates how to create a simple JavaCallout policy that executes Java code within the context of a proxy flow. The Java code in this example grabs a query parameter from an API request, performs a simple lookup, and modifies the request with a new query parameter. In this case, the backend API called by the proxy is the Yahoo Weather API. The proxy is called like this:

http://myorg-myenv.apigee.net/java-cookbook/forecastrss?city=venice

The Java code executes in the ProxyEndPoint of the proxy flow. It grabs the city name from the query parameter and "looks up" the location key for that city (the WOEID), then calls the Yahoo Weather API with that key. The API then returns weather information for the city.

After working with this cookbook example, you will understand how to implement, compile, and deploy custom Java code for a proxy. 

More advanced use cases for JavaCallout include implementing blocking or non-blocking behavior for I/O intensive operations like doing file I/O, calling remote services, cache handling, and others. These advanced cases will be explored further in future cookbook examples. For more information about these use cases, see <<TBD: link to patterns topic>>.

Before you begin

Before you explore this cookbook example, you should also be familiar with these fundamental concepts:

  • What policies are and how to attach them to proxies. For a good introduction to policies, see Policy attachment and enforcement
  • The structure of a proxy flow, as explained in Flow configurations. Flows let you specify the sequence in which policies are executed by an API proxy. In this example, several policies are created and added to the proxy's flow. 
  • How a proxy project is organized on your filesystem, as explained in API proxy configuration reference.  
  • A working knowledge of Java.

If you have downloaded the sample code, you can locate all of the files discussed in this topic in the mashup-policy-cookbook sample folder. The following sections discuss the sample code in detail. 

Going with the flow

Before moving to the JavaCallout policy and Java code, let's take a look a the main flow of our example proxy. The flow XML, shown below, tells us a lot about this proxy and how the JavaCallout policy is called. 

In the sample download, you can find this XML in the file doc-samples/java-cookbook/apiproxy/proxies/default.xml.

<ProxyEndpoint name="default">
 <FaultRules>
 <FaultRule name="MissingCityName">
 <Condition>fault.name = "ExecutionReturnedFailure"</Condition>
 <Step>
 <Name>Fault.MissingCityName</Name>
 </Step>
 </FaultRule>
 </FaultRules>

 <Flows>
 <Flow name="forecast">
 <Condition>(proxy.pathsuffix MatchesPath &quot;/forecastrss&quot;) and (request.verb = &quot;GET&quot;)
 </Condition>
 <!-- for every request lookupCityName policy -->
 <Request>
 <!-- Java extension-->
 <Step>
 <Name>cityLookUp</Name>
 </Step>
 </Request>
 <Response/>
 </Flow>
 </Flows>

 <HTTPProxyConnection>
 <BasePath>java-cookbook</BasePath>
 <Properties/>
 <VirtualHost>default</VirtualHost>
 </HTTPProxyConnection>

 <RouteRule name="default">
 <TargetEndpoint>default</TargetEndpoint>
 </RouteRule>

</ProxyEndpoint>

Here's a summary of the flow's elements.

Note: This is a ProxyEndpoint flow. The ProxyEndpoint configuration defines the inbound (app-facing) interface for an API proxy. When you configure a ProxyEndpoint, you define how apps invoke the proxied API.

  • <Request> - The <Request> element consists of several <Step> elements. Each step calls one of the policies that we'll create through the rest of this topic. These policies are concerned with creating a request message, sending it, and parsing the response. By the end of this topic, you'll understand the role of each of these policies.
  • <Response> - The <Response> element also includes <Steps>. These steps also call policies that are responsible for processing the final response from the target endpoint (the Google Elevation API). 
  • <HttpProxyConnection> - Specifies details about how apps will connect to this API proxy, including the <BasePath>, which specifies how this API will be called. 
  • <RouteRule> - This element specifies what happens immediately after the inbound request messages are processed. In this case, the TargetEndpoint is called. We'll discuss more about this important step later in this topic.

Referencing the Java class in a JavaCallout policy

Before we take a look at the Java implementation, let's see how to reference a Java class (or classes) in a policy. In this cookbook sample, there is a policy called cityLookup.xml. You can find it in the standard policies directory inside the apiproxy folder. If you downloaded the code, you can find the policy XML here: doc-samples/java-cookbook/apiproxy/policies/cityLookup.xml.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<JavaCallout name="cityLookUp">
 <ClassName>com.apigee.CityLookup</ClassName>
 <ResourceURL>java://CityLookup.jar</ResourceURL>
</JavaCallout>

This is a very simple, straightforward policy! Besides a name (cityLookup), this policy references the main Java class file and a JAR file. As you might expect, the class file and all supporting classes are stored in the JAR. If you refer back to the flow, you will note that the cityLookup policy is called in the <Request> stage of the ProxyEndpoint. This means that the Java class is executed at that stage in the flow.

Let's look at the Java class next, and then at how it is compiled, packaged, and deployed.  

Coding the Java class

This example Java class is deliberately simple and trivial. However, it illustrates some fundamental concepts common to any Java code that is executed through a JavaCallout policy.

In the sample download, you can find this Java source file in doc-samples/java-cookbook/java/src/com/apigee/CityLookup.java.

package com.apigee;

import com.apigee.flow.execution.ExecutionContext;
import com.apigee.flow.execution.ExecutionResult;
import com.apigee.flow.execution.spi.Execution;
import com.apigee.flow.message.MessageContext;

public class CityLookup implements Execution{

 public ExecutionResult execute(MessageContext messageContext, ExecutionContext executionContext) {
 try {
 int w = 0;
 String cityName = messageContext.getMessage().getQueryParam("city").toUpperCase();

 if (cityName.equals("MILAN")) w=718345;
 else if (cityName.equals("ROME")) w=721943;
 else if (cityName.equals("VENICE")) w=725746;
 
 if(w>0) { 
 messageContext.getRequestMessage().setQueryParam("w", w);
 } else {
 //default to palo alto
 messageContext.getRequestMessage().setQueryParam("w", 2467861);
 }

 return ExecutionResult.SUCCESS;

 } catch (Exception e) {
 return ExecutionResult.ABORT;
 }
 }
}

The most important things to remember about this Java implementation are:

  • Imports classes from the com.apigee.flow.execution and com.apigee.flow.message packages. These packages must be included in the JAR file that is packaged and deployed. We'll cover packaging and deployment later in this topic. In the cookbook sample download, you can find these packages in doc-samples/java-cookbook/lib. 
  • Implements Execution. Any Java code that is executed within an API proxy must implement Execution. <<note about javadoc>>
     

Brief detour: error handling

The execute() method in our Java class throws an exception if it can't recognize the city query parameter (only three Italian city names are valid: Milan, Venice, and Rome). On error, the method returns an ExectionResult.ABORT status. To handle this error situation, we added a RaiseFault policy to the flow. If you refer back to the proxy flow shown previously, you will see that this policy, called Fault.MissingCityName, is added to the policy's <FaultRules> element. 

Here is the policy XML. If you downloaded the sample code from Github, you can find this policy XML here: doc-samples/java-cookbook/apiproxy/policies/Fault.MissingCityName.xml.

<RaiseFault name="Fault.MissingCityName">
 <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
 <FaultResponse>
 <Set>
 <Headers/>
 <Payload contentType="application/xml">
 <error>
 <httpResponseCode>409</httpResponseCode>
 <errorCode>error_409</errorCode>
 <errorMessage>Missing City Name in Query Parameter</errorMessage>
 <errorMoreInfo>Valid cities are venice, milan, and rome. </errorMoreInfo>
 </error>
 </Payload>
 <StatusCode>409</StatusCode>
 <ReasonPhrase>Conflict</ReasonPhrase>
 </Set>
 </FaultResponse>
</RaiseFault>

The policy simply sets an error code and provides information about the error. This information is sent to the client. You can read more about the structure of RaiseFault policies in Raise Fault policy

Specifying the default target endpoint

Our final task is to specify the Yahoo Weather API URL on the proxy's TargetEndPoint. If you downloaded the code, you can find the policy XML here: doc-samples/java-cookbook/apiproxy/targets/default.xml. Here is the source code:

<TargetEndpoint name="default">
 <HTTPTargetConnection>
 <Properties/>
 <URL>http://weather.yahooapis.com/</URL>
 </HTTPTargetConnection>
</TargetEndpoint>

Testing the example

Before you can deploy and test this sample, you must first compile the CityLookup.java class and generate a JAR file that contains that class plus the required packages that the custom class requires. The required libraries are expressions-1.0.0.jar and message-flow-1.0.0.jar, and they are located in the doc-samples/java-cookbook/lib folder. 

If you have not already done so, try to download, deploy, and run the java-cookbook sample, which you can find in the doc-samples folder in the Apigee Edge API Services samples repository on Github. Just follow the instructions in the README file in the java-cookbook folder. 

To summarize, you can call the API as follows. Substitute {myorg} with your organization name. This example assumes you are using the test environment. 

curl http://{myorg}-test.apigee.net/java-cookbook/forecastrss?city=venice

The response includes weather information for Venice in XML format. Here are the first few lines of the response:

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<rss version="2.0" xmlns:yweather="http://xml.weather.yahoo.com/ns/rss/1.0" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#">
<channel>

<title>Yahoo! Weather - Venice, IT</title>
<link>http://us.rd.yahoo.com/dailynews/rss/weather/Venice__IT/*http://weather.yahoo.com/forecast/ITXX0085_f.html</link>
<description>Yahoo! Weather for Venice, IT</description>
<language>en-us</language>
<lastBuildDate>Fri, 06 Dec 2013 3:54 pm CET</lastBuildDate>
<ttl>60</ttl>
<yweather:location city="Venice" region="VN" country="Italy"/>
<yweather:units temperature="F" distance="mi" pressure="in" speed="mph"/>
<yweather:wind chill="46" direction="" speed="" />
<yweather:atmosphere humidity="76" visibility="3.73" pressure="29.85" rising="0" />
<yweather:astronomy sunrise="7:35 am" sunset="4:26 pm"/>
...

Remember that the Java class is hard-wired to recognize only Venice, Milan, and Rome. Try invoking the API with a city that is not in this list, like Sacremento.

curl http://{myorg}-test.apigee.net/java-cookbook/forecastrss?city=sacramento

This call results in an error and the JavaCallout policy follows it's Fault path. The resulting message returned is generated by the RaiseFault policy, discussed previously:

<error>
 <httpResponseCode>409</httpResponseCode>
 <errorCode>error_409</errorCode>
 <errorMessage>Missing City Name</errorMessage>
 <errorMoreInfo>Provide a city parameter in the querystring e.g. city=venice</errorMoreInfo>
</error>

Summary

This cookbook topic explained how to use the JavaCallout policy and implement custom Java code using the JavaCallout Java API. We also looked at erropr handling using the RaiseFault policy. 

Get Help

For help, see Apigee Customer Support.

Help or comments?

  • Something's not working: See Apigee Support
  • Something's wrong with the docs: Click Send Feedback in the lower right.
    (Incorrect? Unclear? Broken link? Typo?)