Cloud based PDF merge solution for Force.com

  I recently hosted a webinar that covered Cloud to Cloud integrations with Force.com. You can watch a recording of the webinar here.

One of the integration patterns that I covered in the webinar was Hybrid integrations – i.e. when Force.com initiates the integration with an outbound callout to another Cloud application/platform which then turns around and makes an inbound call to Force.com to get/update additional data. To illustrate this pattern, I demoed a PDF merge solution for Force.com (jump to this point in the recording if you're only interested in this demo) that uses a service hosted on Google App Engine (aka GAE) to merge two or more PDF documents – e.g. merging a dynamically generated Quote PDF with a 'static' Terms and Conditions PDF Attachment. I've posted the code for that demo on GitHub and wanted to quickly highlight some of the key code snippets here. (Note: I would recommend watching the webinar recording first to understand the overall integration architecture at work here).

Force.com client

The integration is kicked off by the user clicking on a custom button on an Opportunity record that launches a custom VF page. The 'mergeAttachments' method of the page controller gets invoked when the page is launched and a selected snippet of the method code is listed below.

public with sharing class PDFController {
.....
public PageReference mergeAttachments()
{
.....
HttpRequest req = new HttpRequest();
req.setEndpoint(GAE_PDF_MERGE_SVC_URL + queryStr);
req.setMethod('GET');
req.setTimeout(60000);
HttpResponse res;
if (!Test.isRunningTest())
{
res  = new Http().send(req);
if (res.getStatusCode() != 200)
{
ApexPages.Message msgError = new ApexPages.Message(ApexPages.Severity.Error,
'Could not merge docs - '+ res.getBody());
ApexPages.addMessage(msgError);
return null;
}
}
else
{
res = new HttpResponse();
}
......
}
}

As you can see from the above snippet, the Force.com code is trivial. It merely invokes the GAE service URL using an Apex HTTP callout and passes along the record Ids of the PDF attachments that need to be merged (in the 'queryStr' variable). The one thing of note here is the use the 'Test.isRunningTest()' method. As explained during the webinar, writing test classes for Apex callouts requires special handling and the 'Test.isRunningTest()' method was added in Summer '11 to help with such testing scenarios. Check out the recording of the recent Apex Testing webinar for further details on how to test Apex callouts.

 

Google App Engine PDF merge service

A snippet of the GAE Java application is listed below.

public class PDFMergeServlet extends HttpServlet {
.....
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
String sfdcInstance = req.getParameter("instanceURL");
if (sfdcInstance != null)
{
salesforceOAuthLogin(sfdcInstance);
}
else
{
salesforceOAuthLogin("https://login.salesforce.com");
}
if (accessToken == null)
return;
String ids = req.getParameter("ids");
String parentId = req.getParameter("parentId");
String mergedDocName = req.getParameter("mergedDocName");
List pdfs = new ArrayList();
ByteArrayOutputStream o = null;
if (accessToken != null && ids != null && parentId != null)
{
Salesforce sfdc = new Salesforce(instanceURL, accessToken);
for (String id : ids.split(","))
{
pdfs.add(sfdc.getAttachment(id));
}
o = new ByteArrayOutputStream();
MergePDF.concatPDFs(pdfs, o, false);
sfdc.saveAttachment(parentId, mergedDocName, o, "application/pdf");
}
.......
}
......
}

The code above is from the GAE Servlet that gets invoked by the Apex callout and should be familiar to any Java developer. First, the servlet uses OAuth 2.0 to log into Force.com and get back a valid Access Token (the 'salesforceOAuthLogin' method). The use of OAuth to authenticate with Force.com is consistent with Cloud-Cloud integrations in general since most modern cloud applications/platforms support it. For the sake of brevity, I haven't included the actual OAuth code here, but you can always download the full package from GitHub.

Next, the servlet retrieves the actual PDF attachments from Force.com using the Force.com REST API. Finally, the servlet merges the retrieved PDF's and writes back the merged PDF as a new Attachment record in Force.com (again using the REST API). The sample uses the open source iText Java library to perform the PDF merge (that logic is encapsulated in the 'MergePDF.java' class).

Lets take a quick look at the code for retrieving the PDF Attachments from Force.com which is encapsulated in the 'getAttachment' method of the 'Salesforce.java' class.

	public InputStream getAttachment (String recordId)
{
String restURI = instanceURL + "/services/data/v" + sfdcApiVersion +
"/sobjects/attachment/" + recordId + "/body";
try
{
URL url = new URL(restURI);
URLFetchService urlFetchService = URLFetchServiceFactory.getURLFetchService();
HTTPRequest httpRequest = new HTTPRequest(url, HTTPMethod.GET,
FetchOptions.Builder.withDeadline(10));
httpRequest.setHeader(new HTTPHeader("Authorization", "OAuth "+ accessToken));
HTTPResponse response = urlFetchService.fetch(httpRequest);
InputStream i = null;
if (response.getResponseCode() == 200)
{
i = new ByteArrayInputStream(response.getContent());
}
return i;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

The code is pretty simple and self-explanatory and so I won't expand on it further. Instead, lets discuss the use of the Force.com REST API itself in this integration. As explained during the webinar, you can also use the Force.com toolkit for GAE (a GAE native Java wrapper for the SOAP API) to integrate with Force.com from a GAE application. I chose to use the REST API instead simply because REST is the de facto lingua franca of the Cloud and I wanted to demonstrate how easy it is to use the REST API from an external cloud application. I could just as easily have used the GAE toolkit.

Hopefully this example will help provide a quick introduction to developing hybrid integrations with the Force.com platform. Jeff Douglas, one of the stalwarts of the Force.com developer community (and a Force.com MVP no less!) has done some interesting integration work between Force.com and GAE and you can also search his blog for more samples. As always, comments and questions are welcome. Happy integrations!

 

Published
June 6, 2011
Topics:

Leave your comments...

Cloud based PDF merge solution for Force.com