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:
-
Unpack the source corresponding to the JAR we want to modify
-
Patch these sources
-
Compile the modified source
-
Unpack the JAR we want to modify in another directory
-
Move the freshly compiled classes into the unpacked JAR directory
-
Pack the JAR
-
Exclude the bugged JAR from your main project
-
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).