Friday, October 08, 2010

Grails and JCifs

JCIFS is:
...an Open Source client library that implements the CIFS/SMB networking protocol in 100% Java. CIFS is the standard file sharing protocol on the Microsoft Windows platform.
As part of a project to provide schools and businesses with an open source solution to access their "My Documents" folder anytime/anywhere over the web, I recently had the pleasure of integrating JCIFS into my Grails application.

The obligatory screenshot:


I dropped the latest JCIFS jar file into my $GRAILS-APP/lib folder, and began implementing the "My Documents" feature against a samba server for starters. When I moved to a Windows 2008 server everything fell apart, with all operations started timing out. After some digging around in the rather extensive set of config options, I realized I need the following in my grails config file:
System.setProperty("jcifs.smb.client.dfs.disabled", "true");
Your environment may differ but make sure you take a good look at the JCIFS configuration options at least.

Ok, so here's a simple example of removing a file:
  void removeFile(WorkspacePath p)
{
def ntlm = new NtlmPasswordAuthentication("", p.username, p.password);
SmbFile file = new SmbFile(absoluteFilePath(p.url, p.path), ntlm);
file.delete();
}
Note: I pass "" as the first argument to NtlmPasswordAuthentication as the domain is part of p.username (e.g. joel@example.com).

One thing you need to make sure of is always ending directory paths with a "/", otherwise you will get errors. Here's a more complicated example of a "eachFile" method that takes a closure as it's final argument:
  public void eachFile(WorkspacePath p, Closure c)
{
println "eachFile ${p.url} - ${p.path}";
def path = absoluteDirPath(p.url, p.path);
def ntlm = new NtlmPasswordAuthentication("", p.username, p.password);
SmbFile file = new SmbFile(path, ntlm);

// are we dealing with a directory path or just a single file?
if (!file.isDirectory()) {
c.call([name: file.name, file: file, path: file.canonicalPath,
inputStream: { return new SmbFileInputStream(file); },
outputStream: { return new SmbFileOutputStream(file); }
]);
return;
}

file.listFiles().each {
f-> if (f.isDirectory()) return;
if (f.isHidden()) return;

c.call([name: f.name, file: f, path: f.canonicalPath,
inputStream: { return new SmbFileInputStream(f); },
outputStream: { return new SmbFileOutputStream(f); }
]);
}
}
We've been quite pleased with JCIFS and it well its been working in our grails application. We are currently using 1.3.14 with the patches noted here. I just noticed that 1.3.15 is out so I'm interested in trying that as soon as possible!

2 comments:

Bob Fleischman said...

How are you getting the users password? Are you asking for it in clear text?

jr said...

When a user logs in, they use their AD credentials to do so. The password is cached in the session data.