Lapas

Thursday 30 June 2011

WEBDAV in XPAGES

What is WebDav, you can read at http://www.webdav.org.

WebDav and Lotus Domino
Basicaly webdav natively is supported only for managing file resources in .nsf databases. Read this for details: http://www.codestore.net/store.nsf/unid/FISR-6U8SN7

Next question is how to create webdav service for working with documents or set's of documents. This is very important when you're building DMS (document management systems). So why so late to create webdav in only 8.5 for document management?
Main problem,that there were no methods to pass back/receive binary data to browser, so webdav could be created only for txt type documents. Now binary data could be passed back using servlet response streams and read using request streams. Check the article about binary response in xpages.

Now our goal is to make available document listing and attachments for reading through webdav from simple database. db url is http://server/folder/db.nsf
Our webdav service will be served at xpage webdav.xsp. So full service url is http://server/folder/db.nsf/webdav.nsf

Now we have to create enable webdav on the server (native webdav support configuration), create website substitutions for better links (read this: http://www.dominoguru.com/pages/domino_rest_xpages_part1.html) for example when we entering url http://server/folder/db.nsf/webdav/documentid/attachmentname we get attachment, http://server/folder/db.nsf/webdav/documentid - attachment list for document. So basic substitution could be /folder/db.nsf/webdav/* -> /folder/db.nsf/webdav.xsp

Now we have create webdav.xsp, which will contain main service code. I will show only simple example, but it will be enough to start thinking in that way and build Your more complex application.

All service code could be put in beforeRenderResponse:
xpage main settings:
1. gzip compression for xpages disabled!
2. create form =false
3. rendered = false

Init code
var req:javax.servlet.http.HttpServletRequest=facesContext.getExternalContext().getRequest();
var resp:javax.servlet.http.HttpServletResponse=facesContext.getExternalContext().getResponse();

@code end we should end with
facesContext.responseComplete(); //because domino will generate html if we don' t do that

Next we should create blocks for handling requests, which contains of if's on request method. Ex. if (req.getMethod()=="OPTIONS"){} and so on
Each operation must return correct status code using resp.setStatus()

Main methods used for basic webdav support are:
1. "OPTIONS" - resource options, not required, should send (status: 200)
2. "PROPFIND" - property listing/dav folder contents with status (207)
3. "GET" - for resource downloading

now the theory of request urls and substitution. when user requests url like http://server/folder/db.nsf/webdav/documentid server will redirect it to http://server/folder/db.nsf/webdav.xsp and original requested url will be available through req.getRequestURI(), which sould be parsed, to determine what to do with request and what kind of response should be formed. For example if we got request with url like: http://server/folder/db.nsf/webdav/documentid - we could return attachment list in document(PROPFIND method), when user browses folder and clicks on document/attachment GET request is initated with HREF specified in returning XML by PROPFIND method handler. So on get request we are returning attachment data.

Look for sample code:
In sample code on propfind returned attachment list in document, which found by unid.
In get request attachment data is returned.


var req:javax.servlet.http.HttpServletRequest=facesContext.getExternalContext().getRequest();
var resp:javax.servlet.http.HttpServletResponse=facesContext.getExternalContext().getResponse();

var result="";
if (req.getMethod()=="OPTIONS"){
resp.setStatus(200);
resp.setHeader("DAV","1, 2")
resp.setHeader("MS-Author-Via", "DAV")
resp.setHeader("Allow","OPTIONS, GET, HEAD, POST, DELETE, TRACE, PROPPATCH, COPY, MOVE, LOCK, UNLOCK, PROPFIND")
resp.setContentType("application/xml")
resp.setContentLength(0);
}else if (req.getMethod()=="PROPFIND")// req.getMethod()=="GET")
{
resp.setStatus(207);
resp.setHeader("DAV","1, 2")
resp.setHeader("MS-Author-Via", "DAV")
resp.setHeader("Allow","OPTIONS, GET, HEAD, POST, DELETE, TRACE, PROPPATCH, COPY, MOVE, LOCK, UNLOCK, PROPFIND")
resp.setContentType("application/xml; charset=utf-8")

var writer=resp.getWriter();

result+=""
result+=""
result+=" "+req.getRequestURI()+"/folder/ folder HTTP/1.1 200 OK "
try{

var doc:NotesDocument=database.getDocumentByUNID("DBFDB0C60BF0B073C22578BE00406E45")

attachs=session.evaluate("@AttachmentNames",doc)
for (i=0;i "+req.getRequestURI()+"/"+attachs[i]+""+attachs[i]+"application/octet-stream"+emb.getFileSize()+" HTTP/1.1 200 OK "
}
}catch(e){

}

result+="
"

writer.write(result);
writer.flush();
resp.setContentLength(result.length);
facesContext.responseComplete()

}else if (req.getMethod()=="GET"){
resp.setStatus(200);
var writer=resp.getOutputStream()
resp.setHeader("DAV","1, 2")
resp.setHeader("MS-Author-Via", "DAV")
resp.setHeader("Allow","OPTIONS, GET, HEAD, POST, DELETE, TRACE, PROPPATCH, COPY, MOVE, LOCK, UNLOCK, PROPFIND")
resp.setContentType("application/msword")
resp.setHeader("Content-Disposition","attachment; filename=testdocument")

var doc:NotesDocument=database.getDocumentByUNID("DBFDB0C60BF0B073C22578BE00406E45")
var attach:NotesEmbeddedObject=doc.getAttachment("testdocument")
if (attach!=null)
{
var fin:java.io.InputStream = attach.getInputStream()
var flength=fin.available();
while (fin.available()>0)
{

writer.write(fin.read());
}
writer.flush();
}
resp.setContentLength(flength);
facesContext.responseComplete()
doc.recycle()

}



P.S. if You need some detailed info on this or some help, then just PM, i' ll write back and post additional comment on this. Sorry, but have no time to fully explain (described just main steps)