The Annotation Processor has been made available to simplify the creation of the files needed to configure the OSGi services exposed by the livebase plugins; its task in particular is to automatically generate the blueprint files for these services by interpreting the annotations present in the source.
Gradle project setup #
All base projects are already configured to run the Annotation Processor during development in Eclipse and concurrently with the Gradle build. If you do not use the base projects, you will have to follow the detailed procedure below.
First, open your project’s build.gradle
and, if it is not in the repositories
section, add jcenter()
to link Gradle to the JCenter repository, which currently hosts the Annotation Processor library:
repositories {
//...
jcenter()
//...
}
Now, include the relevant library with the following code:
def processorLib = 'com.fhoster.livebase:cloudlet-spi-annotation-processor:+'
dependencies {
//...
compileOnly processorLib
annotationProcessor processorLib
//...
}
You will need to add its identifier (stored here in the processorLib
variable for convenience) to the dependencies
section, using both the compileOnly
and annotationProcessor
configurations to perform compile-time inclusion and link annotation processing to the build operation. Note the use of +
in the library version segment: this instructs Gradle to always take the latest available version.
The next step is to configure the Annotation Processor by specifying the output directory of the OSGi blueprints that will be generated:
def blueprintsDir = "generated"
sourceSets.main.output.resourcesDir = blueprintsDir
compileJava.options.annotationProcessorGeneratedSourcesDirectory = file("${projectDir}/${blueprintsDir}")
The code snippet creates a new generated
directory (whose name is stored in the blueprintsDir
variable) under the project root, and then designates it as the destination for blueprints.
To ensure that the generated files are removed each time the clean
task is run from Gradle, add the following line:
clean.doLast { project.delete(blueprintsDir) }
The removal will be launched as the last operation of the task.
Using the Annotation Processor #
To use the Annotation Processor, decorate the handler implementations defined for the Cloudlet with one of the available annotations:
@CloudletEventHandler
: for classes that implement the SPI interfaces referring to the handlers defined in the engine model (e.g.FormActionHandler
,DBInsertHandler
, etc.);@CloudletScheduledTask
: for classes that implement theSPICloudletScheduledTask
interface; the annotation also has the String attributedefaultCronExpression
, to be defined with the expression CRON that sets the task execution frequency.
You can also define custom REST or GraphQL resources, and decorate them with the @CloudletRestletServerResource
annotation to allow generation of their endpoints.
Let’s take an example: suppose you’ve defined a FormActionHandler named Handler1
in your Cloudlet engine; the SPI will contain the related interface named SpiClass1FormActionHandlerHandler1
.
After implementing the logic of this handler, add the @CloudletEventHandler
annotation to the class:
import com.fhoster.livebase.CloudletEventHandler;
import com.fhoster.livebase.cloudlet.SpiClass1FormActionHandlerHandler1;
@CloudletEventHandler
public class MyFormImpl implements SpiClass1FormActionHandlerHandler1 {
...
}
For a Scheduled Task, you will need to add the @CloudletScheduledTask
annotation by defining its defaultCronExpression
attribute. The following code allows you to make a Scheduled Task that runs every minute:
import com.fhoster.livebase.CloudletScheduledTask;
import com.fhoster.livebase.cloudlet.SpiCloudletScheduledTask;
@CloudletScheduledTask(defaultCronExpression = "* * * * *")
public class MyScheduledTask implements SpiCloudletScheduledTask {
...
}
Running the Annotation Processor #
To start generating files, run the gradle build
command from a shell after positioning at the root of your project: the Annotation Processor will generate an OSGi blueprint for each annotated class, whose name is in the format plugin.<ClassName>.blueprint.xml
; for example, for a class named MyHandlerImpl
, we’ll have a blueprint named plugin.MyHandlerImpl.blueprint.xml
.
If you followed the initial setup steps as detailed in the Gradle Project Setup section, all generated blueprints will be placed in the /<ProjectRoot>/generated/OSGI-INF/blueprint
directory. If you want to change the destination directory, you can change the value of the blueprintsDir
variable.
The generated files will also be included in the jar resulting from the build, in the /OSGI-INF/blueprint
directory.
Using the Annotation Processor with Eclipse #
If you use Eclipse, it is possible to activate real-time blueprint generation by adding the following code snippet:
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
}
Run the gradle eclipse
command from an open shell on the project root, then import the project into Eclipse. Finally, right-click on the imported project and select Gradle > Refresh Gradle Project
to include the blueprintsDir
directory in the project classpath…
Using annotations with injectable services #
When writing code for a Handler, it is possible to refer to some services offered by the Cloudlet Framework by including them in its constructor. The Annotation Processor takes care of their dependency injection during blueprint generation.
The available services are:
- CloudletIdGenerator: allows you to generate unique IDs for a table record; essential in all scenarios in which we want to add new objects on the database ensuring a unique ID for all of them;
- CloudletDataSource: allows access to the Cloudlet datasource, i.e. the interface to its database, to run a query directly;
- CloudletMailSystem: allows to send emails;
- InventitySessionFactory/CloudletEntitySessionFactory: allows you to open work sessions on Cloudlet objects;
- EntityLockManager: allows to manage locks on entities representing Cloudlet objects;
- SPICloudletSMSSender: allows the Cloudlet to send SMS notifications. Unlike the other services, this one does not have its own implementation and it will be up to the developer to make one;
- CloudletPluginActivator: allows you to intercept the activation event of a plugin and execute a custom callback.
It is also possible to access the BundleContext, i.e. the OSGi context of the Cloudlet, to manipulate the bundles it contains.
For example, the following class…
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
}
...
}
… corresponds to the following 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>