package org.onehippo.forge.konakart.cms.replication.synchronization.job;
import com.konakart.al.KKAppEng;
import com.konakart.app.*;
import com.konakart.appif.DataDescriptorIf;
import com.konakart.appif.LanguageIf;
import com.konakart.appif.ProductSearchIf;
import com.konakartadmin.bl.AdminMgrFactory;
import com.konakartadmin.blif.AdminProductMgrIf;
import org.apache.commons.lang.StringUtils;
import org.onehippo.forge.konakart.cms.replication.factory.DefaultProductFactory;
import org.onehippo.forge.konakart.cms.replication.factory.ProductFactory;
import org.onehippo.forge.konakart.cms.replication.utils.Codecs;
import org.onehippo.forge.konakart.cms.replication.utils.NodeHelper;
import org.onehippo.forge.konakart.common.KKCndConstants;
import org.onehippo.forge.konakart.common.bl.CustomProductMgr;
import org.onehippo.forge.konakart.common.engine.KKAdminEngine;
import org.onehippo.forge.konakart.common.engine.KKEngine;
import org.onehippo.forge.konakart.common.engine.KKStoreConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import java.util.ArrayList;
import java.util.List;
public class KonakartSyncProducts {
private static final int NBR_PRODUCTS_PER_BATCH = 100;
public static final Logger log = LoggerFactory.getLogger(KonakartSyncProducts.class);
/**
* Copy products from Konakart to Hippo
*
* @param kkStoreConfig the store config
* @throws Exception an exception
*/
public static synchronized boolean updateKonakartToHippo(KKStoreConfig kkStoreConfig, Session jcrSession) throws Exception {
KKAppEng kkengine = KKEngine.get(kkStoreConfig.getStoreId());
// Retrieve the list of languages defined into konakart.
LanguageIf[] languages = kkengine.getEng().getAllLanguages();
// Retrieve the content root
String currentLocale = kkStoreConfig.getLocale();
boolean updated = false;
// For each language defined into Konakart we need to add the product under Hippo
for (LanguageIf language : languages) {
// Get the Hippo product folder name
String storeId = kkStoreConfig.getStoreId();
if (currentLocale == null || !StringUtils.equals(currentLocale, language.getLocale())) {
log.info("############################################################################");
log.info("##");
log.info("##");
log.info("Unable to map the Konakart locale <" + language.getLocale() + "> with any available hippo locale");
log.info("##");
log.info("##");
log.info("############################################################################");
continue;
}
log.info("############################################################################");
log.info("##");
log.info("##");
log.info("Sync the Konakart locale <" + language.getLocale() + ">");
log.info("##");
log.info("##");
log.info("############################################################################");
updated = true;
// Initialize the KKEngine
kkengine = KKEngine.get(storeId);
// Retrieve the product factory
CustomProductMgr productMgr;
// In the development mode the entire product lists is retrieved.
if (kkStoreConfig.isDevelopmentMode() || !hasProducts(kkStoreConfig, jcrSession)) {
productMgr = new CustomProductMgr(kkengine.getEng());
} else {
productMgr = new CustomProductMgr(kkengine.getEng(), kkStoreConfig.getLastUpdatedTimeKonakartToRepository());
}
// Get all products - visible or not
DataDescriptorIf dataDescriptorIf = new DataDescriptor();
dataDescriptorIf.setShowInvisible(true);
dataDescriptorIf.setFillDescription(true);
dataDescriptorIf.setLimit(NBR_PRODUCTS_PER_BATCH);
// Search products from all stores
ProductSearchIf productSearchIf = new ProductSearch();
// Used default products options
FetchProductOptions fetchProductOptions = new FetchProductOptions();
if (StringUtils.isNotEmpty(kkStoreConfig.getCatalogId())) {
fetchProductOptions.setCatalogId(kkStoreConfig.getCatalogId());
}
// Retrieve the path where the images are saved
// First check if this config has been set within Hippo, if not retrieved from Konakart
String baseImagePath;
if (StringUtils.isNotEmpty(kkStoreConfig.getImageBasePath())) {
baseImagePath = kkStoreConfig.getImageBasePath();
} else {
baseImagePath = kkengine.getEng().getConfiguration("IMG_BASE_PATH").getValue();
}
ProductFactory productFactory = createProductFactory(kkStoreConfig.getProductFactoryClassName());
productFactory.setSession(jcrSession);
productFactory.setKKStoreConfig(kkStoreConfig);
// We get the products by batches, otherwise it takes too much memory.
int nbrDone = 0;
Products products;
do {
// set the offset to start the search from
dataDescriptorIf.setOffset(nbrDone);
// Search
products = productMgr.searchForProductsWithOptions(kkengine.getSessionId(),
dataDescriptorIf,
productSearchIf,
language.getId(),
fetchProductOptions);
if (products.getProductArray().length > 0) {
nbrDone += products.getProductArray().length;
if (log.isInfoEnabled()) {
log.info("A batch of " + products.getProductArray().length + " product(s) will be synchronized from Konakart to Hippo.");
}
// Insert products into konakart
for (Product product : products.getProductArray()) {
productFactory.add(storeId, product, language, baseImagePath);
}
// Each batch session, save the inserted products
jcrSession.save();
}
} while (nbrDone < products.getTotalNumProducts());
}
return updated;
}
/**
* Synchronize produts status updates and multi-store update
*
* @param kkStoreConfig the store config
* @param jcrSession JCR Session
* @throws Exception .
*/
public static synchronized void updateHippoToKonakart(KKStoreConfig kkStoreConfig, Session jcrSession) throws Exception {
NodeHelper nodeHelper = new NodeHelper(jcrSession);
AdminMgrFactory adminMgrFactory = KKAdminEngine.getInstance().getFactory();
AdminProductMgrIf productMgrIf = adminMgrFactory.getAdminProdMgr(true);
Node seed = getProductRoot(kkStoreConfig, jcrSession);
List<SyncProduct> syncProducts = findAllProductIdsFromRepository(seed, false);
if (syncProducts.size() > 0) {
if (log.isInfoEnabled()) {
log.info(syncProducts.size() + " product(s) will be synchronized from Hippo to Konakart");
}
}
for (SyncProduct syncProduct : syncProducts) {
// Retrieve the hippo node
Node node = jcrSession.getNodeByIdentifier(syncProduct.getHippoUuid());
// Check if the product exists
if (!productMgrIf.doesProductExist(syncProduct.getkProductId())) {
nodeHelper.updateState(node, NodeHelper.UNPUBLISHED_STATE);
}
}
}
/**
* During the development process, sometimes the sync node contains the sync date but no product has been synchronized.
* The goal of this method is to validate if the sync dates must be forgot.
*
* @param kkStoreConfig the current konakart config
* @param jcrSession the JCR session
* @return true if Hippo repository has products, false otherwise
*/
private static boolean hasProducts(KKStoreConfig kkStoreConfig, Session jcrSession) {
try {
Node seed = getProductRoot(kkStoreConfig, jcrSession);
List<SyncProduct> syncProducts = findAllProductIdsFromRepository(seed, true);
return syncProducts != null && syncProducts.size() > 0;
} catch (Exception e) {
log.error("Failed to check if at least a product has been synchronized.", e);
}
return false;
}
@Nonnull
private static Node getProductRoot(KKStoreConfig kkStoreConfig, Session jcrSession) throws Exception {
Node seed = null;
String productRoot = kkStoreConfig.getContentRoot() + "/" + Codecs.encodeNode(kkStoreConfig.getProductFolder());
if (jcrSession.itemExists(productRoot)) {
seed = jcrSession.getNode(productRoot);
}
if (seed == null) {
String absPath = kkStoreConfig.getContentRoot() + "/" + kkStoreConfig.getProductFolder();
NodeHelper nodeHelper = new NodeHelper(jcrSession);
seed = nodeHelper.createMissingFolders(absPath);
}
return seed;
}
private static List<SyncProduct> findAllProductIdsFromRepository(Node seed, Boolean onlyFirstRetrieve) throws RepositoryException {
List<SyncProduct> syncProducts = new ArrayList<SyncProduct>();
try {
if (seed.isNodeType("hippo:handle")) {
seed = seed.getNode(seed.getName());
}
if (seed.isNodeType(KKCndConstants.PRODUCT_DOC_TYPE)) {
SyncProduct syncProduct = new SyncProduct();
syncProduct.setHippoUuid(seed.getIdentifier());
syncProduct.setkProductId((int) seed.getProperty(KKCndConstants.PRODUCT_ID).getLong());
syncProducts.add(syncProduct);
if (onlyFirstRetrieve) {
return syncProducts;
}
} else if (seed.isNodeType("hippostd:folder")) {
for (NodeIterator nodeIt = seed.getNodes(); nodeIt.hasNext(); ) {
Node child = nodeIt.nextNode();
if (child != null) {
syncProducts.addAll(findAllProductIdsFromRepository(child, false));
}
}
}
} catch (Exception e) {
log.error("Failed to retrieve the list of products");
}
return syncProducts;
}
private static ProductFactory createProductFactory(String productFactoryClassName) {
if (StringUtils.isNotBlank(productFactoryClassName)) {
try {
return (ProductFactory) Class.forName(productFactoryClassName).newInstance();
} catch (InstantiationException e) {
log.error("Unable to find the extension class: " + e.toString());
} catch (IllegalAccessException e) {
log.error("Unable to find the extension class: " + e.toString());
} catch (ClassNotFoundException e) {
log.error("Unable to find the extension class: " + e.toString());
}
}
return new DefaultProductFactory();
}
public static class SyncProduct {
private int kProductId;
private String hippoUuid;
public int getkProductId() {
return kProductId;
}
public void setkProductId(int kProductId) {
this.kProductId = kProductId;
}
public String getHippoUuid() {
return hippoUuid;
}
public void setHippoUuid(String hippoUuid) {
this.hippoUuid = hippoUuid;
}
}
}