X

Best Practices from Oracle Development's A‑Team

Creating a Documents Cloud Service REST Client from the DOCS WADL

Mark Foster
Director

Documents Cloud Service (DOCS) provides a WADL file for the REST API. This can be used in JDeveloper or other tools to quickly create a REST client in Java with minimal coding. The WADL file contains all of the 1.1 REST API service calls, thus the WADL2Java output generates handles for calling any DOCS service.

JDeveloper 12c has a builtin "RESTful Client And Proxy" feature that can take a WADL file and produce Java code. Other tools like SoapUI can also take a WADL file and produce a project that quickly enables calling REST services. Both JDeveloper and SoapUI use WADL2Java to produce the artifacts. The WADL2Java tool can also be used from the command line but tools like these simplify the turnaround time. For more details on the WADL2Java tool, see this page.

Download the Documents Cloud Service WADL file from this page.

Create a new application and project

Create a new custom Java application in JDeveloper.

Create a new “RESTful Client and Proxy”

Follow the wizard.

Click next on the first page.

 

[caption id="attachment_30972" align="aligncenter" width="804" class=" "]Create new RESTful Client and Proxy Create new RESTful Client and Proxy[/caption]

Select the style of output. The default is Jersey 1.x.

 

[caption id="attachment_30973" align="aligncenter" width="800"]Select style for generated Java classes Select style for generated Java classes[/caption]

Browse to the WADL file. Name the package as you like. Click Next.

 

[caption id="attachment_30974" align="aligncenter" width="800"]Select the WADL file and name the package Select the WADL file and name the package[/caption]

 

Accept the defaults. Note that the Base URI will be changed to your DOCS instance URL, but this isn't necessary to change at this point. The WADL uses a Base URI of https://DocumentsCloudService to create cleaner class names. The WADL file could be updated prior to creating the JDeveloper application - however, the class names will be generated using the URI and are a bit cumbersome to use. Changing the Base URI after the code is generated is a simple one line code change.

[caption id="attachment_30975" align="aligncenter" width="799"]Use the default Base URI and Class Name.  The URI will be updated in a later step. Use the default Base URI and Class Name. The URI will be updated in a later step.[/caption]

On the next page, do not select any policies.

 

[caption id="attachment_30968" align="aligncenter" width="800" class=" "]Click next on the Policies page Click next on the Policies page[/caption]

 

Click Next to create the REST client.  This executes the wadl2java tool against the WADL file to create Java classes that can be used to call any Documents Cloud Service.

 

The generated code creates a basic client. The DocumentsCloudService.java class contains the logic to call all services. The newly generated Java files are DocumentCloudService.java and a corresponding test client called DocumentCloudServiceClient.java. Notice that the different API resources are defined (Files, Catalog, Users).

Generated code creates a basic client. The DocumentsCloudService.java class contains the logic to call all services.

 

One update must be made the DocumentsCloudService.java file. The endpoint must be set. Change the https url to point to a valid endpoint. The https://DocumentsCloudService url must be set to an actual host.

Change:

URI originalURI = URI.create("https://DocumentsCloudService");

To a valid endpoint:

URI originalURI = URI.create("https://scdemos-scuscdc.documents.us2.oraclecloud.com/documents/api/1.1");

[caption id="attachment_30970" align="aligncenter" width="1024"]Update the DOCS instance URL Update the DOCS instance URL[/caption]

 

Adding HTTP Logging for Troubleshooting

 

For troubleshooting REST requests and responses, add the logging filter to the client in DocumentsCloudServiceClient.java. This will print out the HTTP requests and responses when it runs. Note that this is useful for testing or troubleshooting but is verbose and may only be needed when the HTTP dialogue is of interest to you.

public class DocumentsCloudServiceClient {     public static void main(String[] args) {         Client client = DocumentsCloudService.createClient();         client.addFilter(new LoggingFilter(System.out)); 

Adding the Authorization Parameter

 

The authorization header must be present on all DOCS service calls. The Authorization header is required. For testing the authorization string can be used in the client. However, this must be stored and accessed in a keystore when doing anything beyond basic client testing. For demo purposes, the encoded authorization string can be used in a variable in your client class.

    import com.sun.jersey.core.util.Base64;     // ...     String authorization  "Basic " + new String(Base64.encode("username:password"));

 

Calling Services

 

Under the “add your code here” comment, add service calls to the REST API. For now, get the responses back as Strings (JSON output).  Use the clients to call services. For example, the calls to REST services follow the URI format to the resources. Using documentscloudservicefolders.items() will get the authorized user’s Personal Workspace.

        //************************************************************************************         // Folders API Test - get personal workspace         //************************************************************************************         //GET folders/items         System.out.println("\n*******************************\n* GET folders/items\n*******************************\n");         ClientResponse response = documentscloudservicefolders.items().get(authorization, ClientResponse.class);         status = response.getStatus();         if(status == 200){             System.out.println(response.getEntity(String.class));         }         else{             System.out.println("Error: " + status + ": " + response.getStatusInfo());         }

Services that Requires a "role" parameter

The WADL2Java output creates a Role.java class file. This is because for Documents Cloud Service, the "role" parameter contains a fixed set of options that can be used: viewer, downloader, contributor, or manager. The Role.java class contains an Enumeration of these values. To use the values, reference the parameter in the service call using, for example, Role.DOWNLOADER.

        response = documentscloudserviceshares.id(folderId).role().put(userId, Role.DOWNLOADER, authorization, ClientResponse.class);

 

File upload

 

To use the upload service, add a line to DocumentsCloudService.java method customizeClientConfiguration in order to post multipart forms. The customizeClientConfiguration is the method where additional options can be placed when the client objects are created. If this is not done, this error occurs when attempting to upload a file to DOCS:

Caused by: com.sun.jersey.api.client.ClientHandlerException: A message body writer for Java type, class com.sun.jersey.multipart.FormDataMultiPart, and MIME media type, multipart/form-data, was not found

 

    /**      * Template method to allow tooling to customize the new Client      *      */     private static void customizeClientConfiguration(ClientConfig cc) {         cc.getClasses().add(MultiPartWriter.class);     }

 

Then in the application, include the Multipart jar (jersey-multipart-1.18.jar). This jar ships with JDeveloper 12 in oracle_common/modules. This can be included alone, or it is included in the JDev library “REST Client Lib (JAX-RS 2.x)”. Note that the other two libraries, JAX-RS Jersey 1.x and JAX-RS Jersey Jackson (Bundled), are automatically added when the RESTful Client and Proxy Wizard completes.

 

image015

 

Once the multipart jar is available, the POST to upload a file requires a format that looks like the following, with two body parts. The first is a json payload that contains the parent folder id. The second part is the actual file content. Note that this example uploads a small text file that contains only the text “Hello World!”.

 

----1234567890 Content-Disposition: form-data; name="jsonInputParameters" { "parentID": "<yourfolderguid>" } ----1234567890 Content-Disposition: form-data; name="primaryFile"; filename="helloworld.txt" Content-Type: text/plain Hello World! ----1234567890--

 

Sample code to create the multipart request, with json payload and file.

        //POST files/data  - needs MultiPart features.         System.out.println("\n*******************************\n* POST files/data\n*******************************\n");         String parentID = "self"; //self is the top level folder. Use a folder GUID to uplaod to a specific subfolder.         String jsonPayload = "{\"parentID\":\"" + parentID + "\"}";         //Add the jsonInputParameters to the multi part form         FormDataMultiPart multipart = new FormDataMultiPart();         FormDataBodyPart fdpjson = new FormDataBodyPart("jsonInputParameters", jsonPayload);         multipart.bodyPart(fdpjson);         //Add the file contents to the multi part form. Sample is assuming text/plain as type. MediaType will need to be updated according to file being uploaded.         File f = new File("c:/temp/helloworld.txt");         FileDataBodyPart fdp = new FileDataBodyPart("file", f, MediaType.TEXT_PLAIN_TYPE);         fdp.setName("primaryFile");         multipart.bodyPart(fdp);         response = documentscloudservicefiles.data().postMultipartFormData(multipart, authorization, ClientResponse.class);         status = response.getStatus();         if(status == 200){             System.out.println(response.getEntity(String.class));             System.out.println("File uploaded successfully.");         }         else{             System.out.println("Error: " + status + ": " + response.getStatusInfo());             System.out.println(response.getEntity(String.class));         }

File Download

 

To download a file, the service call must write the stream to a file. The filename is in the Content-Disposition header. Use regex to extract it. Once that is found, use Java IO classes to write the file to disk.

Add these imports:

import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.StandardCopyOption; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.annotation.Generated; import javax.ws.rs.core.MultivaluedMap;

Sample code to call the service, extract the Content-Disposition header and filename attribute, and save the file to a directory, with the file name being the same as what is in DOCS.

        //GET files/{id}/data         response = documentscloudservicefiles.id(fileId).data().get(authorization, ClientResponse.class);         status = response.getStatus();         if(status == 200){             MultivaluedMap<String, String>  headers = response.getHeaders();             String contentDisposition = headers.getFirst("Content-Disposition");  //This header is formatted like this:     attachment; filename*=UTF-8''helloworld.txt; filename="helloworld.txt"             //Use regex to pull out filename from content-disposition header. Following sample from Stack Overflow http://stackoverflow.com/questions/8092244/regex-to-extract-filename             String fileName = null;             Pattern regex = Pattern.compile("(?<=filename=\").*?(?=\")");             Matcher regexMatcher = regex.matcher(contentDisposition);             if (regexMatcher.find()) {                 fileName = regexMatcher.group();             }             String outputDir = "C:/temp/";             InputStream is = response.getEntity(InputStream.class);             try {                 //For sample test, overwrite file if it exists                 Files.copy(is, new File(outputDir + fileName).toPath(), StandardCopyOption.REPLACE_EXISTING);                 System.out.println("Saved file " + fileId + " to path : " + outputDir + fileName + ".");             } catch (IOException e) {                 e.printStackTrace();             }         }         else{             System.out.println("Error: " + status + ": " + response.getStatusInfo());         } 

Be the first to comment

Comments ( 0 )
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.Captcha