org.xwiki.test.integration.utils.XWikiExecutor.java Source code

Java tutorial

Introduction

Here is the source code for org.xwiki.test.integration.utils.XWikiExecutor.java

Source

/*
 * 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.integration.utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.apache.commons.lang3.SystemUtils;
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;
import org.apache.tools.ant.types.Environment;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Start and stop a xwiki instance.
 *
 * @version $Id$
 * @since 2.0RC1
 */
public class XWikiExecutor {
    protected static final Logger LOGGER = LoggerFactory.getLogger(XWikiExecutor.class);

    public static final String SKIP_STARTING_XWIKI_INSTANCE = System.getProperty("xwiki.test.skipStart", "false");

    public static final String BASEDIR = System.getProperty("basedir");

    public static final String URL = System.getProperty("xwiki.test.baseURL", "http://localhost");

    public static final String DEFAULT_PORT = System.getProperty("xwikiPort", "8080");

    public static final String DEFAULT_STOPPORT = System.getProperty("xwikiStopPort", "8079");

    public static final String DEFAULT_RMIPORT = System.getProperty("rmiPort", "9010");

    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 int TIMEOUT_SECONDS = 120;

    private Project project;

    private int port;

    private int stopPort;

    private int rmiPort;

    private String executionDirectory;

    private List<Environment.Variable> env = new ArrayList<Environment.Variable>();

    private String opts;

    /**
     * Was XWiki server already started. We don't try to stop it if it was already started.
     */
    private boolean wasStarted;

    private class Response {
        public boolean timedOut;

        public byte[] responseBody;

        public int responseCode;
    }

    public XWikiExecutor(int index) {
        this.project = new Project();
        this.project.init();
        this.project.addBuildListener(new AntBuildListener(DEBUG));

        // resolve ports
        String portString = System.getProperty("xwikiPort" + index);
        this.port = portString != null ? Integer.valueOf(portString) : (Integer.valueOf(DEFAULT_PORT) + index);
        String stopPortString = System.getProperty("xwikiStopPort" + index);
        this.stopPort = stopPortString != null ? Integer.valueOf(stopPortString)
                : (Integer.valueOf(DEFAULT_STOPPORT) - index);
        String rmiPortString = System.getProperty("rmiPort" + index);
        this.rmiPort = rmiPortString != null ? Integer.valueOf(rmiPortString)
                : (Integer.valueOf(DEFAULT_RMIPORT) + index);

        // resolve execution directory
        this.executionDirectory = System.getProperty("xwikiExecutionDirectory");
        if (this.executionDirectory == null) {
            this.executionDirectory = DEFAULT_EXECUTION_DIRECTORY;
            if (this.executionDirectory == null) {
                this.executionDirectory = BASEDIR + "/target/xwiki";
            }
            if (index > 0) {
                this.executionDirectory += "-" + index;
            }
        }
    }

    public int getPort() {
        return this.port;
    }

    public int getStopPort() {
        return this.stopPort;
    }

    public int getRMIPort() {
        return this.rmiPort;
    }

    public String getExecutionDirectory() {
        if (this.executionDirectory == null) {
            throw new RuntimeException("Invalid configuration for the execution directory. The "
                    + "[xwikiExecutionDirectory] system property must be specified.");
        }
        return this.executionDirectory;
    }

    public void addEnvironmentVariable(String key, String value) {
        Environment.Variable variable = new Environment.Variable();

        variable.setKey(key);
        variable.setValue(value);

        this.env.add(variable);
    }

    public void setOpts(String opts) {
        this.opts = opts;
    }

    public void start() throws Exception {
        if (SKIP_STARTING_XWIKI_INSTANCE.equals("true")) {
            System.out.println(String.format("Using running instance at [%s:%s]", URL, getPort()));
        } else {
            System.out.println(String.format("Starting XWiki server at [%s:%s]", URL, getPort()));
            // First, verify if XWiki is started. If it is then don't start it again.
            this.wasStarted = !isXWikiStarted(getURL(), 15).timedOut;
            if (!this.wasStarted) {
                startXWikiInSeparateThread();
                waitForXWikiToLoad();
            } else {
                System.out.println("XWiki server is already started!");
            }
        }
    }

    private void startXWikiInSeparateThread() {
        Thread startThread = new Thread(new Runnable() {
            @Override
            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()));
            for (Environment.Variable variable : this.env) {
                execTask.addEnv(variable);
            }

            if (this.opts != null) {
                Environment.Variable optsVariable = new Environment.Variable();
                optsVariable.setKey("XWIKI_OPTS");
                optsVariable.setValue(this.opts);
                execTask.addEnv(optsVariable);
            }

            String startCommand = getDefaultStartCommand(getPort(), getStopPort(), getRMIPort());
            System.out.println("start command: " + startCommand);
            Commandline commandLine = new Commandline(startCommand);
            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()));

            String stopCommand = getDefaultStopCommand(getStopPort());
            Commandline commandLine = new Commandline(stopCommand);
            execTask.setCommand(commandLine);
        } else {
            throw new Exception("Invalid directory from where to stop XWiki [" + this.executionDirectory + "]");
        }

        return execTask;
    }

    private void waitForXWikiToLoad() throws Exception {

        System.out.println("sleeping 3 sec allowing xwiki to start");
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
        }
        // Wait till the main page becomes available which means the server is started fine
        System.out.println("Checking that XWiki is up and running...");

        Response response = isXWikiStarted(getURL(), TIMEOUT_SECONDS);
        if (response.timedOut) {
            String message = "Failed to start XWiki in [" + TIMEOUT_SECONDS + "] seconds, last error code ["
                    + response.responseCode + ", message [" + new String(response.responseBody) + "]"
                    + "  url tried was: " + getURL();
            System.out.println(message);
            stop();
            throw new RuntimeException(message);
        } else {
            System.out.println("Server is answering to [" + getURL() + "]... cool");
        }
    }

    public Response isXWikiStarted(String url, int timeout) throws Exception {
        HttpClient client = new HttpClient();

        boolean connected = false;
        long startTime = System.currentTimeMillis();
        Response response = new Response();
        response.timedOut = false;
        response.responseCode = -1;
        response.responseBody = new byte[0];
        while (!connected && !response.timedOut) {
            GetMethod method = new GetMethod(url);

            // Don't retry automatically since we're doing that in the algorithm below
            method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
                    new DefaultHttpMethodRetryHandler(0, false));
            // Set a socket timeout to ensure the server has no chance of not answering to our request...
            method.getParams().setParameter(HttpMethodParams.SO_TIMEOUT, new Integer(10000));

            try {
                // Execute the method.
                response.responseCode = client.executeMethod(method);

                // We must always read the response body.
                response.responseBody = method.getResponseBody();

                if (DEBUG) {
                    System.out.println(String.format("Result of pinging [%s] = [%s], Message = [%s]", url,
                            response.responseCode, new String(response.responseBody)));
                }

                // 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 = (response.responseCode < 400 || response.responseCode == 401);
            } catch (Exception e) {
                // Do nothing as it simply means the server is not ready yet...
            } finally {
                // Release the connection.
                method.releaseConnection();
            }
            Thread.sleep(500L);
            response.timedOut = (System.currentTimeMillis() - startTime > timeout * 1000L);
        }
        return response;
    }

    public void stop() throws Exception {
        // Stop XWiki if it was started by start()
        if (!this.wasStarted) {
            createStopTask().execute();
        }
        System.out.println("XWiki server stopped");
    }

    public String getWebInfDirectory() {
        return getExecutionDirectory() + WEBINF_PATH;
    }

    public String getXWikiCfgPath() {
        return getExecutionDirectory() + XWIKICFG_PATH;
    }

    public String getXWikiPropertiesPath() {
        return getExecutionDirectory() + XWIKIPROPERTIES_PATH;
    }

    public Properties loadXWikiCfg() throws Exception {
        return getProperties(getXWikiCfgPath());
    }

    public Properties loadXWikiProperties() throws Exception {
        return getProperties(getXWikiPropertiesPath());
    }

    public PropertiesConfiguration loadXWikiPropertiesConfiguration() throws Exception {
        return getPropertiesConfiguration(getXWikiPropertiesPath());
    }

    /**
     * @deprecated since 4.2M1 use {@link #getPropertiesConfiguration(String)} instead
     */
    @Deprecated
    private Properties getProperties(String path) throws Exception {
        Properties properties = new Properties();

        FileInputStream fis;
        try {
            fis = new FileInputStream(path);

            try {
                properties.load(fis);
            } finally {
                fis.close();
            }
        } catch (FileNotFoundException e) {
            LOGGER.debug("Failed to load properties [" + path + "]", e);
        }

        return properties;
    }

    /**
     * @since 4.2M1
     */
    private PropertiesConfiguration getPropertiesConfiguration(String path) throws Exception {
        PropertiesConfiguration properties = new PropertiesConfiguration();

        FileInputStream fis;
        try {
            fis = new FileInputStream(path);

            try {
                properties.load(fis);
            } finally {
                fis.close();
            }
        } catch (FileNotFoundException e) {
            LOGGER.debug("Failed to load properties [" + path + "]", e);
        }

        return properties;
    }

    public void saveXWikiCfg(Properties properties) throws Exception {
        saveProperties(getXWikiCfgPath(), properties);
    }

    /**
     * @deprecated since 4.2M1 use {@link #saveXWikiProperties(PropertiesConfiguration)} instead
     */
    @Deprecated
    public void saveXWikiProperties(Properties properties) throws Exception {
        saveProperties(getXWikiPropertiesPath(), properties);
    }

    /**
     * @since 4.2M1
     */
    public void saveXWikiProperties(PropertiesConfiguration properties) throws Exception {
        savePropertiesConfiguration(getXWikiPropertiesPath(), properties);
    }

    private void saveProperties(String path, Properties properties) throws Exception {
        FileOutputStream fos = new FileOutputStream(path);
        try {
            properties.store(fos, null);
        } finally {
            fos.close();
        }
    }

    private void savePropertiesConfiguration(String path, PropertiesConfiguration properties) throws Exception {
        FileOutputStream fos = new FileOutputStream(path);
        try {
            properties.save(fos);
        } finally {
            fos.close();
        }
    }

    private String getURL() {
        // We use "xpage=plain" for 2 reasons:
        // 1) the page loads faster since it doesn't need to display the skin
        // 2) if the page doesn't exist it won't return a 404 HTTP Response code
        return URL + ":" + getPort() + "/";
    }

    private String getDefaultStartCommand(int port, int stopPort, int rmiPort) {
        String startCommand = START_COMMAND;
        if (startCommand == null) {
            if (SystemUtils.IS_OS_WINDOWS) {
                startCommand = String.format("cmd /c start_xwiki.bat %s %s", port, stopPort);
            } else {
                startCommand = String.format("sh -f start_xwiki.sh %s %s", port, stopPort);
            }
        } else {
            startCommand = startCommand.replaceFirst(DEFAULT_PORT, String.valueOf(port));
            startCommand = startCommand.replaceFirst(DEFAULT_STOPPORT, String.valueOf(stopPort));
            startCommand = startCommand.replaceFirst(DEFAULT_RMIPORT, String.valueOf(rmiPort));
        }

        return startCommand;
    }

    private String getDefaultStopCommand(int stopPort) {
        String stopCommand = STOP_COMMAND;
        if (stopCommand == null) {
            if (SystemUtils.IS_OS_WINDOWS) {
                stopCommand = String.format("cmd /c stop_xwiki.bat %s", stopPort);
            } else {
                stopCommand = String.format("sh -f stop_xwiki.sh %s", stopPort);
            }
        } else {
            stopCommand = stopCommand.replaceFirst(DEFAULT_STOPPORT, String.valueOf(stopPort));
        }

        return stopCommand;
    }
}