Although out of the box CQ offers a num­ber of ver­sa­tile object types and their asso­ci­ated APIs, it can some­times be help­ful to define your own Java value objects. How­ever the actual data is stored in CRX, so I need an easy way to map my prod­uct node in CRX to my Prod­uct class in Java. This is where the Adapter­Fac­tory comes in.

Most CQ devel­op­ers are famil­iar with the con­cept of the adaptTo(), allow­ing them to adapt a few basic CQ objects to a num­ber of more com­plex and more struc­tured classes, as described here.

Well the Adapter­Fac­tory actu­ally lets us cre­ate our own adapters, so we can then adaptTo() from/to any class we want! And it’s really easy to do too, as you’ll soon find out.

For exam­ple if I am build­ing an eCom­merce web­site I might want to cre­ate a Prod­uct class, con­tain­ing all the prop­er­ties for my prod­uct, and their asso­ci­ated get­ters and set­ters, as such:

pub­lic class Prod­uct {
pub­lic sta­tic final String PROP_SERIAL = “ser­ial”;
pub­lic sta­tic final String PROP_PRICE = “price”;
pub­lic sta­tic final String PROP_CATEGORY = “cat­e­gory”;
pub­lic sta­tic final String PROP_IMG_URL = “imgURL”;

pri­vate String path;
pri­vate String ser­ial;
pri­vate Dou­ble price;
pri­vate String cat­e­gory;
pri­vate String imgURL;

pub­lic String get­Path() {
return path;
}

pub­lic void setPath(String path) {
this.path = path;
}

pub­lic String get­Se­r­ial() {
return ser­ial;
}

pub­lic void setSerial(String ser­ial) {
this.serial = ser­ial;
}

pub­lic Dou­ble get­Price() {
return price;
}

pub­lic void setPrice(Double price) {
this.price = price;
}

pub­lic String get­Cat­e­gory() {
return cat­e­gory;
}

pub­lic void setCategory(String cat­e­gory) {
this.category = cat­e­gory;
}

pub­lic String getImgURL() {
return imgURL;
}

pub­lic void setImgURL(String imgURL) {
this.imgURL = imgURL;
}

}

This is a sim­ple POJO, a typed object that will allow me to manip­u­late my prod­ucts eas­ily. But to ease the manip­u­la­tion of this object and its asso­ci­ated data in CRX we’ll cre­ate a cus­tom adapter for it. For instance if our prod­ucts are stored as pages here’s the code to adapt a Page object to our Prod­uct object:

@Component(metatype = true, imme­di­ate = true)
@Service
pub­lic class Pro­duc­tAdapter imple­ments AdapterFactory {

pri­vate sta­tic final Log­ger LOG = Log­ger­Fac­tory
.getLogger(ProductAdapter.class);

pri­vate sta­tic final Class<ProductVO> PRODUCT_CLASS = Product.class;
pri­vate sta­tic final Class<Page> PAGE_CLASS = Page.class;

@Property(name = “adapters”)
pro­tected sta­tic final String[] ADAPTER_CLASSES = { PRODUCT_CLASS
.getName() };

@Property(name = “adapt­a­bles”)
pro­tected sta­tic final String[] ADAPTABLE_CLASSES = { PAGE_CLASS.getName() };

@Override
pub­lic <AdapterType> Adapter­Type getAdapter(Object adapt­able,
Class<AdapterType> type) {
if (adapt­able instanceof Page) {
return this.adaptFromPage((Page) adapt­able, type);
}
return null;
}

pri­vate <AdapterType> Adapter­Type adaptFromPage(Page page,
Class<AdapterType> type) {
if ((page != null) && (page.getContentResource() != null)) {
return adaptFromResource(page.getContentResource(), type);
}
return null;
}

@SuppressWarnings(“unchecked”)
pri­vate <AdapterType> Adapter­Type adaptFromResource(Resource resource,
Class<AdapterType> type) {
Prod­uct prod­uct = new Prod­uct();
try {
Node pro­ductN­ode = resource.adaptTo(Node.class);
product.setPath(productNode.getParent().getPath());

// Set ser­ial
if (productNode.hasProperty(Product.PROP_SERIAL))
product.setSerial(productNode.getProperty(Product.PROP_SERIAL)
.getString());

// Set cat­e­gory
if (productNode.hasProperty(Product.PROP_CATEGORY))
product.setCategory(productNode.getProperty(
Product.PROP_CATEGORY).getString());

// Set price
if (productNode.hasProperty(Product.PROP_PRICE))
product.setPrice(productNode
.getProperty(Product.PROP_PRICE).getDouble());

// Set image url
if (productNode.hasProperty(Product.PROP_IMG_URL))
product.setImgURL(productNode.getProperty(
Product.PROP_IMG_URL).getString());

} catch (Repos­i­to­ryEx­cep­tion repos­i­to­ryEx­cep­tion) {
LOG.error(“RepositoryException —— > “, repos­i­to­ryEx­cep­tion);
}
return (Adapter­Type) prod­uct;
}

}

A few things to point out here: we are sim­ply declar­ing a new ser­vice imple­ment­ing the Adapter­Fac­tory class, and then we con­fig­ure it by declar­ing a list of “adapt­a­bles” which are the classes we can adapt from, and a list of “adapters” which are the classes we’re adapt­ing to. We then over­ride the getAdapter() method and insert the logic in there.

Now when­ever I need to manip­u­late a prod­uct I can sim­ply use: