Index: src/main/java/org/xwiki/test/XWikiTestSetup.java
===================================================================
--- src/main/java/org/xwiki/test/XWikiTestSetup.java	(revision 22751)
+++ src/main/java/org/xwiki/test/XWikiTestSetup.java	(working copy)
@@ -21,27 +21,26 @@
 
 import junit.extensions.TestSetup;
 import junit.framework.Test;
-import org.apache.tools.ant.Project;
-import org.apache.tools.ant.Task;
-import org.apache.tools.ant.types.Commandline;
-import org.apache.tools.ant.taskdefs.ExecTask;
+
+import java.util.ArrayList;
+import java.util.List;
 
-import java.io.*;
-import java.net.HttpURLConnection;
-import java.net.URL;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 
 /**
  * JUnit TestSetup extension that starts/stops XWiki using a script passed using System Properties. These properties are
  * meant to be passed by the underlying build system. This class is meant to wrap a JUnit TestSuite. For example:
  * 
- * <pre><code>
+ * <pre>
+ * &lt;code&gt;
  * public static Test suite()
  * {
  *     // Create some TestSuite object here
  *     return new XWikiTestSetup(suite);
  * }
- * </code></pre>
- * 
+ * &lt;/code&gt;
+ * </pre>
  * <p>
  * Note: We could start XWiki using Java directly but we're using a script so that we can test the exact same script
  * used by XWiki users who download the standalone distribution.
@@ -51,27 +50,22 @@
  */
 public class XWikiTestSetup extends TestSetup
 {
-    private static final String EXECUTION_DIRECTORY = System.getProperty("xwikiExecutionDirectory");
+    protected static final Log LOG = LogFactory.getLog(XWikiTestSetup.class);
 
-    private static final String START_COMMAND = System.getProperty("xwikiExecutionStartCommand");
-
-    private static final String STOP_COMMAND = System.getProperty("xwikiExecutionStopCommand");
-
-    private static final String PORT = System.getProperty("xwikiPort", "8080");
-
-    private static final boolean DEBUG = System.getProperty("debug", "false").equalsIgnoreCase("true");
-
-    private static final int TIMEOUT_SECONDS = 120;
-
-    private Project project;
+    private List<XWikiExecutor> executors = new ArrayList<XWikiExecutor>();
 
     public XWikiTestSetup(Test test)
     {
+        this(test, 1);
+    }
+
+    public XWikiTestSetup(Test test, int nb)
+    {
         super(test);
 
-        this.project = new Project();
-        this.project.init();
-        this.project.addBuildListener(new AntBuildListener(DEBUG));
+        for (int i = 0; i < nb; ++i) {
+            this.executors.add(new XWikiExecutor(i));
+        }
     }
 
     /**
@@ -82,87 +76,8 @@
     @Override
     protected void setUp() throws Exception
     {
-        startXWikiInSeparateThread();
-        waitForXWikiToLoad();
-    }
-
-    private void startXWikiInSeparateThread()
-    {
-        Thread startThread = new Thread(new Runnable()
-        {
-            public void run()
-            {
-                try {
-                    startXWiki();
-                } catch (Exception e) {
-                    throw new RuntimeException(e);
-                }
-            }
-        });
-        startThread.start();
-    }
-
-    private void startXWiki() throws Exception
-    {
-        File dir = new File(EXECUTION_DIRECTORY);
-        if (dir.exists()) {
-            ExecTask execTask = (ExecTask) this.project.createTask("exec");
-            execTask.setDir(new File(EXECUTION_DIRECTORY));
-            Commandline commandLine = new Commandline(START_COMMAND);
-            execTask.setCommand(commandLine);
-            execTask.execute();
-        } else {
-            throw new Exception("Invalid directory from where to start XWiki [" + EXECUTION_DIRECTORY + "]");
-        }
-    }
-
-    private Task createStopTask() throws Exception
-    {
-        ExecTask execTask;
-        File dir = new File(EXECUTION_DIRECTORY);
-        if (dir.exists()) {
-            execTask = (ExecTask) this.project.createTask("exec");
-            execTask.setDir(new File(EXECUTION_DIRECTORY));
-            Commandline commandLine = new Commandline(STOP_COMMAND);
-            execTask.setCommand(commandLine);
-        } else {
-            throw new Exception("Invalid directory from where to stop XWiki [" + EXECUTION_DIRECTORY + "]");
-        }
-
-        return execTask;
-    }
-
-    private void waitForXWikiToLoad() throws Exception
-    {
-        // Wait till the main page becomes available which means the server is started fine
-        System.out.println("Checking that XWiki is up and running...");
-        URL url = new URL("http://localhost:" + PORT + "/xwiki/bin/view/Main/WebHome");
-        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
-        boolean connected = false;
-        boolean timedOut = false;
-        long startTime = System.currentTimeMillis();
-        while (!connected && !timedOut) {
-            try {
-                connection.connect();
-                int responseCode = connection.getResponseCode();
-                if (DEBUG) {
-                    System.out.println("Result of pinging [" + url + "] = [" + responseCode + "], Message = ["
-                        + connection.getResponseMessage() + "]");
-                }
-                // check the http response code is either not an error, either "unauthorized"
-                // (which is the case for products that deny view for guest, for example).
-                connected = (responseCode < 400 || responseCode == 401);
-            } catch (IOException e) {
-                // Do nothing as it simply means the server is not ready yet...
-            }
-            Thread.sleep(100L);
-            timedOut = (System.currentTimeMillis() - startTime > TIMEOUT_SECONDS * 1000L);
-        }
-        if (timedOut) {
-            String message = "Failed to start XWiki in [" + TIMEOUT_SECONDS + "] seconds";
-            System.out.println(message);
-            tearDown();
-            throw new RuntimeException(message);
+        for (XWikiExecutor executor : this.executors) {
+            executor.start();
         }
     }
 
@@ -174,7 +89,18 @@
     @Override
     protected void tearDown() throws Exception
     {
-        // Stop XWiki
-        createStopTask().execute();
+        for (XWikiExecutor executor : this.executors) {
+            executor.stop();
+        }
+    }
+
+    protected XWikiExecutor getXWikiExecutor()
+    {
+        return getXWikiExecutor(0);
+    }
+
+    protected XWikiExecutor getXWikiExecutor(int index)
+    {
+        return this.executors.get(index);
     }
 }
Index: src/main/java/org/xwiki/test/XWikiExecutor.java
===================================================================
--- src/main/java/org/xwiki/test/XWikiExecutor.java	(revision 0)
+++ src/main/java/org/xwiki/test/XWikiExecutor.java	(revision 0)
@@ -0,0 +1,265 @@
+/*
+ * See the NOTICE file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.xwiki.test;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.Properties;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.ExecTask;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * Start and stop a xwiki instance.
+ * 
+ * @version $Id$
+ * @since 2.0RC1
+ */
+public class XWikiExecutor
+{
+    public static final String DEFAULT_PORT = System.getProperty("xwikiPort", "8080");
+
+    private static final String DEFAULT_EXECUTION_DIRECTORY = System.getProperty("xwikiExecutionDirectory");
+
+    private static final String START_COMMAND = System.getProperty("xwikiExecutionStartCommand");
+
+    private static final String STOP_COMMAND = System.getProperty("xwikiExecutionStopCommand");
+
+    private static final boolean DEBUG = System.getProperty("debug", "false").equalsIgnoreCase("true");
+
+    private static final String WEBINF_PATH = "/webapps/xwiki/WEB-INF";
+
+    private static final String XWIKICFG_PATH = WEBINF_PATH + "/xwiki.cfg";
+
+    private static final String XWIKIPROPERTIES_PATH = WEBINF_PATH + "/xwiki.properties";
+
+    private static final String LOG4PROPERTIES_PATH = WEBINF_PATH + "/classes/log4j.properties";
+
+    private static final int TIMEOUT_SECONDS = 120;
+
+    private Project project;
+
+    private int port;
+
+    private String executionDirectory;
+
+    public XWikiExecutor(int index)
+    {
+        this.project = new Project();
+        this.project.init();
+        this.project.addBuildListener(new AntBuildListener(DEBUG));
+
+        // resolve port
+        String portString = System.getProperty("xwikiPort" + index);
+        this.port = portString != null ? Integer.valueOf(portString) : (Integer.valueOf(DEFAULT_PORT) + index);
+
+        // resolve execution directory
+        this.executionDirectory = System.getProperty("xwikiExecutionDirectory" + index);
+        if (this.executionDirectory == null) {
+            this.executionDirectory = DEFAULT_EXECUTION_DIRECTORY;
+            if (index > 0) {
+                this.executionDirectory += "-" + index;
+            }
+        }
+    }
+
+    public int getPort()
+    {
+        return this.port;
+    }
+
+    public String getExecutionDirectory()
+    {
+        return this.executionDirectory;
+    }
+
+    public void start() throws Exception
+    {
+        startXWikiInSeparateThread();
+        waitForXWikiToLoad();
+    }
+
+    private void startXWikiInSeparateThread()
+    {
+        Thread startThread = new Thread(new Runnable()
+        {
+            public void run()
+            {
+                try {
+                    startXWiki();
+                } catch (Exception e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        });
+        startThread.start();
+    }
+
+    private void startXWiki() throws Exception
+    {
+        File dir = new File(getExecutionDirectory());
+        if (dir.exists()) {
+            ExecTask execTask = (ExecTask) this.project.createTask("exec");
+            execTask.setDir(new File(getExecutionDirectory()));
+            Commandline commandLine =
+                new Commandline(START_COMMAND.replaceFirst(DEFAULT_PORT, String.valueOf(getPort())));
+            execTask.setCommand(commandLine);
+            execTask.execute();
+        } else {
+            throw new Exception("Invalid directory from where to start XWiki [" + this.executionDirectory + "]");
+        }
+    }
+
+    private Task createStopTask() throws Exception
+    {
+        ExecTask execTask;
+        File dir = new File(getExecutionDirectory());
+        if (dir.exists()) {
+            execTask = (ExecTask) this.project.createTask("exec");
+            execTask.setDir(new File(getExecutionDirectory()));
+            Commandline commandLine = new Commandline(STOP_COMMAND);
+            execTask.setCommand(commandLine);
+        } else {
+            throw new Exception("Invalid directory from where to stop XWiki [" + this.executionDirectory + "]");
+        }
+
+        return execTask;
+    }
+
+    private void waitForXWikiToLoad() throws Exception
+    {
+        // Wait till the main page becomes available which means the server is started fine
+        System.out.println("Checking that XWiki is up and running...");
+        URL url = new URL("http://localhost:" + getPort() + "/xwiki/bin/view/Main/WebHome");
+        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+        boolean connected = false;
+        boolean timedOut = false;
+        long startTime = System.currentTimeMillis();
+        while (!connected && !timedOut) {
+            try {
+                connection.connect();
+                int responseCode = connection.getResponseCode();
+                if (DEBUG) {
+                    System.out.println("Result of pinging [" + url + "] = [" + responseCode + "], Message = ["
+                        + connection.getResponseMessage() + "]");
+                }
+                // check the http response code is either not an error, either "unauthorized"
+                // (which is the case for products that deny view for guest, for example).
+                connected = (responseCode < 400 || responseCode == 401);
+            } catch (IOException e) {
+                // Do nothing as it simply means the server is not ready yet...
+            }
+            Thread.sleep(100L);
+            timedOut = (System.currentTimeMillis() - startTime > TIMEOUT_SECONDS * 1000L);
+        }
+        if (timedOut) {
+            String message = "Failed to start XWiki in [" + TIMEOUT_SECONDS + "] seconds";
+            System.out.println(message);
+            stop();
+            throw new RuntimeException(message);
+        }
+    }
+
+    public void stop() throws Exception
+    {
+        // Stop XWiki
+        createStopTask().execute();
+    }
+
+    public String getWebInfDirectory()
+    {
+        return getExecutionDirectory() + WEBINF_PATH;
+    }
+
+    public String getXWikiCfgPath()
+    {
+        return getExecutionDirectory() + XWIKICFG_PATH;
+    }
+
+    public String getXWikiPropertiesPath()
+    {
+        return getExecutionDirectory() + XWIKIPROPERTIES_PATH;
+    }
+
+    public String getLog4JPropertiesPath()
+    {
+        return getExecutionDirectory() + LOG4PROPERTIES_PATH;
+    }
+
+    public Properties loadXWikiCfg() throws Exception
+    {
+        return getProperties(getXWikiCfgPath());
+    }
+
+    public Properties loadXWikiProperties() throws Exception
+    {
+        return getProperties(getXWikiPropertiesPath());
+    }
+
+    public Properties loadLog4JProperties() throws Exception
+    {
+        return getProperties(getLog4JPropertiesPath());
+    }
+
+    private Properties getProperties(String path) throws Exception
+    {
+        Properties initialXWikiConf = new Properties();
+
+        FileInputStream fis = new FileInputStream(path);
+        try {
+            initialXWikiConf.load(fis);
+        } finally {
+            fis.close();
+        }
+
+        return initialXWikiConf;
+    }
+
+    public void saveXWikiCfg(Properties properties) throws Exception
+    {
+        saveProperties(getXWikiCfgPath(), properties);
+    }
+
+    public void saveXWikiProperties(Properties properties) throws Exception
+    {
+        saveProperties(getXWikiPropertiesPath(), properties);
+    }
+
+    public void saveLog4JProperties(Properties properties) throws Exception
+    {
+        saveProperties(getLog4JPropertiesPath(), properties);
+    }
+
+    private void saveProperties(String path, Properties properties) throws Exception
+    {
+        FileOutputStream fos = new FileOutputStream(path);
+        try {
+            properties.store(fos, null);
+        } finally {
+            fos.close();
+        }
+    }
+}
