In den Update Notes von Confluence 5.2 ist ziemlich kurz eine Änderung erwähnt, die für Plugin-Entwickler von relativ großer Bedeutung ist:

With Confluence 5.2, we have upgraded the Lucene search library to version 4.3 as part of larger efforts to make search better and faster.

Das bedeutet konkret, dass alle Confluence Versionen < 5.2 die Lucene API in Version 2.9 und ab Confluence 5.2 Version 4.3 enthalten ist. Confluence Plugins, die sich auf die alte Lucene API stützen (das tun sie sobald man einen Extractor implementiert) sind grundsätzlich nicht mit Confluence >= 5.2 kompatibel und lassen sich nicht nutzen. Andererseits sind Plugins, die sich auf die neue API stützen nicht mit Confluence < 5.2 kompatibel und lassen sich dort nicht einmal installieren. Glücklicherweise gibt es einen von Atlassian beschriebenen Workaround für dieses Problem.

Ich werde diesen Weg kurz am Beispiel unseres Marketplace-Plugins PocketQuery demonstrieren, das ab Version 1.12 einen Extractor implementiert, um Daten aus externen Datenbanken in Confluence suchbar zu machen. Grundsätzlich war das Ziel hier relativ klar: wir brauchen eine PocketQuery Version, die sich auf allen Confluence-Versionen (ab 4.0) installieren lässt und der Extractor muss auf allen Versionen funktionieren. Ein Verwalten zweier verschiedener Branches für Confluence-Versionen kam für uns nicht in Frage. Im folgenden sind die nötigen Änderungen beschrieben.

pom.xml  - die von Atlassian zur Verfügung gestellten Bibliotheken einbinden

<!-- Be aware of Lucene update in Confluence 5.2 -->
<!-- <extractor name="PocketQuery Result Extractor" key="pocketquery-result-extractor"
		   class="de.scandio.confluence.plugins.pocketquery.extractors.PocketQueryResultExtractor" priority="900">
	<description>Extracts data rendered by the PocketQuery macro and adds them to the search index.</description>
</extractor> -->
<component name="PocketQuery Result Extractor" key="pocketquery-result-extractor"
	class="de.scandio.confluence.plugins.pocketquery.extractors.PocketQueryResultExtractor" public="true">
	<interface>com.atlassian.confluence.plugins.index.api.Extractor2</interface>
</component>

atlassian-plugin.xml  - neues Extractor Module löst das alte ab

<!-- Be aware of Lucene update in Confluence 5.2 -->
<!--
<extractor name="PocketQuery Result Extractor" key="pocketquery-result-extractor"
           class="de.scandio.confluence.plugins.pocketquery.extractors.PocketQueryResultExtractor" priority="900">
    <description>Extracts data rendered by the PocketQuery macro and adds them to the search index.</description>
</extractor>
-->
<component name="PocketQuery Result Extractor" key="pocketquery-result-extractor"
    class="de.scandio.confluence.plugins.pocketquery.extractors.PocketQueryResultExtractor" public="true">
    <interface>com.atlassian.confluence.plugins.index.api.Extractor2</interface>
</component>

PocketQueryResultExtractor.java  - die Extractor Implementierung

package de.scandio.confluence.plugins.pocketquery.extractors;
// ...
/**
 * Extractor class responsible for writing PocketQuery results to the
 * Lucene index.
 *
 * Runs through all PocketQuery macros in a ContentEntityObject (COE), extracts
 * the query name from the XHTML content and checks whether indexing is enabled
 * for that query. If so, the query is run and all data in the result written to
 * the index.
 *
 * NOTE: this class implements the Extractor2 interface which ensures that the
 * extractor is compatible with both Lucene versions used in Confluence < 5.2
 * and >= 5.2. See: https://developer.atlassian.com/display/CONFDEV/How+to+fix+broken+Extractors
 *
 * @author Felix Grund <felix.grund@scandio.de>
 *
 */
public class PocketQueryResultExtractor implements Extractor2 {
    // ...
    @Override
    public StringBuilder extractText(Object searchable) {
        StringBuilder textBuilder = new StringBuilder();
        if (searchable instanceof ContentEntityObject) {
            ContentEntityObject ceo = (ContentEntityObject) searchable;
            info("indexing result of coe: title = {}", ceo.getTitle());
            addTextForContentEntityObject(ceo, textBuilder);
        }
        return textBuilder;
    }
    // We don't want to extract any metadata for PocketQuery at the moment. If we'd
    // like to at some point, we'd have to create a collection of FieldDescriptors like this:
    // new FieldDescriptor("pocketquery", "value", FieldDescriptor.Store.NO, FieldDescriptor.Index.ANALYZED)
    @Override
    public Collection<FieldDescriptor> extractFields(Object searchable) {
        return null;
    }
    // ...
}

pom.xml (Teil 2) - Kampf mit den Abhängigkeiten

Das neue Confluence Plugin benötigt für diese Änderung ein paar Abhängigkeiten. Diese werden in der POM spezifiziert und in die Distribution des Plugins hinzugefügt. Es entsteht somit ein OBR, das dafür sorgt, das fehlende Abhängigkeiten im Zielsystem mitinstalliert werden. Diese Änderungen sind in der pom.xml nötig:

<build>
    <plugins>
        <plugin>
            <groupId>com.atlassian.maven.plugins</groupId>
            <artifactId>maven-confluence-plugin</artifactId>
            <version>4.2.3</version>
            <extensions>true</extensions>
            <configuration>
                <instructions>
                    <Import-Package>
                        com.atlassian.confluence.plugins.index.api;version="[1,2)",
                        *;resolution:=optional
                    </Import-Package>
                    <Require-Bundle>
                        com.atlassian.confluence.plugins.confluence-extractor-api-plugin;bundle-version="${confluence-extractor-api-plugin.version}",
                        com.atlassian.labs.lucene-compat-plugin;bundle-version="${lucene-compat-plugin.version}"
                    </Require-Bundle>
                </instructions>
                <pluginDependencies><!-- this section declares the following as dependent bundles to be incorporated into the OBR -->
                    <pluginDependency>
                        <groupId>com.atlassian.confluence.plugins</groupId>
                        <artifactId>confluence-extractor-api-plugin</artifactId>
                    </pluginDependency>
                    <pluginDependency>
                        <groupId>com.atlassian.labs</groupId>
                        <artifactId>lucene-compat-plugin</artifactId>
                    </pluginDependency>
                </pluginDependencies>
                <bundledArtifacts><!-- this section is for atlas-XXX commands -->
                    <bundledArtifact>
                        <groupId>com.atlassian.confluence.plugins</groupId>
                        <artifactId>confluence-extractor-api-plugin</artifactId>
                    </bundledArtifact>
                    <bundledArtifact>
                        <groupId>com.atlassian.labs</groupId>
                        <artifactId>lucene-compat-plugin</artifactId>
                    </bundledArtifact>
                </bundledArtifacts>
            </configuration>
        </plugin>
    </plugins>
</build>

Weitere Informationen findet man auf dem schon oben verlinkten Atlassian Artikel.

Fazit

Confluence Plugins sollten grundsätzlich mit einer möglichst breiten Palette an Confluence-Versionen kompatibel sein. Durch das Lucene-Update in Confluence 5.2 gilt es in diesem Rahmen Änderungen an Plugins durchzuführen, die sich auf die Lucene API stützen und einen Extractor implementieren. Das obige Beispiel beschreibt diese Änderung und PocketQuery funktioniert somit bestens auf allen Confluence Versionen 4.0-5.x.