Michael Hodgson


8 April 2008


Prerequisite knowledge

To understand this article, you should have a working knowledge of Java/J2EE development and the basics of the LiveCycle ES environment.

User level


Additional Requirements

LiveCycle ES

One of its most useful features of the Adobe LiveCycle ES software is that you can call processes from applications using a variety of methods: EJBs for RMI/IIOP-enabled Java applications, Remoting for Flex/AIR applications, SOAP for .NET, and others. When using Java applications that cannot use the EJB interface, you can use the SOAP interface. This article discusses three different ways to call a LiveCycle ES process from a Java application using SOAP:

  1. Using the supplied LiveCycle ES SDK
  2. Using generated proxy classes and base64-encoded data
  3. Using generated proxy classes and DIME/MIME attachments

Import the process

In this article you will make three different calls to a simple LiveCycle ES process from a Java application using SOAP. First, however, you will need to import the supplied LiveCycle Archive (LCA) file into your LiveCycle ES environment using the LiveCycle ES Admin UI tool. When imported, the LiveCycle ES software will use the active LCA file to automatically create a SOAP endpoint that you will call from your Java code.

You will also need to create a Forms folder in your repository and add the attached XML Data Package (XDP) file to that folder. The process uses LiveCycle Forms ES to merge the XML data with the XDP template and return a PDF file.

The Java code

All three methods will perform the same task: the user will access a JSP page, from which they will select an XML file to upload. Once the user clicks the Upload button, a servlet will be called. The servlet will extract the XML file from the request and will then call the LiveCycle ES software via the SOAP interface.

Method 1: Using the LiveCycle ES SDK

If your Java application cannot use EJBs, but is using a subversion of the JDK 1.5, then you may be able to use the LiveCycle ES SDK. Using the SDK will give you access to higher-level objects (such as the com.adobe.idp.Document object). It also means that you won't have to mess around with proxy classes or attachments.

The first thing you need to do is set the connection properties (for more information see Setting connection properties in the Adobe LiveCycle ES LiveDocs). In this case LiveCycle ES is running on a JBoss server (localhost). The connection properties are where you specify the connection server and port as well as the transport protocol. If the process is secure, then you also specify the user name and password here.

Properties ConnectionProps = new Properties(); ServiceClientFactory m_scf = null; ServiceClient myServiceClient = null; ConnectionProps.setProperty("DSC_DEFAULT_SOAP_ENDPOINT", "http://localhost:8080"); ConnectionProps.setProperty("DSC_TRANSPORT_PROTOCOL", "SOAP"); ConnectionProps.setProperty("DSC_SERVER_TYPE", "JBoss"); //login information ConnectionProps.setProperty("DSC_CREDENTIAL_USERNAME", "administrator"); ConnectionProps.setProperty("DSC_CREDENTIAL_PASSWORD", "password");

Once the connection properties are specified, you instantiate a service client that will be used in the call to LiveCycle.

m_scf = ServiceClientFactory.createInstance(ConnectionProps); myServiceClient =m_scf.getServiceClient();

Now you need to populate a HashMap variable, into which you put all of the parameters that the LiveCycle ES process requires. The HashMap key must be the same as the input variable name in the LiveCycle ES process. In this case, I have one parameter called xmlData which should be populated with the contents of the uploaded file. The method getUploadedData gets a com.adobe.idp.Document object from the uploaded XML file.

//create a Map object to hold the input parameters Map generatePDFparams = new HashMap(); Document myDoc = getUploadedData(request); generatePDFparams.put("xmlData",myDoc );

Once you have all of the input parameters populated, you can make the call to LiveCycle ES. First you create the request, with the service name, method and parameters. Then you execute the request. Since this is a synchronous request to a short-lived process, you should have a response immediately.

//Create the request InvocationRequest Irequest = m_scf.createInvocationRequest( "FormAndData", //Specify the short-lived process name "invoke", //Specify the operation name generatePDFparams, //Specify input values true); //Create a synchronous request //execute the request InvocationResponse Iresponse = myServiceClient.invoke(Irequest);

Finally, you will get the response from the invocation. In this case, I only have one returned parameter, pdfDocument, which is a com.adobe.idp.Document object.

//get the PDF Document resultDoc = (Document) Iresponse.getOutputParameter("pdfDocument");

You can then handle the Document object as you see fit. (I'm writing it to the browser.)

Generating proxy classes

If you cannot use the Adobe LiveCycle ES SDK (for example, if your application server only supports JDK 1.4.2), then you can generate your own proxy classes from the WSDL. There are several tools for generating proxy classes, such as WSDL2Java and the Eclipse WTP plugin. With most of these tools, you locate the WSDL file and the result is several Java classes that can be imported into your application.

The WSDL for active processes is automatically generated by LiveCycle ES and is located at: http://server:port/soap/services/serviceName?WSDL

For example: My server is tranquility and the port is 8080 (JBoss), my service name is FormsAndData. The WSDL is therefore located at: http://tranquility:8080/soap/services/FormAndData?WSDL

The generated proxy classes will usually have a package name of com.adobe.idp.services. A BLOB object will usually be created; this will hold binary and Document data. You will also usually see Proxy, Service, Service Locator and Stub classes that will be used in your application.

The proxy classes can be used for both Base64 and DIME/MIME attachments.

Method 2: Using generated proxy classes and Base64 encoding

With Base64 encoding, the data is converted into a string, which is then sent inside the SOAP message. The LiveCycle ES software will decode the string before it is passed to the process. Returned parameters are Base64-encoded by LiveCycle ES and are transmitted as part of the SOAP message. Thankfully, you won't need to worry too much about the encoding or decoding of the messages, as the proxy classes will deal with most of this for you.

First, you configure the connection to the LiveCycle ES server using the generated proxy classes. Although the URL to the LiveCycle ES service may be populated, you need to specify in what format the binary data will be transmitted. In this case it's Base64 encoding, so you need to add the string ?blob=base64 to the URL.

//create a service locator FormAndDataServiceLocator locate = new FormAndDataServiceLocator(); //specify the service target URL and object type URL serviceURL = new URL("http://tranquility:8080/soap/services/FormAndData?blob=base64"); //Use the binding stub with the locator FormAndDataSoapBindingStub formAndDataClient = new FormAndDataSoapBindingStub(serviceURL, locate); formAndDataClient.setUsername("administrator"); formAndDataClient.setPassword("password");

Next, you need to populate the input variables. In this case there is only one object: a com.adobe.idp.Document input. Unfortunately, there is no Document object in the SOAP description, so a more general-purpose BLOB object is substituted. You can populate the BLOB's binary data with a byte array using the setBinaryData method.

//populate the input variable BLOB xmlAttachment = new BLOB(); byte[] xmlData = getUploadedData(request); xmlAttachment.setBinaryData(xmlData);

The setBinaryData method will perform the Base64 encoding for you.

The LiveCycle ES service can now be called. The result is another BLOB object from which you can retrieve binary data using the getBinaryData method.

//call the process BLOB resultDoc = formAndDataClient.invoke(xmlAttachment); byte[] resultData = resultDoc.getBinaryData();

Method 3: Using generated proxy classes and DIME/MIME attachments

The code for the Base64 encoding is quite simple, however; all of that encoding and decoding on both the server and client side can be a performance drain—especially if the data file sizes are large. Fortunately, LiveCycle ES supports both Direct Internet Message Encapsulation(DIME) and Multipurpose Internet Mail Extensions (MIME). SOAP attachments which are much more efficient than Base64 encoding.

The configuration of the service connection is the same as with Base64 encoding, except the BLOB type is specified as dime or mime.

//create a service locator FormAndDataServiceLocator locate = new FormAndDataServiceLocator(); //specify the service target URL and object type URL serviceURL = new URL("http://tranquility:8080/soap/services/FormAndData?blob=dime"); //Use the binding stub with the locator FormAndDataSoapBindingStub formAndDataClient = new FormAndDataSoapBindingStub(serviceURL, locate); formAndDataClient.setUsername("administrator"); formAndDataClient.setPassword("password");

Next, you must add the attachment. To do this, you create a DataHandler object, populate it with the binary data and then attach it to the SOAP message. This will give you an attachment ID. You then tell LiveCycle ES what attachment goes with what parameter by using the passing the attachment ID to the BLOB object.

//Do the DIME attachment byte[] uploadedFile = getUploadedData(request); DataHandler buildFile = new DataHandler(uploadedFile, "application/xml"); AttachmentPart part = new AttachmentPart(buildFile); //get the attachment ID String attachmentID = part.getContentId(); //add the attachment formAndDataClient.addAttachment(part); //Tell LC ES where the attachment is stored by providing the attachment id BLOB xmlAttachment = new BLOB(); xmlAttachment.setAttachmentID(attachmentID);

Once the attachments are made, you can make the call to LiveCycle ES.

//call the process BLOB resultDoc = formAndDataClient.invoke(xmlAttachment);

The result will be another BLOB object. The BLOB contains the reference to the attachment, not the binary data itself. You can use the returned attachment ID to locate the proper attachment in the message. The resulting binary object can then be read into a byte array for later use.

//go through the retuned attachments and get the PDF byte[] resultByte = null; attachmentID = resultDoc.getAttachmentID(); //now find the proper attachment Object[] parts = formAndDataClient.getAttachments(); for (int i=0;i<parts.length;i++){ AttachmentPart attPart = (AttachmentPart) parts[i]; //get the content of the attachment that matches the BLOB's attachment id if (attPart.getContentId().equals(attachmentID)) { buildFile = attPart.getDataHandler(); InputStream stream = buildFile.getInputStream(); resultByte = newbyte[stream.available()]; stream.read(resultByte); } }

Where to go from here

For more information on this topic, refer to the official Adobe documentation for more information.