Cronjob emulatie met Quartz scheduler

August 29th, 2012 | 5 min read | JPA, Lucene, Quartz, Spring

Tijdens vorige tutorial heb ik jullie kennis laten maken met Lucene en Hibernate search. Hibernate search zorgt ervoor dat objecten/entiteiten bij het persisteren ervan, tevens in Lucene up-to-date blijft.

Hier is echter ook een nadeel mee verbonden, records die niet via Hibernate gewijzigd worden in de database (rechtstreeks of via een andere applicatie), worden dus niet opgenomen in de Lucene indexes.

Om dit probleem op te lossen heb ik vorige keer bij elke zoekopdracht de indexes gesynchroniseerd. Dit is een eenvoudige maar zeer zware oplossing als je database iets groter wordt. Bij elke zoekopdracht zouden immers alle indexes verwijderd worden, alle records uit de database worden opgehaald en toegevoegd aan de indexes.

In dit voorbeeld ga ik echter niet meer bij elke zoekopdracht alles updaten, maar ga ik dit op bepaalde tijdstippen doen.

Heads up!

In deze tutorial wordt met regelmaat verwezen naar mijn vorige Java tutorials. Het is dus handig dat je deze eens doorneemt.

Project opzetten

In dit project ga ik gebruik maken van de code uit het vorige voorbeeld. Download deze en hernoem het project indien gewenst.

Het eerste wat we gaan doen is enkele dependencies toevoegen van een framework die operaties op bepaalde tijdstippen kan uitvoeren, namelijk Quartz scheduler.

<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>${quartz.version}</version>
</dependency>
<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz-oracle</artifactId>
    <version>${quartz.version}</version>
</dependency>
<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz-weblogic</artifactId>
    <version>${quartz.version}</version>
</dependency>
<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz-jboss</artifactId>
    <version>${quartz.version}</version>
</dependency>

Vergeet natuurlijk niet om de versie bij je properties te plaatsen met de volgende code.

<quartz.version>1.8.5</quartz.version>

Voor de configuratie van Quartz gaan we een Spring bean configuration file in

src/main/resources

aanmaken met de naam

spring-quartz.xml

.

Ten slotte mag je ook nog de package

be.g00glen00b.tasks

aanmaken.

Configuratie

spring-quartz.xml

Zoals je al zal kunnen raden gaan we toch weer wat moeten configureren. In

spring-quartz.xml

moeten we de volgende code plaatsen.

<bean id="luceneIndexingTask" class="be.g00glen00b.tasks.LuceneIndexingTask" />

<bean id="luceneIndexingJob"
    class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
    <property name="targetObject" ref="luceneIndexingTask" />
    <property name="targetMethod" value="updateIndexes" />
</bean>

<bean id="luceneIndexingCronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
    <property name="jobDetail" ref="luceneIndexingJob" />
    <property name="cronExpression" value="0 0 */2 * * ?" />
</bean>

<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="jobDetails">
        <list>
            <ref bean="luceneIndexingJob" />
        </list>
    </property>

    <property name="triggers">
        <list>
            <ref bean="luceneIndexingCronTrigger" />
        </list>
    </property>
</bean>

De bean met de naam

luceneIndexingTask

wordt de class die we zelf gaan schrijven met daarin wat er juist uitgevoerd moet worden.
In de bean

luceneIndexingJob

gaan we het object en de methode doorgeven die eigenlijk uitgevoerd moeten worden.

De bean

luceneIndexingTrigger

is een samenvoegsel van de job (de vorige bean) en een cron-expressie. Een cron-expressie wordt gebruikt bij cronjobs (en dit framework emuleert cronjobs). Hierin staat beschreven op welke tijdstippen de taak uitgevoerd moet worden. De code

0 0 */2 * * ?

wilt zeggen dat elke 2 uur te beginnen vanaf 00:00:00 de operatie uitgevoerd wordt.

Dan ten slotte hebben we nog de laatste bean die een lijst van alle triggers bevat. Als je dus meerdere taken hebt die uitgevoerd moeten worden, moeten deze aan de lijst toegevoegd worden.

springmvc-servlet.xml

In de servlet configuratie moeten we enkel nog een component-scan voorzien voor

be.g00glen00b.tasks

en moeten we

spring-quartz.xml

als resource importeren.

<context:component-scan base-package="be.g00glen00b.tasks" />
<import resource="classpath:spring-quartz.xml" />

DAO laag

DAOService

Eerst gaan we een methode

updateIndexes

definiëren in de

DAOService

interface. In deze methode zal de

updateIndexes

methode van elke DAO opgeroepen worden.

void updateIndexes();

DAOServiceImpl

Nu moeten we enkel nog deze methode implementeren met de volgende code.

@Override
public void updateIndexes() {
    userDao.updateIndexes();
}

Vergeet ook zeker niet om het indexeren van de indexes uit de methode

findUserByFirstName

te verwijderen.

LuceneIndexingTask

In de package

be.g00glen00b.tasks

maak je nu een nieuwe class

LuceneIndexingTask

aan met de volgende code.

@Component
public class LuceneIndexingTask {

    @Autowired
    private DAOService service;

    public void updateIndexes() {
        service.updateIndexes();
    }
}

Hiermee zijn we dan ook aan het einde gekomen van deze vrij korte tutorial.
De uiteindelijke structuur van het project zou er moeten uitzien als in onderstaande afbeelding.

Builden

Het resultaat van dit voorbeeld is niet veel verschillend ten opzichte van vorig voorbeeld, we hebben immers enkel wat back-end toestanden gewijzigd.

Wat je wel kan doen is om in de class LuceneIndexingTask een System.out.println() te plaatsen en de cron expressie wat aanpassen zodat het elke 10 seconden uitgevoerd wordt met

*/10 * * * * ?

.

Het resultaat daarvan zie je in onderstaande afbeelding.

De SQL query die steeds getoond wordt heeft te maken met dat elke keer bij het updaten van de indexen er een SQL query uitgevoerd wordt om alle nieuwe entiteiten op te vragen. Dit kan je oplossen door in

spring-orm.xml
show_sql

op

false

te zetten.

Download project

Download – quartz-example.rar (12 kB)

Back to tutorialsContact me on TwitterDiscuss on Twitter

Profile picture

Dimitri "g00glen00b" Mestdagh is a consultant at Cronos and tech lead at Aquafin. Usually you can find him trying out new libraries and technologies. Loves both Java and JavaScript.