Briefcase sample (Java)
The Briefcase console sample demonstrates storing & retrieving data from a Briefcase which could be used to persist data between runs of the program. The sample also demonstrates using Dynamic Where to selecting retrieve 3 rows from the "Orders" schema table and Dynamic Select to limit the fields that are retrieved from the "Orders" & "OrderDetails" tables.
Getting Started
The Briefcase sample is installed in C:\Users\Public\Documents\RemObjects Samples\Data Abstract for Java\Java\Console\
.
To build it you can either build & execute it from the command line or make use of the provided Eclipse .classpath
and .project
files.:
If using the command line remember that you will need to explicitly include the two jar files provided by the Data Abstract for Java package in the classpath. So for example to build and run this sample you'll need:
- Build:
javac -cp "C:/Program Files/RemObjects Software/Data Abstract for Java/Bin/com.remobjects.dataabstract.jar";"C:/Program Files/RemObjects Software/Data Abstract for Java/Bin/com.remobjects.sdk.jar";src src\com\remobjects\dataabstract\samples\briefcase\Program.java
- Execute:
java -cp "C:/Program Files/RemObjects Software/Data Abstract for Java/Bin/com.remobjects.dataabstract.jar";"C:/Program Files/RemObjects Software/Data Abstract for Java/Bin/com.remobjects.sdk.jar";src com.remobjects.dataabstract.samples.briefcase.Program
Like all the samples provided with Data Abstract for Java you will need to be running the Relativity Server with the DASamples
Domain and the Simple
Schema available.
Lastly this sample makes use of a convenience class (DataTableExtension
) which is provided with Data Abstract for Java. It provides a single public method LogToString
that takes a DataTable and converts its contents into a String in a pretty fashion that could be logged to a console or into a file. Please note that this class extension is intended for test purposes only.
Running the Sample
When run the sample sets up a connection to an instance of Relativity Server and logs in (Step 1). It then creates a Dynamic Where expression which will retrieve three rows from the "Orders" table where the "Id" field matches particular values and then Dynamic Select is used to limit which fields are retrieved. Then the equivalent rows are retrieved from the "OrderDetails" table using DA SQL (Step 2).
The value in the "Amounts" field of each row retrieved from "OrderDetails" is increased so that the local data is different than the data on the server (Step 3). A Briefcase object is created and some properties set to indicate the date and the version of the briefcase. Next the tables are added to the briefcase and then written to disk (Step 4).
The Briefcase is freshly loaded from disk and the tables extracted and printed to the console to show that the changed data remains (Step 5). Then the changes are applied to the server and the tables are freshly added to the briefcase and written to disk.
Examining the Code
All of the code can be found in the Program.java source file.
Setting up a Connection & Logging in (Step 1)
There are three stages to setting up a connection to an instance of Relativity Server.
In the first stage we initialize the underlying services that we will use to establish the connection to the server. The BinMessage is a message type that will be used to encode the data as a message. The HttpClientChannel is the communications channel over which the data will be sent, here we are establishing that it uses "HTTP" and specifying the address of the server. Finally an instance of Bin2DataStreamer is created which will handle the encoding and decoding of the data packets being transmitted.
The second stage is to configure a RemoteDataAdapter object that we interact with to communicate with the server. After its created you pass it the three objects we created previously and set the DataServiceName
and LoginServiceName
. At this point we have a working connection but we will be unable to manipulate the schema data until we log in. Attempting to do so will cause an ROException
exception to be thrown with the message "Session could not be found.".
The final stage passes a special login string (lLoginString
) to the login method to establish a login session. The login
method returns a boolean which will be true
if the login attempt was successful or false
if it failed. The login string is comprised of 4 parts:
- Username the username to attempt to login with
- Password the password to use
- Domain the name of the domain that the schema is located in
- Schema the name of the schema we wish to use
For example, the login string from this sample is "User Id=simple;Password=simple;Domain=DASamples;Schema=Simple"
NOTE If there is a problem attempting to communicate with the server, or if the schema or domain name is incorrect then an ROException
will be thrown and should be handled appropriately.
BinMessage lMessage = new BinMessage();
HttpClientChannel lChannel = new HttpClientChannel();
try {
lChannel.setTargetUrl(new URI("http://localhost:7099/bin"));
} catch (URISyntaxException e1) {
e1.printStackTrace();
return;
}
System.out.println(String.format("Target URL is %s", lChannel
.getTargetUrl().toString()));
System.out.println("RO SDK layer is configured.");
Bin2DataStreamer lDataStreamer = new Bin2DataStreamer();
RemoteDataAdapter lDataAdapter = new RemoteDataAdapter();
lDataAdapter.setDataStreamer(lDataStreamer);
lDataAdapter.setMessage(lMessage);
lDataAdapter.setClientChannel(lChannel);
lDataAdapter.setDataServiceName("DataService");
lDataAdapter.setLoginServiceName("LoginService");
System.out.println("RO DataAbstract layer is configured.");
System.out.println("");
System.out.println("");
System.out.println("STEP 1: Login to DataService");
String RelativityConnectionStringTemplate = "User Id=\"%s\";Password=\"%s\";Domain=\"%s\";Schema=\"%s\"";
String lLoginString = String.format(RelativityConnectionStringTemplate,
"simple", "simple", "DASamples", "Simple");
System.out.print(String.format("Login string is %s", lLoginString));
Boolean lLogged = lDataAdapter.login(lLoginString);
if (lLogged) {
System.out.println("Login has been successful. Going further...");
} else {
System.out
.println("Cannot login. Please check your user name and password. Exiting...");
return;
}
Selectively retrieving data (Step 2)
This step makes use of Dynamic Where, Dynamic Select and DA SQL to selectively retrieve data from the schema.
The first action to create an array of int's which are the Id
s that we are going to retrieve from the schema. The next step is create a DataTable which will hold the results from the server. The name passed to the constructor should be the name of the table you wish to retrieve data from; here it is "Orders".
Then a Dynamic Where expression is created that will match again those records where their "Id" field matches one of those in the array of WhereExpression.
int[] lId = new int[] { 20, 22, 24 };
DataTable lTableOrder = new DataTable("Orders");
BinaryExpression lWhere = new BinaryExpression(
new FieldExpression("Id"),
new ListExpression(new WhereExpression[] {
new ConstantExpression(lId[0], DataType.Integer),
new ConstantExpression(lId[1], DataType.Integer),
new ConstantExpression(lId[2], DataType.Integer), }),
BinaryOperator.In);
To use the Dynamic Where expression a TableRequestInfoV5 object needs to be created so we can specify custom parameters for the data request. We pass "-1" to setMaxRecords to request all of the table's records, if a positive value is passed then the number of records is limited to that. To request the schema to be returned in the results we pass "true" to setIncludeSchema.
To specify which fields we are interested in we need to create a StringArray
object and add a String
object per field we are interested in. Here we pass an empty StringArray
to the setDynamicSelectFieldNames method and then loop through a normal array of Strings of adding each String
to the StringArray
which is retrieved using getDynamicSelectFieldNames.
The previously created Dynamic Where expression is passed to the setWhereClause of TableRequestInfoV5.
To indicate that Dynamic Select select should be used, we pass "true" to the setDynamicSelect method.
Finally to retrieve the data we pass the TableRequestInfoV5 and DataTable to the fill method of RemoteDataAdapter which executes in a synchronous fashion delaying further processing until the request completes. Once the data has been retrieved it is printed to the console.
TableRequestInfoV5 lRequestInfo = new TableRequestInfoV5();
lRequestInfo.setMaxRecords(-1); // no limits - return all records
lRequestInfo.setIncludeSchema(true); // update schema in the result data
// table to match recieved data
// (not necessarily).
// Array of field names to select - this is DynamicSelect feature
// itself.
String[] lDynamicSelect = new String[] { "Id", "Date", "Status",
"Type", "Contractor", "Manager" };
lRequestInfo.setDynamicSelectFieldNames(new StringArray());
for (int i = 0; i < lDynamicSelect.length; i++) {
lRequestInfo.getDynamicSelectFieldNames()
.addItem(lDynamicSelect[i]);
}
lRequestInfo.setWhereClause(new XmlType(lWhere.toXmlNode()));
// And finally enable Dynamic Select feature at DataAdapter level
lDataAdapter.setDynamicSelect(true);
lDataAdapter.fill(lTableOrder, lRequestInfo);
To make use of DA SQL a TableRequestInfoV6 rather than a TableRequestInfoV5 object is required. The DA SQL statement is stored in a String
and passed to the setSql of TableRequestInfoV6.
DataTable lTableOrderDetails = new DataTable("OrderDetails");
TableRequestInfoV6 lRequestInfoV6 = new TableRequestInfoV6();
String lDASql = String
.format("SELECT Id, [Order], Product, Amount, Price FROM OrderDetails WHERE [Order] IN (%d, %d, %d)",
lId[0], lId[1], lId[2]);
lRequestInfoV6.setMaxRecords(-1);
lRequestInfoV6.setIncludeSchema(true);
lRequestInfoV6.setSql(lDASql);
lDataAdapter.fill(lTableOrderDetails, lRequestInfoV6);
Modifying Table Data (Step 3)
Making a change to a value in a DataRow simply call the setField passing in the name of the field as the first argument and the new value as the second argument.
for (int i = 0; i < lTableOrderDetails.getRows().getCount(); i++) {
DataRow dr = lTableOrderDetails.getRows().getRow(i);
dr.setField("Amount", (Integer) dr.getField("Amount") + 1);
}
Writing to a Briefcase (Step 4)
A Briefcase provides support for persisting data retrieved from the server to the local filesystem. Along with storing Data Tables and any modifications to those tables, a briefcase can also store some additional information as string based properties like that date that the briefcase was last updated.
To create a briefcase you need to create an instance of FileBriefcase which is a concrete version of the Briefcase class that will store all of the data tables and properties in a single file which generally has a .daBriefcase.
The constructor takes two arguments, the first is the filename & path to use for the briefcase and the second argument indicates if the briefcase should be opened for reading.
Briefcase lBriefcase = new FileBriefcase(BRIEFCASE_FILENAME, false);
The properties are stored in a dictionary of String
pairs. To add a property simply remove the properties Map
using getProperties and then call the put
method where the first argument is the key and the second is the value to store. If a property with the same name was already there it would be replaced.
lBriefcase.getProperties().put("UpdateDate", "");
lBriefcase.getProperties().put("Version", BRIEFCASE_VERSION);
To put a table into the briefcase pass a DataTable object to the addTable method of FileBriefcase.
lBriefcase.addTable(lTableOrder);
lBriefcase.addTable(lTableOrderDetails);
At this point the properties and tables have been added to the briefcase in memory, they have not yet been written to disk. To store the changes on disk call the writeBriefcase method.
lBriefcase.getProperties().put("UpdateDate",
Calendar.getInstance().getTime().toString());
lBriefcase.writeBriefcase();
Reading from a Briefcase (Step 5)
To load the Briefcase back from disk we create a new FileBriefcase object, passing in the name of the briefcase file to be loaded as the first parameter and boolean to indicate if the briefcase should be opened for reading.
lBriefcase = new FileBriefcase(BRIEFCASE_FILENAME, true);
To retrieve the properties stored in the briefcase you need to use the getProperties method which returns a map object that contains all of the properties. To get at a particular property we use the get
method of Map
System.out.println(String.format("Briefcase version is: %s", lBriefcase
.getProperties().get("Version")));
System.out.println(String.format("Briefcase update date is: %s",
lBriefcase.getProperties().get("UpdateDate")));
To retrieve a table we need to use the tableNamed method which takes a string which is the name of the table and it returns a DataTable if it is found.
for (int i = 0; i < lBriefcase.getTableNames().length; i++) {
System.out.println(DataTableExtension.LogToString(lBriefcase
.tableNamed(lBriefcase.getTableNames()[i])));
}
Example Output
This is an example of the output you will see when the sample is run. In Step 2 you can see the required three rows from the Orders
table and 5 rows from the OrderDetails
tables. In Step 3 you can see the increased values in the Amounts
field of the OrderDetails
table. Finally in Step 5 you can see that the tables retrieved from the briefcase contain the changes to the Amounts
fields of the OrderDetails
table.
Breifcase sample has been started.
Target URL is http://localhost:7099/bin
RO SDK layer is configured.
RO DataAbstract layer is configured.
STEP 1: Login to DataService
Login string is User Id="simple";Password="simple";Domain="DASamples";Schema="Simple"Login has been successful. Going further...
STEP 2. Select some data (certain orders and their details) to store as briefcase...
Table: Orders (3 rows from 3)
|--------------------------------------------------------------------------------|
|Id |Date |Status |Type |Contractor |Manager |
|--------------------------------------------------------------------------------|
|20 |Fri Jan 03 ... |0 |2 |21 |8 |
|22 |Fri Jan 03 ... |0 |2 |22 |8 |
|24 |Sat Jan 04 ... |0 |2 |22 |8 |
|--------------------------------------------------------------------------------|
Table: OrderDetails (5 rows from 5)
|--------------------------------------------------------------------------|
|Id |Order |Product |Amount |Price |
|--------------------------------------------------------------------------|
|129 |20 |79 |1 |357.0 |
|132 |22 |16 |2 |360.0 |
|135 |24 |37 |2 |23.0 |
|136 |24 |24 |2 |347.0 |
|137 |24 |86 |1 |17.0 |
|--------------------------------------------------------------------------|
STEP 3. Make some changes but does not apply them ...
Table: Orders (3 rows from 3)
|--------------------------------------------------------------------------------|
|Id |Date |Status |Type |Contractor |Manager |
|--------------------------------------------------------------------------------|
|20 |Fri Jan 03 ... |0 |2 |21 |8 |
|22 |Fri Jan 03 ... |0 |2 |22 |8 |
|24 |Sat Jan 04 ... |0 |2 |22 |8 |
|--------------------------------------------------------------------------------|
Table: OrderDetails (5 rows from 5)
|--------------------------------------------------------------------------|
|Id |Order |Product |Amount |Price |
|--------------------------------------------------------------------------|
|129 |20 |79 |2 |357.0 |
|132 |22 |16 |3 |360.0 |
|135 |24 |37 |3 |23.0 |
|136 |24 |24 |3 |347.0 |
|137 |24 |86 |2 |17.0 |
|--------------------------------------------------------------------------|
STEP 4. Prepare briefcase and store tables there ...
Briefcase: BriefcaseSample.daBriefcase
Added tables: OrderDetails, Orders
Writting briefcase to file system...
STEP 5. Read briefcase from file system with its pending changes ...
Briefcase version is: v.0.1
Briefcase update date is: Sat Apr 09 22:34:37 CEST 2016
Table: Orders (3 rows from 3)
|--------------------------------------------------------------------------------|
|Id |Date |Status |Type |Contractor |Manager |
|--------------------------------------------------------------------------------|
|20 |Fri Jan 03 ... |0 |2 |21 |8 |
|22 |Fri Jan 03 ... |0 |2 |22 |8 |
|24 |Sat Jan 04 ... |0 |2 |22 |8 |
|--------------------------------------------------------------------------------|
Table: OrderDetails (5 rows from 5)
|--------------------------------------------------------------------------|
|Id |Order |Product |Amount |Price |
|--------------------------------------------------------------------------|
|129 |20 |79 |2 |357.0 |
|132 |22 |16 |3 |360.0 |
|135 |24 |37 |3 |23.0 |
|136 |24 |24 |3 |347.0 |
|137 |24 |86 |2 |17.0 |
|--------------------------------------------------------------------------|
Apply pending changes to the server and update briefcase....
Done!