L’Annotation Processor è stato reso disponibile per facilitare la creazione dei file necessari a configurare i servizi OSGi esposti dai plugin livebase; il suo compito in particolare è quello di generare automaticamente i file blueprint per questi servizi, interpretando le annotazioni presenti nel sorgente.
Setup del progetto Gradle #
Tutti i progetti base sono già configurati per eseguire l’Annotation Processor durante lo sviluppo in Eclipse e in concomitanza con il build Gradle. Se non utilizzi i progetti base, dovrai seguire la procedura dettagliata qui di seguito.
Innanzitutto apri il build.gradle
del tuo progetto e, se non è presente nella sezione repositories
, aggiungi jcenter()
per collegare Gradle al repository JCenter, che attualmente ospita la libreria dell’Annotation Processor:
repositories {
//...
jcenter()
//...
}
Adesso, includi la relativa libreria con il seguente codice:
def processorLib = 'com.fhoster.livebase:cloudlet-spi-annotation-processor:+'
dependencies {
//...
compileOnly processorLib
annotationProcessor processorLib
//...
}
Dovrai aggiungerne l’identificatore (qui memorizzato per comodità nella variabile processorLib
) alla sezione dependencies
, utilizzando entrambe le configurazioni compileOnly
e annotationProcessor
per effettuare l’inclusione a tempo di compilazione e collegare il processamento delle annotazioni all’operazione di build. Nota l’uso del +
nel segmento relativo alla versione della libreria: questo ordinerà a Gradle di prendere sempre l’ultima versione disponibile.
Il passo successivo è configurare l’Annotation Processor specificando la directory di output dei blueprint OSGi che verranno generati:
def blueprintsDir = "generated"
sourceSets.main.output.resourcesDir = blueprintsDir
compileJava.options.annotationProcessorGeneratedSourcesDirectory = file("${projectDir}/${blueprintsDir}")
Il frammento di codice crea una nuova cartella generated
(il cui nome è memorizzato nella variabile blueprintsDir
) sotto la root del progetto, per poi designarla come destinazione dei blueprint.
Per far sì che i file generati vengano rimossi ogni volta che viene lanciato il task clean
da Gradle, aggiungi la seguente riga:
clean.doLast { project.delete(blueprintsDir) }
La rimozione sarà lanciata come ultima operazione del task.
Uso dell’Annotation Processor #
Per servirti dell’Annotation Processor, decora le implementazioni degli Handler definiti per la Cloudlet con una delle annotazioni disponibili:
@CloudletEventHandler
: per le classi che implementano le interfacce dello SPI riferite agli handler definiti nel modello (es.FormActionHandler
,DBInsertHandler
, ecc.);@CloudletScheduledTask
: per le classi che implementano l’interfacciaSPICloudletScheduledTask
; l’annotazione è anche dotata dell’attributo di tipo StringdefaultCronExpression
, da definire con l’espressione CRON che stabilirà la frequenza di esecuzione del task.
Puoi anche definire delle risorse REST o GraphQL personalizzate, e decorarle con l’annotazione @CloudletRestletServerResource
per consentire la generazione dei relativi endpoint.
Facciamo un esempio: supponi di aver definito un FormActionHandler denominato Handler1
nel modello della tua Cloudlet; lo SPI conterrà la relativa interfaccia denominata SpiClass1FormActionHandlerHandler1
.
Dopo aver implementato la logica di questo handler, aggiungi l’annotazione @CloudletEventHandler
alla classe:
import com.fhoster.livebase.CloudletEventHandler;
import com.fhoster.livebase.cloudlet.SpiClass1FormActionHandlerHandler1;
@CloudletEventHandler
public class MyFormImpl implements SpiClass1FormActionHandlerHandler1 {
...
}
Per uno Scheduled Task, dovrai aggiungere l’annotazione @CloudletScheduledTask
definendone l’attributo defaultCronExpression
. Il seguente codice permette di realizzare uno Scheduled Task che si esegue ogni minuto:
import com.fhoster.livebase.CloudletScheduledTask;
import com.fhoster.livebase.cloudlet.SpiCloudletScheduledTask;
@CloudletScheduledTask(defaultCronExpression = "* * * * *")
public class MyScheduledTask implements SpiCloudletScheduledTask {
...
}
Esecuzione dell’Annotation Processor #
Per lanciare la generazione dei file, esegui il comando gradle build
da una shell dopo esserti posizionato sulla root del tuo progetto: l’Annotation Processor genererà un blueprint OSGi per ogni classe annotata, il cui nome è nel formato plugin.<ClassName>.blueprint.xml
; ad esempio, per una classe di nome MyHandlerImpl
, avremo un blueprint di nome plugin.MyHandlerImpl.blueprint.xml
.
Se hai seguito i passaggi iniziali di configurazione come dettagliato nella sezione Setup del progetto Gradle, tutti i blueprint generati saranno inseriti nella directory /<ProjectRoot>/generated/OSGI-INF/blueprint
. Se desideri modificare la directory di destinazione, puoi agire sul valore della variabile blueprintsDir
.
I file generati saranno inclusi anche nel jar risultante dalla build, nella directory /OSGI-INF/blueprint
.
Uso dell’Annotation Processor con Eclipse #
Se utilizzi Eclipse, è possibile attivare la generazione in tempo reale dei blueprint aggiungendo il seguente frammento di codice:
plugins {
id 'eclipse'
id 'net.ltgt.apt-eclipse' version '0.21'
}
project.eclipse.jdt.apt.aptEnabled = true
project.eclipse.jdt.apt.reconcileEnabled = true
project.eclipse.jdt.apt.genSrcDir = blueprintsDir
}
Esegui il comando gradle eclipse
da una shell aperta sulla root del progetto, quindi importa il progetto in Eclipse. Infine, fai clic destro sul progetto importato e seleziona Gradle > Refresh Gradle Project
per includere la directory blueprintsDir
nel classpath del progetto.
Uso delle annotazioni con servizi iniettabili #
Durante la scrittura del codice per un Handler, è possibile fare riferimento ad alcuni servizi offerti del Cloudlet Framework includendoli nel suo costruttore. L’Annotation Processor si occuperà della loro dependency injection durante la generazione dei blueprint.
I servizi disponibili sono:
- CloudletIdGenerator: permette di generare ID univoci per un record di tabella; essenziale in tutti gli scenari in cui vogliamo aggiungere nuovi oggetti sul database garantendo per tutti un ID univoco;
- CloudletDataSource: permette di accedere al datasource della Cloudlet, ovvero all’interfaccia verso il suo database, per effettuarvi query in modo diretto;
- CloudletMailSystem: permette di inviare email;
- EntitySessionFactory/CloudletEntitySessionFactory: permettono di aprire sessioni di lavoro sugli oggetti della Cloudlet;
- EntityLockManager: permette di gestire i lock sulle entità che rappresentano oggetti della Cloudlet;
- SPICloudletSMSSender: permette alla Cloudlet di inviare notifiche tramite SMS. A differenza degli altri servizi, questo non ha un’implementazione propria e sarà compito dello sviluppatore realizzarne una;
- CloudletPluginActivator: permette di intercettare l’evento di attivazione di un plugin ed eseguire una callback personalizzata.
È inoltre possibile accedere al BundleContext, ovvero al context OSGi della Cloudlet, per manipolare i bundle in esso presenti.
Per esempio, la seguente classe…
import com.fhoster.livebase.CloudletEventHandler;
import com.fhoster.livebase.cloudlet.SpiClass1FormActionHandlerHandler1;
import com.fhoster.livebase.cloudlet.CloudletIdGenerator;
@CloudletEventHandler
public class MyFormImpl implements SpiClass1FormActionHandlerHandler1 {
private CloudletIdGenerator idgen;
public MyFormImpl(CloudletIdGenerator idgen) {
this.idgen = idgen
}
...
}
…corrisponde al seguente blueprint:
<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
xmlns:ext="http://aries.apache.org/blueprint/xmlns/blueprint-ext/v1.0.0">
<reference id="idGenerator1"
interface="com.fhoster.livebase.cloudlet.CloudletIdGenerator"
ext:proxy-method="classes" />
<bean id="bid.plugin.myformimpl" class="plugin.MyFormImpl">
<argument ref="idGenerator1"/>
</bean>
<service ref="bid.plugin.myformimpl" id="sid.plugin.myformimpl"
interface="com.fhoster.livebase.cloudlet.SpiClass1FormActionHandlerHandler1">
</service>
</blueprint>