org.sonar.plugins.scm.perforce.PerforceExecutor.java Source code

Java tutorial

Introduction

Here is the source code for org.sonar.plugins.scm.perforce.PerforceExecutor.java

Source

/*
 * SonarQube :: Plugins :: SCM :: Perforce
 * Copyright (C) 2014 SonarSource
 * dev@sonar.codehaus.org
 *
 * This program 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 3 of the License, or (at your option) any later version.
 *
 * This program 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 program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
 */
package org.sonar.plugins.scm.perforce;

import com.perforce.p4java.client.IClient;
import com.perforce.p4java.client.IClientViewMapping;
import com.perforce.p4java.core.file.FileSpecBuilder;
import com.perforce.p4java.core.file.IFileSpec;
import com.perforce.p4java.exception.MessageSeverityCode;
import com.perforce.p4java.exception.P4JavaException;
import com.perforce.p4java.impl.generic.client.ClientView;
import com.perforce.p4java.impl.generic.client.ClientView.ClientViewMapping;
import com.perforce.p4java.impl.mapbased.rpc.sys.helper.RpcSystemFileCommandsHelper;
import com.perforce.p4java.option.server.TrustOptions;
import com.perforce.p4java.server.IOptionsServer;
import com.perforce.p4java.server.ServerFactory;
import com.perforce.p4java.server.callback.ICommandCallback;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.utils.MessageException;

import javax.annotation.Nullable;

import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.List;

public class PerforceExecutor {

    private static final Logger LOG = LoggerFactory.getLogger(PerforceExecutor.class);

    /** Perforce server. */
    private IOptionsServer server;

    /** Perforce client. */
    private IClient client;

    private final PerforceConfiguration config;

    /**
     * Instantiates a new p4 command helper.
     *
     * @param repository
     *            the repository
     * @param fileSet
     *            the file set
     * @param logger
     *            the logger
     * @throws ScmException
     *             the scm exception
     */
    public PerforceExecutor(PerforceConfiguration config, File workDir) {
        this.config = config;
        init(workDir);
    }

    /**
     * Gets the server.
     *
     * @return the server
     */
    public IOptionsServer getServer() {
        return server;
    }

    /**
     * Gets the client.
     *
     * @return the client
     */
    public IClient getClient() {
        return client;
    }

    /**
     * Sets the client.
     *
     * @param client
     *            the new client
     */
    public void setClient(IClient client) {
        this.client = client;
    }

    /**
     * Initialize Perforce server and client instances.
     *
     */
    protected void init(File workDir) {
        // Initialize the Perforce server.
        initServer();
        // Initialize the Perforce client.
        initClient(workDir);
    }

    /**
     * Cleanup Perforce server and client instances; logout, disconnect, etc.
     *
     */
    public void clean() {
        // Cleanup the Perforce server.
        cleanServer();
    }

    /**
     * Initialize an instance of the Perforce server from the factory using the
     * specified protocol, server port, protocol specific properties and usage
     * options. Register callback on the server. Connect to server; set the user
     * (if present) to server and login to the server with the user's password
     * (if present).
     *
     */
    private void initServer() {

        try {
            createServer();
            // Connect to the server.
            server.connect();
            // Set the Perforce charset.
            String charset = config.charset();
            if (charset != null && server.isConnected() && server.supportsUnicode()) {
                server.setCharsetName(charset);
            }
            // Set server user.
            String username = config.username();
            if (username != null) {
                server.setUserName(username);
                // Check if user is already logged (reuse previous ticket)
                if (!isLogin(server)) {
                    // Login to the server with a password.
                    // Password can be null if it is not needed (i.e. SSO logins).
                    server.login(config.password(), null);
                }
            }
        } catch (URISyntaxException e) {
            throw new IllegalArgumentException(e.getLocalizedMessage(), e);
        } catch (P4JavaException e) {
            throw new IllegalStateException(e.getLocalizedMessage(), e);
        }
    }

    private boolean isLogin(IOptionsServer connection) throws P4JavaException {
        String status = connection.getLoginStatus();
        LOG.debug(status);
        if (status.contains("not necessary")) {
            return true;
        }
        if (status.contains("ticket expires in")) {
            return true;
        }
        // If there is a broker or something else that swallows the message
        if (status.isEmpty()) {
            return true;
        }

        return false;
    }

    private void createServer() throws URISyntaxException, P4JavaException {
        // Set default system file helper
        ServerFactory.setRpcFileSystemHelper(new RpcSystemFileCommandsHelper());
        // Get an instance of the P4J server.
        if (StringUtils.isEmpty(config.port())) {
            throw MessageException
                    .of("Please configure perforce port using " + PerforceConfiguration.PORT_PROP_KEY);
        }
        if (config.useSsl()) {
            server = ServerFactory.getOptionsServer("p4javassl://" + config.port(), null, null);
            server.addTrust(new TrustOptions().setAutoAccept(true));
        } else {
            server = ServerFactory.getOptionsServer("p4java://" + config.port(), null, null);
        }
        // Register server callback.
        server.registerCallback(new CommandLogger());
    }

    private static class CommandLogger implements ICommandCallback {
        @Override
        public void receivedServerMessage(int key, int genericCode, int severityCode, String message) {
            // Log warning messages from server, since it's not included in the other callback methods.
            if (severityCode == MessageSeverityCode.E_WARN) {
                LOG.warn(message);
            }
        }

        @Override
        public void receivedServerInfoLine(int key, String infoLine) {
            LOG.info(infoLine);
        }

        @Override
        public void receivedServerErrorLine(int key, String errorLine) {
            LOG.error(errorLine);
        }

        @Override
        public void issuingServerCommand(int key, String command) {
            LOG.info(command);
        }

        @Override
        public void completedServerCommand(int key, long millisecsTaken) {
            LOG.info("Command completed in " + millisecsTaken + "ms");
        }
    }

    /**
     * Cleanup the Perforce server instance. Disconnect from the Perforce
     * server. Also, set the server to null.
     *
     * Note: It does not logout, because that will delete the user's ticket.
     *
     */
    private void cleanServer() {
        try {
            server.disconnect();
            server = null;
        } catch (P4JavaException e) {
            throw new IllegalStateException(e.getLocalizedMessage(), e);
        }
    }

    /**
     * Initialize an instance of the Perforce client from the server with a
     * specified client name. Set the current client on the server.
     *
     */
    private void initClient(File workDir) {

        String rootDir = workDir.getAbsolutePath();
        try {
            rootDir = workDir.getCanonicalPath();
        } catch (IOException ex) {
            throw new IllegalStateException("Unable to compute rootDir", ex);
        }

        rootDir = encodeWildcards(rootDir);

        try {
            // Get an instance of the Perforce client.
            String p4ClientName = config.clientName();
            if (p4ClientName == null) {
                throw MessageException.of("Please configure client (aka workspace) name using "
                        + PerforceConfiguration.CLIENT_PROP_KEY);
            }
            // Get an instance of the Perforce client.
            client = server.getClient(p4ClientName);

            if (client == null) {
                throw new IllegalStateException("Unable to find client with name " + p4ClientName);
            }
            // Set it to the server as the current client.
            server.setCurrentClient(client);

            boolean exists = false;
            ClientViewMapping clientViewMapping = createClientViewMapping(workDir, p4ClientName);
            ClientView clientView = client.getClientView();
            if (clientView == null) {
                clientView = new ClientView();
            }
            List<IClientViewMapping> list = clientView.getEntryList();
            if (list != null) {
                for (IClientViewMapping map : list) {
                    if (map.getDepotSpec().equals(clientViewMapping.getDepotSpec())
                            && map.getClient().equals(clientViewMapping.getClient())) {
                        exists = true;
                        break;
                    }
                }
            }
            if (!exists) {
                clientView.addEntry(clientViewMapping);
                client.setClientView(clientView);
                if (client.canUpdate()) {
                    client.update();
                }
                if (client.canRefresh()) {
                    client.refresh();
                }
            }
        } catch (P4JavaException e) {
            throw new IllegalStateException(e.getLocalizedMessage(), e);
        }
    }

    /**
     * Creates the client view mapping.
     *
     * @param repo
     *            the repo
     * @param basedir
     *            the basedir
     * @return the client view mapping
     */
    private ClientViewMapping createClientViewMapping(File basedir, String p4ClientName) {
        // Create a new client
        String repoPath = getRepoLocation(encodeWildcards(basedir.getAbsolutePath()));
        String viewPath = getCanonicalRepoPath(repoPath);
        return new ClientViewMapping(0, viewPath, "//" + p4ClientName + "/...");
    }

    /**
     * Gets the repo location.
     *
     * @param path
     *            the path
     * @return the repo location
     */
    private String getRepoLocation(String path) {
        String location = null;
        if (StringUtils.isNotBlank(path) && client != null) {
            try {
                List<IFileSpec> fileSpecs = client.where(FileSpecBuilder.makeFileSpecList(path));
                for (IFileSpec fileSpec : fileSpecs) {
                    if (fileSpec != null && StringUtils.isNotBlank(fileSpec.getDepotPathString())) {
                        location = fileSpec.getDepotPathString();
                        break;
                    }
                }
            } catch (P4JavaException e) {
                throw new IllegalStateException(e);
            }
        }
        return location;
    }

    /**
     * Gets the canonical repo path.
     *
     * @param repoPath
     *            the repo path
     * @return the canonical repo path
     */
    private static String getCanonicalRepoPath(String repoPath) {
        if (repoPath == null) {
            return null;
        }
        if (repoPath.endsWith("/...")) {
            return repoPath;
        } else if (repoPath.endsWith("/")) {
            return repoPath + "...";
        } else {
            return repoPath + "/...";
        }
    }

    /**
     * Perforce wildcards expansion.
     *
     * @param filePath the file path
     * @return the string
     */
    public static String encodeWildcards(@Nullable String filePath) {
        if (filePath != null) {
            return filePath.replaceAll("%", "%25").replaceAll("\\*", "%2A").replaceAll("#", "%23").replaceAll("@",
                    "%40");
        }
        return "";
    }

}