Briefcase sample (desktop) (Delphi)

The Briefcase sample demonstrates using the TDAFolderBriefcase class to save and then load two Data Tables that may have changes that are waiting to be sent to the server. It also demonstrates using briefcase properties to ensure that the data in the briefcase is valid and if not delete the briefcase.

The Briefcase can be helpful for situations where there isn't a persistent connection to the server and the user needs to be able to quit your app or otherwise work in an offline fashion. Another use of a it might be to aid in the application start-up process, as you can load the data from disk without needing to rely on the server being available.

Getting Started

The sample is located in C:\Users\Public\Documents\RemObjects Samples\Data Abstract for Delphi\Briefcase.

To build it, you will need Delphi Rad Studio, and like all the samples provided with Data Abstract for Delphi you will need to be running Relativity Server with the DASamples Domain and the Simple Schema available.

Running the Sample

The UI for the sample is split into three parts. A toolbar with various buttons, a table that displays data retrieved either from the server or from a briefcase, and finally a "console" like view where log data from the sample is displayed.

The toolbar has 4 buttons; "Reload", "Apply", "Reveal in Explorer" and "Invalidate" along with a drop down box which contains the default address used for an instance of Relativity Server running on a local machine; http://localhost:7099/bin. The "Reload" button retrieves the data again from the server and saves it to a briefcase. The "Apply" button sends any changes to the server to be saved and then a fresh briefcase is saved to the filesystem which will remove the highlighting on the rows. The "Reveal in Explorer" button will open an Explorer window in the folder that contains the briefcase file. Finally the "Invalidate" button will set a boolean which will be checked next time the briefcase is saved to disk and display a information dialog.

When the sample is run, it immediately attempts to access an instance of Relativity Server running on the local machine at http://localhost:7099/bin.

The sample checks to see if there is a Briefcase file available and if not then the data is freshly retrieved from the server. If on the other hand a Briefcase is available, then the BRIEFCASE_DATA_VERSION_KEY stored in the briefcase as a property is tested to see if the data is valid. If it isn't valid then fresh data is retrieved from the server and the briefcase replaced with the new data. If the property is valid then the tables are retrieved from the briefcase. You will also see that the date property is also retrieved and logged to the console when the briefcase is being validated.

At this point the UI will be visible with a list of products, the product group they belong to and a minimum and maximum amount available. Unlike the figure above, when the sample is first run no rows will have a yellow highlight, which means that there are no changes compared to the server version of the data. (See below)

If you double click on a in the "Min Amount" column of a row, the field will become editable allowing you to change the value. If you make a change and press Enter, the row will have a yellow highlight to indicate that a change has been made compared to the DataTable retrieved from the server.

If you quit the app at this point, those changes will be saved to the briefcase along with the information that the data is changed compared to the server, so that when you next run the app you will see those very same rows marked as changed. The highlighting will only be removed by either reloading the data from the server or by applying the changes to the server.

If you press the "Reveal in Explorer" button you will see the briefcase folder (Products.briefcase) that was written to disk when quitting the app, or after the data is retrieved from the server. If you open this folder, you will find three files, two with the extension .daBriefcase which are the tables themselves, and a plist file which has the data stored in any briefcase properties. If you double click on one of the .daBriefcase files it will open in the Briefcase Explorer tool and display the contents of the briefcase for examination. The plist file is viewable in any text editor.

Examining the Code

The code for creating the connection to the server, retrieving and modifying the data are covered by other samples. In this section we will be focusing on the code needed to create the briefcase, store the tables & properties in it and load those tables & properties from disk.

The sample is built around two classes; ClientDataModule and ClientForm. The ClientDataModule class handles everything related to interacting with the Data Abstract SDK; including retrieving data from an instance of Relativity Server, applying changes to the server and working with the briefcase. All of the code you are interested in is located in this class and will be discussed below. The ClientForm handles everything related to the user interface.

Store the tables in a new briefcase

In this sample saving the briefcase to disk is handled over two procedures. SaveBriefcase which handles the creation of the TDAFolderBriefcase object, recording some information as briefcase properties and writing the briefcase to disk, while the PutTablesToBriefcase adds the data tables we are interested in into the briefcase.

As noted in the Briefcase Overview, briefcases can be stored in one of two formats; either as a single .daBriefcase file which contains all of the tables and properties that you set or as a package (folder) where each table is stored in its own .daBriefcase file and the properties are stored in a .plist file. This sample uses the folder format, but if you wanted to use the other briefcase format you can replace the two instances of TDAFolderBriefcase with TDAFileBriefcase instead and alter the check to see if the briefcase folder exists in LoadData.

If the fInvalidateBriefcase property has been set to true then the value currently stored in briefcase for the BRIEFCASE_DATA_VERSION_KEY property is retrieved and tested to see if it has already been set to INVALID. If it hasn't, then the property is set to BRIEFCASE_DATA_VERSION_INVALID and the briefcase is immediately written to disk using the WriteBriefcase procedure. No changes are saved to disk unless WriteBriefcase is called.

Otherwise if fInvalidateBriefcase is set to false, we call PutTablesToBriefcase which adds the two tables, "Products" and "ProductGroups", to the briefcase using the AddTable procedure. If a table with the same name is already there, it will be replaced with this new table. The briefcase is then written to disk using the WriteBriefcase procedure. As noted, no changes to the briefcase are saved to disk unless WriteBriefcase is called.

procedure TClientDataModule.PutTablesToBriefcase(briefcase: TDABaseBriefcase);
begin
  AddToLog('Putting tables to the briefcase...');
  briefcase.AddTable(tbl_ProductGroups, False);
  briefcase.AddTable(tbl_Products, False);
end;

procedure TClientDataModule.SaveBriefcase;
var
  lbriefcase: TDABaseBriefcase;
begin
  lbriefcase := TDAFolderBriefcase.Create(GetBriefcaseFileName,False,False);
  if fInvalidateBriefcase then begin
    if AnsiSameText(BRIEFCASE_DATA_VERSION_INVALID, lbriefcase.PropValues[BRIEFCASE_DATA_VERSION_KEY]) then begin
      AddToLog('Briefcase already marked as INVALID.')
    end
    else
    begin
      lbriefcase.PropValues[BRIEFCASE_DATA_VERSION_KEY] := BRIEFCASE_DATA_VERSION_INVALID;
      AddToLog('Briefcase has been marked as INVALID.');
      lbriefcase.WriteBriefcase;
    end
  end
  else begin
    PutTablesToBriefcase(lbriefcase);
    lbriefcase.PropValues[BRIEFCASE_DATA_VERSION_KEY] := BRIEFCASE_DATA_VERSION;
    lbriefcase.PropValues[BRIEFCASE_UPDATE_DATE_KEY] := DateTimeToStr(Now);
    lbriefcase.WriteBriefcase;
    AddToLog('Briefcase has been updated.')
  end
end;

Loading the briefcase from disk and retrieving the tables

These three procedures handle loading the briefcase from disk and retrieving the tables from the briefcase based on the BRIEFCASE_DATA_VERSION_KEY property stored inside the briefcase.

The LoadData first tests to see if the briefcase folder exists, if not a fresh set of data is retrieved from the server. Otherwise a TDAFolderBriefcase object is created using Create procedure. The boolean values passed in indicate that the data should be pre-loaded into memory, and that the briefcase owns the tables.

The fully loaded briefcase is then passed to the ValidateBriefcase function which checks the value of the BRIEFCASE_DATA_VERSION_KEY property. If the briefcase is considered to be valid then the tables are retrieved with a call to GetTablesFromBriefcase: which calls the Fill procedure with an array containing the tables to be filled.

procedure TClientDataModule.LoadData;
var
  lbriefcaseName: string;
  lbriefcase: TDAFolderBriefcase;
begin
  AddToLog('Initial loading...');
  AddToLog('Looking for the local briefcase first...');
  lbriefcaseName := GetBriefcaseFileName;
  AddToLog('Briefcase: '+ lbriefcaseName);

  if not DirectoryExists(lbriefcaseName) then
  begin
    AddToLog('No briefcase found. Downloading fresh data...');
    LoadFromServer;
  end
  else begin
    AddToLog('Briefcase has been found.');
    lbriefcase:= TDAFolderBriefcase.Create(lbriefcaseName,True,True);
    if ValidateBriefcase(lbriefcase) then begin
      GetTablesFromBriefcase(lbriefcase);
    end
    else begin
      AddToLog('Clearing briefcase and downloading fresh data...');
      LoadFromServer;
    end
  end;
  AddToLog('Data is ready.')
end;

procedure TClientDataModule.GetTablesFromBriefcase(briefcase: TDABaseBriefcase);
begin
  AddToLog('Loading tables from the briefcase...');

  briefcase.Fill([tbl_ProductGroups,tbl_Products],True);
end;