/******************************************************************************* Copyright (c) 2001-2009, 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 : P4ClientApi.H | | Author : Michael Bishop <mbishop@perforce.com> | | Description: Objective-C wrapper for the Perforce API. \===================================================================*/ #import <CoreFoundation/CoreFoundation.h> /*! \interface P4ClientApi \brief P4ClientApi is an Objective-C wrapper around the <a href="http://www.perforce.com/perforce/doc.current/manuals/p4api/index.html">C++ ClientApi</a>. This documentation is a supplement to the more complete Perforce C++ API documentation available at: <a href="http://www.perforce.com/perforce/doc.current/manuals/p4api">http://www.perforce.com/perforce/doc.current/manuals/p4api</a> \see <a href="http://kbmain.perforce.com/AllPerforceApplications/PerforceCApi">Perforce C++ Client API Knowledge Base Articles</a>. \section OVERVIEW Overview To retrieve data from a Perforce server you must complete three tasks: -# Establish a connection -# Run one (or more) commands -# Disconnect In addition to those tasks, there are a list of properties you may set on the api instance to affect its operation. \section SCHEDULING_ISSUES Scheduling Issues The Perforce C++ API is a blocking API; that is, when a method is executed to connect to, or retrieve data from the server, that method does not return until the data has been sent and the command is finished. This is usually unacceptable in a graphical user environment. The Mac's solution to this is to create a network request and add it as a source to the current RunLoop. The core Perforce C++ API has not been written to support this mechanism directly. Instead you must execute the client api requests in a separate thread so the current thread does not block and send the results back to the originating thread. Typically, this involves a lot of work to package mesages so they can be sent across threads. Much care has been taken to handle as much of this for you as possible. You are still responsible for scheduling the client api request in a separate thread (to retain the most scheduling control) but you can specify the thread to which the server results will be sent and the return messages will be packaged up and sent to your delegate in that thread. Convenience methods to create NSInvocation objects for key client api methods are provided for your use. You can add these invocations to an NSOperationQueue and let the system schedule them, or you can ask the invocation object itself to execute in another thread using NSObject's <tt>performSelectorInBackground:withObject:</tt> (and variants). Below is an example of blocking, and non-blocking ways of connecting to the server: BLOCKING: \code P4ClientApi * api = [[[P4ClientApi alloc] init] autorelease]; NSError * e; // Your thread could block for a while if the server is not running if (![api connectToPort:@"perforce:1666" withProtocol:nil error:&e] ) return e; ... run the command \endcode NON-BLOCKING: \code P4ClientApi * api = [[P4ClientApi alloc] init]; // Instead, of calling the method directly, create an invocation to be // executed in another thread api.callbackThread = [NSThread currentThread]; NSInvocation * invocation = [api invocationForConnectToPort:@"perforce:1666" protocol:nil delegate:self]; NSInvocationOperation * operation = [[NSInvocationOperation alloc] initWithInvocation:invocation]; // the operation queue will schedule the operation in another thread and return immediately [operationQueue addOperation:operation]; [operation release]; ... -(void)clientApi:(P4ClientApi *) api didFailToConnectWithError:(NSError*)s { NSLog(@"failed to connect to %@", api.p4port); [api release]; } -(void)clientApiDidConnect:(P4ClientApi*)api { ... run the command on the client api } \endcode \section HANDLING_ERRORS Error Handling The Perforce Objective-C API wraps up native Perforce errors into an NSError object. It does so in the following manner: -# The domain of the NSError is set to: \p P4ErrorDomain. -# The formatted message from the C++ Error object is passed in the NSError object as the \p localizedDescription. -# The code of the first ErrorId in the C++ Error is placed into the NSError \p code. You can use this to compare with the included ErrorId instances in the C++ header files. -# Additionally, you can see the pieces of the above ErrorId by looking in the userInfo field of the NSError and finding the NSValues for these keys: P4SubsystemErrorKey, P4SubCodeErrorKey, P4SeverityErrorKey, P4GenericErrorKey */ /// \mainpage P4ClientApi /// \copydoc P4ClientApi #import <Foundation/Foundation.h> /// \brief establishes the NSError domain for Perforce client errors extern NSString * const P4ErrorDomain; /// \brief Key to retrieve the subsystem code of an Error from Perforce extern NSString * const P4SubsystemErrorKey; /// \brief Key to retrieve the code (within a subsystem) of an Error from Perforce extern NSString * const P4SubCodeErrorKey; /// \brief Key to retrieve the severity of an error from Perforce extern NSString * const P4SeverityErrorKey; /// \brief Key to retrieve the generic code of an Error from Perforce extern NSString * const P4GenericErrorKey; /// \brief Used to notify the server of the protocol level the client /// understands /// \see <a href="http://kb.perforce.com/P4dServerReference/ProtocolLevels/PerforceClientLevels">Perforce Protocol Levels</a> extern NSString * const P4ProtocolVersion; @protocol P4ClientApiConnectionDelegate; @protocol P4ClientApiCommandDelegate; @interface P4ClientApi : NSObject { @private void * _clientApi; // untyped to prevent C++ header inclusion void * _currentClientUser; // untyped to prevent C++ header inclusion NSString * _currentCommand; NSArray * _currentArgs; id _userInfo; NSThread * _callbackThread; BOOL _connected; NSString * _ticketFilePath; NSString * _programIdentifier; NSString * _version; NSDictionary * _requestedProtocol; BOOL _prefersTaggedOutput; int _rowScanningLimit; int _tableLockingTimeLimit; int _returnedResultsLimit; } /*! \name Connecting Use these to establish a connection to the server. */ //@{ /*! \brief Establish a connection and prepare to run commands. Use this method to establish a connection to a Perforce server. It must be made before running any commands with runCommand:withArguments:delegate: The protocol is an NSDictionary of keys and values. The values can be of these types NSString, NSNumber, NSNull. They will all be converted to strings when sent to the server. \note This method does not return until after a connection is made or unsuccessful. For unsucessful connections, this could last 5 seconds, blocking your event-loop. You may wish to instead call connectToPort:withProtocol:delegate: in another thread and have it return messages to your current thread. \param port a P4PORT value or \p nil to use the default. \param requestedProtocol A dictionary of protocol values to configure the connection. You may pass \p nil if you don't have a specific protocol requirement. \param error a pointer to an NSError pointer or \p nil (see \ref HANDLING_ERRORS) \result YES if the connection was completed \result NO if the connection could not be made. An error will be returned in \p which the caller must retain. \note Internally, this is equivalent to calling <tt>ClientApi::SetPort(); ClientApi::SetProtocol(); ClientApi::Init();</tt> \see connectToPort:withProtocol:delegate: \see ClientApi::SetPort() \see ClientApi::SetProtocol() \see ClientApi::Init() */ -(BOOL)connectToPort:(NSString*)port withProtocol:(NSDictionary*)requestedProtocol error:(NSError **)error; /*! \brief Establish a connection and prepare to run commands. Use this method to establish a connection to a Perforce server. It must be made before running any commands with runCommand:withArguments:delegate: The protocol is an NSDictionary of keys and values. The values can be of these types NSString, NSNumber, NSNull. They will all be converted to strings when sent to the server. \note This method does not return until after a connection is made or unsuccessful. For unsucessful connections, this could last 5 seconds, blocking your event-loop. You may wish to instead call it in another thread and have it return messages in your current thread to your delegate. \param port a P4PORT value or \p nil to use the default. \param requestedProtocol A dictionary of protocol values to configure the connection. You may pass \p nil if you don't have a specific protocol requirement. \param delegate a delegate object that is used to process messages from the server. It is not retained. \note Internally, this is equivalent to calling <tt>ClientApi::SetPort(); ClientApi::SetProtocol(); ClientApi::Init();</tt> \see connectToPort:withProtocol:error: \see ClientApi::SetPort() \see ClientApi::SetProtocol() \see ClientApi::Init() */ -(void)connectToPort:(NSString*)port withProtocol:(NSDictionary*)requestedProtocol delegate:(id<P4ClientApiConnectionDelegate>)delegate; /*! \brief Convenience method to create an invocation for use in non-blocking calls. Use this method to create an NSInvocation that can be called in another thread or added to an NSOperationQueue as an NSInvocationOperation. The protocol is an NSDictionary of keys and values. The values can be of these types NSString, NSNumber, NSNull. They will all be converted to strings when sent to the server. \note as is consistent with NSInvocations, the arguments are not retained by default. Creating an NSInvocationOperation will retain the arguments automatically. \param port a P4PORT value or \p nil to use the default. \param requestedProtocol A dictionary of protocol values to configure the connection. You may pass \p nil if you don't have a specific protocol requirement. \param delegate a delegate object that is used to process messages from the server. \see connectToPort:withProtocol:delegate: */ -(NSInvocation*)invocationForConnectToPort:(NSString*)port withProtocol:(NSDictionary*)requestedProtocol delegate:(id<P4ClientApiConnectionDelegate>)delegate; // @} /*! \brief The port connected to in connectToPort:withProtocol:error: \note It could be a Bonjour name. \see ClientApi::GetPort() */ @property(readonly, copy) NSString * p4port; /*! \brief The current user name. \see ClientApi::Set/GetUser() */ @property(readwrite, copy) NSString * user; /*! \brief The current client name. \see ClientApi::Set/GetClient() */ @property(readwrite, copy) NSString * client; /*! \brief The name of the character set used for translation between the server and the client. This is required field when talking to a server in unicode mode and required to be \p nil when communicating with a non-unicode server. Perforce recommends the value of \p utf8 with Mac OS X-based clients. For more information on using the P4API with unicode-enabled servers, see <a href="http://kbmain.perforce.com/P4dServerReference/Internationalization/UnicodeAndP4..sAndAnswers">Unicode and P4API</a> \note This calls ClientApi::SetTrans() before establishing a connection */ @property(readwrite, copy) NSString * charset; /*! \brief The current working directory. \see ClientApi::Set/GetCwd() */ @property(readwrite, copy) NSString * currentWorkingDirectory; /*! \brief The password sent to the server for this connection. \see ClientApi::Set/GetPassword() */ @property(readwrite, copy) NSString * password; /*! \brief Indicates to the server that the client prefers data to be returned via the clientApi:didReceiveTaggedResponse: method of the command delegate. This is \p YES by default. */ @property(readwrite, assign) BOOL prefersTaggedOutput; /*! \brief The name of this host. Unless set explicitly may not be available until after connecting to the server. \see ClientApi::Set/GetHost() */ @property(readwrite, copy) NSString * hostname; /*! \brief The location of the user's ticketfile \note This must be the full path to the file and not a directory. \see ClientApi::Set/GetTicketFile() */ @property(readwrite, copy) NSString * ticketFilePath; /*! \brief The identifier of the application program which will show up in logging viewed with the <tt>p4 monitor</tt> command and in server log output. It's "P4V" below. \code 45381 P4V/v60 10.0.105.2194 R joeuser joeuser 04:01:53 IDLE none \endcode \note Unlike the C++ API, you can set this at anytime. \see ClientApi::Set/GetProg() */ @property(readwrite, copy) NSString * programIdentifier; /*! \brief The version of the application program which will show up in logging viewed with the <tt>p4 monitor</tt> command and in server log output. When not set, it defaults to the protocol level of the Client API libraries. It's "v60" below. \code 45381 P4V/v60 10.0.105.2194 R joeuser joeuser 04:01:53 IDLE none \endcode \note Unlike the C++ API, you can set this at anytime. \see ClientApi::Set/GetProg() */ @property(readwrite, copy) NSString * version; /*! \brief The userInfo object set on the api object as context when connecting or running commands. \see runCommand:withArguments:delegate: */ @property(readwrite, retain) id userInfo; /*! \brief The thread in which the client api will send back its responses. This is useful when executing the client api in another thread and sending back responses to the main thread for asynchronous operation. When the value is \p nil, the api object will call back to whatever the currently executing thread is. It's \p nil by default */ @property(readwrite, retain) NSThread * callbackThread; /*! \brief The filename used when Perforce searches for configuration files from which to retrieve default values. \see ClientApi::GetConfig() */ @property(readonly) NSString * configurationFilename; /*! \brief The state of the connection. If the connection was dropped or otherwise disconnected, this will return NO. \see ClientApi::Dropped() */ @property(readonly) BOOL connected; /*! \name Executing commands Once you have established a connection to the server, you can use these to execute commands. */ //@{ /*! \brief Executes a command on the Perforce server. Use this method to actually execute a command on the Perforce server. Results are returned via the delegate. \note This method does not return until after after the server has completely processed the command and has sent clientApiDidFinishCommand: to your delegate. Because this stops the current RunLoop from continuing, you may find it desirable to make this call in another thread and have the server send messages back to the current thread, allowing the RunLoop to continue. \note Before making thing call, you must have a connection established through connectToPort:withProtocol:error: \attention This method is not thread-safe. If you are scheduling this call by scheduling invocation objects, you must make sure that this call returns before the next invocation is called. You can do this by creating a separate NSOperationQueue per client api instance and making sure the queue's <tt>maxConcurrentOperationCount</tt> equals 1 before queuing your invocation objects in it. \param command the command you want to run (eg: "sync"). \param args An array of arguments to be sent with the command. You may pass \p nil if the \p command doesn't require arguments to execute. \param variables a set of protocol variables that are valid only for this command \param delegate a delegate object that is used to process messages from the server. It is not retained. \note Internally, this is equivalent to calling <tt>ClientApi::SetArgs(); ClientApi::RunCmd();</tt> */ -(void)runCommand:(NSString *)function withArguments:(NSArray*)args commandVariables:(NSDictionary*)variables delegate:(id<P4ClientApiCommandDelegate>)delegate; -(void)runCommand:(NSString *)function withArguments:(NSArray*)args delegate:(id<P4ClientApiCommandDelegate>)delegate; /*! \brief Convenience method to create an invocation for use in non-blocking calls. Use this method to create an NSInvocation that can be called in another thread or added to an NSOperationQueue as an NSInvocationOperation. \note as is consistent with NSInvocations, the arguments are not retained by default. Creating an NSInvocationOperation will retain the arguments automatically. \param command the command you want to run (eg: "sync"). \param args An array of arguments to be sent with the command. You may pass \p nil if the \p command doesn't require arguments to execute. \param delegate a delegate object that is used to process messages from the server. \see runCommand:withArguments:delegate: */ -(NSInvocation*)invocationForRunCommand:(NSString *)command withArguments:(NSArray*)args delegate:(id<P4ClientApiCommandDelegate>)delegate; //@} /*! \name Advanced Performance Options Use these to control or limit server calculation or output. */ //@{ /*! \brief Limits the rows of data considered and prevents the server from making large-scale scans. Assigning a value of 0 (the default) has no effect. \see http://kb.perforce.com/AdminTasks/PerformanceTuning/MaximizingPe..Performance \see p4 help maxscanrows */ @property(readwrite, assign) int rowScanningLimit; /*! \brief Limits the amount of time spent during data scans to prevent the server from locking tables for too long. Assigning a value of 0 (the default) has no effect. \see http://kb.perforce.com/AdminTasks/PerformanceTuning/MaximizingPe..Performance \see p4 help maxlocktime */ @property(readwrite, assign) int tableLockingTimeLimit; /*! \brief Limits the rows of resulting data buffered and prevents the server from using excessive memory. Assigning a value of 0 (the default) has no effect. \see http://kb.perforce.com/AdminTasks/PerformanceTuning/MaximizingPe..Performance \see p4 help maxresults */ @property(readwrite, assign) int returnedResultsLimit; //@} /*! \name While running commands... In your P4ClientApiCommandDelegate methods, you can call these to retrieve the items passed to <tt>runCommand:withArguments:delegate:</tt>... */ //@{ /*! \brief The command that the client api instance is executing. \see runCommand:withArguments:delegate: */ @property(readonly, copy) NSString * currentCommand; /*! \brief The arguments for the currently executing command. \see runCommand:withArguments:delegate: */ @property(readonly, copy) NSArray * currentArguments; //@} /*! \name Disconnecting These terminate your connection to the server, reporting any residual errors. */ //@{ /*! \brief Disconnects from the server and reports disconnection errors. \param error a pointer to an NSError or \p nil (see \ref HANDLING_ERRORS) \result YES if an error was raised \result NO if the disconnection has no errors. \see ClientApi::Final() */ -(BOOL)disconnect:(NSError **)error; /*! \brief Disconnects from the server and reports disconnection errors. \param delegate a delegate object that is used to process messages from the server. It is not retained. \see ClientApi::Final() */ -(void)disconnectWithDelegate:(id<P4ClientApiConnectionDelegate>)delegate; /*! \brief Convenience method to create an invocation for use in non-blocking calls. Use this method to create an NSInvocation that can be called in another thread or added to an NSOperationQueue as an NSInvocationOperation. \note as is consistent with NSInvocations, the arguments are not retained by default. Creating an NSInvocationOperation will retain the arguments automatically. \param delegate a delegate object that is used to process messages from the server. \see disconnectWithDelegate: */ -(NSInvocation*)invocationForDisconnectWithDelegate:(id<P4ClientApiConnectionDelegate>)delegate; //@} /*! \name Setting and inspecting the server protocol You can request specific protocol parameters when calling connectToPort:withProtocol:error:. After running a command, the server will also send back additional parameters about the connection. */ //@{ /*! \brief The protocol dictionary passed in to connectToPort:withProtocol:... \see connectToPort:withProtocol:error: */ @property(readonly, copy) NSDictionary * requestedProtocol; /*! \brief The protocol parameters (as a property) containing information about communication between the server and client. After making a call to runCommand:withArguments:delegate:, the server sends a dictionary of additional protocol variables back to the client with additional information about the communication between server and client. \note This is \b different from the dictionary passed in to connectToPort:withProtocol:error: \see ClientApi::GetProtocol() */ -(NSObject*)serverProtocolValueForKey:(NSString*)key; //@} @end /*! \protocol P4ClientApiConnectionDelegate \brief Implement this in a delegate when establishing a non-blocking connection to a Perforce server. connectToPort:withProtocol:error: is a blocking call that will not return until a connection is made, or fails. The timeout is roughly 5 seconds. For this reason, you are encouraged to implement this protocol and make your connection in another thread. */ @protocol P4ClientApiConnectionDelegate <NSObject> @optional /*! \brief Called on your delegate when the client has sucessfully established a connection. You may now start running commands with runCommand:withArguments:delegate: \param api the client api instance calling this method. */ -(void)clientApiDidConnect:(P4ClientApi*)api; /*! \brief Called on your delegate when the client was not able to establish a connection. The client may wait for about 5 seconds before it calls your delegate with this method. \param api the client api instance calling this method. \param error the error message which you can report to the user. (see \ref HANDLING_ERRORS) */ -(void)clientApi:(P4ClientApi*)api didFailToConnectWithError:(NSError*)error; /*! \brief Called on your delegate when the client disconnects from the server (by calling disconnectWithDelegate:). \param api the client api instance calling this method. \param error the residual errors from the server (see \ref HANDLING_ERRORS) */ -(void)clientApi:(P4ClientApi*)api didDisconnectWithError:(NSError *)error; @end /*! \protocol P4ClientApiCommandDelegate \brief Implement this to receieve messages from the Perforce server as a result of calling runCommand:withArguments:delegate: on a P4ClientApi instance. Each method returns a different type of response depending on the executing command and the configuration of the P4ClientApi instance. It is closest to the ClientUser class in the C++ API. \note If a message is not implemented by your delegate class, the ClientUser behavior is executed by default. That behavior is what the p4 command-line CLI uses. Each of of these methods has an analagous callback in the C++ API. They are documented with each method. */ @protocol P4ClientApiCommandDelegate <NSObject> @optional /*! \brief Called on your delegate when the client was not able to establish a connection. The client may wait for about 5 seconds before it calls your delegate with this method. \param api the client api instance calling this method. \param error the error message which you can report to user. (see \ref HANDLING_ERRORS) \see ClientUser::OutputError */ -(void)clientApi:(P4ClientApi*)api didReceiveError:(NSError*)error; /*! \brief Called on your delegate by the server during most Perforce commands; its most common use is to display listings of information about files. \note The messages from the server in this call are "simple" because they are meant to be sent directly to the console and do not include a code for the message. \param api the client api instance calling this method. \param message the printable message from the server \param level the indentation "level" of the output \see ClientUser::OutputInfo( */ -(void)clientApi:(P4ClientApi*)api didReceiveSimpleServerMessage:(NSString*)message level:(char)level; /*! \brief Called on your delegate when the server has formatted output. This is much more useful than a simple message. Normally, only the results of an "fstat" command come through this method, but you can get many more commands to output in this way, by setting \p prefersTaggedOutput to YES on the P4ClientApi instance. \code api.prefersTaggedOutput = YES; \endcode \param api the client api instance calling this method. \param response the dictionary containin a structured response. \see ClientUser::OutputStat */ -(void)clientApi:(P4ClientApi*)api didReceiveTaggedResponse:(NSDictionary*)response; /*! \brief Called on your delegate typically as a result of a "print" command on a binary file. \param api the client api instance calling this method. \param data the binary data from the server \see ClientUser::OutputBinary */ -(void)clientApi:(P4ClientApi*)api didReceiveBinaryContent:(NSData*)data; /*! \brief Called on your delegate typically as a result of a "print" command on a text file. \param api the client api instance calling this method. \param text the text data from the server \see ClientUser::OutputText( */ -(void)clientApi:(P4ClientApi*)api didReceiveTextContent:(NSString*)text; -(void)clientApi:(P4ClientApi*)api didRequestInputAsText:(NSString**)text; /*! \brief Called on your delegate when the server has finished processing a command. \param api the client api instance calling this method. \see ClientUser::Finished() */ -(void)clientApiDidFinishCommand:(P4ClientApi*)api; @end
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#1 | 8331 | Matt Attaway |
Adding initial version of MacMenu for Perforce MacMenu is a helpful Perforce client that sits in your toolbar. It allows you to run standard Perforce operations on the document that is open the currently active editor/viewer. |