001: /***************************************************************
002: * This file is part of the [fleXive](R) project.
003: *
004: * Copyright (c) 1999-2007
005: * UCS - unique computing solutions gmbh (http://www.ucs.at)
006: * All rights reserved
007: *
008: * The [fleXive](R) project is free software; you can redistribute
009: * it and/or modify it under the terms of the GNU General Public
010: * License as published by the Free Software Foundation;
011: * either version 2 of the License, or (at your option) any
012: * later version.
013: *
014: * The GNU General Public License can be found at
015: * http://www.gnu.org/copyleft/gpl.html.
016: * A copy is found in the textfile GPL.txt and important notices to the
017: * license from the author are found in LICENSE.txt distributed with
018: * these libraries.
019: *
020: * This library is distributed in the hope that it will be useful,
021: * but WITHOUT ANY WARRANTY; without even the implied warranty of
022: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
023: * GNU General Public License for more details.
024: *
025: * For further information about UCS - unique computing solutions gmbh,
026: * please see the company website: http://www.ucs.at
027: *
028: * For further information about [fleXive](R), please see the
029: * project website: http://www.flexive.org
030: *
031: *
032: * This copyright notice MUST APPEAR in all copies of the file!
033: ***************************************************************/package com.flexive.war.servlet;
034:
035: import com.flexive.shared.*;
036: import com.flexive.shared.content.FxPK;
037: import com.flexive.shared.exceptions.FxApplicationException;
038: import com.flexive.shared.exceptions.FxNoAccessException;
039: import com.flexive.shared.exceptions.FxStreamException;
040: import com.flexive.shared.stream.BinaryDownloadCallback;
041: import com.flexive.shared.stream.FxStreamUtils;
042: import com.flexive.shared.value.BinaryDescriptor;
043: import com.flexive.war.FxThumbnailURIConfigurator;
044: import org.apache.commons.lang.StringUtils;
045: import org.apache.commons.logging.Log;
046: import org.apache.commons.logging.LogFactory;
047:
048: import javax.servlet.*;
049: import javax.servlet.http.HttpServletRequest;
050: import javax.servlet.http.HttpServletResponse;
051: import java.io.IOException;
052: import java.io.UnsupportedEncodingException;
053: import java.net.URLDecoder;
054: import java.net.URLEncoder;
055:
056: /**
057: * Thumbnail servlet.
058: * Usage: <code>/thumbnail/[options][/filename.extension]</code>
059: * <br/>
060: * Options are optional but must at least contain a way to identify the thumbnail to display.
061: * There can be multiple options seperated by a "/".
062: * Options may not contain spaces and values have to be properly URL encoded!
063: * <p/>
064: * Valid options are:
065: * <ul>
066: * <li><code>pk{n.m}</code> - id and version of the content, if no version is given the live version is used</li>
067: * <li><code>xp{path}</code> - URL encoded XPath of the property containing the image (optional, else default will be used)</li>
068: * <li><code>lang{lang}</code> - 2-digit ISO language code
069: * <li><code>lfb{0,1}</code> - language fallback: 0=generate error if language not found, 1=fall back to default language
070: * <li><code>e{3 digit error number}u{error url}</code> - URL encoded error url to redirect for http errors specified in {error number}, if no number is given then the url is a fallback for unclassified errors
071: * <li><code>s{0,1,2,3}</code> - use a predefined image/thumbnail size</li>
072: * <li><code>w{n}</code> - scale to width</li>
073: * <li><code>h{n}</code> - scale to height</li>
074: * <li><code>rot{90,180,270}</code> - rotate 90, 180 or 270 degrees (rotate is always executed before flip operations)</li>
075: * <li><code>flip{h,v}</code> - flip horizontal or vertical (rotate is always executed before flip operations)</li>
076: * <li><code>cropx{x}y{y}w{w}h{h}</code> - crop a box from image defined by x,y,w(idth),h(eight), scaling applies to cropped image!</li>
077: * </ul>
078: * <p/>
079: * Sizes for the <code>s</code> parameters:
080: * 0 ... original image
081: * 1 ... image scaled to fit a 42x42 box
082: * 2 ... image scaled to fit a 85x85 box
083: * 3 ... image scaled to fit a 232x232 box
084: * Selecting a predefined image or thumbnail disables all image manipultion parameters: the image will be served exactly how it
085: * exists in the database/storage.
086: * <p/>
087: * <br/>
088: * Examples:
089: * <code>/thumbnail/pk27.1/s21/test.jpg</code>
090: * <code>/thumbnail/s0/w100/h300/pk4711.MAX/vermax/rot90/test.jpg</code>
091: *
092: * TODO:
093: * rotate, flip, scale and crop are not implemented yet and have no effect!
094: *
095: * @author Daniel Lichtenberger (daniel.lichtenberger@flexive.com), UCS - unique computing solutions gmbh (http://www.ucs.at)
096: * @author Markus Plesser (markus.plesser@flexive.com), UCS - unique computing solutions gmbh (http://www.ucs.at)
097: * @version $Rev: 103 $
098: */
099: public class ThumbnailServlet implements Servlet {
100: private static final Log LOG = LogFactory
101: .getLog(ThumbnailServlet.class);
102: private static final String URI_BASE = "/thumbnail/";
103:
104: private ServletConfig servletConfig = null;
105:
106: /**
107: * Callback to set mimetype and size
108: */
109: private class ThumbnailBinaryCallback implements
110: BinaryDownloadCallback {
111:
112: /**
113: * current response
114: */
115: private HttpServletResponse response;
116:
117: /**
118: * Constructor
119: *
120: * @param response current response
121: */
122: public ThumbnailBinaryCallback(HttpServletResponse response) {
123: this .response = response;
124: }
125:
126: /**
127: * {@inheritDoc}
128: */
129: public void setMimeType(String mimeType) {
130: response.setContentType(mimeType);
131: }
132:
133: /**
134: * {@inheritDoc}
135: */
136: public void setBinarySize(int size) {
137: response.setContentLength(size);
138: }
139: }
140:
141: /**
142: * {@inheritDoc}
143: */
144: public void init(ServletConfig servletConfig)
145: throws ServletException {
146: this .servletConfig = servletConfig;
147: }
148:
149: /**
150: * {@inheritDoc}
151: */
152: public ServletConfig getServletConfig() {
153: return servletConfig;
154: }
155:
156: /**
157: * {@inheritDoc}
158: */
159: public void service(ServletRequest servletRequest,
160: ServletResponse servletResponse) throws ServletException,
161: IOException {
162: final HttpServletRequest request = (HttpServletRequest) servletRequest;
163: final HttpServletResponse response = (HttpServletResponse) servletResponse;
164: final String uri = request.getRequestURI();
165: FxThumbnailURIConfigurator conf = new FxThumbnailURIConfigurator(
166: URLDecoder.decode(uri, "UTF-8"));
167: if (conf.getPK() == null) {
168: if (LOG.isDebugEnabled()) {
169: LOG
170: .debug("Empty request for thumbnail servlet: "
171: + uri);
172: }
173: response.sendError(HttpServletResponse.SC_NOT_FOUND);
174: return;
175: }
176: long binaryId;
177: try {
178: //authorization check and binary lookup
179: binaryId = EJBLookup.getContentEngine().getBinaryId(
180: conf.getPK(),
181: XPathElement.stripType(conf.getXPath()),
182: conf.getLanguage());
183: } catch (FxNoAccessException na) {
184: binaryId = BinaryDescriptor.SYS_NOACCESS;
185: } catch (FxApplicationException e) {
186: binaryId = BinaryDescriptor.SYS_UNKNOWN;
187: }
188: try {
189: response.setDateHeader("Expires", System
190: .currentTimeMillis() + 24L * 3600 * 1000);
191: FxStreamUtils.downloadBinary(new ThumbnailBinaryCallback(
192: response), CacheAdmin.getStreamServers(), response
193: .getOutputStream(), binaryId, conf.getSize());
194: } catch (FxStreamException e) {
195: LOG.error(e);
196: response.sendRedirect(request.getContextPath()
197: + "/adm/images/layout/Logo_Flexive.gif");
198: }
199: }
200:
201: /**
202: * Return a thumbnail link for the given object.
203: *
204: * @param pk the object id
205: * @param size the preview size (if null, the default size is used by the servlet)
206: * @return a thumbnail link for the given object.
207: */
208: public static String getLink(FxPK pk,
209: BinaryDescriptor.PreviewSizes size) {
210: return getLink(pk, size, null);
211: }
212:
213: /**
214: * Return a thumbnail link for the given object.
215: *
216: * @param pk the object id
217: * @param size the preview size (if null, the default size is used by the servlet)
218: * @param xpath the binary xpath (if null, the default preview for the object will be used)
219: * @return a thumbnail link for the given object.
220: */
221: public static String getLink(FxPK pk,
222: BinaryDescriptor.PreviewSizes size, String xpath) {
223: return getLink(pk, size, xpath, 0);
224: }
225:
226: /**
227: * Return a thumbnail link for the given object.
228: *
229: * @param pk the object id
230: * @param size the preview size (if null, the default size is used by the servlet)
231: * @param xpath the binary xpath (if null, the default preview for the object will be used)
232: * @param timestamp the binary timestamp, to be added to the URL for fine-grained cache control
233: * @return a thumbnail link for the given object.
234: */
235: public static String getLink(FxPK pk,
236: BinaryDescriptor.PreviewSizes size, String xpath,
237: long timestamp) {
238: return getLink(pk, size, xpath, timestamp, null);
239: }
240:
241: /**
242: * Return a thumbnail link for the given object.
243: *
244: * @param pk the object id
245: * @param size the preview size (if null, the default size is used by the servlet)
246: * @param xpath the binary xpath (if null, the default preview for the object will be used)
247: * @param timestamp the binary timestamp, to be added to the URL for fine-grained cache control
248: * @param language the language (for multi-lingual objects, otherwise the default language will be used)
249: * @return a thumbnail link for the given object.
250: */
251: public static String getLink(FxPK pk,
252: BinaryDescriptor.PreviewSizes size, String xpath,
253: long timestamp, FxLanguage language) {
254: try {
255: return URI_BASE
256: + "pk"
257: + pk
258: + (size != null ? "/s" + size.getBlobIndex() : "")
259: + (StringUtils.isNotBlank(xpath) ? "/xp"
260: + URLEncoder.encode(FxSharedUtils
261: .escapeXPath(xpath), "UTF-8") : "")
262: + "/ts"
263: + timestamp
264: + (language != null ? "/lang"
265: + language.getIso2digit() : "");
266: } catch (UnsupportedEncodingException e) {
267: // shouldn't happen with UTF-8
268: throw new IllegalArgumentException(e);
269: }
270: }
271:
272: /**
273: * {@inheritDoc}
274: */
275: public String getServletInfo() {
276: return getClass().getName();
277: }
278:
279: /**
280: * {@inheritDoc}
281: */
282: public void destroy() {
283:
284: }
285: }
|