/*
This file is part of Cyclos (www.cyclos.org).
A project of the Social Trade Organisation (www.socialtrade.org).
Cyclos 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.
Cyclos 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 Cyclos; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package nl.strohalm.cyclos.servlets;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import nl.strohalm.cyclos.entities.customization.images.Image;
import nl.strohalm.cyclos.entities.customization.images.Image.Nature;
import nl.strohalm.cyclos.utils.ClassHelper;
import nl.strohalm.cyclos.utils.ImageHelper.ImageType;
import nl.strohalm.cyclos.utils.ResponseHelper;
import nl.strohalm.cyclos.utils.SpringHelper;
import nl.strohalm.cyclos.utils.WebImageHelper;
import nl.strohalm.cyclos.utils.customizedfile.CustomizedFileHandler;
import nl.strohalm.cyclos.utils.customizedfile.ImageChangeListener;
import org.apache.commons.lang.StringUtils;
/**
* Servlet used to display system images
* @author luis
*/
public class ImageByNameServlet extends HttpServlet {
private static final long serialVersionUID = 8368765800788965163L;
private ServletContext context;
private Nature nature;
private File imagePath;
private File thumbnailPath;
private final Map<String, String> resolvedNames = new HashMap<String, String>();
private WebImageHelper webImageHelper;
private ResponseHelper responseHelper;
@Override
public synchronized void init(final ServletConfig config) throws ServletException {
super.init(config);
context = getServletContext();
webImageHelper = SpringHelper.bean(context, WebImageHelper.class);
responseHelper = SpringHelper.bean(context, ResponseHelper.class);
// Whenever the image is updated, we have to invalidate the cache
SpringHelper.bean(context, CustomizedFileHandler.class).addImageChangeListener(new ImageChangeListener() {
@Override
public void onImageChanged(final Image image) {
if (nature.equals(image.getNature())) {
removeFromCache(image.getName());
}
}
});
final String natureParameter = config.getInitParameter("nature");
try {
nature = Nature.valueOf(natureParameter);
} catch (final Exception e) {
context.log("Invalid nature for " + ClassHelper.getClassName(getClass()) + ": " + natureParameter);
}
imagePath = webImageHelper.imagePath(nature);
thumbnailPath = webImageHelper.thumbnailPath(nature);
}
public void removeFromCache(final String name) {
if (StringUtils.isEmpty(name)) {
return;
}
synchronized (resolvedNames) {
final Iterator<Map.Entry<String, String>> iterator = resolvedNames.entrySet().iterator();
while (iterator.hasNext()) {
final Entry<String, String> entry = iterator.next();
final String current = entry.getKey();
if (name.equals(current) || name.startsWith(current + ".")) {
iterator.remove();
}
}
}
}
@Override
protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
try {
render(request, response);
} catch (final Exception e) {
return;
}
}
/**
* Render the image
*/
private void render(final HttpServletRequest request, final HttpServletResponse response) throws IOException {
final File file = resolveFile(request);
if (file == null) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
} else {
ImageType type;
try {
type = ImageType.getByFileName(file.getName());
} catch (final Exception e) {
type = null;
}
write(request, response, file, type);
}
}
private File resolveFile(final boolean thumbnail, final String name) {
if (name == null) {
return null;
}
final File file = new File(thumbnail ? thumbnailPath : imagePath, name);
if (!file.exists()) {
throw new IllegalArgumentException("Invalid image file: " + file);
}
return file;
}
/**
* Resolve the image file
*/
private File resolveFile(final HttpServletRequest request) {
final boolean thumbnail = "true".equals(request.getParameter("thumbnail"));
final String name = resolveName(request);
return resolveFile(thumbnail, name);
}
/**
* Resolve the image name, searching for an image with or without extension
*/
private String resolveName(final HttpServletRequest request) {
final String name = request.getParameter("image");
if (StringUtils.isEmpty(name)) {
return null;
}
synchronized (resolvedNames) {
String resolved = resolvedNames.get(name);
if (resolved == null) {
final File path = webImageHelper.imagePath(nature);
for (final String image : path.list()) {
if (image.equals(name) || image.startsWith(name + ".")) {
resolved = image;
break;
}
}
resolvedNames.put(name, resolved);
}
return resolved;
}
}
/**
* Write the image file to the response
*/
private void write(final HttpServletRequest request, final HttpServletResponse response, final File file, final ImageType type) throws IOException {
final long ifModifiedSince = request.getDateHeader("If-Modified-Since");
if (ifModifiedSince > 0 && file.lastModified() <= ifModifiedSince) {
// The contents have not changed
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
return;
}
// The contents are modified, or not in browser's cache. Render it
if (type != null) {
response.setContentType(type.getContentType());
}
responseHelper.writeFile(response, file);
response.flushBuffer();
}
}