/*
* Copyright 2009-2010 the original author or authors.
*
* Licensed under the Apache license, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.internna.iwebmvc.spring.mvc.controllers;
import static org.springframework.util.FileCopyUtils.copy;
import static org.springframework.util.StringUtils.hasText;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringReader;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.internna.iwebmvc.crypto.Decipherer;
import org.internna.iwebmvc.dao.DAO;
import org.internna.iwebmvc.model.Document;
import org.internna.iwebmvc.model.UUID;
import org.internna.iwebmvc.utils.Assert;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
/**
* Serves files from META-INF directory.
*
* @author Jose Noheda
* @since 2.0
*/
@Controller
@RequestMapping({"/resource.iwebmvc"})
public final class ResourceController implements InitializingBean {
protected Log logger = LogFactory.getLog(getClass());
private Calendar calendar;
private DateFormat httpDateFormat;
private int seconds = 3 * 24 * 60 * 60;
private long lastModified = new Date().getTime();
private DAO dao;
private Decipherer decipherer;
private Map<String, String> mimes;
public void setDao(DAO dao) {
this.dao = dao;
}
public void setDecipherer(Decipherer decipherer) {
this.decipherer = decipherer;
}
@Override public void afterPropertiesSet() throws Exception {
mimes = new HashMap<String, String>();
mimes.put("swf", "application/x-shockwave-flash");
mimes.put("gif", "image/gif");
mimes.put("png", "image/png");
mimes.put("jpg", "image/jpeg");
mimes.put("jpeg", "image/jpeg");
calendar = Calendar.getInstance();
calendar.add(Calendar.DATE, seconds / 60 / 60 / 24);
httpDateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
httpDateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
}
protected BufferedImage getScaledInstance(BufferedImage original, int width, int height) {
int w = original.getWidth();
int h = original.getHeight();
BufferedImage ret = original;
int type = original.getTransparency() == Transparency.OPAQUE ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
while ((w > width) & (h > height)) {
if (w > width) {
w /= 2;
if (w < width) w = width;
}
if (h > height) {
h /= 2;
if (h < height) h = height;
}
BufferedImage tmp = new BufferedImage(w, h, type);
Graphics2D g2 = tmp.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
g2.drawImage(ret, 0, 0, w, h, null);
g2.dispose();
ret.flush();
ret = tmp;
}
return ret;
}
@RequestMapping(method = RequestMethod.GET)
public void serve(@RequestParam("file") String file, @RequestParam(required = false, value = "width") String width, @RequestParam(required = false, value = "height") String height, @RequestParam(required = false, value = "escape") String escape, HttpServletRequest request, HttpServletResponse response) throws IOException {
if (hasText(file)) {
if (decipherer.isEncrypted(file)) {
Document doc = dao.find(Document.class, new UUID(decipherer.decrypt(file)));
response.setContentType(doc.getMimeType());
if (logger.isDebugEnabled()) logger.debug("Setting MIME type [" + doc.getMimeType() + "] to document [" + doc.getId() + "]");
if (doc.isText()) sendText(doc, escape, response);
else send(new ByteArrayInputStream(doc.getContents()), doc.getMimeType(), response.getOutputStream(), width, height);
} else {
if (file.contains("..")) throw new IllegalArgumentException("Security alert: Will not serve file [" + file + "]");
InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("META-INF" + (file.startsWith("/") ? file : "/" + file));
String ext = StringUtils.getFilenameExtension(file);
String mimeType = null;
if (mimes.containsKey(ext)) {
mimeType = mimes.get(ext);
response.setContentType(mimeType);
if (logger.isDebugEnabled()) logger.debug("Setting MIME type [" + mimeType + "] to [" + file + "]");
}
response.setHeader("Pragma", "public");
response.setDateHeader("Last-Modified", lastModified);
response.setHeader("Cache-Control", "PUBLIC, max-age=" + seconds + ", must-revalidate");
response.setHeader("Expires", httpDateFormat.format(calendar.getTime()));
if (request.getDateHeader("If-Modified-Since") > -1) response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
else if ((in != null) && (in.available() > 0)) send(in, mimeType, response.getOutputStream(), width, height);
}
response.getOutputStream().close();
}
}
protected void sendText(Document doc, String escape, HttpServletResponse response) throws IOException {
byte[] contents = doc.getContents();
if (hasText(escape) && "true".equals(escape.toLowerCase(Locale.ENGLISH))) {
String data = new String(doc.getContents());
if (hasText(data)) data = data.replaceAll("<", "<");
contents = data.getBytes();
}
send(new ByteArrayInputStream(contents), doc.getMimeType(), response.getOutputStream(), null, null);
}
private void send(InputStream content, String mimetype, OutputStream out, String width, String height) throws IOException {
if (hasText(width)) {
Assert.notNull(height);
BufferedImage im = getScaledInstance(ImageIO.read(content), Integer.parseInt(width), Integer.parseInt(height));
ImageIO.write(im, getImageFormat(mimetype), out);
} else copy(content, out);
}
protected String getImageFormat(String mimeType) {
return "PNG";
}
}