Magnolia 6.1 reached end of life on March 31, 2021. This branch is no longer supported, see End-of-life policy.
Magnolia REST client module enables easy creation, configuration and usage of REST clients. All it takes is a few clicks and a few bits of Java code. The module:
restfn
templating functions that give access to REST clients within FreeMarker template scripts.You also can obtain a REST client within Java to build apps, custom fields for dialogs and similar things.
This page describes the module, explains how to install it and gives a quick reference how to declare, configure and use REST clients.
<dependency> <groupId>info.magnolia.restclient</groupId> <artifactId>magnolia-rest-client-app</artifactId> <version>1.7</version> </dependency>
magnolia-rest-client-app
to your bundle automatically adds the magnolia-rest-client
and magnolia-resteasy-client
modules.Add all the submodules to your bundle.
All preconfigured Magnolia bundles (besides magnolia-empty-webapp) contain magnolia-resteasy-client but not magnolia-rest-client-app.
magnolia-resteasy-client
: The easy-to-use Magnolia REST client implementation. It enables interface based service declaration. The module has a dependency to RESTEasy from JBoss.magnolia-rest-client-app
: Installs an app to test REST clients in the browser. You can test your clients in the app before using them in template scripts, models or other Java classes. See Testing clients in the REST client app. magnolia-rest-client
: Defines the API for a Magnolia REST client definition class (to configure), for a REST client factory (to create instances) and for a registry (to obtain configured clients). It is an abstract layer which, besides the registry, you will hardly ever use directly. If you have existing Java clients based on jersey or raw javax.ws.rs, you can implement a custom ClientFactory to instantiate your existing custom clients the Magnolia way. You can configure REST clients within all Magnolia modules in the rest-client
folder.
Properties
<rest client name> | required Add a node for each REST client. The name is arbitrary, but it is good practice to use a descriptive name. The node name is used when obtaining instances of clients. |
| required The base URL of the REST endpoint to connect with the client. |
| required The definition class, it must implement ConfiguredRestClientDefinition, for instance use RestEasyClientDefinition or SSLRestEasyClientDefinition for secure connections or subclasses. |
| required The client factory class. Must implement DefaultClientFactory. Use RestEasyClientFactory or SSLRestEasyClientFactory for secure connections or subclasses. |
| optional, default is Enables or disables a configured client. |
components | optional A list of components (e.g. Registered components will be executed on every request. |
| optional A map of ClientServiceDefinition objects. |
| required, default value is An integer specifying the HTTP port number for a REST Easy client definition with SSL support. |
| required, default value is An integer specifying the HTTPS port number for a REST Easy client definition with SSL support. |
| optional A string containing a keystore password. Used with a REST Easy client definition with SSL support. |
| optional A string specifying the location of a keystore. Used with a REST Easy client definition with SSL support. |
| optional A string specifying the keystore type. Used with a REST Easy client definition with SSL support. |
| required A string specifying the password for the certification trust store. Required when used with a REST Easy client definition with SSL support. |
| required A string specifying the location of the certification trust store. Required when used with a REST Easy client definition with SSL support. |
| required A string specifying the type of the certification trust store. Required when used with a REST Easy client definition with SSL support. |
| optional A map of AbstractRestCall objects. |
| required Add a node for each call type. The name is arbitrary. |
| optional A hashmap of accepted request template values. |
| optional A hashmap of accepted request query parameters. |
| optional A hashmap of accepted request cookies. |
| optional A hashmap of accepted request headers. |
| required The following subclasses can be used depending on the type of the call:
|
| required The entity type of the REST call. |
| required The definition of the path for the REST call. |
Example: A configuration of a REST client called posts
for GET and POST calls to https://jsonplaceholder.typicode.com/posts
.
Node name | Value |
---|---|
modules | |
<your-module> | |
rest-client | |
posts | |
restCalls | |
all | |
templateValues | |
queryParams | |
cookies | |
headers | |
class | info.magnolia.rest.client.call.GetRestCall |
entityClass | java.lang.String |
path | /posts |
create | |
class | info.magnolia.rest.client.call.PostRestCall |
entityClass | java.lang.String |
path | /posts |
baseUrl | https://jsonplaceholder.typicode.com |
class | info.magnolia.rest.client.ConfiguredRestClientDefinition |
clientFactoryClass | info.magnolia.rest.client.factory.DefaultClientFactory |
Create a service interface.
When working with a RestEasy client, a service must be declared as a Java interface using
javax.ws.rs
annotations.
Note that a Java interface must be part of a Magnolia Maven module, it is not possible to have Java classes or interfaces within a pure file-based module (light module).
Example: Declaring an interface to get a client that requests the URL
http://api.icndb.com/jokes/random?firstName=John&lastName=Doe
.
import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; public interface IcndbService { @GET @Path("/jokes/{select}") @Produces(MediaType.APPLICATION_JSON) JsonNode joke(@PathParam("select") String select, @QueryParam("firstName") String firstName, @QueryParam("lastName") String lastName); }
@Produces
declaration must correspond. In the example above the method #joke will return a org.codehaus.jackson.JsonNode
object - this fits well together with @Produces(MediaType.APPLICATION_JSON)
.@Path
annotation is relative to the baseUrl of this java client. In this example the relative URL starts with /jokes followed by a parameter. The baseUrl itself is defined in the configuration.You can configure REST clients within all Magnolia modules in the rest-client
folder.
Example: REST Easy client configuration
Node name | Value |
---|---|
modules | |
<your-module> | |
rest-client | |
icndbClient | |
baseUrl | http://api.icndb.com |
class | info.magnolia.resteasy.client.RestEasyClientDefinition |
clientFactoryClass | info.magnolia.resteasy.client.factory.RestEasyClientFactory |
components | |
authentication | |
class | org.example.filters.AuthenticationInterceptor |
Properties:
<rest client name> | required Add a node for each REST client. The name is arbitrary, but it is good practice to use a descriptive name. The node name is used when obtaining instances of clients. |
| required The base URL of the REST endpoint to connect with the client. |
| required The definition class, it must implement ConfiguredRestClientDefinition, for instance use RestEasyClientDefinition or SSLRestEasyClientDefinition for secure connections or subclasses. |
| required The client factory class. Must implement DefaultClientFactory. Use RestEasyClientFactory or SSLRestEasyClientFactory for secure connections or subclasses. |
| optional, default is Enables the underlying caching mechanism provided by RESTEasy. |
components | optional 1.0.8+ A list of components (e.g. |
| optional components instead A list of components (e.g. |
| optional A map of ClientServiceDefinition objects. |
Note that ClientErrorInterceptor
is not supported.
To test a client:
info.magnolia.documentation.modules.restclientexamples.client.IcndbService
, and click Confirm. IcndbService
has 3 methods.The restfn templating functions allow you to obtain and use REST clients. This works only for clients that have been configured and declared with RESTEasy client.
Example:
[#assign jokesService = restfn.getService("icndbClient", "info.magnolia.documentation.modules.restclientexamples.client.IcndbService")] [#assign response = jokesService.joke("random", "Tiger", "Lilly") /] <i>${response.get("value").get("joke").getTextValue()!"Nix found"}</i>
IcndbService
. icndbClient
), info.magnolia.documentation.modules.restclientexamples.client.IcndbService
).#joke
which returns a JsonNode
object.Note that is a simplified example. See jackson javadoc how to further process the JsonNode
or assign a raw response to a JavaScript variable to process JSON in a JavaScript context.
<script> var jokeJson = '${jokesService.joke("random", "Tiger", "Lilly")!}'; </script>
During installation of the magnolia-resteasy-client
module, RestEasyClientModuleVersionHandler adds restfn
to renderer configurations. Make sure that the renderType
of your template definition points to a renderer that is configured to use restfn
. See Configure the functions in a renderer on how to add templating functions to a renderer.
To explain how to obtain and use REST client, we look at a model class which uses the same client as in the above example:
public class JokesModel<RD extends RenderableDefinition> extends RenderingModelImpl<RD> { private static final Logger log = LoggerFactory.getLogger(JokesModel.class); private final RestClientRegistry restClientRegistry; public JokesModel(Node content, RD definition, RenderingModel<?> parent, RestClientRegistry restClientRegistry) { super(content, definition, parent); this.restClientRegistry = restClientRegistry; } public String getRandomJoke() { String joke = "No joke! Failed to fetch it due to an error. :-("; RestEasyClient client = null; IcndbService service = null; try { client = (RestEasyClient) restClientRegistry.getRestClient("icndbClient"); service = client.getClientService(IcndbService.class); } catch (RegistrationException e) { log.error("Failed to get a client for [icndbClient].", e); } if (service != null) { JsonNode response = service.joke("random", "Tiger", "Lilly"); try { joke = response.get("value").get("joke").getTextValue(); } catch (Exception e) { log.error("Failed to fetch the joke when parsing response.", e); } } return joke; } }
IcndbService
. (See line 18.)You have the option to configure components (filters) to be executed on every request. One use case could be authentication.
Here is a sample filter that would handle authorization for each request.
package org.example.filters; import info.magnolia.context.Context; import info.magnolia.context.MgnlContext; import java.io.IOException; import javax.ws.rs.client.ClientRequestContext; import javax.ws.rs.client.ClientRequestFilter; import javax.ws.rs.ext.Provider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Interceptor for rest easy to handle authentication with an access token. */ @Provider public class AuthenticationInterceptor implements ClientRequestFilter { private static final Logger log = LoggerFactory.getLogger(AuthenticationInterceptor.class); @Override public void filter(ClientRequestContext requestContext) throws IOException { if (MgnlContext.hasInstance() && MgnlContext.hasAttribute("ACCESS_TOKEN", Context.APPLICATION_SCOPE)) { requestContext.getHeaders().putSingle("Authorization", "OAuth " + MgnlContext.getAttribute("ACCESS_TOKEN", Context.APPLICATION_SCOPE)); log.trace("Authorization : {}", (String) MgnlContext.getAttribute("ACCESS_TOKEN", Context.APPLICATION_SCOPE)); } } }
1 Comment
Will Scheidegger
I just now discovered, that there was a new type of rest client definitions added to Magnolia 6.1: One without the need to define a RestEasy interface it seems. However: Could. it be that only RestEasy clients can be tested in the rest-client app? The app is asking for a mandatory "Rest service" and expects a fully qualified class name – which we of course don't have without the RestEasy service interface...