Servizi HA-Singleton in JBoss

HA-Singleton è un particolare servizio di JBoss che consente ad una applicazione distribuita in un cluster di essere eseguita come singleton (una sola istanza in esecuzione) e, nel contempo, garantire alta disponibilità. Come è facile intuire si tratta di una soluzione adatta per tutte quelle applicazioni che, per motivi progettuali, non possono essere eseguite in istanze multiple. Si prenda ad esempio un semplice job schedulato.

Il principio si basa sulla presenza di un nodo master (allo startup è il primo nodo avviato) su cui vengono eseguite tutte queste applicazioni. In caso di failure del nodo master, il cluster procede ad una elezione di un nuovo master fra i nodi esistenti e su quello vengono avviate le applicazioni.

Il modo più semplice per avere una applicazione in HA-Singleton è di fare il deploy nella directory deploy-hasingleton. Questa directory viene monitorizzata esclusivamente alla partenza del nodo master e il deploy delle applicazioni in essa contenute viene effettuato solo in quel nodo. Lo svantaggio principale di questo tipo di deploy consiste nel fatto che non è possibile fare deploy a caldo in quanto tale directory non viene letta dal deployer in nessun altro momento se non all’avvio del nodo master.

Se prendiamo ad esempio il seguente file scheduler-service.xml che rappresenta un semplice job schedulato:

<?xml version="1.0" encoding="UTF-8"?>
<server>
    <mbean code="org.jboss.varia.scheduler.Scheduler" name="it.test:service=Scheduler">
        <attribute name="StartAtStartup">true</attribute>
        <attribute name="SchedulableClass">it.test.SchedulableClass
        </attribute>
        <attribute name="InitialStartDate">NOW</attribute>
        <attribute name="SchedulePeriod">5000</attribute>
        <attribute name="InitialRepetitions">-1</attribute>
        <depends>
            <mbean code="javax.management.timer.Timer" name="jboss:service=Timer" />
        </depends>
    </mbean>
</server>

Posto all’interno della directory deploy di uno dei nodi, il risultato sarà che il nostro scheduler andrà in esecuzione esclusivamente nel nodo dove l’abbiamo pubblicato. Ma in caso di failure di questo nodo avremo il problema che il nostro scheduler si ferma inesorabilmente.

Se invece lo poniamo all’interno della directory farm avremo che il nostro scheduler andrà distribuito in tutto il cluster, ma anche che sarà avviato su tutti i nodi. E questo, con ogni probabilità, è un effetto indesiderato.

Ponendolo infine nella directory deploy-hasingleton di ciascun nodo (è importante!) e analizzando le varie jmx-console, vedremo che il servizio sarà presente solo e soltanto su un nodo e nella fattispecie il primo avviato.

Questo metodo come abbiamo avuto modo di ricordare non consente deploy a caldo, salvo forzare manualmente il deploy tramite la jmx-console e il servizio MainDeployer. Un po’ una scocciatura. Esiste però un altro metodo, basato sempre sullo stesso servizio, che consente di ottenere lo stesso risultato pratico facendo il canonico deploy della applicazione in farm. E’ sufficiente aggiungere al deployment descriptor la seguente dipendenza:

<depends>jboss.ha:service=HASingletonDeployer,type=Barrier</depends>

Una volta fatto questo, l’applicazione distribuita in farm ha un comportamento interessante: viene effettivamente distribuita sul cluster e su ciascun nodo viene effettuato il deploy, ma viene avviata solo sul nodo master. L’alta disponibilità è garantita dal fatto che nel momento in cui il nodo master andrà down l’applicazione verrà avviata sul neo-eletto master. Inutile dire che questo è il mio modo preferito di pubblicare applicazioni HA-Singleton.

Da notare che il deploy tramite Barrier ha effetto solo sullo start/stop del servizio, non sulla sua creazione o distruzione.

Le date in Java

Una delle domande più ricorrenti del linguaggio Java è perchè la classe java.util.Date non fornisca alcun metodo di conversione e, oltretutto, quelli presenti sono deprecati oramai dalla versione 1.1 del JDK.
Per conoscere la risposta occorre sapere cosa questa classe rappresenta. Una istanza di java.util.Date contiene il numero di millisecondi trascorsi dalla mezzanotte del primo gennaio 1970, ora di Greenwich. Il suo significato quindi è quello di fornire le coordinate assolute di un istante di tempo, con precisione al millisecondo.
Quando si va a convertire una java.util.Date da o verso una data di calendario si hanno due ordini di problemi: il fuso orario (o timezone) e il calendario. Il fatto che l’ora mondiale sia suddivisa in timezone e cosa ben nota, un po’ meno noto è il fatto che non tutti i paesi del mondo adottano lo stesso nostro calendario Gregoriano.
Con il JDK 1.1 è stata introdotta la classe astratta java.util.Calendar, da questa classe derivano le varie implementazioni concrete dei calendari associati ai vari Locale.

java.util.Calendar è quindi astratta, ovvero non può essere istanziata direttamente. Però implementa un metodo statico getInstance() che restituisce la corretta implementazione concreta sulla base del TimeZone e del Locale. Esso è disponibile in quattro overload:

public static Calendar getInstance();
public static Calendar getInstance(TimeZone zone);
public static Calendar getInstance(Locale aLocale);
public static Calendar getInstance(TimeZone zone, Locale aLocale);

In tutti i casi il metodo restituisce una istanza che punta alla data/ora di sistema. Il tipo concreto restituito dipenderà invece dal Locale passato, nella maggior parte dei casi java.util.GregorianCalendar che è una rappresentazione del calendario Gregoriano in vigore in gran parte dei paesi del mondo (compresa ovviamente l’Italia).
Se invece provate a reperire il calendario per la Locale Thailandese:

Calendar.getInstance(new Locale("th","TH"));

vedrete il calendario risultante sarà di tipo sun.util.BuddhistCalendar.
Locale e TimeZone sono due parametri indipendenti l’uno dall’altro, nel senso che si può ottenere un Calendar relativo ad un Locale di un determinato paese del mondo ma che punta ad un fuso orario diverso. Se uno di questi parametri viene ommesso, si assume quello di sistema della JVM in esecuzione.

Una volta ottenuta una istanza di Calendar, questa deve essere utilizzata per qualsiasi conversione e/o operazione sulle date. Il metodo set() consente di impostare sul calendario una data/ora specifica; il metodo add() consente operazioni di aggiunta o sottrazione di un certo numero di unità (ore, giorni, mesi, anni, etc…); i metodi after() e before() consentono di fare comparazioni di data; infine, i metodi setTime() e getTime() consentono la conversione di una specifica data, espressa come java.util.Date, da/a questo Calendar.

JBoss, webservices e Spring

Problema: supponiamo di avere una applicazione (web o non web) basata su Spring e di voler pubblicare come web service su JBoss uno o più degli oggetti business gestiti da Spring.
La prima cosa che salta all’occhio è che qualora si utilizzino le comodissime annotazioni standard per fare il deploy del web service, il vostro oggetto manager non viene istanziato tramite l’IOC container di Spring, quindi diventa sostanzialmente inutilizzabile.
Una soluzione potrebbe essere convertire il progetto a Ejb3, chi può farlo lo faccia, ma chi non può? Per non parlare di chi non vuole…
In questi casi il metodo conveniente è quello di derivare la vostra classe che esporrà il web service da una n-esima classe di supporto di Spring, la SpringBeanAutowiringSupport. Questa classe astratta abilita l’annotazione @Autowire su qualsiasi istanza di oggetto purchè creata all’interno di una Web Application Spring-based. In questo modo potete “iniettare” oggetti configurati nel vostro IOC container di Spring nel vostro Web Service, esattamente come se fosse esso stesso un oggetto di Spring, continuando quindi ad utilizzare il paradigma del inversion of control.