Article summary

Summary
Discusses how to create an Adobe Experience Manager application that is able to query data from MySQL as well as persist data back to the database. This article creates an OSGi bundle and injects a DataSourcePool into it. In addition, discusses how to invoke an OSGi bundle operation from the client web page.
Digital Marketing Solution(s) Adobe Experience Manager (Adobe CQ)
Audience
Developer (intermediate)
Required Skills
Java, JQuery, JDBC, SQL, AJAX, CSS, HTML
Version Adobe Experience Manager 5.5, 5.6, 6.0
Video https://youtu.be/yYo1epoZp_4

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 community these concepts in action. That is, it's to illustrate how to write an OSGi bundle that contains a service into which a DataSourcePool is injected. The service is able to retrieve data into MySQL as well as persist data back into the database.

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/content/customerDataSourcePool.html (assuming you deploy on author). 

Before running this example CQ application, you must configure a local MySQL database and add a database schema named cq. Next add a table named customer (the table structure is specified in this article). Finally, configure the DataSourcePool as described in this article. 

* Customer-DataSourcePool-1.0.zip
The AEM DataSourcePool example package for AEM 5.6
* Customer-DataSourcePool_aem6-1.0.zip
The AEM DataSourcePool example package for AEM 6.x

Introduction

This development article discusses how to inject a DataSourcePool service into an OSGi component. By injecting a DataSourcePool, your OSGi bundle can connect to a relational database such as MySQL. You configure a DataSourcePool using Adobe Experience Manager.

DataPoolConfig

As shown in the previous illustration, a DataSourcePool named Customer is configured. This article walks you through how to configure a DataSourcePool, how to inject a DataSourcePool into an OSGi bundle, and then how to invoke operations of the OSGi bundle by using a CQ web page. In this development article, the OSGi bundle is created by using Maven.   

Note:

You can create an OSGi bundle that is able to connect to a relational database without using a DataSourcePool. In this situation, you have to write a Java class that uses the JDBC API to connect to the database. For information, see http://helpx.adobe.com/adobe-cq/using/querying-persisting-cq-data-mysql.html.

In this development article, the CQ web application queries and updates a MySQL database. That is, a user can create a new customer record that is stored in the Customer table. Likewise, a user can view all Customer records, as shown in the following illustration. 

MainApp

 

The Java JDBC API is used within an OSGi bundle that returns a data result set based on three options:

  1. All customers (both active and past customers are displayed as shown in the previous illustration).

  2. Active customers (only active customers are displayed).

  3. Past customers (only past customers are displayed).

The following represents the Customer table that belongs to a MySQL database named CQ. 

Field Name Field Type Key
custId An integer that specifies the customer identifier value. PK
custFirst A string value that specifies the customer’s first name. N/A
custLast A string value that specifies the customer’s last name. N/A
custDesc A string value that specifies the customer’s description. N/A
custAddress A string value that specifies the customer’s address or phone number. N/A
dbcust

Note:

 

Before following along with this development article, setup MySQL and create a database schema named CQ that contains the Customer table. See www.mysql.com.

Note:

Instead of querying and persisting data from MySQL, you can persist and query data from the Experience Manager JCR. For information, see Querying Adobe Experience Manager Data using the JCR API

Create an AEM application folder structure  

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

CQAppSetup

 

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 AEM application folder structure:

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

  2. Go to CRXDE Lite at http://localhost:4502/crx/de/index.jsp

  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 customer

  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 CRXDE Lite for the changes to be made.  

Create a template  

You can create a template by using CRXDE Lite. A CQ 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 CQ welcome page, enter the URL http://[host name]:[port] into a web browser. For example, http://localhost:4502.

  2. Go to CRXDE Lite at http://localhost:4502/crx/de/index.jsp.

  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 templateCustomer
    • 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 customer/components/page/templateCustomer.
    • 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 a CQ 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 CQ welcome page, enter the URL http://[host name]:[port] into a web browser. For example, http://localhost:4502.

  2. Go to CRXDE Lite at http://localhost:4502/crx/de/index.jsp.

  3. Right-click /apps/customer/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 templateCustomer
    • Title: The title that is assigned to the component.
    • Description: The description that is assigned to the template.
    • Super Type: Enter foundation/components/page (in AEM 6.x, you specify this value for page components. In previous versions of AEM, this was not required.)
  5. Select Next for Advanced Component Settings and Allowed Parents.

  6. Select OK on Allowed Children.

  7. Open the templateCustomer.jsp located at: /apps/customer/components/page/templateCustomer/templateCustomer.jsp.

  8. Enter the following JSP code.

<html>
<head>
<title>Hello World !!!</title>
</head>
<body>
<h1>Hello MySQL!!!</h1>
<h2>This page will query MySQL</h2>
</body>
</html>

Configure the DataSourcePool connection properties 

Add a configuration for the JDBC Connections Pool service that uses the JDBC driver to create data source objects. The OSGi bundle created in this development article uses this service to connect to the MySQL database. For information, see Connecting to SQL Databases.  

Config
DataSourcePool Configuration

To configure a DataSourcePool, peform these tasks:

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

  2. Click the Configuration tab..

  3. Click the + icon that appearts in the JDBC Configuration Pool row (in AEM 6.x, the row is titled Day Commons JDBC Connections Pool).

  4. Enter the following values:

    • JBDC Driver class - the driver class to use to connect to the database. To connect to MySQL, enter com.mysql.jdbc.Driver.
    • JDBC connection URI - the URI to the database. In this example, enter jdbc:mysql://localhost:3306/cq.
    • Username - the user name to use to connect to MySQL. 
    • Password - the corresponding password.
    • Datasource name - the datasource name. This is the value that you reference in the Java logic located in the OSGi bundle. In this example, enter Customer
  5. Click Save.

Setup Maven in your development environment 

You can use Maven to build an OSGi bundle that uses the QueryBuilder API and is deployed to Experience Manager. 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:

OS name: "windows 7", version: "6.1", arch: "amd64", family: "windows"

Note:

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 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 -DarchetypeRepository=https://repo.adobe.com/nexus/content/groups/public/ -DarchetypeGroupId=com.day.jcr.vault -DarchetypeArtifactId=multimodule-content-package-archetype -DarchetypeVersion=1.0.2 -DgroupId=com.adobe.cq.sql  -DartifactId=customerdb -Dversion=1.0-SNAPSHOT -Dpackage=com.adobe.cq.sql  -DappsFolderName=myproject -DartifactName="My Project" -DcqVersion="5.6.1" -DpackageGroup="My Company"

  3. When prompted, specify Y. 

    [INFO] Finished at: Wed Mar 27 13:38:58 EDT 2013
    [INFO] Final Memory: 10M/184M

  4. Change the command prompt to the generated project. For example: C:\AdobeCQ\customerdb. 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.

Note:

This archetype creates a single bundle project. If you are interested in Experience Manager Archetype 10, then see Creating your first AEM 6.2 Project using Adobe Maven Archetype 10.

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.

Java

 

The next step is to add Java files to the com.adobe.cq.sql package. The Java files that you create in this section use the Java JDBC API and also references the DataSourcePool that was configured earlier in this development article. For information about the Java JDBC API, see http://docs.oracle.com/javase/7/docs/technotes/guides/jdbc/.

Add the following Java files to the package named com.adobe.cq.sql:

  • A Java class named Customer that stores customer information.
  • A Java interface named CustomerService.
  • A Java class named CustomerServiceImp that implements the CustomerService interface.

Customer class 

The following code represents the Customer class that is used to store customer information. This class contains getter and setter methods that modify class members.

package com.adobe.cq.sql;

//This bean holds customer information
public class Customer {
     
    //Define private class members
    private String custId ;
    private String first;
    private String last;
    private String address;
    private String description;
     
     
    public void setCustId(String id)
    {
        this.custId = id;
    }
     
    public String getCustId()
    {
        return this.custId;
    }
     
    public void setCustFirst(String first)
    {
        this.first = first;
    }
     
    public String getCustFirst()
    {
        return this.first;
    }
     
    public void setCustLast(String last)
    {
        this.last = last;
    }
     
    public String getCustLast()
    {
        return this.last;
    }
     
    public void setCustAddress(String address)
    {
        this.address = address;
    }
     
    public String getCustAddress()
    {
        return this.address;
    }
     
    public void setCustDescription(String description)
    {
        this.description = description;
    }
     
    public String getCustDescription()
    {
        return this.description;
    }
 
}

CustomerService interface  

The following code represents the CustomerService interface. This interface contains two method signatures named injestCustData and getCustomerData. The implementation logic for these methods is located in the CustomerServiceImp class. The getCustomerData method uses the Java JDBC API to query customer data from the Customer table in MySQL. Likewise, the injectCustData uses the Java JDBC API to update the Customer table with a new customer record. 

package com.adobe.cq.sql;

public interface CustomerService {
	
	//Adds a new customer record in the Customer table
	public int injestCustData(String firstName, String lastName, String phone, String desc);
     
    /*
     * Retrieves customer data from the Customer table and returns all customer
     *The filter argument specifies one of the following values:
     *    
     *Customer - retrieves all customer data
     *Active Customer- retrieves current customers
     *Past Customer - retrieves old customers no longer current customers
     */
    public String getCustomerData(String filter); 

}

CustomerServiceImp class

The CustomerServiceImp 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

For information about Apache Felix SCR annotations, see http://felix.apache.org/documentation/subprojects/apache-felix-maven-scr-plugin/scr-annotations.html.

In this development article, a DataSourcePool instance is injected into the getConnection method. This method uses a DataSourcePool to return a Connection instance to the database. To inject a DataSourcePool instance, you use the @Reference annotation to define a class member, as shown in the following example.

Notice that DataSourcePool instance's getDataSource method is called and that the datasource named Customer is referenced. This is the name of the DataSourcePool that was configured earlier in this development article.  

@Reference
   private DataSourcePool source;


 //Returns a connection using the configured DataSourcePool 
 private Connection getConnection()
 {
 DataSource dataSource = null;
Connection con = null;
try
{
    //Inject the DataSourcePool right here! 
   dataSource = (DataSource) source.getDataSource("Customer");
   con = dataSource.getConnection();
    return con;
	           
      }
catch (Exception e)
{
	e.printStackTrace(); 
    }
return null; 
  }

The CustomerServiceImp class contains a method named getCustomerData that returns an XML schema that contains customer data within a string. The Java JDBC API is used to query the Customer table based on user input. In this example, the custDesc field of each Customer record stores one of these values:

  • Active Customer
  • Past Customer

Therefore the MySQL query is setup to query all customer records, only active customers, or only past customers. This is achieved by querying the custDesc field.

//Set the query and use a preparedStatement
if (filter.equals("All Customers"))
   query = "Select custId,custFirst,custLast,custAddress,custDesc FROM Customer";
else if(filter.equals("Past Customer"))
  query = "Select custId,custFirst,custLast,custAddress,custDesc FROM Customer where custDesc = 'Past Customer'; ";
else if(filter.equals("Active Customer"))
   query = "Select custId,custFirst,custLast,custAddress,custDesc FROM Customer where custDesc = 'Active Customer'; ";
               
 pstmt = c.prepareStatement(query); 
 rs = pstmt.executeQuery();

The getCustomerData method returns XML that contains customer data that is queried from the Customer table. This XML is parsed on the client web page and displayed within a grid control (this is shown later in this development environment).

<?xml version="1.0" encoding="UTF-8"?>
<Customers>
    <Customer>
        <First>Tony</First>
        <Last>Blue</Last>
        <Description>Past Customer</Description>
        <Address>Active</Address>
    </Customer>
    <Customer>
        <First>Sam</First>
        <Last>White</Last>
        <Description>Past Customer</Description>
        <Address>Active</Address>
    </Customer>
    <Customer>
        <First>Kim</First>
        <Last>Red</Last>
        <Description>Past Customer</Description>
        <Address>Active</Address>
    </Customer>
</Customers>

The following Java code represents the CustomerServiceImp class. For each customer record that is queried from the Customer table, a new Customer object is created and stored in an ArrayList. Data from each Customer object is retrieved from the ArrayList and stored in an XML schema by calling the toXML method. 

package com.adobe.cq.sql;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import com.adobe.cq.sql.Customer ;

import java.sql.SQLException;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import com.day.commons.datasource.poolservice.DataSourcePool;

import java.util.List;
import java.util.ArrayList;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
 
import java.io.StringWriter;
import java.util.Iterator;


import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

@Component

@Service
public class CustomerServiceImp implements CustomerService {

	protected final Logger LOGGER = LoggerFactory.getLogger(this.getClass());
    
    
    @Reference
   private DataSourcePool source;
	
   //Returns a connection using the configured DataSourcePool 
   private Connection getConnection()
   {
	        DataSource dataSource = null;
	        Connection con = null;
	        try
	        {
	            //Inject the DataSourcePool right here! 
	        	dataSource = (DataSource) source.getDataSource("Customer");
	            con = dataSource.getConnection();
	            return con;
	           
	          }
	        catch (Exception e)
	        {
	        	e.printStackTrace(); 
	        }
	            return null; 
   }
    
 //Adds a new customer record in the Customer table
	@Override
	public int injestCustData(String firstName, String lastName, String phone,			String desc) {
       Connection c = null;
       
       int rowCount= 0; 
       try {
                                  
             // Create a Connection object
             c =  getConnection();
         
              ResultSet rs = null;
              Statement s = c.createStatement();
              Statement scount = c.createStatement();
               
              //Use prepared statements to protected against SQL injection attacks
              PreparedStatement pstmt = null;
              PreparedStatement ps = null; 
                         
              //Set the query and use a preparedStatement
              String query = "Select * FROM Customer";
              pstmt = c.prepareStatement(query); 
              rs = pstmt.executeQuery();
               
              while (rs.next()) 
           	       rowCount++;
                            
              //Set the PK value
              int pkVal = rowCount + 2; 
               
              String insert = "INSERT INTO Customer(custId,custFirst,custLast,custDesc,custAddress) VALUES(?, ?, ?, ?, ?);";
              ps = c.prepareStatement(insert);
              ps.setInt(1, pkVal);
              ps.setString(2, firstName);
              ps.setString(3, lastName);
              ps.setString(4, phone);
              ps.setString(5, desc);
              ps.execute();
              return pkVal;
       }
       catch (Exception e) {
         e.printStackTrace();
        }
       finally {
         try
         {
    	   c.close();
         }
        
           catch (SQLException e) {
             e.printStackTrace();
           }
   }
       return 0; 
}

	/*
    * Retrieves customer data from the Customer table and returns all customer
    *The filter argument specifies one of the following values:
    *    
    *Customer - retrieves all customer data
    *Active Customer- retrieves current customers
    *Past Customer - retrieves old customers no longer current customers
    */
	@Override
	public String getCustomerData(String filter) {
       Connection c = null;
       int rowCount= 0; 
       Customer cust = null; 
       String query = "";
       List<Customer> custList = new ArrayList<Customer>();
       try {
                                  
             // Create a Connection object
             c =  getConnection();
         
              ResultSet rs = null;
              Statement s = c.createStatement();
              Statement scount = c.createStatement();
               
              //Use prepared statements to protected against SQL injection attacks
              PreparedStatement pstmt = null;
              PreparedStatement ps = null; 
                         
              //Set the query and use a preparedStatement
              if (filter.equals("All Customers"))
           	   query = "Select custId,custFirst,custLast,custAddress,custDesc FROM Customer";
              else if(filter.equals("Past Customer"))
           	   query = "Select custId,custFirst,custLast,custAddress,custDesc FROM Customer where custDesc = 'Past Customer'; ";
              else if(filter.equals("Active Customer"))
           	   query = "Select custId,custFirst,custLast,custAddress,custDesc FROM Customer where custDesc = 'Active Customer'; ";
              
              pstmt = c.prepareStatement(query); 
              rs = pstmt.executeQuery();
               
              while (rs.next()) 
              {
           	   //for each pass - create a Customer object
           	   cust = new Customer(); 
           	   
           	   //Populate customer members with data from MySQL
           	   int custId = rs.getInt(1);
           	   String id = Integer.toString(custId);
           	   cust.setCustId(id); 
           	   
           	   cust.setCustFirst(rs.getString(2));
           	   cust.setCustLast(rs.getString(3));
           	   cust.setCustAddress(rs.getString(4));
           	   cust.setCustDescription(rs.getString(5));
           	   
           	 //Push Customer to the list
           	 custList.add(cust);
                   
              }             
              //return xml that contains all customer taken from MySQL
              return convertToString(toXml(custList));               
              
       }
   catch(Exception e)
       {
        e.printStackTrace();
       }
       return null;
   }
        
        
   //Convert Customer data retrieved from the AEM JCR
   //into an XML schema to pass back to client
   private Document toXml(List<Customer> custList) {
   try
   {
       DocumentBuilderFactory factory =     DocumentBuilderFactory.newInstance();
       DocumentBuilder builder = factory.newDocumentBuilder();
       Document doc = builder.newDocument();
                    
       //Start building the XML to pass back to the AEM client
       Element root = doc.createElement( "Customers" );
       doc.appendChild( root );
                 
       //Get the elements from the collection
       int custCount = custList.size();
       
       //Iterate through the collection to build up the DOM           
        for ( int index=0; index < custCount; index++) {
    
            //Get the Customer object from the collection
            Customer myCust = (Customer)custList.get(index);
                         
            Element Customer = doc.createElement( "Customer" );
            root.appendChild( Customer );
                          
            //Add rest of data as child elements to customer
            //Set First Name
            Element first = doc.createElement( "First" );
            first.appendChild( doc.createTextNode(myCust.getCustFirst() ) );
            Customer.appendChild( first );
                                                             
            //Set Last Name
            Element last = doc.createElement( "Last" );
            last.appendChild( doc.createTextNode(myCust.getCustLast() ) );
            Customer.appendChild( last );
                       
            //Set Description
            Element desc = doc.createElement( "Description" );
            desc.appendChild( doc.createTextNode(myCust.getCustDescription() ) );
            Customer.appendChild( desc );
                      
            //Set Address
            Element address = doc.createElement( "Address" );
            address.appendChild( doc.createTextNode(myCust.getCustAddress()) );
            Customer.appendChild( address );
         }
               
   return doc;
   }
   catch(Exception e)
   {
       e.printStackTrace();
       }
   return null;
   }
    
    
   private String convertToString(Document xml)
   {
   try {
      Transformer transformer = TransformerFactory.newInstance().newTransformer();
     StreamResult result = new StreamResult(new StringWriter());
     DOMSource source = new DOMSource(xml);
     transformer.transform(source, result);
     return result.getWriter().toString();
   } catch(Exception ex) {
         ex.printStackTrace();
   }
     return null;
    }
}      

Add the MySQL driver file to Experience Manager

You have to deploy a bundle fragment to Experience Manager that contains the database driver file. In this development article, the name of the database driver file is mysql-connector-java-5.1.22-bin. To create an OSGi bundle fragment that contains this database driver file, 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 jdbcBundle.

  4. In the JAR selection dialog, click the Add external button, and browse to the database driver file.

  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 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.

  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 the 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\customerdb\bundle, add the following dependencies. 

  • org.apache.felix.scr
  • org.apache.felix.scr.annotations
  • com.day.commons

Because the DataSourcePool API is used, a Maven dependency on the repository that contains the DataSourcePool API exists. Add the following <repositories> element to your POM file.

<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>  

Once you add this repository element to your POM file, you can add the following dependency to your POM file, that lets you use the DataSourcePool API in your Java code.  

<dependency>
            <groupId>com.day.commons</groupId>
            <artifactId>day.commons.datasource.poolservice</artifactId>
            <version>1.0.10</version>
            <scope>provided</scope>
</dependency>

The following XML represents the POM file to build the OSGi bundle that contains the DataSourcePool API.  

<?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.adobe.cq.sql</groupId>
        <artifactId>customerdb</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>customerdb-bundle</artifactId>
    <packaging>bundle</packaging>
    <name>Csutomer Training 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.adobe.cq.sql.customerdb-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/customer-training/install</slingUrl>
                    <usePut>true</usePut>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencies>
       <dependency>
            <groupId>com.day.commons</groupId>
            <artifactId>day.commons.datasource.poolservice</artifactId>
            <version>1.0.10</version>
            <scope>provided</scope>
        </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.sling</groupId>
        <artifactId>org.apache.sling.jcr.api</artifactId>
        <version>2.0.4</version>
      </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 CustomerServiceImp 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.adobe.cq.sql.CustomerServiceImp">
        <implementation class="com.adobe.cq.sql.CustomerServiceImp"/>
        <service servicefactory="false">
            <provide interface="com.adobe.cq.sql.CustomerService"/>
        </service>
        <property name="service.pid" value="com.adobe.cq.sql.CustomerServiceImp"/>
        <reference name="source" interface="com.day.commons.datasource.poolservice.DataSourcePool" cardinality="1..1" policy="static" bind="bindSource" unbind="unbindSource"/>
    </scr:component>
    <scr:component enabled="true" name="com.adobe.cq.sql.SimpleDSComponent">
        <implementation class="com.adobe.cq.sql.SimpleDSComponent"/>
        <service servicefactory="false">
            <provide interface="java.lang.Runnable"/>
        </service>
        <property name="service.pid" value="com.adobe.cq.sql.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.adobe.cq.sql.CustomerServiceImp. The service element contains an interface attribute that specifies com.adobe.cq.sql.CustomerService. This corresponds to the Java interface that was created in an earlier step.

In order for the service injection to work, the reference element must be configured correctly. In this example, notice that the name of the reference is source. Also notice that it’s based on com.day.commons.datasource.poolservice.DataSourcePool, that is part of the DataSourcePool API.

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

  1. Open the command prompt and go to the C:\AdobeCQ\customerdb folder.

  2. Run the following maven command: mvn clean install.

  3. The OSGi component can be found in the following folder: C:\AdobeCQ\customerdb\bundle\target. The file name of the OSGi component is customerdb-bundle-1.0-SNAPSHOT.

Deploy the OSGi bundle that contains JBDC application logic

Once you deploy the OSGi bundle, you are able to invoke the getCustomerData and injestCustData methods defined in the CustomerServiceImpl 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

 

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\customerdb\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.

  9. If the status is not Active, check the error.log for exceptions.

Add CSS and JQuery files to a CQ:ClientLibraryFolder node

You add CSS files and JQuery framework files to a cq:ClientLibraryFolder node to define the style of the client JSP. The JQuery framework file that is added is named jquery-1.6.3.min.js.

In addition to the JQuery framework file, a data grid plugin is used in this sample AEM application named DataTables. This plugin is used to display the customer data that is returned by the Customer service in a tabular format.

Download the DataTables plugin from the following URL:

http://www.datatables.net/

Download and extract the DataTables archive file. The client AEM application uses these files from the DataTables archive file:

  • demo_table.css
  • jquery.dataTables.js
  • jquery.dataTables.min.js

 

Set the following properties to the cq:ClientLibraryFolder node.

Name Type Value
dependencies  String[] jquerycustomer
categories String[]  jquery

The dependencies property informs Experience Manager to include the CSS files and JQuery libraries in the page. The categories property informs Experience Manager which clientlibs must be included.

After you create the Clientlibs folder, add two CSS files, and the JQuery library files, and two map text files.

Site css file

The site.css file defines the display style for the client JSP file that lets the user enter and submit data. The following code represents the site.css file.

/* reset */
html, body, div, span, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td {
 margin: 0;
 padding: 0;
 border: 0;
 font-size: 100%;
 font: inherit;
 vertical-align: baseline;
}
html , body{
 line-height: 1;
 background-color: #334873;
 background-image: url(../_images/bg-page2.png);
}
    
ol, ul {
 list-style: none;
}
    
    
table {
 border-collapse: collapse;
 border-spacing: 0;
}
/* end reset*/
    
    
    
h1, h2, h3 {
 font-family: 'ColaborateRegular', Arial, sans-serif;
}
    
    
strong {
 font-family: 'ColaborateMediumRegular', Arial, sans-serif;
}
    
em {
 font-family: 'ColaborateThinRegular', Arial, sans-serif;
}
    
.content {
 max-width: 760px;
 margin: 20px 0 0 100px;
}
    
.clear:after {
content: "."; display: block; height: 0; clear: both; visibility: hidden;
}
    
.clear {
 min-height: 1px;
}
    
* html .clear {
 height: 1px;
}
    
.header {
 position: relative;
 border-top: solid 6px white;
 padding: 10px 0 10px 0;
 margin-bottom: 20px;
}
    
    
.main {
 xxposition: relative;
 padding-bottom: 1em;
 border-bottom: solid 1px rgba(255,255,255,.5);
 xxoverflow:hidden;
 xxmin-height: 300px;
}
    
.main h1 {
 font-size: 32px;
 color: white;
 text-shadow: 1px 1px 1px rgba(0,0,0,.75);
 border-bottom: solid 1px rgba(255,255,255,.5);
 margin-bottom: 0.75em;
}
    
    
p , li, legend , form{
 font-size: 18px;
 color: white;
 font-family: 'ColaborateLightRegular', Arial, sans-serif;
 line-height: 125%;
 margin-bottom: 10px;
}
    
fieldset {
 padding: 10px;
 border: 1px solid white;
 margin: 25px 0;
}
    
.nav {
 margin: 10px 0 0 100px;
}
    
.nav li {
 display: inline-block;
}
    
.nav a:hover, .example:hover{
 background-color: rgba(255,255,255,.85);
 color: rgb(0,0,0);
}
    
h3 {
 font-size: 18px;
 color: rgb(227,198,133);;
}
    
.results h2 {
 color: rgba(255,255,255,1);
}
.results div {
 padding-bottom: 10px;
}
.results div code {
 float: right;
 width: 60%;
}
    
input {
 font-size: 20px;
}
.form .wide {
 font-size: 18px;
 width: 100%;
}
.resultSection {
 float: right;
 width: 45%;
 margin-left: 20px;
}
#regexTester {
 margin-right: 55%;
}
.sideBySide li {
 float: left;
 overflow: hidden;
 width: 220px;
}
.clickable {
 cursor:pointer;
 margin-bottom: 5px;
}
    
.clickable:hover {
background-color:#FFC;
}
    
    
.col1 {
 float: left;
 width: 75%;
}
.col2 {
 float: right;
 width: 20%;
}
    
.col2 ul {
 margin-left: 20px;
 list-style: square;
}
.col2 li {
 font-size: 90%;
}
    
    
#selectorList {
 overflow: hidden;
}
#selector {
 width: 275px;
}
    
    
form#signup .label {
 width: 200px;
}

Text files

You have to add two text files to the clientlibs folder. These text files map to the JS file and the CSS file. The names of the text files are: css.txt and js.txt. The css.txt file contains the CSS file names: site.css and demo_table.css. Likewise, the js.txt file contains the JS file names: jquery-1.6.3.min.js, jquery.dataTables.js,and jquery.dataTables.min.js.

Add the files to the ClientLibs folder

  1. Right-click /apps/customer/components then select New, Node.

  2. Make sure that the node type is cq:ClientLibraryFolder and name the node clientlibs.

  3. Right click on clientlibs and select Properties. Add the two properties specified in the previous table to the node.

  4. On your file system, navigate to the folder where the JQuery JS files are located. Drag and drop the JS files to the clientlibs node by using CRXDE.

  5. On your file system, navigate where you placed the CSS files. Drag and drop the CSS files to the clientlibs folder by using CRXDE.

  6. Add a TXT file to the clientlibs folder named js.txt. Add the content specified in this section.

  7. Add a TXT file to the clientlibs node named css.txt. Add the content specified in this section.

Modify the Client JSP to invoke the getCustomerData and injestCustData methods

To create the AEM application that queries and persists data from MySQL, create these files:

  • query.json.jsp: contains application logic that calls the OSGi bundle's getCustomerData method.
  • persist.json.jsp: contains application logic that calls the OSGi bundle’s injestCustData method. 
  • templateCustomer.jsp: contains application logic that defines the JSP that lets a user enter customer data and query existing data.

Create the query.json.jsp

Add a new JSP file named query.json.jsp to the following CQ path:

/apps/customer/components/page/templateCustomer

In query.json.jsp, create a CustomerService instance by using the sling.getService method, as shown in the following example:

//create a CustomerService instance
com.adobe.cq.sql.CustomerService custService = sling.getService(com.adobe.cq.sql.CustomerService.class);

Pass the fully qualified name of the service to sling.getService method. Because the OSGi bundle is a managed component that injects a DataSourcePool instance into the service, you must use the sling.getService method to create a CustomerService object.

If you attempt to create a CustomerService using the new operation, the OSGi bundle is not considered a managed component and will not successfully inject a DataSourcePool instance. A Java null pointer exception is thrown.

After you create a CustomerService object, you can invoke the getCustomerData method exposed by the OSGi bundle. You pass a string value to this method that specifies whether to query all customers, past customers, or active customers.

The following code represents the query.json.jsp file. In this code example, notice that a JSONWriter instance is created. This object returns XML that contains customer information to the main JSP client after the call to the OSGi bundle is made. The client JSP parses the XML and displays the customer data in a grid control.
 

<%@include file="/libs/foundation/global.jsp"%>
<%@ page import="org.apache.sling.commons.json.io.*,org.w3c.dom.*" %><%
String filter = request.getParameter("filter");
  
//create a CustomerService instance
com.adobe.cq.sql.CustomerService cs = sling.getService(com.adobe.cq.sql.CustomerService.class);

String XML = cs.getCustomerData(filter); 
  
    
//Send the data back to the client
JSONWriter writer = new JSONWriter(response.getWriter());
writer.object();
writer.key("xml");
writer.value(XML);
  
writer.endObject();
%>

Create the persist.json.jsp

Add a new JSP file named persist.json.jsp to the following CQ path:

/apps/customer/components/page/templateCustomer

The following code represents the persist.json.jsp file.

<%@include file="/libs/foundation/global.jsp"%>
<%@ page import="org.apache.sling.commons.json.io.*,com.adobe.cq.*" %><%
String first = request.getParameter("first");
String last = request.getParameter("last");
String phone = request.getParameter("phone");
String desc = request.getParameter("desc");
  
//create a CustomerService instance
com.adobe.cq.sql.CustomerService custService = sling.getService(com.adobe.cq.sql.CustomerService.class);

int myPK = custService.injestCustData(first, last, phone, desc) ; 
   
//Send the data back to the client
JSONWriter writer = new JSONWriter(response.getWriter());
writer.object();
writer.key("pk");
writer.value(myPK);
  
writer.endObject();
%>

Modify the templateCustomer file

Modify the templateCustomer.jsp file to call both the query.json.jsp and the persist.json.jsp. In this example, a JQuery Ajax HTTP request is used and the corresponding values are passed. This code shows the modified  templateCustomer.jsp file.   

<%@include file="/libs/foundation/global.jsp"%>
<cq:includeClientLib categories="jquerycustomer" />
<html>
<head>
<meta charset="UTF-8">
<title>AEM Persist Page</title>
<style>
#signup .indent label.error {
  margin-left: 0;
}
#signup label.error {
  font-size: 0.8em;
  color: #F00;
  font-weight: bold;
  display: block;
  margin-left: 215px;
}
#signup  input.error, #signup select.error  {
  background: #FFA9B8;
  border: 1px solid red;
}
</style>
<script>
$(document).ready(function() {
      
       var aDataSet = [
                       ['','','','',''],
                       ['','','','','']
                   ];
                
       $('#dynamic').html( '<table cellpadding="0" cellspacing="0" border="0" class="display" id="example"></table>' );
      $('#example').dataTable( {
          "aaData": aDataSet,
          "aoColumns": [
              { "sTitle": "First Name" },
              { "sTitle": "Last Name" },
              { "sTitle": "Address", "sClass": "center" },
              { "sTitle": "Description", "sClass": "center" }
          ]
      } );
 
 
$('body').hide().fadeIn(5000);
      
$('#submit').click(function() {
    var failure = function(err) {
      //  $(".main").unmask();
        alert("Unable to retrive data "+err);
          
    };
          
    //Get the user-defined values to persist in the database
    var myFirst= $('#first').val() ;
    var myLast= $('#last').val() ;
    var myDescription= $('#description').val() ;
    var myAddress= $('#address').val() ;
      
    var url = location.pathname.replace(".html", "/_jcr_content.persist.json") + "?first="+ myFirst +"&last="+myLast +"&desc="+myDescription +"&phone="+myAddress;
      
    //$(".main").mask("Saving Data...");
  
    $.ajax(url, {
        dataType: "text",
        success: function(rawData, status, xhr) {
            var data;
            try {
                data = $.parseJSON(rawData);
                  
                          
                //Set the fields in the forum
                $('#custId').val(data.pk);
             
            } catch(err) {
                failure(err);
            }
        },
        error: function(xhr, status, err) {
            failure(err);
        }
    });
  });
 
 
//Get customer data -- called when the submitget button is clicked
//this method populates the data grid with data 
$('#submitget').click(function() {
    var failure = function(err) {
          alert("Unable to retrive data "+err);
      };
       
    //Get the query filter value from drop down control
    var filter=   $('#custQuery').val() ;
        
    var url = location.pathname.replace(".html", "/_jcr_content.query.json") + "?filter="+ filter;
        
    $.ajax(url, {
        dataType: "text",
        success: function(rawData, status, xhr) {
            var data;
            try {
                data = $.parseJSON(rawData);
                  
                             
                //Set the fields in the forum
                var myXML = data.xml;
 
                var loopIndex = 0;
 
                //Reference the data grid, clear it, and add new records
                 var oTable = $('#example').dataTable();
                 oTable.fnClearTable(true);
 
 
                 //Loop through this function for each Customer element
                 //in the returned XML
                 $(myXML).find('Customer').each(function(){
                         
                    var $field = $(this);
                    var firstName = $field.find('First').text();
                     
                    var lastName = $field.find('Last').text();
                    var Description = $field.find('Description').text();
                    var Address = $field.find('Address').text();    
 
                    //Set the new data
                    oTable.fnAddData( [
                        firstName,
                        lastName,
                        Address,
                        Description,]
                    );
            
                    });
            
            } catch(err) {
                failure(err);
            }
        },
        error: function(xhr, status, err) {
            failure(err);
        }
    });
  });
  
}); // end ready
</script>
 
</head>
<body>
<div class="wrapper">
    <div class="header">
        <p class="logo">Experience Manager MySQL Customer Persist/Query Application</p>
    </div>
    <div class="content">
    <div class="main">
    <h1>MySQL Persist Example</h1>
      
    <form name="signup" id="signup">
     <table>
    <tr>
    <td>
    <label for="first">First Name:</label>
    </td>
     <td>
    <input type="first" id="first" name="first" value="" />
    </td>
    </tr>
    <tr>
    <td>
    <label for="last">Last Name:</label>
    </td>
     <td>
    <input type="last" id="last" name="last" value="" />
    </td>
    </tr>
     <tr>
    <td>
    <label for="address">Address:</label>
    </td>
     <td>
    <input type="address" id="address" name="address" value="" />
    </td>
    </tr>
     <tr>
    <td>
   <label for="description">Description:</label>
    </td>
    <td>
    <select id="description"  >
            <option>Active Customer</option>
            <option>Past Customer</option> 
        </select>
    </td>
    </tr>
     <tr>
    <td>
    <label for="custId">Customer Id:</label>
    </td>
     <td>
    <input type="custId" id="custId" name="custId" value="" readonly="readonly"/>
    </td>
    </tr>
     
</table>
            <div>
                <input type="button" value="Add Customer!"  name="submit" id="submit" value="Submit">
            </div>
        </form>
        </div>
    </div>
     
    <div id="container">
     <form name="custdata" id="custdata">
    
    <h1>Query Customer Data from MySQL</h1>
   <div>
     <select id="custQuery"  >
            <option>All Customers</option>
            <option>Active Customer</option>
            <option>Past Customer</option>   
        </select>
    </div>
    <div id="dynamic"></div>
    <div class="spacer"></div>
 
   <div>
      <input type="button" value="Get Customers!"  name="submitget" id="submitget" value="Submit">
    </div>
   </form>
      
</div>
</body>
</html>

Modify the templateCustomer file  

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

  2. Go to CRXDE Lite at http://localhost:4502/crx/de/index.jsp

  3. Double-click 

    apps/customer/components/page/templateCustomer/templateCustomer.jsp.

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

  5. Click Save All.

Create an Experience Manager web page that queries and persists data  

The final task is to create a site that contains a page that is based on the templateCustomer (the template created earlier in this development article). When the user selects the customer data to retrieve from the drop down and clicks the Get Customer button, customer data is displayed in the data grid control.

In addition, this client application lets users add new customer data to MySQL.

MainApp
The AEM Client that queries and persists data

Create an Experiene Manager web page that queries and persists data from MySQL:

  1. Go to Websites at http://localhost:4502/siteadmin#/content

  2. Select New Page.

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

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

  5. Select templateCustomer 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. You should see a page similar to the previous illustration.

See also

Congratulations, you have just created an AEM application that interacts with MySQL by using a DataSourcePool. Please refer to the AEM community page for other articles that discuss how to build AEM services/applications.

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