Améliorer (un peu) le démarrage de SOLR pour Alfresco

Il y a quelques mois (pour ne pas dire années) de cela, lorsque j’ai commencé à mener des tests avec SOLR comme moteur d’indexation/recherche pour Alfresco, je me suis rendu compte que des appels sortants étaient émis à destination de terracotta.org :

  1. http://www.terracotta.org/kit/reflector?kitID=quartz&pageID=update.properties&id=-1062717439&os-name=Windows+7&jvm-name=Java+HotSpot%28TM%29+64-Bit+Server+VM&jvm-version=1.7.0_07&platform=amd64&tc-version=1.8.3&tc-product=Quartz&source=Quartz&uptime-secs=1&patch=UNKNOWN
  2. http://svn.terracotta.org/svn/tc/update-checker/quartz-update-list.properties

Ces appels ne sont en eux-mêmes pas impactant sur le bon fonctionnemnt du système, il serait préférable de ne pas avoir ce genre d’appels sur un système de production.

Dans la suite de ce billet, nous verrons comment résoudre ce problème.

Analyse du problème

Comme on peut le déduire des urls, ces appels ont un rapport avec « Quartz », c’est-à-dire le scheduler qui va être en charge du polling régulier d’Alfresco pour connaitre les nouveaux nœuds à indexer.
Les appels à ces urls sont réalisées par Quartz pour vérifier si de plus récentes versions du moteur existent. La documentation de Quartz indique :

Quartz contains an « update check » feature that connects to a server to check if there is a new version of Quartz available for download. This check runs asynchronously and does not affect startup/initialization time of Quartz, and it fails gracefully if the connection cannot be made. If the check runs, and an update is found, it will be reported as available in Quartz’s logs

Cette même documentation indique également :

It is recommended that you disable the update check for production deployments.

Je suis donc dans le vrai en souhaitant éviter ces appels.

Correction du problème

Comme souvent lorsque l’on cherche à corriger un problème, plusieurs solutions sont possibles, nous en aborderons différentes dans les paragraphes à venir.

La solution simple, rapide et efficace

Cette solution, la documentation de Quartz nous la donne : il suffit de démarrr la JVM avec le paramètre :

-Dorg.quartz.scheduler.skipUpdateCheck=true

Et le tour est joué !

La solution moins simple, mais qui permet de comprendre

La solution ci-dessus est aisée à mettre en place, mais elle pose la question suivante : pourquoi ces appels n’ont-ils pas lieu au démarrage d’Alfresco qui intègre lui aussi Quartz ?
La réponse est à chercher du côté de l’intégration qui est réalisée. Quartz est en effet intégré par l’intermédiaire de Spring et le fichier « quartz.properties » est utilisé pour définir un certain nombre de propriétés, dont la désormais fameuse org.quartz.scheduler.skipUpdateCheck qui est fixée à true par défaut.
Côté Alfresco-SOLR, l’instanciation du scheduler est différente et les propriétés sont définies directement dans la classe AlfrescoCoreAdminHandler, sans usage d’un fichier de propriétés. Malheureusement, dans l’ensemble de propriétés qui sont définies, il manque org.quartz.scheduler.skipUpdateCheck !

La définition de ces propriétés est réalisée dans le constructeur AlfrescoCoreAdminHandler(CoreContainer coreContainer) :

Properties properties = new Properties();
properties.setProperty("org.quartz.scheduler.instanceName", "SolrTrackerScheduler");
properties.setProperty("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
properties.setProperty("org.quartz.threadPool.threadCount", "3");
properties.setProperty("org.quartz.threadPool.makeThreadsDaemons", "true");
properties.setProperty("org.quartz.scheduler.makeSchedulerThreadDaemon", "true");
properties.setProperty("org.quartz.jobStore.class", "org.quartz.simpl.RAMJobStore");
factory.initialize(properties);

La correction va donc être toute simple : on ajoute simplement la définition de la propriété que l’on veut :

Properties properties = new Properties();
properties.setProperty("org.quartz.scheduler.instanceName", "SolrTrackerScheduler");
properties.setProperty("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
properties.setProperty("org.quartz.threadPool.threadCount", "3");
properties.setProperty("org.quartz.threadPool.makeThreadsDaemons", "true");
properties.setProperty("org.quartz.scheduler.makeSchedulerThreadDaemon", "true");
properties.setProperty("org.quartz.jobStore.class", "org.quartz.simpl.RAMJobStore");
// AJOUT DE LA PROPRIETE org.quartz.scheduler.skipUpdateCheck 
properties.setProperty("org.quartz.scheduler.skipUpdateCheck","true");
factory.initialize(properties);

Une recompilation plus tard, et notre SOLR ne vérifie plus les mises à jour de Quartz !

La solution qui se rapproche le plus de la cible

Alfresco est un produit hautement paramétrable. Regardez tout ce que l’on peut faire, juste avec le fichier alfresco-global.properties !
Il est donc surprenant de voir des propriétés définies « en dur », sans possibilité de les configurer. Un commentaire de la classe AlfrescoCoreAdminHandler nous indique d’ailleurs que du travail reste à faire sur ces propriétés :

// TODO: pick scheduler properties from SOLR config or file ...

Puisqu’on en est à modifier du code, pourquoi ne pas aller jusqu’au bout de l’exercice ?

L’objectif va être de pouvoir reproduire la logique de configuration de Quartz pour Alfresco au niveau de SOLR, c’est-à-dire lire les propriétés de configuration dans un fichier quartz.properties du classpath.
L’étude de la classe AlfrescoCoreAdminHandler permet de découvrir que la configuration du logger est déjà lu dans des fichiers « .properties ». La méthode clé pour cette lecture de fichier est private InputStream openResource(CoreContainer coreContainer, String resource) qui va rechercher le fichier « resource » dans le dossier racine de SOLR ou dans le classpath. Nous pouvons donc la réutiliser pour traiter notre fichier quartz.properties.

Cette méthode openResource va être appelée dans une nouvelle méthode générique de chargement des propriétés :

private void initProperties(Properties properties, final CoreContainer coreContainer, final String resource) {
	InputStream is = null;
	try {
		is = openResource(coreContainer, resource);
		if (is != null)  {
			properties.load(is);
		}
	} catch (IOException e) {
		log.error(new StringBuilder().append("Failed to load ").append(resource).toString(), e);
	} finally {
		if (is != null)  {
			try {
				is.close();
			} catch (IOException e) {
				log.error("Failed to close stream for resource " + resource, e);
			}
		}
	}
}

Cette méthode va compléter les propriétés properties passées en paramètre avec les propriétés trouvées dans le fichier resource.
Elle va être appelée dans une méthode référençant directement le fichier quartz.properties :

private void initQuartzProperties(Properties properties, final CoreContainer coreContainer) {
	 initProperties(properties, coreContainer, "quartz.properties");
}

Il ne reste plus qu’à faire usage de cette dernière méthode dans le constructeur AlfrescoCoreAdminHandler(CoreContainer coreContainer) :

Properties properties = new Properties();
properties.setProperty("org.quartz.scheduler.instanceName", "SolrTrackerScheduler");
properties.setProperty("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
properties.setProperty("org.quartz.threadPool.threadCount", "3");
properties.setProperty("org.quartz.threadPool.makeThreadsDaemons", "true");
properties.setProperty("org.quartz.scheduler.makeSchedulerThreadDaemon", "true");
properties.setProperty("org.quartz.jobStore.class", "org.quartz.simpl.RAMJobStore");
properties.setProperty("org.quartz.scheduler.skipUpdateCheck","true");
initQuartzProperties(properties, coreContainer);

Ainsi, on conserve les propriétés par défaut et on les complète ou remplace par celles définies dans le fichier quartz.properties.

A titre d’exemple, voici ce que j’ai pu mettre dans mon fichier @solrHome@/quartz.properties :

org.quartz.scheduler.skipUpdateCheck=true

Simple, mais explicite ;-)

Attention : ce fichier devra être présent obligatoirement pour qu le système fonctionne correctement. La faute en revient à openResource qui génère une RuntimeException si le fichier ne peut être trouvé. On aurait pu « s’amuser » à modifier cette méthode pour qu’elle soit moins radicale dans la remontée d’erreur, mais ce n’est pas le but aujourd’hui.

Remarques générales sur le code utilisé

Pour ceux qui voudraient juste récupérer un jar fonctionnel pour la version Alfresco 4.2.f, cliquer ici
(remplacer le jar du même nom dans @solrHome@/lib et ne pas oublier le fichier quartz.properties)

Pour ceux qui voudraient appliquer les éléments présentés ci-dessus, le code source sur lequel je me suis fondé provient du SVN d’Alfresco :
http://svn.alfresco.com/repos/alfresco-open-mirror/alfresco/COMMUNITYTAGS/V4.2f/root/projects/solr
Le diff concernant mes modifications est disponible dans la JIRA ALF-20905 (tant qu’à essayer d’améliorer le produit, autant proposer tout cela à l’éditeur).

Lorsque vous parcourrez le code source de ce projet, vous pourrez vous étonner, comme je l’ai été, du très grand nombre de warnings que remonte Eclipse. Les plus fréquents concernent des imports ou des variables inutilisés.
Monsieur Alfresco, vous avez construit un très beau produit et l’intégration SOLR ouvre de nouvelles possibilités, il serait tout de même bon de conserver la même discipline et lese mêmes règles de validation de code dans tous vos projets, non ? D’autant plus que c’est plutôt simple de retirer tous les imports inutiles ;-)

Cette entrée a été publiée dans Alfresco, avec comme mot(s)-clef(s) , , , , . Vous pouvez la mettre en favoris avec ce permalien.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée.

Vous pouvez utiliser ces balises et attributs HTML : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Protected by WP Anti Spam