/******************************************************************************* Copyright (c) 2010, Perforce Software, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL PERFORCE SOFTWARE, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *******************************************************************************/ /******************************************************************************* * Name : P4Command.cs * * Author : dbb * * Description : Classes encapsulting running a command on the P4 server, then * collecting and returning the bundled results. * ******************************************************************************/ using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Perforce.P4 { /// /// Class wrapping command execution. /// public class P4Command : IDisposable { // Server public P4Server pServer { get; private set; } // Server public Connection Connection { get; private set; } /// /// Command opcode /// private String cmd = String.Empty; /// /// Unique Id set each time command is run /// public uint CommandId { get; private set; } /// /// The arguments used by the command /// private StringList args; /// /// Tagged protocol flag /// private bool tagged; private Dictionary responses; /// /// Capture info results so they can be reformatted /// public P4Server.InfoResultsDelegate infoResultsCallbackFn = null; // various flavors of output collected over the run of this command InfoList infoOutput = null; // Our override so the info output can be built up as a list and not a string private P4Server.InfoResultsDelegate onInfoResultsDelegate = null; // Handle any Resolve callbacks from the server public P4Server.ResolveHandlerDelegate CmdResolveHandler { get; set; } // Handle any Resolve callbacks from the server public P4Server.ResolveAHandlerDelegate CmdResolveAHandler { get; set; } // Handle any input prompts from the server public P4Server.PromptHandlerDelegate CmdPromptHandler { get; set; } /// /// Get the info results from the command execution /// public InfoList InfoOutput { get { return infoOutput; } } /// /// Get the error results from the command execution /// public P4ClientErrorList ErrorOutput { get { P4ClientError conErr = pServer.ConnectionError; P4ClientErrorList errors = pServer.GetErrorResults(CommandId); if (conErr != null) { if ((errors == null) || (errors.Count == 0)) { errors = new P4ClientErrorList(conErr); } else { errors.Insert(0, conErr); } } return errors; } } /// /// Get the text output from the command execution /// public String TextOutput { get { return pServer.GetTextResults(CommandId); } } /// /// Get the tagged results from the command execution /// public TaggedObjectList TaggedOutput { get { return pServer.GetTaggedOutput(CommandId); } } /// /// Get the binary from the command execution /// public byte[] BinaryOutput { get { return pServer.GetBinaryResults(CommandId); } } /// /// Create a new command /// public P4Command(P4Server server) :this (server, null) { } /// /// Create a new command /// public P4Command(P4Server server, P4Server.PromptHandlerDelegate promptHandler) { if (server == null) { throw new ArgumentNullException("server", "P4Command requires a P4Server"); } pServer = server; CommandId = server.getCmdId(); onInfoResultsDelegate = new P4Server.InfoResultsDelegate(OnInfoOut); if (promptHandler != null) CmdPromptHandler = promptHandler; else CmdPromptHandler = new P4Server.PromptHandlerDelegate(HandlePrompt); } /// /// Constructer /// /// Connection to the target Repository /// Command String i.e 'submit' /// Run in tagged protocol /// Arguments for the command public P4Command( Connection connection, String command, bool taggedOutput, params String[] arguments) : this(connection._p4server, command, null, taggedOutput, arguments) { Connection = connection; } /// /// Constructer /// /// Target Repository /// Command String i.e 'submit' /// Run in tagged protocol /// Arguments for the command public P4Command( Repository repository, String command, bool taggedOutput, params String[] arguments) : this(repository.Connection._p4server, command, null, taggedOutput, arguments) { Connection = repository.Connection; } /// /// Constructer /// /// Target Repository /// Command String i.e 'submit' /// Run in tagged protocol /// Arguments for the command public P4Command( Repository repository, String command, P4Server.PromptHandlerDelegate promptHandler, bool taggedOutput, params String[] arguments) : this(repository.Connection._p4server, command, promptHandler, taggedOutput, arguments) { Connection = repository.Connection; } /// /// Constructer /// /// Target P4Server /// Command String i.e 'submit' /// Run in tagged protocol /// Arguments for the command public P4Command( P4Server server, String command, bool taggedOutput, params String[] arguments) : this(server, command, null, taggedOutput, arguments) { } /// /// Constructer /// /// Target P4Server /// Command String i.e 'submit' /// Run in tagged protocol /// Arguments for the command public P4Command( P4Server server, String command, P4Server.PromptHandlerDelegate promptHandler, bool taggedOutput, params String[] arguments) : this(server, promptHandler) { cmd = command; tagged = taggedOutput; args = arguments; } /// /// Command String i.e 'submit' /// public String Cmd { get { return cmd; } set { cmd = value; } } /// /// Arguments for the command /// public StringList Args { get { return args; } set { args = value; } } /// /// Run in tagged protocol /// public bool Tagged { get { return tagged; } set { tagged = value; } } /// /// Dictionary of responses to prompts from the server, where the key /// is the expected prompt from the server and the value is the /// desired response. /// public Dictionary Responses { get { return responses; } set { responses = value; } } /// /// Use the infoResultsReceived event to build up a list of info data. /// /// level of the message /// message text private void OnInfoOut(uint cmdId, int level, String info) { infoOutput.Add(new InfoLine(cmdId, level, info)); } /// /// Respond to a prompt from the server for input /// /// /// /// private String HandlePrompt(uint cmdId, String msg, bool displayText) { if ((responses == null) || (cmdId != CommandId)) return null; if (responses.ContainsKey(msg)) return responses[msg]; if (responses.ContainsKey("DefaultResponse")) return responses["DefaultResponse"]; if (responses.ContainsKey(String.Empty)) return responses[String.Empty]; return null; } /// /// Data to be processed by the command /// public String DataSet { get { return pServer.GetDataSet(CommandId); } set { pServer.SetDataSet(CommandId, value); } } /// /// Run the command supplying additional arguments /// /// Additional arguments inserted in front of the current arguments /// Success/Failure public P4CommandResult Run(StringList flags) { lock (this) { P4CommandResult results = null; results = new P4CommandResult(this, flags); return results; } } /// /// Run the command using the existing arguments /// /// public P4CommandResult Run() { return new P4CommandResult(this); } /// /// Run the command supplying additional arguments /// /// Additional arguments inserted in front of the current arguments /// internal bool RunInt(StringList flags) { lock (this) { // Capture the the info output if (onInfoResultsDelegate != null) pServer.InfoResultsReceived += onInfoResultsDelegate; // Handle any Resolve callbacks from the server if (CmdResolveHandler != null) pServer.ResolveHandler = CmdResolveHandler; // Handle any Resolve callbacks from the server if (CmdResolveAHandler != null) pServer.ResolveAHandler = CmdResolveAHandler; // Handle any prompts for input from the server if (CmdPromptHandler != null) pServer.PromptHandler = CmdPromptHandler; // clear any saved results infoOutput = new InfoList(); Exception lastError = null; bool success = false; try { StringList paramList = flags + args; pServer.EchoCommand(cmd, paramList); while (true) { //retries--; try { success = pServer.RunCommand( cmd, CommandId, tagged, paramList, paramList == null ? 0 : paramList.Count); break; } catch (P4Exception ex) { if (ex is P4CommandCanceledException) { throw; } if (ex is P4CommandTimeOutException) { if (Connection != null) { Connection.Disconnect(); } throw; } if (lastError != null) { if (Connection != null) { Connection.Disconnect(); } // been here before, so don't try again string msg = string.Format("The connection to the Perforce server at {0} has been lost", pServer.Port); P4Exception p4ex = new P4LostConnectionException(ErrorSeverity.E_FATAL, msg); throw; } lastError = ex; if ((ex.Message.Contains("socket: WSA")) || P4ClientError.IsTCPError(ex.ErrorCode) || P4ClientError.IsSSLError(ex.ErrorCode)) { try { pServer.Reconnect(); } catch { if (Connection != null) { Connection.Disconnect(); } string msg = string.Format("The connection to the Perforce server at {0} has been lost", pServer.Port); P4Exception p4ex = new P4LostConnectionException(ErrorSeverity.E_FATAL, msg); throw; } } else { throw; } } catch (Exception ex) { throw; } } if (success) { // info output is gathered by OnInfoOut() } else { // info output is gathered by OnInfoOut() } } catch (Exception ex) { LogFile.LogException("P4Command", ex); throw; } finally { // Cancel the redirected the output, this will reset the callbacks if this command does not have callbacks set pServer.InfoResultsReceived -= onInfoResultsDelegate; pServer.PromptHandler = null; pServer.ResolveHandler = null; pServer.ResolveAHandler = null; } return success; } } /// /// Dispose of any resources /// public virtual void Dispose() { if ((pServer != null) && (pServer.KeepAlive != null)) { pServer.KeepAlive.CommandCompleted(CommandId); } pServer.ReleaseConnection(CommandId); } } }