Created

Jan 28, 2013

Create a custom adapter

Posted by Vianney Baron

Although out of the box CQ offers a number of versatile object types and their associated APIs, it can sometimes be helpful to define your own Java value objects. However the actual data is stored in CRX, so I need an easy way to map my product node in CRX to my Product class in Java. This is where the AdapterFactory comes in.

Most CQ developers are familiar with the concept of the adaptTo(), allowing them to adapt a few basic CQ objects to a number of more complex and more structured classes, as described here.

Well the AdapterFactory actually lets us create 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 example if I am building an eCommerce website I might want to create a Product class, containing all the properties for my product, and their associated getters and setters, as such:

public class Product {

	public static final String PROP_SERIAL = "serial";
	public static final String PROP_PRICE = "price";
	public static final String PROP_CATEGORY = "category";
	public static final String PROP_IMG_URL = "imgURL";

	private String path;
	private String serial;
	private Double price;
	private String category;
	private String imgURL;

	public String getPath() {
		return path;
	}

	public void setPath(String path) {
		this.path = path;
	}

	public String getSerial() {
		return serial;
	}

	public void setSerial(String serial) {
		this.serial = serial;
	}

	public Double getPrice() {
		return price;
	}

	public void setPrice(Double price) {
		this.price = price;
	}
	
	public String getCategory() {
		return category;
	}

	public void setCategory(String category) {
		this.category = category;
	}

	public String getImgURL() {
		return imgURL;
	}

	public void setImgURL(String imgURL) {
		this.imgURL = imgURL;
	}

}

This is a simple POJO, a typed object that will allow me to manipulate my products easily. But to ease the manipulation of this object and its associated data in CRX we'll create a custom adapter for it. For instance if our products are stored as pages here’s the code to adapt a Page object to our Product object:

@Component(metatype = true, immediate = true)
@Service
public class ProductAdapter implements AdapterFactory {

	private static final Logger LOG = LoggerFactory
			.getLogger(ProductAdapter.class);

	private static final Class<ProductVO> PRODUCT_CLASS = Product.class;
	private static final Class<Page> PAGE_CLASS = Page.class;

	@Property(name = "adapters")
	protected static final String[] ADAPTER_CLASSES = { PRODUCT_CLASS
			.getName() };

	@Property(name = "adaptables")
	protected static final String[] ADAPTABLE_CLASSES = { PAGE_CLASS.getName() };

	@Override
	public  <AdapterType> AdapterType getAdapter(Object adaptable,
			Class<AdapterType> type) {
		if (adaptable instanceof Page) {
			return this.adaptFromPage((Page) adaptable, type);
		}
		return null;
	}

	private  <AdapterType> AdapterType adaptFromPage(Page page,
			Class<AdapterType> type) {
		if ((page != null) && (page.getContentResource() != null)) {
			return adaptFromResource(page.getContentResource(), type);
		}
		return null;
	}

	@SuppressWarnings("unchecked")
	private  <AdapterType> AdapterType adaptFromResource(Resource resource,
			Class<AdapterType> type) {
		Product product = new Product();
		try {
			Node productNode = resource.adaptTo(Node.class);
			product.setPath(productNode.getParent().getPath());

			// Set serial
			if (productNode.hasProperty(Product.PROP_SERIAL))
				product.setSerial(productNode.getProperty(Product.PROP_SERIAL)
						.getString());

			// Set category
			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 (RepositoryException repositoryException) {
			LOG.error("RepositoryException ------ > ", repositoryException);
		}
		return (AdapterType) product;
	}

}

A few things to point out here: we are simply declaring a new service implementing the AdapterFactory class, and then we configure it by declaring a list of “adaptables” which are the classes we can adapt from, and a list of “adapters” which are the classes we’re adapting to. We then override the getAdapter() method and insert the logic in there.

Now whenever I need to manipulate a product I can simply use:

Product currentProduct = currentPage.adaptTo(Product.class);

COMMENTS

ADD A COMMENT