Thursday, July 19, 2007

Ideas for posts

  • A quick primer on merging from a branch to the head of a CVS module with Tortoise CVS.
  • SSL for dummies
  • Stylesheet basics

Monday, July 09, 2007

SSL For Dummies

Some time back I interviewed with Amazon.com for a developer position at their local Cape Town office here in South Africa. Unfortunately, after surviving the first harrowing telephonic interview, I then proceeded to bomb gloriously in the second interview, conducted by a guy called Quinton. I have to say, despite my short circuit, this was the toughest interview process I've ever been through - which bodes well for the quality of people hired by Amazon.

While I'm not sure how well I fared on the initial part of the second interview, I do know that I completely fell apart when Quinton asked me to explain SSL to him, as I would if I were explaining it to my grandmother. Now that's an excellent question. It tests how well you know SSL, and it tests your communication skills - most notably your ability to relate difficult concepts to others. I knew what SSL was and how it worked, but I guess in the heat of the moment, I wasn't expecting the whole 'splain it to ya grandma' angle, and I folded like a bad hand of poker.

So, the question remains, how would I explain SSL to my grandmother (ignoring of course the challenging aspect that both my grandmothers have passed on)? Okay, well here's my second attempt. Quinton, you listening?

Grandma, I want to explain something called SSL to you. SSL is an abbreviation for Secure Sockets Layer. Now I know you have absolutely no idea what that is so let me see if I can explain it to you in a way you'll understand.

Let's suppose that you want to send a letter to uncle Ben with some confidential information in it. (That's okay grandma, I don't know about your secrets, you don't need to look so uncomfortable.) As you know, our postal service is not very trustworthy, and they constantly snoop in our mail, and steal valuables like cash notes we send inserted in cards. So how do we ensure that your letter arrives at uncle Paul intact, and untouched? Well here's how we do it.

You will have a special box, that can only be locked with a key that only you have. That box can only be unlocked with a key that only uncle Ben has.

Friday, July 06, 2007

Format this!

If, like me, you've got a free Blogspot blog, and you've wrestled with your blog posts, painstakingly adding spaces throughout your sample source code, only to see them cruelly removed and reformatted by Blogspot, then here's a useful on-line resource to use to keep you from falling into the pit of insanity.

http://formatmysourcecode.blogspot.com/

It'll take your source code for you, convert it to HTML that renders in a neat little dotted margin box.

Here's a sample of arbitrary source code. Here's what it looks like without any assistance - I simply took some code, copied it, and pasted it in. Bear in mind, it was correctly formatted initially:

/**
* The HelloWorldApp class implements an application that
* simply prints "Hello World!" and some other stuff to
* standard output.
*/
public class HelloWorldApp {

/**
* Creates a new instance of HelloWorldApp
*/
public HelloWorldApp() {
}

/**
* The main entry point.
*
* @param args the command line arguments
*/
public static void main(String[] args) {
System.out.println("Hello World!");
for (int i = 0; i < 10; i++) {
switch (i) {
case 0:
System.out.println("Starting");
break;
case 9:
System.out.println("Ending");
break;
}
System.out.println("Looping " + (i + 1));
}
System.out.println("Goodbye World!");
}
}

Yick! What a mess. Clearly you can see that's not what we're after. The indentation has gone, the line breaks are gone and if you stare too long, your eyes may bleed.

So, a quick Google search and we discover that putting a "pre" block around your source code will preserve its formatting thusly:

/**
* The HelloWorldApp class implements an application that
* simply prints "Hello World!" and some other stuff to
* standard output.
*/
public class HelloWorldApp {

/**
* Creates a new instance of HelloWorldApp
*/
public HelloWorldApp() {
}

/**
* The main entry point.
*
* @param args the command line arguments
*/
public static void main(String[] args) {
System.out.println("Hello World!");
System.out.println("This is a really long string that will probably not show the entire line since it is really long.");
for (int i = 0; i < 10; i++) {
switch (i) {
case 0:
System.out.println("Starting");
break;
case 9:
System.out.println("Ending");
break;
}
System.out.println("Looping " + (i + 1));
}
System.out.println("Goodbye World!");
}
}


But it still isn't quite there yet is it? In my case, my blog template snips off the end of long lines as per the second line printed above.
So, FormatMySourceCode to the rescue. I pasted the above code into the top text area, hit the "Format Text" button, and the copied the resultant HTML in here:

/**
* The HelloWorldApp class implements an application that
* simply prints "Hello World!" and some other stuff to
* standard output.
*/
public class HelloWorldApp {

/**
* Creates a new instance of HelloWorldApp
*/
public HelloWorldApp() {
}

/**
* The main entry point.
*
* @param args the command line arguments
*/
public static void main(String[] args) {
System.out.println("Hello World!");
System.out.println("This is a really long string that will probably not show the entire line since it is really long.");
for (int i = 0; i < 10; i++) {
switch (i) {
case 0:
System.out.println("Starting");
break;
case 9:
System.out.println("Ending");
break;
}
System.out.println("Looping " + (i + 1));
}
System.out.println("Goodbye World!");
}
}


Now that looks a lot better don't you think? And it has a nifty scrolling feature for any long lines.

It's still not ideal. It'd be nice if the Blogspot website had support for stuff like this built-in, so you didn't have to go to the "Edit Html" tab.
But it's better than nothing.

Tuesday, July 03, 2007

Crossing JACOB's Java COM bridge

We've used the JACOB Java COM bridge recently to overcome a few hurdles with our Exchange mailbox backup plug-in for our main product, Backup Professional, here at Attix5. And I have to send out thanks to the guys who are responsible for developing this very capable utility library.

Microsoft do not recommend backing up individual mailboxes. The reason for this is that currently there is no protocol that can do this efficiently, and Microsoft do not plan to provide one, as they feel the full Exchange backup, plus prudent mailbox management eliminates the need to backup individual mailboxes. While they do provide a utility, Exmerge, to export mailboxes to PST files, this is intended as an administration tool. But hey, people will take the path of least resistance, and as such Exmerge is used to do single mailbox backups.

The problem with Exmerge (and as far as I know, all products that do single mailbox backups) is that it uses MAPI to accomplish the export. MAPI was never intended to be an efficient high-volume backup protocol. So as a result, backups via MAPI are s..l..o..w. Furthermore, configuring MAPI on a machine generally requires extra DLL's and MAPI profile configuration. What this means for us is that we frequently ran into problems at client installations getting all of this to play nice.

All we actually need to do in our plug-in, is to retrieve a list of mailboxes for the user to select from, for backup. This might not sound like much of a task, but you'd be surprised how MAPI complicates this. For one thing, bare in mind that our client software is a Java codebase, so we have to jump through a JNI hoop to accomplish all of this. Anyway after the changes to Exchange 2000 and up, where the directory store was extended to Active Directory, I realized we could actually just query Active Directory to accomplish the same task.

Cue JACOB and Active Directory scripting.

JACOB, as the name implies, is a bridge between Java and COM. I won't elaborate further in this post, but you can find out all the dirty sordid details here. There are other Java COM bridge products out there, but I found good coverage of JACOB, and I also noticed that my favourite Java IDE, Intellij IDEA, have started using it in their product, so hey, it must be good!

I'm drawing this out a bit now, so let me cut to the chase, as the say. If you download JACOB, you'll find a samples folder, that has a very useful set of classes for use with ADO. That is what I used to do our AD scripting. This turned out to be quite an easy exercise though I did have to do my own mental translation between the ADSI scripts and the ADO class API usage.

Here's a scripting example of how to do a mailbox query:

set oConn = CreateObject("ADODB.Connection")
set oCommand = CreateObject("ADODB.Command")
set oRS = CreateObject("ADODB.Recordset")
oConn.Provider = "ADsDSOObject"
oConn.Open "Ads Provider"
set oCommand.ActiveConnection = oConn 'set the active connection
strQuery= ";(objectClass=person);adspath,name,legacyExchangeDN;subtree"
oCommand.CommandText = strQuery
set oRS = oCommand.Execute 'Execute the query
While not oRS.EOF
vObjectClass=oRS.Fields("objectClass")
// do stuff here
oRS.MoveNext
wend
This is what the Java ADO implementation translates to:

Connection conn = null;
ActiveXComponent ax = null;
try {
conn = new Connection();
conn.setProvider("ADsDSOObject");
conn.Open();
Command command = new Command();
command.setActiveConnection(conn);
command.setCommandType(CommandTypeEnum.adCmdText);
ax = new ActiveXComponent("LDAP://RootDSE");
Variant v = Dispatch.call(ax, "Get", "defaultNamingContext");
String dnc = v.toString();
String ldapStr = ";" + ldapQuery;
command.setCommandText(ldapStr);

Recordset rs = command.execute();
Fields fs = null;
try {
fs = rs.getFields();
int columns = fs.getCount();

if (rs.getEOF()) {
return list;
}

rs.MoveFirst();
String[] row;
while (!rs.getEOF()) {
row = new String[columns];
for (int i = 0; i <>fs.getCount();
i++){
Field f = fs.getItem(i);
try {
row[i] = f.getValue().toString();
} finally {
JacobUtils.safeRelease(f);
}
}
list.add(row);
rs.MoveNext();
}
} finally {
JacobUtils.safeRelease(fs);
JacobUtils.safeRelease(rs);
}
} finally {
JacobUtils.safeRelease(ax);
JacobUtils.close(conn);
JacobUtils.safeRelease(conn);
}
Quite a mouthful eh? (Disclaimer: I've ripped most of this out of my code and simplified it some for simplicity, so if you copy and paste and it doesn't compile, don't blame me. I promise you it all compiles on my side.)

The problem with this code is that if you run that query, expecting to get all (say) 2091 mailboxes in your AD forest, you'll be surprised that you'll only get 1000. That's what happened to us, but we didn't realize this until we had deployed into a live site at a client site, and had the client complaining they couldn't see all the mailboxes in certain OU's. Man, talk about a hair-pulling exercise. Yes, I know, those of you seasoned AD LDAP'ers will be going "Well d'ugh!", but you see I wasn't and still am not a seasoned AD LDAP'er.

So after running a test script on the production server, we noticed the 1000 row limit returned by the query. This was a peculiar limit. I doubted that there were exactly 1000 AD mailboxes, though I did for a moment wonder whether they had a "1000 user Exchange license". I said just for a moment, so stop laughing. Nevertheless, a Google search quickly revealed that this was actually an Active Directory search issue. Active Directory has a MaxPageSize setting, that limits any queries to 1000 rows, unless you enable paging. Google again to the rescue, and I determined that to enable paging, you simply do the following:

oCommand.Properties("Page size") = 99

This simple line tells AD to retrieve ALL possible rows. AD handles the actual paging in the background. That's it! That's the fix. Now, this sounds simple, but alas it didn't turn out to be so simple on the Java API side. I tried numerous things, using the Dispatch.get(), Dispatch.call() and Dispatch.invoke() methods of JACOB with all sorts of permutations targeting the command, or doing a command.getProperties() and setting "Page size" on that, but all attempts got me a JacobException. At the edge of my sanity, at the point where I was about to give up programming for a career sweeping the streets, I had an epiphany. Actually, I had the foresight to open Visual Studio and have a look at the API docs for the Properties returned by ADODB.Command.Properties. To my surprise, it was a read-only Properties class. What gives? Well that gave me the clue that all required properties were already there in the Command, and that I simply needed to find that property and set its value appropriately. So to cut a long story short, I came up with this:


Properties props = command.getProperties();
try {
for (int propI = 0; propI <> props.size(); propI) {
Property property = props.getItem(propI);
try {
String name = property.getName();
String value = property.getValue().toString();
if (name.equalsIgnoreCase("Page size")) {
property.setValue(new Variant(1000));
value = property.getValue().toString();
}
} finally {
JacobUtils.safeRelease(property);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JacobUtils.safeRelease(props);
}
I inserted that block just after I set the command text, and just before I execute() the command, and hey presto! It all worked like a charm. If on the 4 July, you heard an exuberant, huge shout of elation and wondered who that was, it was me. Sorry.