/*************************GO-LICENSE-START********************************* * Copyright 2014 ThoughtWorks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *************************GO-LICENSE-END***********************************/ package com.thoughtworks.go.domain.materials.perforce; import com.thoughtworks.go.domain.materials.Modification; import com.thoughtworks.go.domain.materials.Modifications; import com.thoughtworks.go.domain.materials.Revision; import com.thoughtworks.go.domain.materials.SCMCommand; import com.thoughtworks.go.util.command.*; import org.apache.log4j.Logger; import java.io.File; import java.util.HashMap; import java.util.List; import java.util.Map; import static com.thoughtworks.go.util.command.CommandLine.createCommandLine; public class P4Client extends SCMCommand { private static final Logger LOG = Logger.getLogger(P4Client.class); private final String p4Port; private final String p4user; private String p4passwd; private final String p4ClientName; private boolean useTickets; private boolean loggedIn; public static P4Client fromServerAndPort(String materialFingerprint, String serverAndPort, String userName, String password, String clientName, boolean useTickets, File workingdir, String p4view, ConsoleOutputStreamConsumer consumer, boolean failOnError) throws Exception { return new P4Client(materialFingerprint,serverAndPort, userName, password, clientName,useTickets, workingdir, p4view, consumer, failOnError); } private P4Client(String materialFingerprint, String serverAndPort, String userName, String password, String p4ClientName, boolean useTickets, File workingDirectory, String p4view, ConsoleOutputStreamConsumer consumer, boolean failOnError) throws Exception { super(materialFingerprint); this.p4Port = serverAndPort; this.p4user = userName; this.p4passwd = password; this.p4ClientName = p4ClientName; this.useTickets = useTickets; client(clientSpec(p4ClientName, workingDirectory, p4view), consumer, failOnError); } private String clientSpec(String clientName, File workingFolder, String view) { return "Client: " + clientName + "\n\n" + "Root: " + workingFolder.getAbsolutePath() + "\n\n" + "Options: clobber rmdir\n\n" + "LineEnd: local\n\n" + "View:\n" + view; } public ConsoleResult checkConnection() { execute(p4("login", "-s")); return execute(p4("sync", "-n", "-m", "1", clientView())); } public ConsoleResult version() { CommandLine p4 = createCommandLine("p4").withArgs("-V"); return execute(p4); } public int client(String clientSpec, ConsoleOutputStreamConsumer consumer, boolean failOnError) throws Exception { return execute(p4("client", "-i"), clientSpec, consumer, failOnError); } public void removeClient() { execute(p4("client", "-f", "-d", p4ClientName)); } public List<Modification> latestChange() { ConsoleResult result = execute(p4("changes", "-m", "1", clientView())); P4OutputParser parser = new P4OutputParser(this); return parser.modifications(result); } public List<Modification> changesSince(Revision revision) { CommandLine p4 = p4("changes", clientView() + "@" + revision.getRevision() + ",#head"); ConsoleResult result = execute(p4); P4OutputParser parser = new P4OutputParser(this); return Modifications.filterOutRevision(parser.modifications(result), revision); } public void sync(long revision, boolean shouldForce, ConsoleOutputStreamConsumer outputStreamConsumer) { if (shouldForce) { execute(p4("sync", "-f", clientView() + "@" + revision), "", outputStreamConsumer, true); } else { execute(p4("sync", clientView() + "@" + revision), "", outputStreamConsumer, true); } } public String describe(long revision) { return execute(p4("describe", "-s", String.valueOf(revision))).outputAsString(); } private ConsoleResult execute(CommandLine p4, String input) { login(); String[] input1 = new String[]{input}; ConsoleResult result = runOrBomb(p4, input1); if (result.error().size() > 0) throw new RuntimeException(result.describe()); return result; } int execute(CommandLine p4, String input, ConsoleOutputStreamConsumer outputStreamConsumer, boolean failOnError) { login(); int returnCode = run(p4, outputStreamConsumer, input); if (failOnError) { if (ProgramExitCode.COMMAND_NOT_FOUND == returnCode) throw new RuntimeException("Failed to find 'p4' on your PATH. Please ensure 'p4' is executable by the Go Server and on the Go Agents where this material will be used."); if (ProgramExitCode.SUCCESS != returnCode) throw new RuntimeException("Failed to run : " + p4.describe()); } return returnCode; } private ConsoleResult execute(CommandLine p4) { if(LOG.isDebugEnabled()) LOG.debug("about to execute "+ p4.describe()); return execute(p4, ""); } private void login() { if (useTickets && !loggedIn) { loggedIn = true;//To change body of created methods use File | Settings | File Templates. execute(p4("login"), p4passwd + "\n"); } } CommandLine p4(String command, String... params) { Map<String, String> env = new HashMap<>(); env.put("P4PORT", p4Port); env.put("P4CLIENT", p4ClientName); if (hasUser()) { env.put("P4USER", p4user); } if (usingPassword()) { env.put("P4PASSWD", p4passwd); } CommandLine line = createCommandLine("p4") .withArgs(command).withArgs(params) .withEnv(env); if (hasPassword()) { line = line.withNonArgSecret(new PasswordArgument(p4passwd)); } return line; } private boolean hasUser() { return p4user != null && !p4user.trim().isEmpty(); } private boolean usingPassword() { return !useTickets && hasPassword(); } private boolean hasPassword() { return p4passwd != null && !p4passwd.trim().isEmpty(); } private String clientView() { return "//" + p4ClientName + "/..."; } /** * @deprecated returns the output stream as is, without smudging password arguments, this is security problem, and should not be used in production code as is * This is left here only for tests */ public String admin(String command) { return execute(p4("admin", command)).outputAsString(); } public void useTickets(boolean useTickets) { this.useTickets = useTickets; } public String user() { return p4user; } public String password() { return p4passwd; } }