com.adobe.cq.commerce.common.AbstractJcrCommerceService Maven / Gradle / Ivy
/*************************************************************************
*
* ADOBE CONFIDENTIAL
* __________________
*
* Copyright 2012 Adobe Systems Incorporated
* All Rights Reserved.
*
* NOTICE: All information contained herein is, and remains
* the property of Adobe Systems Incorporated and its suppliers,
* if any. The intellectual and technical concepts contained
* herein are proprietary to Adobe Systems Incorporated and its
* suppliers and are protected by trade secret or copyright law.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained
* from Adobe Systems Incorporated.
**************************************************************************/
package com.adobe.cq.commerce.common;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ValueMap;
import aQute.bnd.annotation.ConsumerType;
import com.adobe.cq.commerce.api.CommerceException;
import com.adobe.cq.commerce.api.CommerceQuery;
import com.adobe.cq.commerce.api.CommerceResult;
import com.adobe.cq.commerce.api.CommerceService;
import com.adobe.cq.commerce.api.CommerceSession;
import com.adobe.cq.commerce.api.PaymentMethod;
import com.adobe.cq.commerce.api.Product;
import com.adobe.cq.commerce.api.ShippingMethod;
import com.adobe.cq.commerce.api.collection.ProductCollection;
import com.adobe.cq.commerce.api.conf.CommerceBasePathsService;
import com.adobe.cq.commerce.api.promotion.Promotion;
import com.adobe.cq.commerce.api.promotion.PromotionManager;
import com.adobe.cq.commerce.api.promotion.Voucher;
import com.day.cq.wcm.api.Page;
/**
* An abstract base class for implementing {@link CommerceService}s on top of the JCR repository.
* See GeoCommerceServiceImpl
for an example.
*/
@ConsumerType
public abstract class AbstractJcrCommerceService implements CommerceService {
private final ServiceContext serviceContext;
protected Map context;
protected CommerceSearchProvider searchProvider;
protected ResourceResolver resolver;
protected AbstractJcrCommerceService(ServiceContext serviceContext, Resource resource) {
this.context = new HashMap();
this.serviceContext = serviceContext;
this.resolver = resource.getResourceResolver();
}
/**
* Returns the {@link ServiceContext} of this service.
*/
public ServiceContext serviceContext() {
return serviceContext;
}
/**
* {@inheritDoc}
*/
@Override
public void setContext(Map context) {
this.context = context;
}
/**
* {@inheritDoc}
*/
@Override
public Map getContext() {
return context;
}
/**
* {@inheritDoc}
*/
@Override
public String getServer() {
return null;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isActivated(Product product) throws CommerceException {
// always return true, since we're not dealing with a remote system
return true;
}
/**
* Default implementation assumes a JCR-based promotion (in which the path parameter is
* truly a path).
*/
@Override
public Promotion getPromotion(String path) throws CommerceException {
Resource resource = resolver.getResource(path);
return resource != null ? resource.adaptTo(Promotion.class) : null;
}
/**
* Default implementation assumes a JCR-based voucher (in which the path parameter is
* truly a path).
*/
@Override
public Voucher getVoucher(String path) throws CommerceException {
Resource resource = resolver.getResource(path);
return resource != null ? resource.adaptTo(Voucher.class) : null;
}
/**
* Default implementation assumes a JCR-based product collection (in which the path parameter is
* truly a path).
*/
@Override
public ProductCollection getProductCollection(String path) throws CommerceException {
Resource resource = resolver.getResource(path);
return resource != null ? resource.adaptTo(ProductCollection.class) : null;
}
/**
* Return a vendor record of a placed order for order administration. The given locale
* will be used to format prices.
*/
public VendorJcrPlacedOrder getPlacedOrder(String orderId, Locale locale) {
return new VendorJcrPlacedOrder(this, orderId, locale);
}
/**
* {@inheritDoc}
*/
@Override
public void catalogRolloutHook(Page blueprint, Page catalog) throws CommerceException {
// NOP
}
/**
* {@inheritDoc}
*/
@Override
public void sectionRolloutHook(Page blueprint, Page section) {
// NOP
}
/**
* {@inheritDoc}
*/
@Override
public void productRolloutHook(Product productData, Page productPage, Product productReference) throws CommerceException {
// NOP
}
/**
* {@inheritDoc}
*/
@Override
public CommerceResult search(CommerceQuery query) throws CommerceException {
if (searchProvider == null) {
this.searchProvider = getSearchProvider();
}
if (searchProvider != null) {
return searchProvider.search(query, this);
}
return null;
}
/**
* Gets the search provider used in the current e-commerce implementation. Subclasses may override
* this method to plug-in a custom search provider implementation into the e-commerce system which
* is not available via {@code CommerceSearchProviderManger}.
*/
protected CommerceSearchProvider getSearchProvider() throws CommerceException {
String providerName = getSearchProviderName();
if (providerName != null) {
return serviceContext.searchProviderManager.getSearchProvider(providerName);
}
return null;
}
/**
* Gets the identifier of the search provider used in the current e-commerce implementation.
* Subclasses should override this method to return the name of the custom search provider service
* available in {@code CommerceSearchProviderManger}.
*/
protected String getSearchProviderName() {
return null;
}
/**
* Returns a list of JCR-based promotions (ie, those known to the {@link PromotionManager}).
*/
@Override
public List getAvailablePromotions(ResourceResolver resourceResolver) throws CommerceException {
PromotionManager promotionManager = resourceResolver.adaptTo(PromotionManager.class);
return promotionManager.getAvailablePromotions(resourceResolver);
}
/**
* Returns a list of available shipping methods.
*
* NB: not in the public interface. All public access should be through
* {@link CommerceSession#getAvailableShippingMethods()}.
*
* Implementations which support session- or order-specific shipping methods should implement
* {@link AbstractJcrCommerceSession#getAvailableShippingMethods()} instead.
*
* This implementation picks up the shipping methods from the JCR repository. Override to
* specify a more-specific path or a particular ShippingMethod implementation.
*/
public List getAvailableShippingMethods() throws CommerceException {
CommerceBasePathsService cbps = resolver.adaptTo(CommerceBasePathsService.class);
return enumerateMethods(cbps.getShippingMethodsBasePath(), ShippingMethod.class);
}
/**
* Returns a list of available payment methods.
*
* NB: not in the public interface. All public access should be through
* {@link CommerceSession#getAvailablePaymentMethods()}.
*
* Implementations which support session- or order-specific shipping methods should implement
* {@link AbstractJcrCommerceSession#getAvailablePaymentMethods()} instead.
*
* This implementation picks up the payment methods from the JCR repository. Override to
* specify a more-specific path or a particular PaymentMethod implementation.
*/
public List getAvailablePaymentMethods() throws CommerceException {
CommerceBasePathsService cbps = resolver.adaptTo(CommerceBasePathsService.class);
return enumerateMethods(cbps.getPaymentMethodsBasePath(), PaymentMethod.class);
}
/**
* A helper class used to pick up {@link ShippingMethod}s, {@link PaymentMethod}s, etc. from
* the repository.
* @param path Folder from which to grab methods.
* @param type An adaptTo type for the children of the folder.
* @return A List
of folder children which successfully adapted to the given type.
*/
protected List enumerateMethods(String path, Class type) {
List methods = new ArrayList();
Resource dir = resolver.getResource(path);
if (dir != null) {
for (Resource resource : dir.getChildren()) {
if (resource.getName().equals("jcr:content")) {
continue;
}
T method = resource.adaptTo(type);
if (method != null) {
methods.add(method);
}
}
}
return methods;
}
/**
* Instantiates a new cart entry. Override this method to supply custom extensions of {@link DefaultJcrCartEntry}.
* @param index The index of the entry with the current cart or PlacedOrder.
* @param product The product the new entry represents.
* @param quantity The quantity.
*/
public DefaultJcrCartEntry newCartEntryImpl(int index, Product product, int quantity) {
return new DefaultJcrCartEntry(index, product, quantity);
}
/**
*
* Converts the cart entry data to a String
of the form "productPath;quantity;property1_name=property1_value\fproperty2_name=property2_value\f..."
.
*
* @param productPath the path of the product of the cart entry
* @param quantity the number of products in the cart entry
* @param properties other cart entry properties
*
* @return the String representation of cart entry data
*/
public String serializeCartEntryData(String productPath, int quantity, ValueMap properties) {
StringBuilder sb = new StringBuilder();
sb.append(productPath).append(';').append(quantity);
if (properties == null || properties.isEmpty()) {
return sb.toString();
} else {
sb.append(';');
for (ValueMap.Entry entry : properties.entrySet()) {
String key = entry.getKey();
String value = String.valueOf(entry.getValue());
sb.append(key).append('=').append(value).append('\f');
}
return sb.toString();
}
}
/**
* Creates cart entry data from a String created with {@link #serializeCartEntryData(String, int, org.apache.sling.api.resource.ValueMap)}.
*
* @param str the serialized cart entry data
* @return the an Object[] of length 3 which contains the Product object at index 0,
* the quantity Integer object at index 1 and null or a {@code Map<String, Object>} holding the
* other properties of the cart entry at index 2.
* @throws CommerceException
*/
public Object[] deserializeCartEntryData(String str) throws CommerceException {
Object[] entryData = new Object[3];
String[] entryFields = str.split(";", 3);
Product product = getProduct(entryFields[0]);
entryData[0] = product;
int quantity = Integer.parseInt(entryFields[1]);
entryData[1] = quantity;
if (entryFields.length == 2) {
return entryData;
}
Map properties = new HashMap();
String[] propertyFields = entryFields[2].split("\f");
for (String field : propertyFields) {
if (StringUtils.isNotBlank(field)) {
String[] property = field.split("=", 2);
properties.put(property[0], property[1]);
}
}
entryData[2] = properties;
return entryData;
}
/*
* ==================================================================================================
* Deprecated methods. For backwards-compatibility only.
* ==================================================================================================
*/
/**
* @deprecated use the {@link #serviceContext} field instead.
*/
@Deprecated
public AbstractJcrCommerceServiceFactory.Services services;
/**
* @deprecated since 5.6.1; use {@link #AbstractJcrCommerceService(ServiceContext)} instead.
*/
@Deprecated
public AbstractJcrCommerceService(AbstractJcrCommerceServiceFactory.Services services) {
this((ServiceContext) null);
this.services = services;
}
/**
* @deprecated since 5.6.200; use {@link #AbstractJcrCommerceService(ServiceContext, Resource)}
* instead.
*/
@Deprecated
protected AbstractJcrCommerceService(ServiceContext serviceContext) {
this.context = new HashMap();
this.serviceContext = serviceContext;
}
}