Lapas

Showing posts with label xpages. Show all posts
Showing posts with label xpages. Show all posts

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)

Monday, 27 September 2010

Using XPAGES to pass back binary data

So I would like to write a little article about passing custom generated responses using XPAGES and how to use the for binary output.

Main problem in Lotus Domino editions till 8.5 is that we could not pass back binary data directly to WEB browser (using agent print/printing in form and so on). The only ways were, to create document, attach file, redirect attachment link or create file in shared web dir and pass direct link to user.

Now, when XPAGES with JSF technology comes to place it is possible to do so. Let's get it "how?".

At first You need to create and xpage element and set it render property to false - so nothing to be rendered.
Then You should choose xpage event, where to write the right code, which will serve data. So xpage has following events "beforePageLoad","afterPageLoad","beforeRenderResponse","afterRenderResponse","afterRestoreView". Most interesting for our example are "beforeRenderResponse","afterRenderResponse".

Classes to work with:
1. javax.faces.context.ExternalContext - needed to get response object
2. com.ibm.xsp.webapp.XspHttpServletResponse - needed to get outputstream
3. javax.servlet.ServletOutputStream - stream, where we will write data

XPAGE options to be set:
CreateForm = false
rendered = false

//CODE BLOCK START FOR this example it should be put in beforeRenderResponse
//Initialization
var extContext:javax.faces.context.ExternalContext = facesContext.getexgetExternalContext();
var response:com.ibm.xsp.webapp.XspHttpServletResponse = exCon.getResponse();
var writer:javax.servlet.ServletOutputStream = response.getOutputStream();

//setting response headers for browser to recognize data
response.setContentType("image/gif"); // content type of data, for other binary see mime types or put "application/octet-stream"
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Content-Disposition" ,"attachment; filename=image.gif"); //required if You need to pass it back as file

//convert sample hex to character codes.
var decoded=imgHex.split(" ");
var result=new Array();
for (var i=0;i<decoded.length;i++)
{
result[i]=(parseInt(decoded[i],16))
}
// result is an array containing character codes (byte array). How to prepare for other situations an array You should decide at Your own.
writer.write(result); // just write prepared array to output

//when all output is done. just tell that You have completed the response and close stream
facesContext.responseComplete();
writer.close();




//Sample data
//domino image in hex should move to initialization section
imgHex= "47 49 46 38 39 61 15 00 15 00 e6 00 00 00 00 00 ff ff ff ce 64 66 c9 65 67 e2 7b 7c ff 98 99 ce 65 69 a4 9f a3 68 66 69 a0 b5 ca c8 cc cd c9 cb ca 65 65 63 67 67 65 e5 e5 e2 ca ca c8 c7 c7 c5 f0 f0 ef eb eb ea a8 a8 a7 fe f3 8a fe f2 8c ff f2 8c fe f1 8b d6 d4 d0 ec eb e9 ea e9 e7 c8 c7 c5 f3 ac 38 f2 ad 38 f2 ac 3a f2 ac 3d fc be 59 fc be 5b f9 be 5a d0 cd c8 f4 ab 3a fc bc 5b fc bd 5c fb bc 5d e8 e6 e3 de dc d9 d7 d4 d0 d0 cd c9 d1 cd c8 c0 be bc ee ed ec 81 5b 48 84 59 46 82 5b 4a b7 83 6d b6 82 6d 83 59 49 84 5a 4a b8 81 6d 87 59 4a 84 5a 4c b8 7f 6c ba 7f 6d cd 65 64 cd 67 65 ca 66 66 ff 9a 98 ff 9b 99 fc fc fc ca ca ca ad ad ad ac ac ac 88 88 88 67 67 67 66 66 66 ff ff ff 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 21 f9 04 01 00 00 47 00 2c 00 00 00 00 15 00 15 00 00 07 9e 80 47 82 83 84 85 86 87 88 89 8a 8b 8c 06 03 8c 86 3d 3e 3f 3c 86 42 97 98 99 39 45 05 04 3b 0a 85 42 40 a3 a4 a3 36 14 43 46 02 0f 96 40 11 af b0 3a 14 20 24 30 46 0b ad b0 af 32 14 27 1d 31 07 46 41 ad 19 13 44 12 33 17 25 1c 37 13 18 c2 ad 0e 10 1b 3a 16 26 1f 38 13 0b 40 d0 a1 40 2e 13 33 15 22 1c 2f da 29 dc c3 de 1a 2d 08 21 1e 34 13 2a 18 18 e9 c4 28 0d 0c 35 13 09 2c 2b 23 f6 bc 95 1a 38 aa 1b 21 23 08 13 2a 54 a8 ae 50 90 87 10 23 46 84 44 b1 a2 c5 8b 8c 02 01 00 3b"

REFERENCES, where You could check how to output STRING data.
http://www.wissel.net/blog/d6plinks/SHWL-7MGFBN
http://www-10.lotus.com/ldd/nd85forum.nsf/7756aedc25e6d81285256324005ac76c/1e87cc63dfcf9412852575ae0005079f?OpenDocument - getResponseStream problem.

Friday, 16 October 2009

How to add computed custom control to XPAGE at runtime

Very simple technique:
You need to know, that "include page" control could include not only page elements, but also custom control elements, but you should write then by Your hands.
So @1st create a repeater on page.
Then add include page control in repeater.
As repeater values set array of controls to add ex. ["ccontrol1.xsp", "ccontrol2.xsp",...]
use those values to compute page.