In programmazione il contesto è l’insieme di tutte le informazioni che in qualche modo possono condizionare l’unità di lavoro corrente. Ad esempio l’ambiente di esecuzione utilizzato, le variabili di ambiente, le variabili di istanza, le variabili locali, lo stato di altre classi, lo stato dell’ambiente corrente, ecc. In molte librerie troviamo il contesto in interfacce o classi, come ad esempio ServletContext
di Servlet, FacesContext
di JSF, ApplicationContext
di Spring, Context
di Android, InitialContext
di JNDI, ecc. Molto spesso seguono il pattern facade che astrae i dettagli implementativi che l’utilizzatore non ha bisogno di conoscere raggruppando tutti i metodo in una singola interfaccia o classe.
In questo articolo mostriamo una interessante implementazione di un contesto di esecuzione per le delle API web (es. SOAP o REST) che mantiene isolati i contesti di chiamate distinte, ovvero ciascuna chiamata opera su un proprio contesto che non è condiviso con le altre.
ThreadLocal
Per poter creare contesti distinti per ogni invocazione è necessario disporre di una funzionalità che consenta di gestire variabili che possono essere lette o scritte esclusivamente all’interno dello stesso thread. Questo perchè indipendentemente dal framework utilizzato (Jax-ws, Axis, Jax-rs, etc.) quello che accade è che ad ogni richiesta un nuovo thread viene creato o prelevato da un pool di thread esistenti per gestirla.
A tale scopo java ci mette a disposizione la classe java.lang.ThreadLoca
che consente di creare variabili la cui visibilità è limitata ad un singolo thread. E’ sufficiente generare un oggetto ThreadLocal
per tutti i thread e poi utilizzare i metodi get()
e set()
per accedere alla variabile. La particolarità è che anche se diversi thread accedono allo stesso oggetto ThreadLocal
(magari nella stessa porzione di codice) comunque il valore che vedono è diverso da quello degli altri thread.
Contesto di Esecuzione
Vediamo quindi come sia possibile implementare un contesto di esecuzione per le richieste soap o rest utilizzando un oggetto ThreadLocal
. In realtà nell’articolo JAX-WS Interceptor abbiamo già visto come fare ma ci siamo principalmente concentrati su come intercettare una richiesta. In quell’occasione abbiamo introdotto un oggetto RequestContextHolder
così definito:
1 2 3 4 5 6 7 8 9 |
public class RequestContextHolder { private static final ThreadLocal<Header> requestHeaderHolder = new ThreadLocal<Header>(); public static void setRequestHeader( Header header ) { requestHeaderHolder.set( header ); } public static Header getCurrentRequestHeader() { return requestHeaderHolder.get(); } } |
La classe espone solamente metodi statici che consentono di impostare il valore di un oggetto Header.
In tale oggetto sono valorizzate informazioni di contesto che in generale poco hanno a che fare con il metodo invocato dal client della nostra ipotetica interfaccia webservice e che probabilmente non vogliamo trasferire ai metodi che poi vengono effettivamente invocati nel nostro servizio.
Questo non significa che in un qualche punto del nostro codice non ci sia la necessità di accedere all’oggetto Header
ma mantenendolo isolato nella variabile ThreadLocal
possiamo accedervi immediatamente utilizzando il relativo metodo get()
.
In un prossimo articolo vedremo come utilizzando l’interceptor presentato in JAX-WS Interceptor ed il contesto RequestContextHolder
per implementare un nuovo scope di Spring.