/* * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package com.celements.photo.plugin.cmd; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.xwiki.cache.Cache; import org.xwiki.cache.CacheException; import org.xwiki.cache.CacheManager; import org.xwiki.cache.config.CacheConfiguration; import org.xwiki.cache.eviction.LRUEvictionConfiguration; import org.xwiki.context.Execution; import com.celements.photo.container.ImageDimensions; import com.celements.photo.plugin.CelementsPhotoPlugin.SupportedFormat; import com.xpn.xwiki.XWikiContext; import com.xpn.xwiki.XWikiException; import com.xpn.xwiki.doc.XWikiAttachment; import com.xpn.xwiki.web.Utils; public class ImageCacheCommand { private static final Log LOGGER = LogFactory.getFactory().getInstance( ImageCacheCommand.class); /** * Cache for already served images. */ private Cache<byte[]> imageCache; private boolean initializedCache; /** * The size of the cache. This parameter can be configured using * the key <tt>xwiki.plugin.image.cache.capacity</tt>. */ private int capacity = 50; /** * The time to live (seconds) of a cache entry after last access. This parameter can be * configured using the key <tt>xwiki.plugin.image.cache.ttl</tt>. */ private Integer ttlConfig = 2500000; public ImageCacheCommand() { initializedCache = false; } Cache<byte[]> getImageCache() { if (!initializedCache) { initCache(); } return imageCache; } /* Copy, Paste & Customize from com.xpn.xwiki.plugin.image */ synchronized void initCache() { CacheConfiguration configuration = new CacheConfiguration(); configuration.setConfigurationId("celements.photo"); // Set folder o store cache File tempDir = getContext().getWiki().getTempDirectory(getContext()); File imgTempDir = new File(tempDir, configuration.getConfigurationId()); try { imgTempDir.mkdirs(); } catch (Exception ex) { LOGGER.warn("Cannot create temporary files", ex); } configuration.put("cache.path", imgTempDir.getAbsolutePath()); // Set cache constraints LRUEvictionConfiguration lru = new LRUEvictionConfiguration(); capacity = readIntegerValue("xwiki.plugin.image.cache.capacity", capacity); lru.setMaxEntries(capacity); ttlConfig = readIntegerValue("xwiki.plugin.image.cache.ttl", ttlConfig); lru.setTimeToLive(ttlConfig); LOGGER.debug("creating an image cache with capacity [" + lru.getMaxEntries() + "] and ttl [" + lru.getTimeToLive() + "] and cache.path [" + lru.get("cache.path") + "]."); configuration.put(LRUEvictionConfiguration.CONFIGURATIONID, lru); try { imageCache = getCacheManager().createNewCache(configuration); } catch (CacheException exp) { LOGGER.error("Error initializing the image cache.", exp); } initializedCache = true; } private Integer readIntegerValue(String paramKey, Integer defaultValue) { String capacityParam = ""; try { capacityParam = getContext().getWiki().Param(paramKey); if ((capacityParam != null) && (!capacityParam.equals(""))) { return Integer.parseInt(capacityParam); } } catch (NumberFormatException ex) { LOGGER.error("Error in ImagePlugin reading capacity: " + capacityParam, ex); } return defaultValue; } public void addToCache(String key, XWikiAttachment attachment) throws XWikiException { if (getImageCache() != null) { try { getImageCache().set(key, IOUtils.toByteArray(attachment.getContentInputStream( getContext()))); } catch (IOException exp) { LOGGER.error("Failed to cache image [" + key + "].", exp); } } else { LOGGER.info("Caching of images deactivated."); } } public InputStream getImageForKey(String key) { if (getImageCache() != null) { byte[] cachedData = getImageCache().get(key); if (cachedData != null) { return new ByteArrayInputStream(cachedData); } } return null; } String getCacheKey(XWikiAttachment attachment, ImageDimensions dimension, String copyright, String watermark, int cropX, int cropY, int cropW, int cropH ) throws NoSuchAlgorithmException { String key = attachment.getId() + "-" + attachment.getVersion() + "-" + getType(attachment.getMimeType(getContext())) + "-" + attachment.getDate().getTime() + "-" + dimension.getWidth() + "-" + dimension.getHeight() + "-" + getAditionalInfoHash(copyright, watermark, cropX, cropY, cropW, cropH); return key; } String getAditionalInfoHash(String copyright, String watermark, int cropX, int cropY, int cropW, int cropH) throws NoSuchAlgorithmException { String hashValue = null; if(((watermark != null) && (watermark.length() > 0)) || ((copyright != null) && (copyright.length() > 0))){ hashValue = watermark + "<:>" + copyright; } if((cropX >= 0) && (cropY >= 0) && (cropW > 0) && (cropH > 0)){ if(hashValue != null) { hashValue += "<:>"; } else { hashValue = ""; } hashValue += cropX + ":" + cropY + "_" + cropW + "x" + cropH; } String hash = ""; if(hashValue != null) { MessageDigest md = MessageDigest.getInstance("MD5"); md.update(hashValue.getBytes()); byte[] digest = md.digest(); for(int i = 0; i < digest.length; i++){ hash += Integer.toHexString(Math.abs((int)digest[i])); } } return hash; } /** * @return the type of the image, as an integer code, used in the generation * of the key of the image cache */ public static int getType(String mimeType) { for (SupportedFormat f : SupportedFormat.values()) { if (f.getMimeType().equals(mimeType)) { return f.getCode(); } } return 0; } public void flushCache() { if ((initializedCache) && (imageCache != null)) { imageCache.removeAll(); } imageCache = null; } private XWikiContext getContext() { return (XWikiContext)Utils.getComponent(Execution.class).getContext().getProperty( "xwikicontext"); } private CacheManager getCacheManager() { return Utils.getComponent(CacheManager.class); } }