Friday, September 20, 2013

MarkLogic - using xquery to interact via Alfresco's content repository using the REST API

Using Alfresco community edition 4.2.a, and MarkLogic 6.

It was difficult navigating Alfresco's documentation on the REST API, as there were NO examples, just specs.. and often the specs stated that they were "deprecated".  Ultimately I found this document on CMIS and interacting via http.  (NOTE:  There is a vital typo on page 25 in the cmisra namespace declaration, which causes example to fail.. just remove extraneous slash).

Anyway, I cobbled together an example that demonstrates login, ticket validation, content creation, and logout.  This works in the Query Console.. although the base64 module is my own (see prior post on base64 encoding of binary files).

xquery version "1.0-ml";
declare namespace html = "http://www.w3.org/1999/xhtml";
import module namespace json = "http://marklogic.com/xdmp/json" at "/MarkLogic/json/json.xqy";
import module namespace base64 = "base64" at "utility/base64.xqm";


let $string := concat('{ "username":"admin", "password":"admin" }')
let $data := <data>{$string}</data>

let $login := xdmp:http-post("http:/localhost:8080/alfresco/service/api/login",
  <options xmlns="xdmp:http">
        <data>{$string}</data>
        <headers>
            <content-type>application/json</content-type>
        </headers>
    </options>)
    
let $ticket_xml := json:transform-from-json($login[2])

let $ticket_id := data($ticket_xml//*[local-name() = 'ticket'])

let $uri := concat("http://localhost:8080/alfresco/service/api/login/ticket/",$ticket_id,"?alf_ticket=",$ticket_id)

let $check := xdmp:http-get($uri)
(: if you have a node_id, the following is example of how to retrieve content
let $content_uri := concat("http://localhost:8080/alfresco/service/cmis/i/", $node_id, "/content?alf_ticket=", $ticket_id) :)

let $filedata := base64:encode("/some/path/image.png")  (: insert your own base64 here :)

let $content_uri := concat("http://localhost:8080/alfresco/s/cmis/p/path/children", "?alf_ticket=", $ticket_id)  (: the "path" is the path after "Company Home" :)

let $atomxml :=
<entry xmlns="http://www.w3.org/2005/Atom" 
xmlns:cmisra="http://docs.oasis-open.org/ns/cmis/restatom/200908" 
xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908">
 <title>image.png</title>
 <summary>A sample upload</summary>
 <content type="image/png">{$filedata}</content>
 <cmisra:object>
 <cmis:properties>
 <cmis:propertyId propertyDefinitionId="cm:description"><cmis:value>This is the description property</cmis:value></cmis:propertyId>
 <cmis:propertyId propertyDefinitionId="cmis:objectTypeId"><cmis:value>cmis:content</cmis:value></cmis:propertyId>
 </cmis:properties>
 </cmisra:object>
</entry>

let $upload := xdmp:http-post($content_uri,
  <options xmlns="xdmp:http">
        <data>{concat('<?xml version="1.0" encoding="utf-8"?>',xdmp:quote($atomxml))}</data>
        <headers>
            <content-type>application/atom+xml</content-type>
        </headers>
    </options>)

let $logout := xdmp:http-delete($uri)

return ($check,$upload)


MarkLogic base64 encoding of a binary file using MLJAM

MarkLogic has the function xdmp:base64-encode, which unfortunately only works on xs:string's.

I needed a function to convert an arbitrary file (ie. binary) into a base64 encoded stream in order to put it into an atom+xml http post to feed Alfresco document management system.  Luckily I found MLJAM, which allows you to embed Java into xquery.  I found the needed Java snippet here, and then put it together - the code below should work in the MarkLogic Query console, just set the filename parameter accordingly.

Ultimately MarkLogic should have xdmp:base64-encode work on a binary node, then you could simply do xdmp:base64-encode(xdmp:external-binary("/some/path/image.png"))!

xquery version "1.0-ml";
import module namespace jam = "http://xqdev.com/jam" at "/MarkLogic/jam.xqy";
let $code := <code><![CDATA[
import org.apache.commons.codec.binary.Base64;
private String encodeFileToBase64Binary(String fileName)
throws IOException {
File file = new File(fileName);
byte[] bytes = loadFile(file);
byte[] encoded = Base64.encodeBase64(bytes);
String encodedString = new String(encoded);
return encodedString;
}
 
private static byte[] loadFile(File file) throws IOException {
   InputStream is = new FileInputStream(file);
     long length = file.length();
   if (length > Integer.MAX_VALUE) {
       // File is too large
   }
   byte[] bytes = new byte[(int)length];
   
   int offset = 0;
   int numRead = 0;
   while (offset < bytes.length
          && (numRead=is.read(bytes, offset, bytes.length-offset)) >= 0) {
       offset += numRead;
   }
 
   if (offset < bytes.length) {
       throw new IOException("Could not completely read file "+file.getName());
   }
 
   is.close();
   return bytes;
}
  
    return encodeFileToBase64Binary(filename);
]]></code>
  
let $path := "http://localhost:8080/mljam/mljam"
let $connect := jam:start($path, (), ())
let $vars := jam:set("filename", "/some/path/image.png")
let $eval := jam:eval-get($code)
let $end := jam:end()
return $eval