Such was the case when I first had to invoke a RESTful web service in AEM. The Apache HttpClient is a decent library. It comes to OOTB in AEM. It is a pretty basic tool. It's great for one-offs (i.e. I just want to grep a web page and parse things out) but it can quickly become cumbersome if you are invoking multiple services with different content types, SSL, authentication, cookies, headers or encodings. Your code can quickly become a patchwork of hacks to address all those things.
OpenFeign
I first used OpenFeign when I wrote my first RESTful client in Java years ago. To be honest, I don’t know much about the genesis of this project. I know it started as a Netflix OSS project but now it does not seem associated with Netflix anymore. I don’t know the authors, and there does not seem to be an official project website or wiki, other than the extensive README file. You need support? Go look at the source. Why do I like it? Its pluggable, flexible, and lets me focus on working with the results from a REST service and not having to flip a bazillion switches to get an Apache HTTP request just right.
In the ui.apps/pom.xml add the following to the dependencies section
and to the list of embedded decencies of the content-package-maven-plugin configuration
Feign will now get into the OSGi container, but to actually start using it you’ll need to update the dependencies of your own bundle under core/pom.xml by adding the following to the dependencies section
Setting Up A Mock Service
If you already have a web service you want to invoke, then you can skip this part. Otherwise, we will take a little detour and set up a mock REST service so we can test. We like Docker, so let set something up with a docker-compose.yml file
You’ll also need wiremock/__files/list-items.json
and wiremock/mappings/list-items.json
Fire up the docker container with docker-compose up -d and access the WireMock admin endpoint at http://localhost:8080/__admin where you’ll see the configured request. If you set up a PostMan request with the configured basic authentication, you should be able to get a response from http://localhost:8080/list-items.
I first used OpenFeign when I wrote my first RESTful client in Java years ago. To be honest, I don’t know much about the genesis of this project. I know it started as a Netflix OSS project but now it does not seem associated with Netflix anymore. I don’t know the authors, and there does not seem to be an official project website or wiki, other than the extensive README file. You need support? Go look at the source. Why do I like it? Its pluggable, flexible, and lets me focus on working with the results from a REST service and not having to flip a bazillion switches to get an Apache HTTP request just right.
Start A New AEM Project
To get started, I am creating a new AEM project using the Lazybones template, with pretty much all the defaults. If you have an existing project, look carefully, and adapt.
To get started, I am creating a new AEM project using the Lazybones template, with pretty much all the defaults. If you have an existing project, look carefully, and adapt.
lazybones create aem-multimodule-project aem-feign
Adding Dependencies
Apache HttpClient comes with AEM OOTB, great! Feign does not. To get it in the box means getting the OSGi bundle into the OSGi container, Apache Felix. This can be done in several ways. You can upload straight to the Felix console, you can embed them in a content package, or you can inline them into your own bundle. Lucky for us feign-core is distributed as an OSGi bundle, so we are ready to rock-n-roll. The easiest way for this tutorial is to embed it into the project’s ui.apps package.
Depending on your AEM version, you may or may not have Jackson or Gson. In 6.4, both are included OOTB so we are going to add the feign-jackson dependency to do our parsing.
In the root pom.xml, add the following to the dependencyManagement section
Adding Dependencies
Apache HttpClient comes with AEM OOTB, great! Feign does not. To get it in the box means getting the OSGi bundle into the OSGi container, Apache Felix. This can be done in several ways. You can upload straight to the Felix console, you can embed them in a content package, or you can inline them into your own bundle. Lucky for us feign-core is distributed as an OSGi bundle, so we are ready to rock-n-roll. The easiest way for this tutorial is to embed it into the project’s ui.apps package.
Depending on your AEM version, you may or may not have Jackson or Gson. In 6.4, both are included OOTB so we are going to add the feign-jackson dependency to do our parsing.
In the root pom.xml, add the following to the dependencyManagement section
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-core</artifactId>
<version>9.7.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-jackson</artifactId>
<version>9.7.0</version>
<scope>provided</scope>
</dependency>
In the ui.apps/pom.xml add the following to the dependencies section
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-core</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-jackson</artifactId>
</dependency>
and to the list of embedded decencies of the content-package-maven-plugin configuration
<embedded>
<groupId>io.github.openfeign</groupId>
<target>/apps/my-aem-project/install</target>
</embedded>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-core</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-jackson</artifactId>
</dependency>
Setting Up A Mock Service
If you already have a web service you want to invoke, then you can skip this part. Otherwise, we will take a little detour and set up a mock REST service so we can test. We like Docker, so let set something up with a docker-compose.yml file
version: "3.3"
services:
wiremock:
image: rodolpheche/wiremock:2.23.2-alpine
ports:
- 8080:8080
volumes:
- ./wiremock:/home/wiremock
You’ll also need wiremock/__files/list-items.json
[
{ "name": "foo" },
{ "name": "bar" }
]
{
"request": {
"method": "GET",
"url": "/list-items",
"basicAuth": {
"username": "bill",
"password": "lumbergh"
}
},
"response": {
"status": 200,
"bodyFileName": "list-items.json"
}
}
Fire up the docker container with docker-compose up -d and access the WireMock admin endpoint at http://localhost:8080/__admin where you’ll see the configured request. If you set up a PostMan request with the configured basic authentication, you should be able to get a response from http://localhost:8080/list-items.
Invoking The Service
Via HttpClient
For comparison, here is the HttpClient implementation. When I was writing this up, I ran into issues right away with getting the basic auth set up, and deserializing the response stream.
Via Feign
You’ll need to define an interface and set the annotations as necessary, a pretty trivial task
and create an instance of the client based on your contract
Thats a lot less code to read, write, maintain and unit test.
For comparison, here is the HttpClient implementation. When I was writing this up, I ran into issues right away with getting the basic auth set up, and deserializing the response stream.
final CredentialsProvider credentialProvider = new BasicCredentialsProvider();
final UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(this.username, this.password);
credentialProvider.setCredentials(AuthScope.ANY, credentials);
final HttpHost targetHost = HttpHost.create(this.host);
final AuthCache authCache = new BasicAuthCache();
authCache.put(targetHost, new BasicScheme());
final HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(credentialProvider);
context.setAuthCache(authCache);
final HttpClient client = HttpClientBuilder.create()
.build();
final HttpResponse httpResponse = client.execute(new HttpGet(this.host + "/list-items"), context);
final int statusCode = httpResponse.getStatusLine()
.getStatusCode();
List<Item> listItems = Collections.emptyList();
if (statusCode == HttpStatus.SC_OK) {
final InputStream content = httpResponse.getEntity()
.getContent();
final Item[] items = new ObjectMapper().readValue(content, Item[].class);
listItems = Arrays.asList(items);
}
Via Feign
You’ll need to define an interface and set the annotations as necessary, a pretty trivial task
interface ItemService {
@RequestLine("GET /list-items")
List<Item> listItems();
}
final ItemService api = Feign.builder()
.decoder(new JacksonDecoder())
.requestInterceptor(new BasicAuthRequestInterceptor(this.username, this.password))
.target(ItemService.class, this.host);
List<Item> listItems = api.listItems();
Conclusion
HttpClient is a good API but can sometimes be cumbersome. You wind up doing a lot more plumbing. Feign offers all good API to take care of all that plumbing but it is OSS. It is also limited to text-based APIs. Depending on your needs/constraints it may make sense to switch to an API like Feign. The complete project can be found here.
HttpClient is a good API but can sometimes be cumbersome. You wind up doing a lot more plumbing. Feign offers all good API to take care of all that plumbing but it is OSS. It is also limited to text-based APIs. Depending on your needs/constraints it may make sense to switch to an API like Feign. The complete project can be found here.
No comments:
Post a Comment
If you have any doubts or questions, please let us know.