Learning to Use Cortex Java SDK
Cortex Java SDK (Software Development Kit) can consume Cortex resources in client applications that are built with Java.
For an example to review and use, see cortex-sdk-example.
Setup
Cortex Java SDK is available from the Elastic Path Public Maven Repository.
To add Cortex Java SDK, add the following dependencies to your Maven POM
:
<dependency>
<groupId>com.elasticpath.rest</groupId>
<artifactId>cortex-jaxrs-client</artifactId>
<version>2.1.0</version>
</dependency>
Usage
Cortex Java SDK is based on the JAX-RS Java API and includes a set of extensions to facilitate development with Cortex. The SDK provides both a straight JAX-RS Client service as well as a user-scoped Cortex client that scopes Cortex calls to a specific user.
CortexClient
The Cortex client provides the following:
- A user-scoped wrapper around the JAX-RS client to facilitate REST calls to Cortex
- JSON Unmarhsaller types to marshall/unmarshal JSONs
tip
Storing Client Instances:
Client instances should not be stored between requests.
Using Cortex Client
To use the Cortex client, you must manually instantiate the required beans. The following example shows how to use a Spring application context with the Cortex client.
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
<bean id="jacksonObjectMapper" class="com.elasticpath.example.ApplicationObjectMapper" init-method="init"/>
<!-- Note that this is a custom com.fasterxml.jacson.databind.ObjectMapper extension which disables the
FAIL_ON_UNKNOWN_PROPERTIES on initialization. this.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) -->
<bean id="jsonUnmarshaller" class="com.fasterxml.jackson.contrib.jsonpath.DefaultJsonUnmarshaller"/>
<bean id="zoomModelIntrospector" class="com.elasticpath.rest.client.urlbuilding.zoom.ZoomModelIntrospector"/>
<bean id="zoomQueryFactory" class="com.elasticpath.rest.client.urlbuilding.zoom.ZoomQueryFactory"/>
<bean id="formatModelIntrospector" class="com.elasticpath.rest.client.urlbuilding.format.FormatModelIntrospector"/>
<bean id="formatQueryFactory" class="com.elasticpath.rest.client.urlbuilding.format.FormatQueryFactory"/>
<bean id="cortexUrlFactory" class="com.elasticpath.rest.client.urlbuilding.impl.CortexUrlFactoryImpl">
<constructor-arg ref="zoomModelIntrospector"/>
<constructor-arg ref="zoomQueryFactory"/>
<constructor-arg ref="formatModelIntrospector"/>
<constructor-arg ref="formatQueryFactory"/>
</bean>
<bean id="jacksonProvider" class="com.elasticpath.rest.client.unmarshalling.JacksonProvider" >
<constructor-arg ref="jacksonObjectMapper"/>
</bean>
<bean id="jsonUnmarshallReaderInterceptor" class="com.elasticpath.rest.client.unmarshalling.JsonUnmarshallReaderInterceptor">
<constructor-arg ref="jsonUnmarshaller"/>
</bean>
</beans>
public CortexClientFactory exampleCortexClientFactorySetup(final ApplicationContext context) {
ClientConfig clientConfig = new ClientConfig();
clientConfig.register(new WebApplicationExceptionMapper())
.register(jacksonProvider)
.register(jsonUnmarshallReaderInterceptor)
.register(new LoggingFilter(Logger.getLogger(App.class.getName()), true)) // Should be configurable at runtime
.property(ClientProperties.CONNECT_TIMEOUT, CONNECT_TIMEOUT) // Should be configurable at runtime
.property(ClientProperties.READ_TIMEOUT, READ_TIMEOUT); // Should be configurable at runtime
//Real implementation will want to pass configurable values to this builder.
HttpClientConfigurator httpClientConfigurator = new HttpClientConfiguratorBuilder().build();
httpClientConfigurator.configure(clientConfig);
Client jaxRsClient = ClientBuilder.newClient(clientConfig);
CortexUrlFactory cortexUrlFactory = (CortexUrlFactory) context.getBean("cortexUrlFactoryImpl");
//Cortex url should be configurable at runtime.
return new CortexClientFactoryImpl(cortexUrlFactory, jaxRsClient, "http://localhost:9080/cortex/");
}
Sending Requests
After a client is obtained, it can be used to fetch a view:
CartView cart = client.get(CartView.class);
CartView is a custom class with @JsonPath
and @JsonProperty
annotations.
Constructing URIs
Cortex Java SDK provides four class level annotations to construct Cortex resource URLs:
@Uri
: Defines the URI of a Cortex entry point resource@Zoom
: Adds a Zoom query parameter to the request URL@Path
: Used together with@Zoom
,@Path
specifies the relation/link to follow from the Zoom request@FollowLocation
: Adds a FollowLocation query parameter to the request URL@Format
: Adds a Format query parameter to the request URL
Zoom
, FollowLocation
and Format
Example: A class that models the expected response should be annotated as follows:
@Zoom(@Path("order"))
@FollowLocation
@Format({FormatOptionEnum.STANDARDLINKS, FormatOptionEnum.ZOOM_NOSELF, FormatOptionEnum.ZOOM_NODATALINKS})
public class Cart {}
URLs with Zoom, FollowLocation or Format query parameters can be constructed by calling the createResourceUrlWithQueryParameters()
method:
baseUrl = "http://localhost:9080/cortex/"
path = "carts/mobee/default"
result = cortexUrlFactory.addQueryParametersToResourceUrl(baseUrl, path, Cart.class)
//result = "http://localhost:9080/cortex/carts/mobee/default?zoom=order&followLocation=true"
Example: Without Query Parameters
URLs without query parameters can be constructed by calling the createResourceUrl()
method:
baseUrl = "http://localhost:9080/cortex/"
path = "carts/mobee/default"
result = cortexUrlFactory.createResourceUrl(baseUrl, path)
//result = "http://localhost:9080/cortex/carts/mobee/default"
Example: With Entry Point Resource
A class that models the expected response is annotated as follows:
@Uri(“carts/{scope}/default”)
@Zoom(@Path("lineitems"))
@FollowLocation
public class Carts {}
URLs using entry point URIs can be constructed by calling the createResourceUrlFromAnnotations()
method:
baseUrl = "http://localhost:9080/cortex/"
store = "mobee"
result = cortexUrlFactory.createResourceUrlFromAnnotations(baseUrl, store, Carts.class)
//result = "http://localhost:9080/cortex/carts/mobee/default?zoom=lineitems&followLocation=true"
Zoom
, FollowLocation
and Format
Query Parameters
Example: Programmatically Specify String cartUri = "carts/mobee/default";
Map<String, Object> queryParameters = ImmutableMap.of("zoom", "lineitems",
"format", "standardlinks,zoom.noself,zoom.nodatalinks",
"followLocation", "true");
String cartJsonResponseString = cortexClient.get(cartUri, queryParameters).getCortexView();
tip
An alternative way to get response without annotation:
The CortexClient#get(String resourcePath, Map<String, Object> queryParameters)
method accepts query parameters. You can customize the query parameters without an annotation. It works like a proxying request to Cortex and forwards a raw JSON string response back to the caller.
Unmarshalling Cortex Responses
Cortex Java SDK unmarshalling is provided by the Elastic Path JSON Unmarshaller, an open source project. This section shows some simple examples of how to use the JSON Unmarshaller. For more complex unmarhalling examples, see the JSON Unmarshaller project page.
Cortex Java SDK provides two annotations to extract data from a Cortex response:
@JsonProperty
: Extracts a JSON property from a Cortex response@JsonPath
: Uses JSONPath to extract either a single or multiple JSON properties from a Cortex response. This annotation is useful to extract nested JSON properties
tip
JSONPath Expression Tester
Cortex Studio comes bundled with a JSONPath tester for creating and testing JSONPath expressions.
The examples below parse this response:
{
"self": {
"type": "elasticpath.carts.cart",
"uri": "/carts/mobee/gwu=?zoom=lineitems:element",
"href": "http://api.demo.elasticpath.com/cortex/carts/mobee/gwu=?zoom=lineitems:element"
},
"total-quantity": 1,
"_lineitems": [
{
"_element": [
{
"self": {
"type": "elasticpath.carts.line-item",
"uri": "/carts/mobee/gwu=/lineitems/gq4=",
"href": "http://api.demo.elasticpath.com/cortex/carts/mobee/gwu=/lineitems/gq4="
},
"links": [
{
"rel": "list",
"type": "elasticpath.collections.links",
"uri": "/carts/mobee/gwu=/lineitems",
"href": "http://api.demo.elasticpath.com/cortex/carts/mobee/gwu=/lineitems"
}
],
"quantity": 1
}
]
}
],
"links": [
{
"rel": "lineitems",
"rev": "cart",
"type": "elasticpath.collections.links",
"uri": "/carts/mobee/gwu=/lineitems",
"href": "http://api.demo.elasticpath.com/cortex/carts/mobee/gwu=/lineitems"
},
{
"rel": "order",
"rev": "cart",
"type": "elasticpath.orders.order",
"uri": "/orders/mobee/hbr=",
"href": "http://api.demo.elasticpath.com/cortex/orders/mobee/hbr="
},
{
"rel": "total",
"rev": "cart",
"type": "elasticpath.totals.total",
"uri": "/totals/carts/mobee/gwu=",
"href": "http://api.demo.elasticpath.com/cortex/totals/carts/mobee/gwu="
}
]
}
@JsonProperty
Example: Extract JSON Property using Extract a single, non-nested, JSON property:
public class Cart {
@JsonProperty("total-quantity")
private String totalQuantity;
}
@JsonPath
Example: Extract Nested JSON Properties using Extract nested JSON properties:
public class Cart {
//Non-nested JSON property
@JsonPath("$.total-quantity")
private int totalQuantity;
//Nested Property
@JsonPath("$._lineitems[0]._element[0].quantity")
private int quantity;
//Nested Array
@JsonPath("$._lineitems[0]._element")
private List<LineItem> lineItems;
}