Adding Client API Support To A Filesystem

From FileSys.Org Wiki
Revision as of 14:41, 2 October 2024 by Tommygonk (talk | contribs)

The client API feature was added in the JFileServer 1.4.0/JFileServer Enterprise 1.3.0 release, as an optional interface a filesystem can implement. The client API interface allows a client to send requests to the file server over existing protocols, currently SMB2 and SMB3 protocols are supported. A filesystem must implement the core DiskInterface, and can then add other functionality using optional interfaces such as to implement file locking, or in this case the client API interface.

The client API uses a special path on the server that the client can write a request into, and receives the response from the server. The special path is not visible in folder listings. The main implementation uses JSON format to send a request and for the response, but you can implement your own format of request and response by implementing the lower level ClientAPIInterface, rather than extending the JSONClientAPI implementation.

The JSON client API has a client side application, currently for Windows 10 and 11, that provides a right click context menu into the Windows File Explorer application, with a configurable set of sub-menus that trigger server side actions. The client side application is also able to display message boxes and other user interface items before and after an action has run, show notifications, run applications and open URLs all under control of the server side actions.

The fileServersNG Alfresco add-on module contains a reference implementation of a client API, extending the JSONClientAPI. There is a Wiki document that describes the fileServersNG client API implementation here, and the source code for the fileServersNG implementation is here.

This document describes how to implement a client API by extending the JSONClientAPI implementation.

Filesystem Requirements

To enable client API support a filesystem must implement the optional org.filesys.server.filesys.clientapi.ClientAPI interface, with a client API implementation class that either implements the base org.filesys.server.filesys.clientapi.ClientAPIInterface interface or extends the org.filesys.server.filesys.clientapi.json.JSONClientAPI abstract class.

The source code for the client API interfaces and classes is available here.

The ClientAPI interface has two methods :-

Method Description
public boolean isClientAPIEnabled() Returns true is the client API is enabled for this filesystem, else false
public ClientAPIInterface getClientAPI(SrvSession<?> sess, TreeConnection tree) Returns the ClientAPI implementation that handles the requests from the client

The base ClientAPIInterface interface has the following methods :-

Method Description
public String getClientAPIPath() Returns the special path that the client will write requests and receive responses via. The path should be a path that is relative to the root of the filesystem, for the JSONClientAPI implementation the special path is \__JSONAPI__
public ClientAPINetworkFile openClientAPIFile(SrvSession<?> sess, TreeConnection tree, FileOpenParams params) Returns a special in-memory file that represents a file open to the special client API path. The in-memory file is created per file open, it is not shared between users or sessions
public void processRequest( ClientAPINetworkFile netFile) Process a client request received via the specified client API file. The request data can be retrieved using the byte[] netFile.getRequestData() method. The response is written to the client API file using the setResponseData( byte[]) method.

The JSONClientAPI implementation handles all of the ClientAPIInterface methods, and adds the following methods that must be implemented or can be overridden :-

Method Description
public EnumSet<ApiRequest> getSupportedRequests() Returns the set of API requests that this implementation supports. The APIRequest enum has the values GetAPIInfo, GetURLForPath, GetPathStatus and RunAction. This method must be implemented.
public String getClientAPIVersion() Return the client API version in n.n.n.n format. This method must be implemented.
public ContextMenu getContextMenu() Return the context menu details with the top level menu title and icon, plus the list of server actions that will be displayed on a sub-menu of the client context menu. This method must be implemented.
protected void preProcessRequest( ClientAPINetworkFile netFile, ClientAPIRequest req) Optional method that can be overridden to handle a client request before the main request handler processes the request.
public ClientAPIResponse processGetURLForPath( GetURLForPathRequest req) Optional method that should be overridden if the client API GetURLForPath API request is supported. The GetURLForPathRequest object contains the relative path of the file or folder that was selected on the client.
public ClientAPIResponse processGetPathStatus( GetPathStatusRequest req) Optional method that should be overridden if the client API GetPathStatus API request is supported. The GetPathStatusRequest object contains a list of the relative paths that were selected on the client and a check type value for the path status to be checked.
public ClientAPIResponse processRunAction( RunActionRequest req, ClientAPINetworkFile netFile) This method handles processes running a server side action associated with a context menu. The RunActionRequest object contains the action name, a list of one or more relative paths that were selected on the client and an optional list of parameter values.

Implementing getSupportedRequests()

The JSONClientAPI implements a GetAPIInfo request that is used by the client to probe that the network drive supports the client API, and returns information about the API including the context menu title, description and icon details plus the sub-menu details that includes details of each server action.

Here is a sample context menu from the fileServersNG client API implementation :-

ContextMenu.png

In the fileServersNG context menu example the context menu title is Alfresco Drive with a custom icon. The sub-menu contains the list of server actions such as Check In and Check Out.

Each server action on the sub-menu has an action name, description, details about the file and/or folder selections the action accepts, an optional user interface action to be run before the action request is sent to the server and an optional icon definition.

As the JSONClientAPI implements the GetAPIInfo request, that must be included in the supported requests list. The RunAction API request should be included if you want to handle custom actions in your API implementation. A sample implementation of the getSupportedRequests() method :-

public EnumSet<ApiRequest> getSupportedRequests() {
  return EnumSet.of ( ApiRequest.GetApiInfo, ApiRequest.RunAction);
}


Implementing getClientAPIVersion()

The getClientAPIVersion() method is called by the GetAPIInfo request processing when collecting details about the client API implementation. The method should return a version string with the format <major>.<minor>.<patch>.

A sample implementation of the getClientAPIVersion() method :-

public String getClientAPIVersion() {
  return "1.0.0";
}


Implementing getContextMenu()

The getContextMenu() method is called by the GetAPIInfo request processing when collecting details about the client API implementation. The method returns a ContextMenu object that defines the top level context menu and the server actions used for the sub-menu.

A ContextMenu object is created using the constructor ContextMenu(String title, String description, ActionIcon icon) where title is the menu title that will be shown on the File Explorer right click context menu, description may be used as a tooltip, and icon defines the icon to be displayed for the context menu.

The ContextMenu needs a list of ServerAction objects to be attached using the setActions( List<ServerAction>) method, this defines the items that will be shown on the sub-menu of the context menu.

ActionIcon

The ActionIcon class defines an icon on the client. The icon may be included in the client API context menu DLL, in the Windows shell32.dll system file or you can define a custom icon from another Windows system DLL.

There are convenience methods on the ActionIcon class for creating the different types of icon. To define an icon that is contained in the client API context menu DLL use the ActionIcon.createAppIcon(int) method, to define an icon that is contained in the Windows shell32.dll system file use the ActionIcon.createShellIcon(int) method, and to create a custom icon use the ActionIcon.createCustomIcon(int, String). For a custom icon the values are the index of the icon (should be a negative number) and the name of the Windows system file that contains the icon (without any path).

An ActionIcon can also be created using a name, using the ActionIcon.createByName(String) method, this is useful if the context menu is defined a configuration file.

The table below lists the available ActionIcon types, ids and names. Id types are ActionIcon.IconType enum values :-

Icon Name Type Id Description
ShellScript.png ShellScript Shell ActionIcon.SHELLICON_SCRIPT Script icon
ShellWebBrowser.png ShellWebBrowser Shell ActionIcon.SHELLICON_WEBBROWSER Web browser icon
ShellFolder.png ShellFolder Shell ActionIcon.SHELLICON_FOLDER Folder icon
ShellPrinter.png ShellPrinter Shell ActionIcon.SHELLICON_PRINTER Printer icon
ShellMagnify.png ShellMagnify Shell ActionIcon.SHELLICON_MAGNIFY Magnifying glass icon
ShellStar.png ShellStar Shell ActionIcon.SHELLICON_STAR Star icon
ShellLock.png ShellLock Shell ActionIcon.SHELLICON_LOCK Lock icon
ShellMovie.png ShellMovie Shell ActionIcon.SHELLICON_MOVIE Movie icon
ShellAudio.png ShellAudio Shell ActionIcon.SHELLICON_AUDIO Audio icon
ShellCamera.png ShellCamera Shell ActionIcon.SHELLICON_CAMERA Camera icon
ShellInformation.png ShellInformation Shell ActionIcon.SHELLICON_INFORMATION Information icon
ShellHome.png ShellHome Shell ActionIcon.SHELLICON_HOME Home icon
ShellGear.png ShellGear Shell ActionIcon.SHELLICON_GEAR Gear icon
FileSysOrgAlfresco.png FileSysOrgAlfresco App ActionIcon.APPICON_FILESYSORG_ALFRESCO Filesys.org Alfresco application icon
FileSysOrg.png FileSysOrg App ActionIcon.APPICON_FILESYSORG Filesys.org application icon
AlfrescoCheckIn.png AlfrescoCheckIn App ActionIcon.APPICON_ALFRESCO_CHECKIN Alfresco check in icon
AlfrescoCheckOut.png AlfrescoCheckOut App ActionIcon.APPICON_ALFRESCO_CHECKOUT Alfresco check out icon

ServerAction

The ServerAction class defines a server-side action that will be displayed on the client context menu sub-menu, this includes details of the menu name, description and icon plus details of what type of file/folder selections the action accepts, and an optional user interface action for the client to run before sending the action request to the server, such as displaying an acknowledgement message dialog with Ok/Cancel buttons.

To create a server-side action you must extend the ServerAction class. The ServerAction class requires an action name, that will be used as the menu title, a description and a set of flags that define what combination of file/folder selections the action accepts. The ServerAction.Flags enum class defines the available flags, which are also defined below :-

Flag Description
Files Action accepts file selections
Folders Action accepts folder selections
MultiSelect Action accepts multiple file and/or folder selections

For example, a flags value of EnumSet.of( Flags.Files) would indicate the action only accepts a single file selection. If multiple files are selected on the client, or a folder is selected, the server action sub-menu will be greyed out and not selectable by the user. A flags value of EnumSet.of( Flags.Files, Flags.MultiSelect) would indicate the action accepts file selections with one or more files selected.

A ServerAction can have an optional icon defined, using the same ActionIcon class that the top level context menu uses. Set the icon using the appropriate ServerAction constructor, or using the setIcon(ActionIcon) method. If no icon is defined for a server action the sub-menu item will be shown with no icon.

A ServerAction can have an optional user interface action that will instruct the client application to display a user interface item such as a message box. If the user interface item has an option to cancel such as Ok/Cancel buttons then the action will not be triggered on the server if the cancel option is selected by the user.

A user interface action is configured for a server action using the setUIAction(ClientUIAction) method. The ClientUIAction has the type of user interface item to be displayed with an optional title, message text and the message severity for message box dialogs. The available ClientUIAction.UIAction types are shown below :-

UIAction Description
MessageDialog Display a message box dialog with an Ok button.
YesNoDialog Display a message box dialog with Yes and No buttons. If the No button is selected by the user the action will not be run.
OkCancelDialog Display a message box dialog with Ok and Cancel buttons. If the Cancel button is selected by the user the action will not be run.
CheckInDialog Displays a custom dialog that lists the selected file(s) with a comment input field and buttons Check In, Undo Checkout and Cancel.


An example of the check in dialog :-
CheckInDialog.png
If the Check In button is selected the action is run with the request parameters containing a value of checkin, if the Undo Checkout button is selected the action is run with the request parameters containing a value of cancel. Selecting the Cancel button will not run the server action.

If the title is not set for a ClientUIAction the client application name will be used.

For the message box dialog user interface actions a message level can be specified to indicate whether the message is an informational, warning or error message. The message box dialog will show a different icon depending on the message level. The available values are defined in the ServerAction.MessageLevel enum class, with values of Info, Warn and Error.

Here is example source code of how to create a simple ContextMenu :-

public class MyServerAction extends ServerAction {
  public MyServerAction() {
    super("Server Action", "A sample server action", EnumSet.of( Flags.Files));

    setIcon( ActionIcon.createShellIcon( ActionIcon.SHELLICON_INFORMATION);
    setUIAction( new ClientUIAction( UIAction.YesNoDialog, "Run MyServerAction",
                         "Run the example server action ?");
  }

  public ClientAPIResponse runAction(RunActionRequest req, ClientAPINetworkFile netFile)
    throws ClientAPIException {
    ..
    ..
  }
}

public ContextMenu getContextMenu() {
  ContextMenu ctxMenu = new ContextMenu( "JFileServer Actions", "Sample API context menu",
                                      ActionIcon.createAppIcon( ActionIcon.APPICON_FILESYSORG));

  List<ServerAction> actions = new ArrayList<ServerAction>();
  actions.add( new MyServerAction());

  ctxMenu.addActions( actions);

  return ctxMenu;
}

Implementing ServerAction runAction()

When the user selects an action on the client API sub-menu, the client will show the user interface item if the action has the optional UIAction configured, then if the action is not cancelled by the user or there is no user interface item to be displayed the request to run the action is sent to the server.

To run the action on the server the client will write a RunActionRequest object, in JSON format, to the client API file. The RunActionRequest contains the action name, the list of the selected files/folder paths and an optional list of parameter values. The JSONClientAPI.processRequest() method will be triggered that will pass the RunActionRequest object to the processRunAction() method which must be implemented in your own client API implementation. The default implementation of processRunAction() in the JSONClientAPI class will return a Not Implemented error to the client.

In your processRunAction() method you need to map the action name to the corresponding ServerAction object, then call the runAction() method with the RunActionRequest object and the ClientAPINetworkFile object. The runAction() method should either return a RunActionResponse object, or other object that extends the ClientAPIResponse class, or throw a ClientAPIException.

RunActionResponse

The RunActionResponse object holds the response details that are sent back to the client that indicate if the server action was successful and has various optional actions that the client can be instructed to do with a list of associated parameters, an optional user interface action such as displaying a message box dialog on the client, an optional notification to be displayed on the client, and a flag to indicate if the original selected files/folder details should be refreshed on the client.

The available client actions are listed below, from the ServerAction.ClientAction enum class :-

Action Description
NoAction No action required on the client.
OpenURL Open a URL on the client, the URL value is the first parameter value.
OpenApplication Open an application on the client. The parameter values contain the operation name, application name and an optional parameter for the application.
The application will be launched using the Windows ShellExceute() API call.
UIAction Indicates a user interface action on the client. The details of the user interface action uses the same ClientUIAction class that is used when defining a ServerAction, as documented here
UpdatedPaths The parameter list contains a list of paths that have been updated by the server action, the client will refresh File Explorer views for the updated paths.
NewPaths The parameter list contains a list of the paths that have been created by the server action, the client will refresh File Explorer views for the updated paths.
DeletePaths The parameter list contains a list of the paths that have been deleted by the server action, the client will refresh File Explorer views for the updated paths.

The RunActionResponse class has many convenience methods to set the various sections of the response :-

Method Description
setSuccess() If the server action only needs to indicate it ran successfully then this method will set the status to indicate success and the client action to NoAction.
setError(String) Return an error to the client with the specified error message to be displayed on the client.
There is also the RunActionResponse.createErrorResponse(String) method that will create a RunActionResponse object with the specified error message.
setRefreshOriginal(boolean) Indicates if the client should refresh the original path(s) file details in File Explorer views.
setClientAction(ServerAction.ClientAction,List<String>)
setClientAction(ServerAction.ClientAction,String)
Set a client action with the specified parameter string(s).
setOpenURL(String) Set a client action to open the specified URL on the client.
setMessage(String msg)
setMessage(String title,String msg)
setMessage(String title, String msg, ServerAction.MessageLevel)
Set a client message to be displayed in a message box dialog on the client.
If the ServerAction.MessageLevel is not specified the message will be shown with an informational level.
setWarningMessage(String msg)
setWarningMessage(String title,String msg)
Set a client message to be displayed in a message box dialog on the client with a warning level.
setErrorMessage(String msg)
setErrorMessage(String title,String msg)
Set a client message to be displayed in a message box dialog on the client with an error level.
setExecute(String operation, String app)
setExecute(String operation, String app, String param)
Set an application to be run on the client, using the Windows ShellExecute() API call.
setUpdatePaths(List<String>)
setCreatedPaths(List<String>)
setDeletedPaths(List<String>)
Set a list of paths that were updated, created or deleted by the server action.
setNotification(String msg)
setNotification(String title, String msg)
Set the notification to be displayed on the client.
If the notification title is not specified the client application name will be used.

Client API Implementation

For a more complete client API implementation see the fileServersNG source code :-

  • AlfrescoClientAPI
    Extends the JSONClientAPI base class, adds check in/out and open in Share server actions plus ability to run scripted actions written in Javascript.
  • Check Out Server Action
    Check out files from an Alfresco repository, refresh the original file details, refresh the File Explorer via to show newly created working copy files, display a notification on the client.
  • Check In Server Action
    Check working copy files in to an Alfresco repository, or cancel the check out of the files, remove the working copies from File Explorer views, update the original file details, display a notification on the client.
  • Open In Share Server Action
    Returns a URL for the selected file/folder for the client to open a web browser showing the file/folder in the Share web application.