This blog post explains using HTTP to remotely get and add documents to Liferay’s Document and Media Library. Since documents may require authentication for access, this post explains how to use basic authentication to achieve that. It will also cover accessing documents in a secure (HTTPS) environment, particularly those using self-signed certificates and not from trusted Certification Authorities. This scenario typically happens when trying to access Liferay from an external source.

We’ll be using the Apache httpclient and commons-io libraries. Below is the Maven dependency:

<dependency>
  <groupId>org.apache.httpcomponents</groupId>
  <artifactId>httpclient</artifactId>
  <version>4.3.2</version>
</dependency>
<dependency>
  <groupId>commons-io</groupId>
  <artifactId>commons-io</artifactId>
  <version>2.4</version>
</dependency>

Retrieving Documents

Below is a code snippet for document retrieval, based on the assumption that document is available to the public and no authentication is required. In Liferay terms the document has “View” permission defined for the “Guest” role.

String url = "http://localhost:8080/documents/10182/12019/Test.pdf/" +
                  "d3c361b3-3fae-449a-8965-faf0a3d3b963";
String fileName = "Test";
String extension = "pdf";
try {
  HttpGet httpGet = new HttpGet(url);
  CloseableHttpResponse response = (CloseableHttpResponse)HttpClientBuilder.
                                     create().build().execute(httpGet);
  File file = null;
  try{
    String tempDir = System.getProperty("java.io.tmpdir");
    String path = tempDir + File.separator + fileName + "." + extension;
    file = new File(path);
    FileUtils.copyInputStreamToFile(response.getEntity().getContent(), file);
  } finally{
    response.close();
  }
} catch(Exception e) {
  //Handle Exception
}

The code above assumes the filename and extension are unknown. The input stream from the response is copied to a specified directory, in this case its the JVM temp directory. Apache FileUtils from the Apache commons-io library is used to copy the input stream.

Depending on the permissions defined for a document, a user may be required to log in and have a certain type of role before document can be accessed or downloaded. Liferay accepts Basic Authentication to access these documents, but by default Liferay does not include a basic authentication implementation as part of its auto-login classes. The preconfigured classes include CAS auto login, Facebook auto login, etc.

To configure Liferay to allow basic authentication, go to portal-ext.properties and add the following property:

auto.login.hooks=com.liferay.portal.security.auth.BasicAuthHeaderAutoLogin

And then restart the server.

The following code shows how the Basic Authentication header is set before executing the request.

...
String usernamePassword = "[email protected]:test";
try {
  HttpGet httpGet = new HttpGet(url);
  String base64Encoded=DatatypeConverter.
                       printBase64Binary(usernamePassword.getBytes("UTF-8"));
  httpGet.setHeader("Authorization", "Basic "  + base64Encoded);
  ...              
}
...

Liferay’s JSON WS APIs can also be used to retrieve document library files in JSON format. To view the list of JSON WS APIs available go to “http://<<hostname>>/api/jsonws/”, e.g. http://localhost:8080/api/jsonws/. Within the DLFileEntry Category you will see a couple of APIs that can be used to retrieve file entries. Below is a code snippet that uses one of the APIs to retrieve a DLFileEntry. The API accepts a fileEntryId as input parameter.

...
String url = "http://localhost:8080/api/jsonws/dlfileentry/get-file-entry";
long fileEntryId= 123456;
try {
  HttpPost httpPost = new HttpPost(url);
  List<NameValuePair> urlParameters = new ArrayList<NameValuePair>();
  urlParameters.add(new BasicNameValuePair("fileEntryId", fileEntryId);
  httpPost.setEntity(new UrlEncodedFormEntity(urlParameters));
  CloseableHttpResponse response = (CloseableHttpResponse) HttpClientBuilder.
                                     create().build().execute(httpPost);
  String responseContent = "";
  BufferedReader bufferedReader = null;
  StringBuffer result = null;
  try{
    bufferedReader = new BufferedReader(
                       new InputStreamReader(
                            response.getEntity().getContent()));
    result = new StringBuffer();
    String line = "";
    while ((line = bufferedReader.readLine()) != null){
      result.append(line);
  }
  responseContent = result.toString();
  } finally{
    bufferedReader.close();
    response.close();
  }
} catch (Exception e) {
  //Handle Exception.
}
...

If retrieving the document requires authentication and authorization, add the basic authentication header as in the sample code above.

Adding Documents

We’ll use one of Liferay’s JSON WS APIs as an example of how to add a document. Under the DLApp category there are two APIs for adding file entries. The only difference between the two is that one takes a file object as input and the other take a byte array (byte[]). Which one to use is a matter of individual preference.

In order to support a multipart file upload using Apache HttpClient, a new Maven dependency is required.

<dependency>
  <groupId>org.apache.httpcomponents</groupId>
  <artifactId>httpmime</artifactId>
  <version>4.2.3</version>
</dependency>

This dependency contains all the required classes and APIs needed to upload a file.

The code snippet below uses the File object.

...
String url = "http://localhost:8080/api/jsonws/dlapp/add-file-entry";
String repositoryId = "repositoryId";
String folderId= "654321";
String sourceFileName= "test.pdf";
String mimeType= "application/pdf";
String title= "";
String description= "";
String changeLog= "";
String fileName= "test.pdf";
File file = someFile;
String usernamePassword  = "[email protected]:test";
try{
  HttpPost httpPost = new HttpPost(url);
  String base64Encoded = DatatypeConverter.printBase64Binary(
                           usernamePassword.getBytes("UTF-8"));
  httpGet.setHeader("Authorization", "Basic "  +  base64Encoded);
  MultipartEntity multiPartEntity = new MultipartEntity(
                                         HttpMultipartMode.BROWSER_COMPATIBLE,
                                            null,Charset.forName("UTF-8"));
  multiPartEntity.addPart("repositoryId", 
        new StringBody(repositoryId,"text/plain",Charset.forName("UTF-8")));
  multiPartEntity.addPart("folderId", 
        new StringBody(folderId, "text/plain", Charset.forName( "UTF-8" )));
  multiPartEntity.addPart("sourceFileName", 
        new StringBody(sourceFileName,"text/plain",Charset.forName("UTF-8")));
  multiPartEntity.addPart("mimeType", 
        new StringBody(mimeType, "text/plain", Charset.forName( "UTF-8" )));
  multiPartEntity.addPart("title", 
        new StringBody(title, "text/plain", Charset.forName( "UTF-8" )));
  multiPartEntity.addPart("description", 
        new StringBody(description,"text/plain",Charset.forName("UTF-8")));
  multiPartEntity.addPart("changeLog", 
        new StringBody(changeLog, "text/plain", Charset.forName( "UTF-8" )));
  FileBody fileBody = 
        new FileBody(file, fileName, "application/octect-stream", "UTF-8");
  multiPartEntity.addPart("file", fileBody);
  httpPost.setEntity(multiPartEntity) ;
  CloseableHttpResponse response = (CloseableHttpResponse) HttpClientBuilder.
                                       create().build().execute(httpPost);
  //Process and close response      
  ...
} catch(Exception e) {
  //Handle Exception.
}
...

Make sure the authenticated user has the roles required to add a document to Liferay’s Document and Media Library.

Making Requests to a Secure Environment

If Liferay is running in a secure environment, as in requests are made using HTTPS protocol and the server has a test or self-signed certificate not from a Certification Authority, there is a likelihood of  running into a bunch of certification errors. The solution is to add the server’s certificate to a keystore that has a list of trusted certificates.

The links below have more information on adding a new certificate to a keystore:
http://www.mkyong.com/webservices/jax-ws/suncertpathbuilderexception-unable-to-find-valid-certification-path-to-requested-target
http://www.nodsw.com/blog/leeland/2006/12/06-no-more-unable-find-valid-certification-path-requested-target