Article summary

Summary
Discusses how to create an Adobe Experience Manager custom service that is able to modify PDF documents. The custom service is developed by using Apache PDFBOX Java API.    
Digital Marketing Solution(s) Adobe Experience Manager (Adobe CQ)
Audience
Developer (intermediate)
Required Skills
Java, OSGi Maven, HTML
Version Adobe Experience Manager 5.5, 5.6

Note:

You can download an AEM package that contains code and the two OSGi bundles that are used in this article. Download the package and deploy using package manager. The purpose of this code is to show the communtiy these concepts in action. That is, it's to illustrate how to write an OSGi bundle that uses the PDFBOX open source API to build an AEM service that can perform PDF operations. In this example, the service creates a basic PDF document and uses the AssetManager API to store it into the AEM DAM.

This community code is for teaching purposes only and not meant to go into production as is. You can view the sample community application by using the following URL: http://localhost:4502/cf#/content/pdfApp.html (assuming you deploy on author). 

Before running this example, create a folder in the AEM DAM named pdf located at /content/dam/

Download

Introduction

You can create a custom AEM service that  creates and modifies PDF documents. For example, consider a use case where a user submits data to Adobe Experience Manager (AEM). A custom service creates a PDF document, writes the data to the newly created document, and saves the document in the AEM Digital Asset Manager (DAM). This development article walks you through the process of creating a custom AEM service that is able to perform this use case by using Apache PDF BOX API. For information about this API, see http://pdfbox.apache.org/.

Note:

If you are an Adobe enterprise customer that has both Adobe Experience Manager and LiveCycle ES, then you have access to PDF operations using LiveCycle ES. You do not have to create a custom AEM service that can modify PDF documents. For information, see http://helpx.adobe.com/adobe-cq/using/integrating-livecycle-cq-applications.html.

In this example, a custom AEM service is created by using the PDFBOX API. Data is sent to the AEM service. The PDF service dynamically creates a PDF document, writes the data in the document and saves the document in the AEM DAM, as shown in the following illustration.

DAM

Note:

Before following along with this development article, create a folder in the AEM DAM named pdf located at /content/dam/. For information, see Managing Digital Assets.

Application logic that persists the PDF document into the AEM DAM is implemented as an OSGi bundle that is built using Declarative Services (DS) and Maven. DS is used to inject a ResourceResolverFactory instance into the service. The OSGi bundle is a managed component, which means that the OSGi service container creates the ResourceResolverFactory instance.

To create an AEM application that invokes a custom AEM service that dynamically creates a PDF document and saves it in the DAM, perform these tasks:

Create an Experience Manager application folder structure  

Create an application folder structure that contains templates, components, and pages by using CRXDE Lite.  

CQAppSetup
An application folder structure

The following describes each application folder:

  • application name: contains all of the resources that an application uses. The resources can be templates, pages, components, and so on.
  • components: contains components that your application uses.
  • page: contains page components. A page component is a script such as a JSP file.
  • global: contains global components that your application uses.
  • template: contains templates on which you base page components.
  • src: contains source code that comprises an OSGi component (this development article does not create an OSGi bundle using this folder).
  • install: contains a compiled OSGi bundles container.

To create an application folder structure:

1. To view the welcome page, enter the URL http://[host name]:[port] into a web browser. For example, http://localhost:4502.

2. Select CRXDE Lite.

3. Right-click the apps folder (or the parent folder), select Create, Create Folder.

4. Enter the folder name into the Create Folder dialog box. Enter pdfpersist

5. Repeat steps 1-4 for each folder specified in the previous illustration. 

6. Click the Save All button.

Note:

You have to click the Save All button when working in CRXDELite for the changes to be made.  

Create a template

You can create a template by using CRXDE Lite. An Experience Manager template enables you to define a consistent style for the pages in your application. A template comprises of nodes that specify the page structure. For more information about templates, see Templates.

To create a template, perform these tasks:

1. To view the welcome page, enter the URL http://[host name]:[port] into a web browser. For example, http://localhost:4502.
2. Select CRXDE Lite.
3. Right-click the template folder (within your application), select Create, Create
Template.
4. Enter the following information into the Create Template dialog box:

  • Label: The name of the template to create. Enter templatePDF.
  • Title: The title that is assigned to the template.
  • Description: The description that is assigned to the template.
  • Resource Type: The component's path that is assigned to the template and copied to implementing pages. Enter pdfpersist/components/page/templatePDF.
  • Ranking: The order (ascending) in which this template will appear in relation to other templates. Setting this value to 1 ensures that the template appears first in the list.

5. Add a path to Allowed Paths. Click on the plus sign and enter the following value: /content(/.*)?.
6. Click Next for Allowed Parents.
7. Select OK on Allowed Children.

Create a render component that uses the template  

Components are re-usable modules that implement specific application logic to render the content of your web site. You can think of a component as a collection of scripts (for example, JSPs, Java servlets, and so on) that completely realize a specific function. In order to realize this functionality, it is your responsibility as an Experience Manager developer to create scripts that perform specific functionality. For more information about components, see Components.

By default, a component has at least one default script, identical to the name of the component. To create a render component, perform these tasks:

1. To view the welcome page, enter the URL http://[host name]:[port] into a web browser. For example, http://localhost:4502.
2. Select CRXDE Lite.
3. Right-click /apps/pdfpersist/components/page, then select
Create, Create Component.
4. Enter the following information into the Create Component dialog box:

  • Label: The name of the component to create. Enter templatePDF
  • Title: The title that is assigned to the component.
  • Description: The description that is assigned to the template.

5. Select Next for Advanced Component Settings and Allowed Parents.
6. Select OK on Allowed Children.
7. Open the templateJCR.jsp located at: /apps/pdfpersist/components/page/templatePDF/templatePDF.jsp.
8. Enter the following JSP code.  

<html>
<head>
<title>Hello World !!!</title>
</head>
<body>
<h1>Hello PDF Service!!</h1>
<h2>This page will let a user submit data to a custom AEM PDF Service that creates a PDF Document and writes the data in the PDF document</h2>
</body>
</html>

Setup Maven in your development environment 

You can use Maven to build an OSGi bundle that uses the Java Excel API and the JCR API. Maven manages required JAR files that a Java project needs in its class path. Instead of searching the Internet trying to find and download third-party JAR files to include in your project’s class path, Maven manages these dependencies for you.

You can download Maven 3 from the following URL:

http://maven.apache.org/download.html

After you download and extract Maven, create an environment variable named M3_HOME. Assign the Maven install location to this environment variable. For example:

C:\Programs\Apache\apache-maven-3.0.4

Set up a system environment variable to reference Maven. To test whether you properly setup Maven, enter the following Maven command into a command prompt:

%M3_HOME%\bin\mvn -version

This command provides Maven and Java install details and resembles the following message:

Java home: C:\Programs\Java64-6\jre
Default locale: en_US, platform encoding: Cp1252
OS name: "windows 7", version: "6.1", arch: "amd64", family: "windows"

 

Note:

It is recommended that you use apache-maven-3.0.4. For more information about setting up Maven and the Home variable, see: Maven in 5 Minutes.

Next, copy the Maven configuration file named settings.xml from [install location]\apache-maven-3.0.4\conf\ to your user profile. For example, C:\Users\scottm\.m2\.

You have to configure your settings.xml file to use Adobe’s public repository. For information, see Adobe Public Maven Repository at http://repo.adobe.com/.

Create an Adobe Experience Manager archetype project  

You can create an Experience Manager archetype project by using the Maven archetype plugin. In this example, assume that the working directory is C:\AdobeCQ. 

plugin1

 

To create an Experience Manager archetype project, perform these steps:

1. Open the command prompt and go to your working directory (for example, C:\AdobeCQ).

2. Run the following Maven command:
mvn archetype:generate -DarchetypeGroupId=com.day.jcr.vault -DarchetypeArtifactId=multimodule-content-package-archetype -DarchetypeVersion=1.0.0 -DarchetypeRepository=adobe-public-releases

3. When prompted for additional information, specify these values:

  • groupId: com.aem.pdf.sample
  • artifactId: PDFBox
  • version: 1.0-SNAPSHOT
  • package: com.aem.pdf.sample
  • appsFolderName: customer-training
  • artifactName: Customer Training Package
  • packageGroup: adobe training
  • confirm: Y

4. Once done, you will see a message like:
[INFO] Finished at: Wed Mar 27 13:38:58 EDT 2013
[INFO] Final Memory: 10M/184M

5. Change the command prompt to the generated project. For example: C:\AdobeCQ\PDFBox. Run the following Maven command: mvn eclipse:eclipse.

After you run this command, you can import the project into Eclipse as discussed in the next section.

Add Java files to the Maven project using Eclipse 

To make it easier to work with the Maven generated project, import it into the Eclipse development environment, as shown in the following illustration.  

PDFBox

 

The next step is to add Java files to the com.aem.pdf.sample package. The Java files that you create in this section uses both the Java PDFBOX API and the JCR API. The PDFBOX API lets you dynamically create a PDF document (and other PDF document operations). For information, see http://pdfbox.apache.org/.

Create a Java interface named PDFService, as shown in this code example.  

package com.aem.pdf.sample;

public interface PDFService {
	
	public String createPDF(String filename, String value) ; 

}

Create an implementation class named PDFServiceImpl that implements PDFService. The PDFServiceImpl class uses the following Apache Felix SCR annotations to create the OSGi component:

  • @Component – defines the class as a component
  • @Service - defines the service interface that is provided by the component
  • @Reference – injects a service into the component.  

In this development article, a ResourceResolverFactory instance is injected into the writeToDam method. This instance is required to create a Session instance that lets you persist the PDF file in the AEM DAM. To inject a ResourceResolverFactory instance, you use the @Reference annotation to define a class member, as shown in the following example.

//Inject a Sling ResourceResolverFactory
@Reference
private ResourceResolverFactory resolverFactory;

Within the writeToDam method uses the AssetManager API to place the dynamically created PDF into the AEM DAM, as shown here..

 

//Save the uploaded file into the AEM DAM
private String writeToDam(InputStream is, String fileName)
{
try
{
//Invoke the adaptTo method to create a Session used to create a QueryManager
ResourceResolver resourceResolver = resolverFactory.getAdministrativeResourceResolver(null);

//Use AssetManager to place the file into the AEM DAM
com.day.cq.dam.api.AssetManager assetMgr = resourceResolver.adaptTo(com.day.cq.dam.api.AssetManager.class);
String newFile = "/content/dam/pdf/"+fileName ; 
assetMgr.createAsset(newFile, is, "application/pdf", true);

log.info("THE PDF Asset was placed into the DAM"); 


// Return the path to the document that was stored in CRX. 
return newFile;
}

The createPDF method dynamically creates a PDF document using the PDFBOX API. The following Java code represents the PDFServiceImpl class.

package com.aem.pdf.sample;
 
import java.util.Calendar; 
import org.w3c.dom.Document;
import org.w3c.dom.Element;
   
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.StringWriter;
import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;
import java.io.InputStream;
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
   
   
import javax.jcr.Repository; 
import javax.jcr.SimpleCredentials; 
import javax.jcr.Node; 
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
    
import org.apache.jackrabbit.commons.JcrUtils;
   
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
   
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Service;
import javax.jcr.RepositoryException;
import org.apache.felix.scr.annotations.Reference;
import org.apache.jackrabbit.commons.JcrUtils;
   
import javax.jcr.Session;
import javax.jcr.Node;  
 
//PDFBOX 
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage; 
import org.apache.pdfbox.pdmodel.font.PDFont; 
import org.apache.pdfbox.pdmodel.edit.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDType1Font;
import org.apache.pdfbox.pdmodel.common.PDStream; 
 
//Sling Imports
import org.apache.sling.api.resource.ResourceResolverFactory ; 
import org.apache.sling.api.resource.ResourceResolver; 
import org.apache.sling.api.resource.Resource; 

//AssetManager
import com.day.cq.dam.api.AssetManager; 
//This is a component so it can provide or consume services
@Component
  
@Service
public class PDFServiceImpl implements PDFService {
 
     
/** Default log. */
protected final Logger log = LoggerFactory.getLogger(this.getClass());
          
private Session session;
              
//Inject a Sling ResourceResolverFactory
@Reference
private ResourceResolverFactory resolverFactory;
     
@Override
public String createPDF(String filename,String value) {
// This custom AEM service creates a PDF document using PDFBOX API and stores the PDF in the AEM JCR
         
try
{
    //Create the PDFBOx Object
    // Create a new empty document
    PDDocument document = new PDDocument();
 
    // Create a document and add a page to it
    PDPage page = new PDPage();
    document.addPage( page );
    
    log.info("GOT HERE");
 
    // Create a new font object selecting one of the PDF base fonts
    PDFont font = PDType1Font.HELVETICA_BOLD;
 
    // Start a new content stream which will "hold" the to be created content
    PDPageContentStream contentStream = new PDPageContentStream(document, page);
 
    // Define a text content stream using the selected font, moving the cursor and drawing the text "Hello World"
    contentStream.beginText();
    contentStream.setFont( font, 12 );
    contentStream.moveTextPositionByAmount( 100, 700 );
    contentStream.drawString( value );
    contentStream.endText();
 
    // Make sure that the content stream is closed:
    contentStream.close();
             
    //Save the PDF into the AEM DAM
    ByteArrayOutputStream out = new ByteArrayOutputStream();
                 
    document.save(out);
                 
    byte[] myBytes = out.toByteArray(); 
        InputStream is = new ByteArrayInputStream(myBytes) ; 
    String damLocation = writeToDam(is,filename);
    document.close();
             
    //....
    return damLocation; 
    }
catch(Exception e)
{
    e.printStackTrace();
}
    return null;
}
     
     
//Save the uploaded file into the Adobe CQ DAM
private String writeToDam(InputStream is, String fileName)
{
    try
    {
    //Invoke the adaptTo method to create a Session used to create a QueryManager
    ResourceResolver resourceResolver = resolverFactory.getAdministrativeResourceResolver(null);
     
  //Use AssetManager to place the file into the AEM DAM
    com.day.cq.dam.api.AssetManager assetMgr = resourceResolver.adaptTo(com.day.cq.dam.api.AssetManager.class);
    String newFile = "/content/dam/pdf/"+fileName ; 
    assetMgr.createAsset(newFile, is, "application/pdf", true);
    
    log.info("THE PDF Asset was placed into the DAM");       
 
              
    // Return the path to the document that was stored in CRX. 
    return newFile;
}
catch(Exception e)
{
    e.printStackTrace();
}
return null; 
}
}

Adding the PDFBOX API to Adobe Experience Manager

If you are using AEM 5.5 or lower, the PDFBOX API is part of the Sling Service container. You can ignore this section. However, if you are using AEM 5.6, you have to add an OSGi bundle fragment that contains this library.

First, download the PDFBox JAR from the following URL:

http://pdfbox.apache.org/

To create an OSGi bundle fragment that contains the PDFBox classes, perform these tasks:

1. Start Eclipse (Indigo). The steps below have been tested on Eclipse Java EE IDE for Web Developers version Indigo Service Release 1.

2. Select File, New, Other.

3. Under the Plug-in Development folder, choose Plug-in from Existing JAR Archives. Name your project excelBundle.

4. In the JAR selection dialog, click the Add external button, and browse to the PDFBox JAR file that you downloaded.

5. Click Next.

6. In the Plug-in Project properties dialog, ensure that you check the checkbox for Analyze library contents and add dependencies.

7. Make sure that the Target Platform is the standard OSGi framework.

8. Ensure the checkboxes for Unzip the JAR archives into the project and Update references to the JAR files are both checked.

9. Click Next, and then Finish.

10. Click the Runtime tab.

11. Make sure that the Exported Packages list is populated.

12. Make sure these packages have been added under the Export-Package header in MANIFEST.MF. Remove the version information in the MANIFEST.MF file. Version numbers can cause conflicts when you upload the OSGi bundle.

13. Also make sure that the Import-Package header in MANIFEST.MF is also populated, as shown here (remove JUNIT packages too). Copy the packages from Export and Import sections.
Bundle-Name: PDFBOX
Bundle-SymbolicName: PDFBOX
Bundle-Version: 1.0.0
Export-Package: com.ibm.icu.impl,
com.ibm.icu.impl.data,
com.ibm.icu.impl.duration,
com.ibm.icu.impl.duration.impl,
com.ibm.icu.lang,
com.ibm.icu.math,
com.ibm.icu.text,
com.ibm.icu.util,
org.apache.commons.logging,
org.apache.commons.logging.impl,
org.apache.fontbox.afm,
org.apache.fontbox.cff,
org.apache.fontbox.cff.charset,
org.apache.fontbox.cff.encoding,
org.apache.fontbox.cmap,
org.apache.fontbox.encoding,
org.apache.fontbox.pfb,
org.apache.fontbox.ttf,
org.apache.fontbox.util,
org.apache.jempbox.impl,
org.apache.jempbox.xmp,
org.apache.jempbox.xmp.pdfa,
org.apache.pdfbox,
org.apache.pdfbox.cos,
org.apache.pdfbox.encoding,
org.apache.pdfbox.encoding.conversion,
org.apache.pdfbox.encryption,
org.apache.pdfbox.exceptions,
org.apache.pdfbox.filter,
org.apache.pdfbox.io,
org.apache.pdfbox.io.ccitt,
org.apache.pdfbox.pdfparser,
org.apache.pdfbox.pdfviewer,
org.apache.pdfbox.pdfwriter,
org.apache.pdfbox.pdmodel,
org.apache.pdfbox.pdmodel.common,
org.apache.pdfbox.pdmodel.common.filespecification,
org.apache.pdfbox.pdmodel.common.function,
org.apache.pdfbox.pdmodel.common.function.type4,
org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure,
org.apache.pdfbox.pdmodel.documentinterchange.markedcontent,
org.apache.pdfbox.pdmodel.documentinterchange.prepress,
org.apache.pdfbox.pdmodel.documentinterchange.taggedpdf,
org.apache.pdfbox.pdmodel.edit,
org.apache.pdfbox.pdmodel.encryption,
org.apache.pdfbox.pdmodel.fdf,
org.apache.pdfbox.pdmodel.font,
org.apache.pdfbox.pdmodel.graphics,
org.apache.pdfbox.pdmodel.graphics.color,
org.apache.pdfbox.pdmodel.graphics.optionalcontent,
org.apache.pdfbox.pdmodel.graphics.pattern,
org.apache.pdfbox.pdmodel.graphics.predictor,
org.apache.pdfbox.pdmodel.graphics.shading,
org.apache.pdfbox.pdmodel.graphics.xobject,
org.apache.pdfbox.pdmodel.interactive.action,
org.apache.pdfbox.pdmodel.interactive.action.type,
org.apache.pdfbox.pdmodel.interactive.annotation,
org.apache.pdfbox.pdmodel.interactive.digitalsignature,
org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination,
org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline,
org.apache.pdfbox.pdmodel.interactive.form,
org.apache.pdfbox.pdmodel.interactive.measurement,
org.apache.pdfbox.pdmodel.interactive.pagenavigation,
org.apache.pdfbox.pdmodel.interactive.viewerpreferences,
org.apache.pdfbox.pdmodel.markedcontent,
org.apache.pdfbox.pdmodel.text,
org.apache.pdfbox.persistence.util,
org.apache.pdfbox.util,
org.apache.pdfbox.util.operator,
org.apache.pdfbox.util.operator.pagedrawer,
org.bouncycastle,
org.bouncycastle.asn1,
org.bouncycastle.asn1.cmp,
org.bouncycastle.asn1.cms,
org.bouncycastle.asn1.cms.ecc,
org.bouncycastle.asn1.crmf,
org.bouncycastle.asn1.cryptopro,
org.bouncycastle.asn1.eac,
org.bouncycastle.asn1.esf,
org.bouncycastle.asn1.ess,
org.bouncycastle.asn1.gnu,
org.bouncycastle.asn1.iana,
org.bouncycastle.asn1.icao,
org.bouncycastle.asn1.isismtt,
org.bouncycastle.asn1.isismtt.ocsp,
org.bouncycastle.asn1.isismtt.x509,
org.bouncycastle.asn1.kisa,
org.bouncycastle.asn1.microsoft,
org.bouncycastle.asn1.misc,
org.bouncycastle.asn1.mozilla,
org.bouncycastle.asn1.nist,
org.bouncycastle.asn1.ntt,
org.bouncycastle.asn1.ocsp,
org.bouncycastle.asn1.oiw,
org.bouncycastle.asn1.pkcs,
org.bouncycastle.asn1.sec,
org.bouncycastle.asn1.smime,
org.bouncycastle.asn1.teletrust,
org.bouncycastle.asn1.tsp,
org.bouncycastle.asn1.util,
org.bouncycastle.asn1.x500,
org.bouncycastle.asn1.x509,
org.bouncycastle.asn1.x509.qualified,
org.bouncycastle.asn1.x509.sigi,
org.bouncycastle.asn1.x9,
org.bouncycastle.cms,
org.bouncycastle.crypto,
org.bouncycastle.crypto.agreement,
org.bouncycastle.crypto.agreement.kdf,
org.bouncycastle.crypto.agreement.srp,
org.bouncycastle.crypto.digests,
org.bouncycastle.crypto.encodings,
org.bouncycastle.crypto.engines,
org.bouncycastle.crypto.examples,
org.bouncycastle.crypto.generators,
org.bouncycastle.crypto.io,
org.bouncycastle.crypto.macs,
org.bouncycastle.crypto.modes,
org.bouncycastle.crypto.modes.gcm,
org.bouncycastle.crypto.paddings,
org.bouncycastle.crypto.params,
org.bouncycastle.crypto.prng,
org.bouncycastle.crypto.signers,
org.bouncycastle.crypto.tls,
org.bouncycastle.crypto.util,
org.bouncycastle.i18n,
org.bouncycastle.i18n.filter,
org.bouncycastle.jce,
org.bouncycastle.jce.examples,
org.bouncycastle.jce.exception,
org.bouncycastle.jce.interfaces,
org.bouncycastle.jce.netscape,
org.bouncycastle.jce.provider,
org.bouncycastle.jce.provider.asymmetric,
org.bouncycastle.jce.provider.asymmetric.ec,
org.bouncycastle.jce.provider.symmetric,
org.bouncycastle.jce.provider.util,
org.bouncycastle.jce.spec,
org.bouncycastle.mail.smime,
org.bouncycastle.mail.smime.examples,
org.bouncycastle.mail.smime.handlers,
org.bouncycastle.mail.smime.util,
org.bouncycastle.mail.smime.validator,
org.bouncycastle.math.ec,
org.bouncycastle.mozilla,
org.bouncycastle.ocsp,
org.bouncycastle.openssl,
org.bouncycastle.sasn1,
org.bouncycastle.sasn1.cms,
org.bouncycastle.util,
org.bouncycastle.util.encoders,
org.bouncycastle.util.io,
org.bouncycastle.util.test,
org.bouncycastle.voms,
org.bouncycastle.x509,
org.bouncycastle.x509.examples,
org.bouncycastle.x509.extension,
org.bouncycastle.x509.util
Import-Package: javax.imageio.metadata,
javax.print.attribute.standard,
javax.xml.transform,
javax.servlet,
javax.xml.transform.dom,
javax.print,
javax.imageio.stream,
javax.swing,
org.w3c.dom,
javax.activation,
javax.swing.filechooser,
javax.imageio,
javax.xml.datatype,
org.apache.log4j,
javax.naming,
javax.swing.tree,
javax.crypto.spec,
javax.swing.border,
javax.naming.directory,
javax.xml.parsers,
javax.crypto.interfaces,
javax.crypto,
javax.swing.event,
javax.xml.transform.stream,
javax.security.auth.x500,
org.xml.sax
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
ckage is jxl).

14. Save the project.

15. Build the OSGi bundle by right-clicking the project in the left pane, choose Export, Plug-in Development, Deployable plug-ins and fragments, and click Next.

16. Select a location for the export (C:\TEMP) and click Finish. (Ignore any error messages).

17. In C:\TEMP\plugins, you should now find the OSGi bundle.

18. Login to Apache Felix Web Console at http://server:port/system/console/bundles (default admin user = admin with password= admin).

19. Sort the bundle list by Id and note the Id of the last bundle.

20. Click the Install/Update button.

21. Check the Start Bundle checkbox.

22. Browse to the bundle JAR file you just built. (C:\TEMP\plugins).

23. Click Install.

24. Click the Refresh Packages button.

25. Check the bundle with the highest Id.

26. Your new bundle should now be listed with the status Active.

27. If the status is not Active, check the error.log for exceptions. If you get “org.osgi.framework.BundleException: Unresolved constraint” errors, check the MANIFEST.MF for strict version requirements which might follow: javax.xml.namespace; version=”3.1.0”

28. If the version requirement causes problems, remove it so that the entry looks like this: javax.xml.namespace.

29. If the entry is not required, remove it entirely.

30. Rebuild the bundle.

31. Delete the previous bundle and deploy the new one.

Modify the Maven POM file

Modify the POM files to successfully build the OSGi bundle. In the POM file located at C:\AdobeCQ\PDFBOX\bundle, add the following dependencies.

  • org.apache.pdfbox
  • org.apache.felix.scr
  • org.apache.felix.scr.annotations
  • org.apache.jackrabbit
  • org.apache.sling

The following XML represents this POM file.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd ">
    <modelVersion>4.0.0</modelVersion>
    <!-- ====================================================================== -->
    <!-- P A R E N T P R O J E C T D E S C R I P T I O N -->
    <!-- ====================================================================== -->
    <parent>
        <groupId>com.aem.pdf.sample</groupId>
        <artifactId>PDFBox2</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <!-- ====================================================================== -->
    <!-- P R O J E C T D E S C R I P T I O N -->
    <!-- ====================================================================== -->

    <artifactId>PDFBox2-bundle</artifactId>
    <packaging>bundle</packaging>
    <name>PDFBox2 Bundle</name>

    <!-- ====================================================================== -->
    <!-- B U I L D D E F I N I T I O N -->
    <!-- ====================================================================== -->
    <build>

        <plugins>
            <plugin>
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-scr-plugin</artifactId>
                <executions>
                    <execution>
                        <id>generate-scr-descriptor</id>
                        <goals>
                            <goal>scr</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-bundle-plugin</artifactId>
                <extensions>true</extensions>
                <configuration>
                    <instructions>
                        <Bundle-SymbolicName>com.aem.pdf.sample.PDFBox2-bundle</Bundle-SymbolicName>
                    </instructions>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.sling</groupId>
                <artifactId>maven-sling-plugin</artifactId>
                <configuration>
                    <slingUrl>http://${crx.host}:${crx.port}/apps/PDFBOX2/install</slingUrl>
                    <usePut>true</usePut>
                </configuration>
            </plugin>
        </plugins>
    </build>

     <dependencies>
     
        <dependency>
          <groupId>org.apache.pdfbox</groupId>
          <artifactId>pdfbox</artifactId>
          <version>1.6.0</version>
        </dependency>
     
        <dependency>
            <groupId>org.osgi</groupId>
            <artifactId>org.osgi.compendium</artifactId>
        </dependency>
        <dependency>
            <groupId>org.osgi</groupId>
            <artifactId>org.osgi.core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.felix</groupId>
            <artifactId>org.apache.felix.scr.annotations</artifactId>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
        </dependency>
         
          
        <dependency>
         <groupId>org.apache.felix</groupId>
   
         <artifactId>org.osgi.core</artifactId>
   
         <version>1.4.0</version>
      </dependency>
         
        
          
    <dependency>
    <groupId>org.apache.jackrabbit</groupId>
    <artifactId>jackrabbit-core</artifactId>
    <version>2.4.3</version>
    </dependency>
       
    <dependency>
    <groupId>org.apache.jackrabbit</groupId>
    <artifactId>jackrabbit-jcr-commons</artifactId>
    <version>2.4.3</version>
    </dependency>
   <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
           
    <dependency>
            <groupId>org.apache.sling</groupId>
            <artifactId>org.apache.sling.api</artifactId>
            <version>2.2.4</version>
            <scope>provided</scope>
        </dependency>
         
      <dependency>
         <groupId>javax.jcr</groupId>
         <artifactId>jcr</artifactId>
         <version>2.0</version>
      </dependency>
       
       <dependency>
            <groupId>com.day.cq.wcm</groupId>
            <artifactId>cq-wcm-api</artifactId>
            <version>5.5.0</version>
            <scope>provided</scope>
        </dependency>
         
        <dependency>
            <groupId>com.day.cq</groupId>
            <artifactId>cq-commons</artifactId>
            <version>5.5.0</version>
            <scope>provided</scope>
        </dependency>
        
        <dependency>
	<groupId>com.day.cq.dam</groupId>
	<artifactId>cq-dam-api</artifactId>
	<version>5.6.4</version>
	<scope>provided</scope>
</dependency>
               
    </dependencies>
     
    <repositories>
        <repository>
            <id>adobe</id>
            <name>Adobe Public Repository</name>
            <url>http://repo.adobe.com/nexus/content/groups/public/</url>
            <layout>default</layout>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>adobe</id>
            <name>Adobe Public Repository</name>
            <url>http://repo.adobe.com/nexus/content/groups/public/</url>
            <layout>default</layout>
        </pluginRepository>
    </pluginRepositories>       
      
</project>

Build the OSGi bundle using Maven

Build the OSGi bundle by using Maven. When you build the OSGi bundle, Maven creates the required serviceComponents.xml file based on the annotations that are included in the PDFServiceImpl class. The following XML represents this file.

<?xml version="1.0" encoding="UTF-8"?>
<components xmlns:scr="http://www.osgi.org/xmlns/scr/v1.0.0">
    <scr:component enabled="true" name="com.aem.pdf.sample.PSFServiceImpl">
        <implementation class="com.aem.pdf.sample.PSFServiceImpl"/>
        <service servicefactory="false">
            <provide interface="com.aem.pdf.sample.PDFService"/>
        </service>
        <property name="service.pid" value="com.aem.pdf.sample.PSFServiceImpl"/>
        <reference name="resolverFactory" interface="org.apache.sling.api.resource.ResourceResolverFactory" cardinality="1..1" policy="static" bind="bindResolverFactory" unbind="unbindResolverFactory"/>
    </scr:component>
    <scr:component enabled="true" name="com.aem.pdf.sample.SimpleDSComponent">
        <implementation class="com.aem.pdf.sample.SimpleDSComponent"/>
        <service servicefactory="false">
            <provide interface="java.lang.Runnable"/>
        </service>
        <property name="service.pid" value="com.aem.pdf.sample.SimpleDSComponent"/>
    </scr:component>
</components>

There are a couple of points to note about this XML file. First, notice that the implementation class element specifies com.aem.pdf.sample.PDFServiceImpl. The service element contains an interface attribute that specifies com.aem.pdf.sample.PDFService. This corresponds to the Java interface that was created in an earlier step.

To build the OSGi component by using Maven, perform these steps:

  1. Open the command prompt and go to the C:\AdobeCQ\PDFBOX folder.
  2. Run the following maven command: mvn clean install.
  3. The OSGi component can be found in the following folder: C:\AdobeCQ\PDFBox\bundle\target. The file name of the OSGi component is PDFBox-bundle-1.0-SNAPSHOT.jar.

Deploy the bundle to Experience Manager

Once you deploy the OSGi bundle, you are able to invoke the createPDF method defined in the PDFServiceImpl class (this is shown later in this development article). After you deploy the OSGi bundle, you will be able to see it in the Apache Felix Web Console.

OSGi

Note:

If you are using AEM 5.6, make sure that the OSGi fragment that contains the PDFBox JAR file is in an active state before placing the OSGi bundle that contains the custom service into an active state.

Deploy the OSGi bundle by performing these steps:

  1. Login to the Apache Felix Web Console at http://server:port/system/console/bundles (default admin user = admin with password= admin).
  2. Click the Bundles tab, sort the bundle list by Id, and note the Id of the last bundle.
  3. Click the Install/Update button.
  4. Browse to the bundle JAR file you just built using Maven. (C:\AdobeCQ\PDFBox\bundle\target).
  5. Click Install.
  6. Click the Refresh Packages button.
  7. Check the bundle with the highest Id.
  8. Click Active. Your new bundle should now be listed with the status Active.
    If the status is not Active, check the error.log for exceptions.

Modify the templatePDF JSP to invoke the custom PDF service  

Modify the templatePDF.jsp file to invoke the createPDF method that is defined by the PDFService. The first argument is the name of the PDF file to create. The second argument is the string to write to the PDF document.

To invoke this service, you call sling.getService method as shown in the following example.

//create a PDFService instance
com.aem.pdf.sample.PDFService pdfService = sling.getService(com.aem.pdf.sample.PDFService.class);

The following code represents the templatePDF JSP page.

<%@include file="/libs/foundation/global.jsp"%>
<%@taglib prefix="cq" uri="http://www.day.com/taglibs/cq/1.0" %>
<h1><%= properties.get("title", currentPage.getTitle()) %></h1>
<%
//create a PDFService instance
com.aem.pdf.sample.PDFService pdfService = sling.getService(com.aem.pdf.sample.PDFService.class);

%>
<h2>About to write out the location of the PDF created by the custom AEM PDF Service</h2>
 
<h3>The PDF was saved in this AEM DAM location: <%=  pdfService.createPDF("TestDAM.pdf","This string will be written to the PDF")%></h3>

Modify the templatePDF JSP file:

1. To view the welcome page, enter the URL: http://[host name]:[port] into a web browser. For example, http://localhost:4502.

2. Select CRXDE Lite.

3. Double-click /apps/pdfpersist/components/page/templatePDF/templatePDF.jsp.

4. Replace the JSP code with the new code shown in this section.

5. Click Save All.

Create a web page that lets users invoke the custom PDF service

The final task is to create a site that contains a page that is based on the templatePDF (the template created earlier in this development article). When the custom PDF service is invoked, a PDF document is created and the document is stored in the AEM DAM at /content/dam/pdf/.

DAMVIEW

Note:

Before starting this example, create a folder in the AEM DAM named pdf located at /content/dam/. For information about creating a folder in the AEM DAM, see Managing Digital Assets.

web2

Create a web page that is based on the templatePDF template.

1. Go to the welcome page at http://[host name]:[port]; for example, http://localhost:4502.
Select Websites.

2. From the left hand pane, select Websites.

3. Select New Page.

4. Specify the title of the page in the Title field.

5. Specify the name of the page in the Name field.

6. Select templatePDF from the template list that appears. This value represents the template that is created in this development article. If you do not see it, then repeat the steps in this development article. For example, if you made a typing mistake when entering in path information, the template will not show up in the New Page dialog box.

6. Open the new page that you created by double-clicking it in the right pane. The new page opens in a web browser.  

This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License  Twitter™ and Facebook posts are not covered under the terms of Creative Commons.

Legal Notices   |   Online Privacy Policy