libreccm-legacy/ccm-cms/src/com/arsdigita/cms/dispatcher/BaseImage.java

330 lines
12 KiB
Java
Executable File

/*
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
*
* This library 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 library 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 library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
package com.arsdigita.cms.dispatcher;
import com.arsdigita.bebop.parameters.BigDecimalParameter;
import com.arsdigita.caching.CacheTable;
import com.arsdigita.cms.Asset;
import com.arsdigita.cms.CMS;
import com.arsdigita.cms.CachedImage;
import com.arsdigita.cms.ImageAsset;
import com.arsdigita.dispatcher.DispatcherHelper;
import com.arsdigita.dispatcher.RequestContext;
import com.arsdigita.domain.DataObjectNotFoundException;
import com.arsdigita.domain.DomainObjectFactory;
import com.arsdigita.mimetypes.MimeType;
import com.arsdigita.persistence.OID;
import com.arsdigita.toolbox.ui.OIDParameter;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigDecimal;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
/**
* A resource handler which streams out a blob from the database.
* This class can use a special image cache to speed up image dispatching. Also,
* during dispatch this class will create server-side resized images depending
* on the URL parameter. Resizing is done by ImageScalr. The image cache can be
* activated and configured by com.arsdigita.cms.image_cache.* parameters.
*
* @author Stanislav Freidin (sfreidin@arsdigita.com)
* @author Michael Pih (pihman@arsdigita.com)
* @author Sören Bernstein <quasi@quasiweb.de>
* @version $Revision: #20 $ $DateTime: 2004/08/17 23:15:09 $
* @version $Id: BaseImage.java 1571 2007-04-20 15:57:54Z apevec $
*/
public class BaseImage extends ResourceHandlerImpl {
public static final String IMAGE_ID = "image_id";
public static final String OID_PARAM = "oid";
private final static String s_defaultName = "Image";
private BigDecimalParameter m_imageId;
private OIDParameter m_oid;
private final boolean m_download;
private String m_disposition;
// ImageCache
private static CacheTable s_imageCache = null;
static {
if (CMS.getConfig().getImageCacheEnabled()) {
s_imageCache = new CacheTable("BaseImageCache",
CMS.getConfig().getImageCacheMaxAge(),
CMS.getConfig().getImageCacheMaxSize());
}
}
private final boolean IMAGE_CACHE_PREFETCH = CMS.getConfig().getImageCachePrefetchEnabled();
private static final Logger s_log = Logger.getLogger(BaseImage.class);
/**
* Construct the resource handler
*/
public BaseImage(boolean download) {
m_imageId = new BigDecimalParameter(IMAGE_ID);
m_oid = new OIDParameter(OID_PARAM);
m_download = download;
if (m_download) {
m_disposition = "attachment; filename=";
} else {
m_disposition = "inline; filename=";
}
}
/**
* Sets RFC2183 governed Content-Disposition header to supply filename to
* client. See section 19.5.1 of RFC2616 for interpretation of
* Content-Disposition in HTTP.
*/
protected void setFilenameHeader(HttpServletResponse response,
CachedImage cachedImage) {
String filename = cachedImage.getName();
if (filename == null) {
filename = s_defaultName;
}
// quote the file name to deal with any special
// characters in the name of the file
StringBuilder disposition = new StringBuilder(m_disposition);
disposition.append('"').append(filename).append('"');
response.setHeader("Content-Disposition", disposition.toString());
}
private void setHeaders(HttpServletResponse response, CachedImage cachedImage) {
setFilenameHeader(response, cachedImage);
response.setContentLength(cachedImage.getSize());
MimeType mimeType = cachedImage.getMimeType();
if (m_download || mimeType == null) {
// Section 19.5.1 of RFC2616 says this implies download
// instead of view
response.setContentType("application/octet-stream");
} else {
response.setContentType(mimeType.getMimeType());
}
// Default caching for all other types
if ("live".equals(cachedImage.getVersion())) {
DispatcherHelper.cacheForWorld(response);
} else {
DispatcherHelper.cacheDisable(response);
}
}
private void send(HttpServletResponse response, CachedImage cachedImage) throws IOException {
// Stream the blob.
OutputStream out = response.getOutputStream();
try {
cachedImage.writeBytes(out);
} finally {
out.close();
}
}
/**
* Streams an image from the database.
*
* @param request The servlet request object
* @param response the servlet response object
* @param actx The request context
*/
@Override
public void dispatch(HttpServletRequest request,
HttpServletResponse response,
RequestContext actx)
throws IOException, ServletException {
OID oid = null;
BigDecimal imageId = null;
CachedImage cachedImage = null;
String resizeParam = "";
// Get URL parameters
String maxWidthParam = request.getParameter("maxWidth");
String maxHeightParam = request.getParameter("maxHeight");
// Need the OID, but can work with imageId
try {
// Try to get OID and imageId, there should only be one not both
oid = (OID) m_oid.transformValue(request);
imageId = (BigDecimal) m_imageId.transformValue(request);
} catch (Exception e) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST,
e.toString());
return;
}
// We can't handle both OID and imageId at the same time
if ((imageId == null && oid == null) || (imageId != null && oid != null)) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST,
"either " + IMAGE_ID + " or " + OID_PARAM + " is required.");
return;
}
// If the OID is still null
if (oid == null) {
// Get the OID from the imageID
oid = new OID(ImageAsset.BASE_DATA_OBJECT_TYPE, imageId);
}
// Finally, we have a valid OID
// Process URL parameter
if (maxWidthParam != null && maxHeightParam != null) {
try {
// Set width if supplied by URL parameter
if (!maxWidthParam.isEmpty() && maxWidthParam.matches("^[0-9]*$")) {
resizeParam += "&maxWidth=" + maxWidthParam;
}
} catch (NumberFormatException numberEx) {
s_log.warn("maxWidth parameter invalid " + maxWidthParam);
}
try {
// Set height if supplied by URL parameter
if (!maxHeightParam.isEmpty() && maxHeightParam.matches("^[0-9]*$")) {
resizeParam += "&maxHeight=" + maxHeightParam;
}
} catch (NumberFormatException numberEx) {
s_log.warn("maxHeight parameter invalid " + maxHeightParam);
}
}
// Now, we have all information we need to proceed
// Get the image
cachedImage = this.getImage(response, oid, resizeParam);
if (cachedImage == null) {
// ok, something is really weird now. Can't find image with this oid. Bailing out.
return;
}
setHeaders(response, cachedImage);
send(response, cachedImage);
}
private CachedImage getImage(HttpServletResponse response, OID oid, String resizeParam) throws IOException {
CachedImage cachedImage = null;
// Test for cache
if (s_imageCache != null) {
// Image cache is enabled, try to fetch images from cache
cachedImage = getImageFromCache(response, oid, resizeParam);
} else {
// Image cache is disabled
// Get the original image from db
cachedImage = getImageFromDB(response, oid);
if (cachedImage != null && !resizeParam.isEmpty()) {
cachedImage = new CachedImage(cachedImage, resizeParam);
}
}
return cachedImage;
}
/**
* Fetches the {@link CachedImage} from the image cache. If tge object
* could not be found in the cache, this method falls back to
* {@link #getImageFromDB(javax.servlet.http.HttpServletResponse, com.arsdigita.persistence.OID)}.
* This method will also store the CachedImage in the image cache for future
* use.
*
* @param response The HttpServletResponse
* @param oid the {@link OID} of the wanted object
* @param resizeParam the resize paramters of the wanted object
* @return the wanted {@link CachedImage} in the correct size or null, if the object could not be found
* @throws IOException
*/
private CachedImage getImageFromCache(HttpServletResponse response, OID oid, String resizeParam) throws IOException {
CachedImage cachedImage;
cachedImage = (CachedImage) s_imageCache.get(oid.toString() + resizeParam);
// If we coundn't find the specific version
if (cachedImage == null) {
// If we were looking for a resized version
if (!resizeParam.isEmpty()) {
// try to find the original version in the cache by recursion
cachedImage = this.getImageFromCache(response, oid, "");
if (cachedImage != null) {
cachedImage = new CachedImage(cachedImage, resizeParam);
s_imageCache.put(oid.toString() + resizeParam, cachedImage);
}
} else {
// look for the original version in the database
cachedImage = getImageFromDB(response, oid);
// If we found the image, put it into the image cache
if (cachedImage != null && IMAGE_CACHE_PREFETCH) {
s_imageCache.put(oid.toString(), cachedImage);
}
}
}
return cachedImage;
}
/**
* Fetches the {@link ImageAsset} with the supplied {@link OID} from the database
* and converts it to a {@CachedImage}.
*
* @param response the HttpServletResponse
* @param oid the {@link OID} to the ImageAsset
* @return the ImageAsset with the oid as CachedImage or null, if not found
* @throws IOException
*/
private CachedImage getImageFromDB(HttpServletResponse response, OID oid) throws IOException {
ImageAsset imageAsset = null;
s_log.info(oid.toString() + " is not in imageCache. Fetching from database");
// Try to get the Asset from database and test for ImageAsset
try {
Asset a = (Asset) DomainObjectFactory.newInstance(oid);
// Make sure we have an ImageAsset
if (a instanceof ImageAsset) {
imageAsset = (ImageAsset) a;
} else {
if (s_log.isInfoEnabled()) {
s_log.info("Asset " + oid + " is not an ImageAsset");
}
}
} catch (DataObjectNotFoundException nfe) {
response.sendError(HttpServletResponse.SC_NOT_FOUND, "no ImageAsset with oid " + oid);
return null;
}
return new CachedImage(imageAsset);
}
}