/**********************************************************************
* This file is part of iDempiere ERP Open Source *
* http://www.idempiere.org *
* *
* Copyright (C) Contributors *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301, USA. *
**********************************************************************/
package org.adempiere.webui.util;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.logging.Level;
import org.adempiere.base.Core;
import org.compiere.model.MImage;
import org.compiere.util.CCache;
import org.compiere.util.CLogger;
import org.compiere.util.Env;
import org.zkoss.image.AImage;
import org.zkoss.image.Image;
/**
* Images can come from inside system or from outside system.
* For images from outside, we will cache it for better performance.
* This class is for management of image cache and provide related helper function.
* @author hieplq
*
*/
public class ManageImageCache {
protected static final CLogger log = CLogger.getCLogger (ManageImageCache.class);
/**
* This cache don't expire, you must reset cache when there are update of images.
*/
private final CCache imageCache = new CCache(null, "WindowImageCache", 50, 0, false);
private static ManageImageCache instance;
/**
* Get singleton instance
* @return ManageImageCache instance
*/
public static ManageImageCache instance(){
if (instance == null){
synchronized (ManageImageCache.class){
if (instance == null)
instance = new ManageImageCache();
}
}
return instance;
}
/**
* nvestigate path of MImage, if path is internal return internal url, otherwise return null.
* @param image
* @return URL of image or null
*/
public static URL getImageInternalUrl (MImage image){
if (image == null)
return null;
return getImageInternalUrl(image.getImageURL());
}
/**
* Investigate image path, if path is internal return internal url, otherwise return null.
* @param url
* @return URL of image or null
*/
public static URL getImageInternalUrl (String url){
if (url == null || url.trim().length() == 0 || url.indexOf("://") > 0)
return null;
URL urlRsource = Core.getResourceFinder().getResource(url);
return urlRsource;
}
/**
* Load image from url
* @param imagePath
* @return image data
*/
protected static byte [] loadImageData (String imagePath){
byte [] data = null;
URLConnection conn;
try {
URL url = new URL(imagePath);
conn = url.openConnection();
conn.setUseCaches(false);
InputStream is = conn.getInputStream();
byte[] buffer = new byte[1024*8]; // 8kB
ByteArrayOutputStream os = new ByteArrayOutputStream();
int length = -1;
while ((length = is.read(buffer)) != -1)
os.write(buffer, 0, length);
is.close();
data = os.toByteArray();
os.close();
} catch (IOException e) {
if (log.isLoggable(Level.CONFIG)) log.config (e.toString());
}
return data;
}
/**
* If image is not in cache, load it (imagePath can be id of MImage).
* @param imagePath
* @return image from cache or loaded from path. null if there is exception in loading.
*/
public Image getImage(String imagePath){
if (imagePath == null || imagePath.trim().length() == 0)
return null;
Image aImage = null;
boolean hasCache = false;
synchronized (imageCache) {
hasCache = imageCache.containsKey(imagePath);
}
if (!hasCache) {
try{
int mImageId = Integer.parseInt(imagePath);
loadImage(MImage.get(Env.getCtx(), mImageId));
}catch (NumberFormatException ex){
loadExtend(imagePath);
}
}
synchronized (imageCache) {
aImage = imageCache.get(imagePath);
}
return aImage;
}
/**
* Get image from URL or cache.
* @param url
* @return {@link Image}
*/
public Image getImage(URL url) {
if (url == null)
return null;
Image image = null;
boolean hasCache = false;
synchronized (imageCache) {
hasCache = imageCache.containsKey(url.toString());
if (hasCache)
image = imageCache.get(url.toString());
}
if (!hasCache) {
try {
image = new AImage(url);
} catch (IOException e) {
log.log(Level.SEVERE, e.getMessage(), e);
}
synchronized (imageCache) {
imageCache.put(url.toString(), image);
}
}
return image;
}
/**
* If MImage has image URL or binary image data, load it into cache and return key (id or url).
* other return null
* @param mImage
* @return
*/
public String loadImage (MImage mImage){
if (mImage == null)
return null;
boolean hasCache = false;
String strId = String.valueOf(mImage.get_ID());
synchronized (imageCache) {
hasCache = imageCache.containsKey(strId);
}
if(hasCache){
return strId;
}
if (mImage.getBinaryData() != null){
synchronized (imageCache) {
Image loadImage = null;
try {
loadImage = new AImage (mImage.getName(), mImage.getBinaryData());
} catch (IOException e) {
// do nothing treat image as null
}
imageCache.put(String.valueOf(mImage.get_ID()), loadImage);
}
return strId;
}else if (mImage.getImageURL() != null && mImage.getImageURL().trim().length() > 0 && getImageInternalUrl(mImage.getImageURL()) == null){
synchronized (imageCache) {
hasCache = imageCache.containsKey(mImage.getImageURL());
}
if (!hasCache){
loadExtend (mImage.getImageURL());
}
return mImage.getImageURL();
}
return null;
}
/**
* load external image into cache
* @param imagePath
*/
protected void loadExtend (String imagePath){
byte[] data = loadImageData(imagePath);
AImage aImage = null;
// when can't load image (due to incorrect url or disconnect or any exception), just set image as null
if (data != null)
try {
aImage = new AImage(imagePath, data);
} catch (IOException e) {
aImage = null;
}
synchronized (imageCache) {
imageCache.put(imagePath, aImage);
}
}
}