/*******************************************************************************
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
P4ClientInfoMessageList 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 P4ClientInfoMessageList 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 msgId, int level, String info)
{
infoOutput.Add(new P4ClientInfoMessage(msgId, 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 P4ClientInfoMessageList();
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);
}
}
}