Cache HTTP Client avec CXF

Cache HTTP Client avec CXF

Dans nos applications moderne, le cache est une fonctionnalité technique majeur qui permet de limiter les accès redondant à la même ressources (base de données, fichier, service distant HTTP, …).

On peut implémenter du cache à plusieurs niveau, et généralement nos applications en utilisent sans le savoir (cache OS, cache BDD, cache applicatif de type EhCache, …) et nos browser le font aussi massivement grâce aux directives de cache de la spécification HTTP.

Si vous avait des webservices REST développé avec Apache CXF, vous pouvez vous aussi tirer parti simplement des directives de caches HTTP pour que les clients de vos services puissent mettre en cache les requêtes à vos services et en limiter la charge.

Bien qu’il existe plusieurs type de mise en cache via directives HTTP, je vais uniquement m’attarder ici sur la directive HTTP Cache-Control : https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control

CXF n’implémentant par les directives de cache HTTP et JaxRS ne contenant pas d’annotations permettant de mettre des headers arbitraires sur une des opérations de votre web service REST, je vais vous proposer ici une façon simple et élégante de faire.

Pour commencer, création d’une annotation @CacheControl qui nous permettera de spécifier la directive de cache que nous voudrions envoyer au client

@Target(ElementType.METHOD )
@Retention(RetentionPolicy.RUNTIME)
public @interface CacheControl {
    String value() default "no-cache";
}

Cette annotation sera ensuite utilisée pour configurer une directive de cache HTTP sur votre service web CXF JaxRS

@CacheControl("max-age=600")
@GET
@Path("/person")
public Person getPerson(String name) {
    return personService.getPerson(name);
}

Pour que cette annotation soit transformée en header de cache par CXF, il faut implémenter un interceptor CXF qui se chargera de récupérer les informations de l’annotations et d’ajouter le header nécessaire.

public class CacheInterceptor extends AbstractOutDatabindingInterceptor{
    public CacheInterceptor() {
        super(Phase.MARSHAL);
    }

    @Override
    public void handleMessage(Message outMessage) throws Fault {
        //search for a CacheControl annotation on the operation
        OperationResourceInfo resourceInfo = outMessage.getExchange().get(OperationResourceInfo.class);
        CacheControl cacheControl = null;
        for (Annotation annot : resourceInfo.getOutAnnotations()) {
            if(annot instanceof CacheControl) {
                cacheControl = (CacheControl) annot;
                break;
            }
        }

        //fast path for no cache control
        if(cacheControl == null) {
            return;
        }

        //search for existing headers or create new ones
        Map<String, List<String>> headers = (Map<String, List<String>>) outMessage.get(Message.PROTOCOL_HEADERS);
        if (headers == null) {
            headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
            outMessage.put(Message.PROTOCOL_HEADERS, headers);
        }

        //add Cache-Control header
        headers.put("Cache-Control", Collections.singletonList(cacheControl.value()));
    }
}

Pour terminer, il faut ajouter cet intercepteur à votre server CXF, cela dépend de la manière dont vous définissez votre serveur CXF (standalone, spring, …), plus d’info sur les intercepteurs ici : http://cxf.apache.org/docs/interceptors.html

Par exemple, si vous utilisez Spring, vous pouvez ajouter les lignes suivantes à votre fichier cxf-servlet.xml :

<jaxrs:server id="v1Server" address="/v1">	
	<jaxrs:serviceBeans >
		<ref bean="personRestWS"/>
	</jaxrs:serviceBeans>
		
	<!-- other stuff goes here ... -->
		
	<jaxrs:outInterceptors>
	    <ref bean="cacheControlInterceptor" />
	 </jaxrs:outInterceptors>		 
</jaxrs:server>

<bean id="cacheControlInterceptor" class="fr.loicmathieur.rest.cache.CacheInterceptor" />

Laisser un commentaire

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.