Patch a dependency of Maven

Introduction

The other day, I was using a XWiki instance to make some crazy things (using XWiki in a way that is not the default). My point was to use the functionalities of creating subwikis. No specific configuration of XWiki after that point: Apache Tomcat v7.0.47, HSQLDB (embedded in the XWiki instance downloadable on the website) and XWiki v5.3. Is use Maven v3.1.1.

In the project, I have no build of XWiki, only a Maven dependency to xwiki-enterprise-web. My point is not to compile XWiki (which is a big software to compile), but only depend on its JAR files.

However, if I found a bug in XWiki, how can I manage that without compiling the entire XWiki software? And that is what I will talk about.

The problem

I have written some script to automatically call the WikiManager.CreateWiki page which will create a new subwiki based on a HTTP request with some parameters. Basically, I’m calling the WikiManager.CreateWiki with the URL /xwiki/bin/view/WikiManager/CreateWiki and the following parameters:

  • step=create

  • wikiname=mywiki

  • template=mytpl

  • wikiprettyname=My super wiki

  • ownerId=xwiki:XWiki.Admin

  • userScope=global_only

  • membershipType=open

However, when this page is called, it is not supposed that the subwiki already exists. First of all, it check is the subwiki already exists. Then it will also check that the subwiki is not in the process of creation; and this is where things may go wrong.

Checking the job status of the subwiki creation

To check the status of creation of a subwiki, we ask for the status of a job. The status of the subwiki creation can be called with this Velocity code.

#set($jobId = ["wiki", "provisioning", "wikiprovisioning.template", "mywiki"])
#set($percent = $services.wiki.template.getWikiProvisioningJobStatus($jobId).progress.offset)

$jobId is only a list of parameter where the last one is the identifiant of our wiki being created (see wikiname property above). Then we call the getWikiProvisioningJobStatus function from the script service WikiTemplateManagerScript.java.

Getting a bug

Getting a bug is usually the easy part! The bug is here, right under your nose. Look at the code of the script service. This is the code you should look for.

	public JobStatus getWikiProvisioningJobStatus(List<String> jobId)
	{
	    try {
	        return wikiTemplateManager.getWikiProvisioningJob(jobId).getStatus();
	    } catch (WikiTemplateManagerException e) {
	        return null;
	    }
	}

If the return of the getWikiProvisioningJob function is null, how can the function getWikiProvisioningJobStatus can return without exception? Everybody could have done this error, this is not the point. And since this is open source, the error as been reported in the JIRA of XWiki (XWIKI-9861) and fixed for the next release with the following code.

	public JobStatus getWikiProvisioningJobStatus(List<String> jobId)
	{
	    try {
	        WikiProvisioningJob wikiProvisioningJob = wikiTemplateManager.getWikiProvisioningJob(jobId);
	        if (wikiProvisioningJob == null) {
	            return null;
	        }
	        return wikiProvisioningJob.getStatus();
	    } catch (WikiTemplateManagerException e) {
	        return null;
	    }
	}

Fix the bug without compiling XWiki

I cheat a little because we will not compile the entire XWiki software. However, we still need to compile a small part of XWiki and replace it with the bug fix.

This is the general overview of what we will do to patch the bug:

  1. Unpack the source corresponding to the JAR we want to modify

  2. Patch these sources

  3. Compile the modified source

  4. Unpack the JAR we want to modify in another directory

  5. Move the freshly compiled classes into the unpacked JAR directory

  6. Pack the JAR

  7. Exclude the bugged JAR from your main project

  8. Include your patched JAR in the main project

Create a new Maven module

To begin, our patch JAR will be a new module of our project. Into the root of the project, we create a basic pom.xml file with mvn archetype:generate.

The bug come from the xwiki-platform-wiki-template-script module (artifactId in Maven with the groupId being org.xwiki.platform). We will named our new module with xwiki-platform-wiki-template-script-patched as artifactId.

For the example, the groupId of this module will be com.myxwiki. The main project will be called myxwiki in the same groupId.

You should have something like this at the beginning of your pom.xml.

	<parent>
		<groupId>com.myxwiki</groupId>
		<artifactId>myxwiki</artifactId>
		<version>1.0-SNAPSHOT</version>
	</parent>
	<groupId>com.myxwiki</groupId>
	<artifactId>xwiki-platform-wiki-template-script-patched</artifactId>
	<version>1.0-SNAPSHOT</version>
	<name>XWiki - Platform - Wiki - Template - Script - Patched</name>

Patch the sources

To patch the source, we will create a dependency to the java-source of the module we are looking for then use the maven-dependency-plugin (for unpacking) combined with maven-patch-plugin (for patching).

First of all, we will define directories where to unpack the sources.

	...
	<properties>
	    <src.directory>${project.build.directory}/xwiki-platform-wiki-template-script-src</src.directory>
	</properties>
	...

In the build part, we need to define where is the source directories.

	...
	<build>
	    <sourceDirectory>${src.directory}</sourceDirectory>
	    ...
	</build>
	...

We will now use 2 plugins. The first to unpack the sources and the second to patch them.

Unpack the sources

First of all, we use the maven-dependency-plugin to unpack the sources. We have already defined the sourceDirectory. We still need to give where are the sources.

	<plugin>
	    <groupId>org.apache.maven.plugins</groupId>
	    <artifactId>maven-dependency-plugin</artifactId>
	    <executions>
	        <execution>
	            <id>unpack</id>
	            <phase>generate-sources</phase>
	            <goals>
	            <goal>unpack</goal>
	            </goals>
	            <configuration>
	                <artifactItems>
	                    <artifactItem>
	                        <groupId>org.xwiki.platform</groupId>
	                        <artifactId>xwiki-platform-wiki-template-script</artifactId>
	                        <version>5.3</version>
	                        <type>java-source</type>
	                        <outputDirectory>${src.directory}</outputDirectory>
	                    </artifactItem>
	                </artifactItems>
	            </configuration>
	        </execution>
	    </executions>
	</plugin>

Create the patch

First of all, we need to create the patch. To create the patch, we first need to clone xwiki-platform (do not forget that you should get the v5.3 tagged version because this bug has been fixed in the master branch). Then, we will modify the source we want (WikiTemplateManagerScript.java). Once it is done, we can generate the patch using the following command from the java directory (xwiki-platform/xwiki-platform-core/xwiki-platform-wiki/xwiki-platform-wiki-template/xwiki-platform-wiki-template-script/src/main/java).

git diff -p --src-prefix '' --dst-prefix '' --relative -- org/xwiki/wiki/template/script/WikiTemplateManagerScript.java

You should get the following file.

	diff --git org/xwiki/wiki/template/script/WikiTemplateManagerScript.java org/xwiki/wiki/template/script/WikiTemplateManagerScript.java
	index 74ca25f..8e49bf9 100644
	--- org/xwiki/wiki/template/script/WikiTemplateManagerScript.java
	+++ org/xwiki/wiki/template/script/WikiTemplateManagerScript.java
	@@ -40,6 +40,7 @@
	import org.xwiki.wiki.descriptor.WikiDescriptor;
	import org.xwiki.wiki.descriptor.WikiDescriptorManager;
	import org.xwiki.wiki.manager.WikiManagerException;
	+import org.xwiki.wiki.provisioning.WikiProvisioningJob;
	import org.xwiki.wiki.template.WikiTemplateManager;
	import org.xwiki.wiki.template.WikiTemplateManagerException;
	@@ -223,7 +224,11 @@ public Exception getLastException()
	    public JobStatus getWikiProvisioningJobStatus(List<String> jobId)
	    {
	        try {
	-            return wikiTemplateManager.getWikiProvisioningJob(jobId).getStatus();
	+            WikiProvisioningJob wikiProvisioningJob = wikiTemplateManager.getWikiProvisioningJob(jobId);
	+            if (wikiProvisioningJob == null) {
	+                return null;
	+            }
	+            return wikiProvisioningJob .getStatus();
	        } catch (WikiTemplateManagerException e) {
	            return null;
	        }

Apply the patch onto the sources

In your new Maven module root, you now need to create the directory src/main/patches and put your patch (WikiTemplateManagerScript.patch) inside.

Now, we can add the plugin which will patch our source with the file we have just created.

	<plugin>
	    <groupId>org.apache.maven.plugins</groupId>
	    <artifactId>maven-patch-plugin</artifactId>
	    <version>1.1.1</version>
	    <configuration>
	        <skipApplication>false</skipApplication>
	    </configuration>
	    <executions>
	        <execution>
	            <configuration>
	                <patchDirectory>src/main/patches/</patchDirectory>
	                <targetDirectory>${src.directory}</targetDirectory>
	                <patches>
	                    <patch>WikiTemplateManagerScript.patch</patch>
	                </patches>
	                <patchTrackingFile>${project.build.directory}/patch-log.txt</patchTrackingFile>
	                <naturalOrderProcessing>true</naturalOrderProcessing>
	                <ignoreWhitespace>true</ignoreWhitespace>
	            </configuration>
	            <phase>process-sources</phase>
	            <goals>
	                <goal>apply</goal>
	            </goals>
	        </execution>
	    </executions>
	</plugin>

Create the JAR

To create our modified version of the JAR, we need to unpack the original version. The point of doing this operation is to keep existing files that we may not have in our sources (components.txt for example). First of all, we will unpack the JAR with the same plugin than the one for the source: we will add another <artifactItem> XML element.

	<artifactItem>
	    <groupId>org.xwiki.platform</groupId>
	    <artifactId>xwiki-platform-wiki-template-script</artifactId>
	    <version>5.3</version>
	    <type>jar</type>
	    <outputDirectory>${bin.directory}</outputDirectory>
	</artifactItem>

You can note that the unpacked files will be in the binary directory, exactly where the modified sources (that we patch), will be compiled. So we do not need anything else since the unpacking is done, then the compiling which will replace the compiled files (WikiTemplateManagerScript.class file is the only file). However, we need to make the JAR original project a dependency of our module.

	<dependency>
	    <groupId>org.xwiki.platform</groupId>
	    <artifactId>xwiki-platform-wiki-template-script</artifactId>
	    <version>5.3</version>
	    <type>jar</type>
	</dependency>

The new JAR!

Now, you just need to mvn clean install and see the result. You should find the JAR file xwiki-platform-wiki-template-script-patched-1.0-SNAPSHOT.jar into your target directory. Now, we need to use it in our main project.

Exclude the bugged JAR

To exclude the bugged file, we will use the maven-war-plugin plugin. In your pom.xml, you should have a dependency to XWiki somewhere. Something like that for example.

	<dependency>
	    <groupId>org.xwiki.enterprise</groupId>
	    <artifactId>xwiki-enterprise-web</artifactId>
	    <version>5.3</version>
	    <type>war</type>
	</dependency>

You can add the plugin which will exclude the xwiki-platform-wiki-template-script-5.3.jar JAR file.

	<plugin>
	    <groupId>org.apache.maven.plugins</groupId>
	    <artifactId>maven-war-plugin</artifactId>
	    <configuration>
	        <packagingExcludes>
	            WEB-INF/lib/xwiki-platform-wiki-template-script-5.3.jar
	        </packagingExcludes>
	    </configuration>
	</plugin>

Into the <packagingExcludes> element, you may use wildcard pattern * to find more than one file.

Include the patched JAR

To include you new patched JAR, you just need to add a dependency to it.

	<dependency>
	    <groupId>com.xwikisas</groupId>
	    <artifactId>eesc-xwiki-platform-wiki-template-script</artifactId>
	    <version>1.0-SNAPSHOT</version>
	</dependency>

References

Finally, the project I was working on to apply this patch is hosted by Github and is called EESC XWiki. You can find the patch module inside eesc-xwiki-platform-wiki-template-script and the main project for the WAR in eesc-xwiki-web/eesc-xwiki-web-base (which is not the root of our project).

links

social