/** * */ package com.salesmanager.core.business.modules.cms.content.infinispan; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.net.FileNameMap; import java.net.URLConnection; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.apache.commons.io.IOUtils; import org.infinispan.tree.Fqn; import org.infinispan.tree.Node; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.salesmanager.core.business.exception.ServiceException; import com.salesmanager.core.business.modules.cms.content.FileGet; import com.salesmanager.core.business.modules.cms.content.FilePut; import com.salesmanager.core.business.modules.cms.content.FileRemove; import com.salesmanager.core.business.modules.cms.impl.CacheManager; import com.salesmanager.core.model.content.FileContentType; import com.salesmanager.core.model.content.InputContentFile; import com.salesmanager.core.model.content.OutputContentFile; /** * Manages * - Images * - Files (js, pdf, css...) on infinispan * @author Umesh Awasthi * @since 1.2 * */ public class CmsStaticContentFileManagerImpl implements FilePut,FileGet,FileRemove { private static final Logger LOGGER = LoggerFactory.getLogger( CmsStaticContentFileManagerImpl.class ); private static CmsStaticContentFileManagerImpl fileManager = null; private static final String ROOT_NAME="static-merchant-"; private String rootName = ROOT_NAME; private CacheManager cacheManager; public void stopFileManager() { try { cacheManager.getManager().stop(); LOGGER.info( "Stopping CMS" ); } catch ( final Exception e ) { LOGGER.error( "Error while stopping CmsStaticContentFileManager", e ); } } public static CmsStaticContentFileManagerImpl getInstance() { if ( fileManager == null ) { fileManager = new CmsStaticContentFileManagerImpl(); } return fileManager; } /** * <p>Method to add static content data for given merchant.Static content data can be of following type * <pre> * 1. CSS and JS files * 2. Digital Data like audio or video * </pre> * </p> * <p> * Merchant store code will be used to create cache node where merchant data will be stored,input data will * contain name, file as well type of data being stored. * @see FileContentType * </p> * * @param merchantStoreCode merchant store for whom data is being stored * @param inputStaticContentData data object being stored * @throws ServiceException * */ @Override public void addFile( final String merchantStoreCode, final InputContentFile inputStaticContentData ) throws ServiceException { if ( cacheManager.getTreeCache() == null ) { LOGGER.error( "Unable to find cacheManager.getTreeCache() in Infinispan.." ); throw new ServiceException( "CmsStaticContentFileManagerInfinispanImpl has a null cacheManager.getTreeCache()" ); } try { String nodePath = this.getNodePath(merchantStoreCode, inputStaticContentData.getFileContentType()); final Node<String, Object> merchantNode = this.getNode(nodePath); merchantNode.put(inputStaticContentData.getFileName(), IOUtils.toByteArray( inputStaticContentData.getFile() )); LOGGER.info( "Content data added successfully." ); } catch ( final Exception e ) { LOGGER.error( "Error while saving static content data", e ); throw new ServiceException( e ); } } /** * <p> * Method to add files for given store.Files will be stored in Infinispan and will be retrieved based on * the storeID. Following steps will be performed to store static content files * </p> * <li>Merchant Node will be retrieved from the cacheTree if it exists else new node will be created.</li> <li> * Files will be stored in StaticContentCacheAttribute , which eventually will be stored in Infinispan</li> * * @param merchantStoreCode Merchant store for which files are getting stored in Infinispan. * @param inputStaticContentDataList input static content file list which will get {@link InputContentImage} stored * @throws ServiceException if content file storing process fail. * @see InputStaticContentData * @see StaticContentCacheAttribute */ @Override public void addFiles( final String merchantStoreCode, final List<InputContentFile> inputStaticContentDataList ) throws ServiceException { if ( cacheManager.getTreeCache() == null ) { LOGGER.error( "Unable to find cacheManager.getTreeCache() in Infinispan.." ); throw new ServiceException( "CmsStaticContentFileManagerInfinispanImpl has a null cacheManager.getTreeCache()" ); } try { for(final InputContentFile inputStaticContentData:inputStaticContentDataList){ String nodePath = this.getNodePath(merchantStoreCode, inputStaticContentData.getFileContentType()); final Node<String, Object> merchantNode = this.getNode(nodePath); merchantNode.put(inputStaticContentData.getFileName(), IOUtils.toByteArray( inputStaticContentData.getFile() )); } LOGGER.info( "Total {} files added successfully.",inputStaticContentDataList.size() ); } catch ( final Exception e ) { LOGGER.error( "Error while saving content image", e ); throw new ServiceException( e ); } } /** * Method to return static data for given Merchant store based on the file name. Content data will be searched * in underlying Infinispan cache tree and {@link OutputStaticContentData} will be returned on finding an associated * file. In case of no file, null be returned. * * @param store Merchant store * @param contentFileName name of file being requested * @return {@link OutputStaticContentData} * @throws ServiceException */ @Override public OutputContentFile getFile( final String merchantStoreCode, final FileContentType fileContentType, final String contentFileName ) throws ServiceException { if ( cacheManager.getTreeCache() == null ) { throw new ServiceException( "CmsStaticContentFileManagerInfinispan has a null cacheManager.getTreeCache()" ); } OutputContentFile outputStaticContentData=null; InputStream input = null; try { String nodePath = this.getNodePath(merchantStoreCode, fileContentType); final Node<String, Object> merchantNode = this.getNode(nodePath); final byte[] fileBytes= (byte[]) merchantNode.get( contentFileName ); if ( fileBytes == null ) { LOGGER.warn( "file byte is null, no file found" ); return null; } input=new ByteArrayInputStream( fileBytes ); final ByteArrayOutputStream output = new ByteArrayOutputStream(); IOUtils.copy( input, output ); outputStaticContentData=new OutputContentFile(); outputStaticContentData.setFile( output ); outputStaticContentData.setMimeType( URLConnection.getFileNameMap().getContentTypeFor(contentFileName) ); outputStaticContentData.setFileName( contentFileName ); outputStaticContentData.setFileContentType( fileContentType ); } catch ( final Exception e ) { LOGGER.error( "Error while fetching file for {} merchant ", merchantStoreCode); throw new ServiceException( e ); } return outputStaticContentData != null ? outputStaticContentData : null; } @Override public List<OutputContentFile> getFiles( final String merchantStoreCode, final FileContentType staticContentType) throws ServiceException { if ( cacheManager.getTreeCache() == null ) { throw new ServiceException( "CmsStaticContentFileManagerInfinispan has a null cacheManager.getTreeCache()" ); } List<OutputContentFile> images = new ArrayList<OutputContentFile>(); try { FileNameMap fileNameMap = URLConnection.getFileNameMap(); String nodePath = this.getNodePath(merchantStoreCode, staticContentType); final Node<String, Object> merchantNode = this.getNode(nodePath); for(String key : merchantNode.getKeys()) { byte[] imageBytes = (byte[])merchantNode.get( key ); OutputContentFile contentImage = new OutputContentFile(); InputStream input = new ByteArrayInputStream( imageBytes ); ByteArrayOutputStream output = new ByteArrayOutputStream(); IOUtils.copy( input, output ); String contentType = fileNameMap.getContentTypeFor( key ); contentImage.setFile( output ); contentImage.setMimeType( contentType ); contentImage.setFileName( key ); images.add( contentImage ); } } catch ( final Exception e ) { LOGGER.error( "Error while fetching file for {} merchant ", merchantStoreCode); throw new ServiceException( e ); } return images; } @Override public void removeFile( final String merchantStoreCode, final FileContentType staticContentType, final String fileName ) throws ServiceException { if ( cacheManager.getTreeCache() == null ) { throw new ServiceException( "CmsStaticContentFileManagerInfinispan has a null cacheManager.getTreeCache()" ); } try { String nodePath = this.getNodePath(merchantStoreCode, staticContentType); final Node<String, Object> merchantNode = this.getNode(nodePath); merchantNode.remove(fileName); } catch ( final Exception e ) { LOGGER.error( "Error while fetching file for {} merchant ", merchantStoreCode); throw new ServiceException( e ); } } /** * Removes the data in a given merchant node */ @SuppressWarnings("unchecked") @Override public void removeFiles( final String merchantStoreCode ) throws ServiceException { LOGGER.info( "Removing all images for {} merchant ",merchantStoreCode); if ( cacheManager.getTreeCache() == null ) { LOGGER.error( "Unable to find cacheManager.getTreeCache() in Infinispan.." ); throw new ServiceException( "CmsImageFileManagerInfinispan has a null cacheManager.getTreeCache()" ); } try { final StringBuilder merchantPath = new StringBuilder(); merchantPath.append( getRootName()).append(merchantStoreCode ); cacheManager.getTreeCache().getRoot().remove(merchantPath.toString()); } catch ( final Exception e ) { LOGGER.error( "Error while deleting content image for {} merchant ", merchantStoreCode); throw new ServiceException( e ); } } @SuppressWarnings({ "unchecked"}) private Node<String, Object> getNode( final String node ) { LOGGER.debug( "Fetching node for store {} from Infinispan", node ); final StringBuilder merchantPath = new StringBuilder(); merchantPath.append( getRootName() ).append(node); Fqn contentFilesFqn = Fqn.fromString(merchantPath.toString()); Node<String,Object> nd = cacheManager.getTreeCache().getRoot().getChild(contentFilesFqn); if(nd==null) { cacheManager.getTreeCache().getRoot().addChild(contentFilesFqn); nd = cacheManager.getTreeCache().getRoot().getChild(contentFilesFqn); } return nd; } private String getNodePath(final String storeCode,final FileContentType contentType) { StringBuilder nodePath = new StringBuilder(); nodePath.append(storeCode).append("/").append(contentType.name()); return nodePath.toString(); } public CacheManager getCacheManager() { return cacheManager; } public void setCacheManager(CacheManager cacheManager) { this.cacheManager = cacheManager; } /** * Queries the CMS to retrieve all static content files. Only the name of the file will be returned to the client * @param merchantStoreCode * @return * @throws ServiceException */ @Override public List<String> getFileNames(final String merchantStoreCode, final FileContentType staticContentType) throws ServiceException { if ( cacheManager.getTreeCache() == null ) { throw new ServiceException( "CmsStaticContentFileManagerInfinispan has a null cacheManager.getTreeCache()" ); } try { String nodePath = this.getNodePath(merchantStoreCode, staticContentType); final Node<String, Object> objectNode = this.getNode(nodePath); if(objectNode.getKeys().isEmpty()) { LOGGER.warn( "Unable to find content attribute for given merchant" ); return Collections.<String> emptyList(); } return new ArrayList<String>(objectNode.getKeys()); } catch ( final Exception e ) { LOGGER.error( "Error while fetching file for {} merchant ", merchantStoreCode); throw new ServiceException( e ); } } public void setRootName(String rootName) { this.rootName = rootName; } public String getRootName() { return rootName; } }