Making A Conduit Using JSync
For an overview on conduits, check out
SuperWabaConduits.
There's a decided lack of documentation for making conduits with JSync (which is basically just a Java implementation of the C++ version of the Conduit Development Kit). The examples given are long and convoluted, so I'll give a rundown of how to do things you might want to do using JSync.
What You Need
To make a conduit with JSync, first download the Conduit Development Kit from
PalmSource (you need to register first). The main thing you need is the file
<CDK Directory>\Java\Bin\rt\jsync.jar. Add that to your classpath in order to get your conduit to compile properly.
Basic Program Flow
Your conduit will need at least two classes. (You'll make extensive use of the static methods in
SyncManager, but you don't have to instantiate it.) Firstly, you'll need to extend the
AbstractRecord class. The simplest implementation of this is just a record which has a string for its body. The only two methods that need to be overridden are
readData and
writeData, which are called by the
SyncManager.readRec* and
SyncManager.writeRec methods. Here's a simple record class:
class MyRecord extends AbstractRecord
{ private String body;
public MyRecord (String b, int recordID)
{ setIndex(recordID);
body = b;
}
public String getBody()
{ return body;
}
public void setBody(String b)
{ body = b;
}
public void writeData(DataOutputStream out) throws IOException
{ writeBytes(out, body);
}
public void readData(DataInputStream in) throws IOException
{ body = readCString(in);
}
}
There seems to be some confusion about reading and writing strings... Superwaba's strings, when used in a PDA-side SuperWaba program, have the length of the string come first and may cause problems when switching to other formats. However, the data is not actually
stored that way in its databases, as far as I can tell. You can use the regular read and write methods to turn the database data into plain Java strings.
You will also need to implement the
Conduit class. Here there are three methods you'll need:
configure, which will create an AWT or Swing dialog box for configuration purposes;
name, which will return the name of the conduit; and
open, which contains the body of the conduit (i.e. the code to perform when the user presses the Hotsync button on their cradle).
Configuring Your Conduit
For the user to configure his conduit, he has to open the Hotsync Managers "Custom..." dialog, then click the conduit he wishes to change and click "Change...". When he does that, the
configure method of your conduit will run. Generally, what you want to do here is pop up an AWT or Swing Panel. When you catch the events of the panel, set a class variable to save the settings, then reference that variable in the
configure code. For example, here's what mine looked like for the most part:
public int configure(ConfigureConduitInfo info)
{ try
{ UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
}
catch (Exception e) {}
//popup configure panel.
ConfigFrame cf = new ConfigFrame(info); //pass it the current settings
cf.pack();
cf.show();
if (cf.dataChanged) //this is set after the "OK" button is hit by catching the click event
//In my own conduit, I automatically made it so any changes were permanent. In your own, you can offer a choice
//for the user to have his changes saved permanently or only for the next Hotsync session.
{ info.syncPref = ConfigureConduitInfo.PREF_PERMANENT; //indicate that this change is for the default settings,
//not for one time only
info.syncPermanent = cf.ourSync; //set the permanent settings to whatever was indicated in the config panel...
//this is one of the constants in the SyncProperties class.
info.syncNew = cf.ourSync; //set the current (one-time) settings to the same thing.
}
return 0;
}
Working With Databases
There are several things you should keep in mind in opening and closing databases. Firstly, you can only open
one database at a time. You
must close your open database before opening another. If your program logic has you reading or writing from multiple databases, you would have to read everything into an internal data structure (or for very large databases, a temporary file).
Secondly, there's your creator and type IDs. In Superwaba you're asked for a four-letter string, whereas in JSync you need to supply an integer. You can get the integer like so:
static int strSigToInt(String s)
{ return ((((int) s.charAt(0)) << 24) +
(((int) s.charAt(1)) << 16) +
(((int) s.charAt(2)) << 8) +
(((int) s.charAt(3))));
}
Thirdly, things are a bit counterintuitive if you want to get information about a database. In particular, the
DBList class, which contains info about a database, will
only give you a string with the name, creator ID, and type ID all together. You'll have to search the string to extract the information you want. For example, if you know the creator and type ID, here's how you would search for a database:
boolean foundDB = false;
SyncDatabaseInfo sdi = SyncManager.findDbByTypeCreator(SyncManager.DB_INFO_OPT_GET_ATTRIBUTES,
SyncManager.DB_SRCH_OPT_NEW_SEARCH, typeInt, creatorInt);
try
{ while (true) //keep going until we get a FILE_NOT_FOUND SyncException
{ String s = sdi.baseInfo.toString();
if (s.indexOf("databaseName") >= 0)
{ foundDB = true;
break;
}
sdi = SyncManager.findDbByTypeCreator(SyncManager.DB_INFO_OPT_GET_ATTRIBUTES,
(byte)0, wabtInt, wabtInt);
}
}
catch(SyncException e) {}
Basically, you would be iterating your call of
findDbByTypeCreator until you get a file not found error. You can do something similar if you know the name but not the creator/ID types. BE CAREFUL that you're only getting the correct database name; I got burned when I had one database called "data" and one called "udata" and it was found twice.
If you ever see a
cardNum argument, it's always 0; nothing else is supported right now, as far as I know.
You can get a list of all the databases on the PDA by calling
SyncManager.readDbList, but be warned that you'll be getting a lot of garbage entries whose name is "" and whose creator and type are 0.
Reading and Writing Records
Performing I/O on records is nowhere near as easy to do as in Superwaba. The short explanation is like this: You create an instance of your
Record class and pass it to
SyncManager.readRec* or
SyncManager.writeRec. You should make sure to call
setIndex on the record before reading or writing; this indicates the offset of the record to read or write. (In the sample record class above, this is done in the constructor.) If you want to add a new record, set the index to 0. Oddly enough, this means you can't overwrite the record at index 0. I guess you'd have to delete the whole database and start again. You also can't add a new record anywhere but at the end (no insertion).
Read example (uses the MyRecord class above):
int dbid = SyncManager.openDB("databaseName", 0, SyncManager.OPEN_READ);
SyncDatabaseInfo sdi = SyncManager.readOpenDbInfo((byte)(SyncManager.DB_INFO_OPT_GET_ATTRIBUTES |
SyncManager.DB_INFO_OPT_GET_SIZE), (byte)dbid));
int numRecords = sdi.numRecords;
for (int i = 0; i < numRecords; i++)
{ MyRecord r = new MyRecord("", i);
SyncManager.readRecordByIndex(dbid, r); //calls the "readData" method in MyRecord
String body = r.getBody();
//store the body somewhere or do processing
}
SyncManager.closeDB(dbid);
Add record example:
String records[];
int dbid = SyncManager.openDB("databaseName", 0, SyncManager.OPEN_WRITE);
for (int i = 0; i < records.length; i++)
{ SyncManager.writeRec(dbid, new MyRecord(records[i], 0));
}
SyncManager.closeDB(dbid);
Testing Your Conduit
To register your conduit for testing purposes, run the program
<CDK Directory>\Common\Bin\CondCfg.exe. Click "Add..." to add a new conduit. First click "Java Conduit" along the bottom. Indicate your class name and class path (it's highly recommended to put your classes in a jar file and in their own package, to avoid having the same name as a class in another Java conduit). The rest is pretty straightforward. "File" and "Directory" indicate where you want Hotsync to back up your files to (generally it would be a subfolder and/or file in the Hotsync directory). "Name" is the full name of the conduit. You can leave "Username" blank.
Your conduit should now be registered with Hotsync Manager. If you want to make a change to your conduit and test it, just compile your classes, exit Hotsync Manager, and restart it. Voila.
Installing Your Conduit
Believe it or not, installation is the easiest part of making a conduit! All you need is
handX pInstaller. Unzip the pInstaller files into a directory, and add the following files to that directory:
- Your Java .jar file.
- Your .prc file for the PDA-side program.
- The file
<CDK Directory>\Java\JSync Installer\JSyncInstaller.exe
- The file
<CDK Directory>\Common\Bin\condmgr.dll
- A
settings.xml file. The documentation for pInstaller explains how to create this XML file; it's very simple and intuitive.
Zip up the directory and you have yourself an installer! Just have your users run
pinstaller.exe and the conduit will be installed! If you want something more robust (and will create a single installer file), try the
handX pInstaller Construction Set.
I hope this helps people. I'm no expert, so feel free to edit if I got anything wrong!
--
DanielOrner? - 14 Jun 2004