mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2026-04-03 00:00:04 -04:00
Merge branch 'master' of https://github.com/haraldk/TwelveMonkeys
Conflicts: sandbox/sandbox-common/src/main/java/com/twelvemonkeys/io/enc/DeflateEncoder.java sandbox/sandbox-common/src/main/java/com/twelvemonkeys/io/enc/InflateDecoder.java servlet/src/main/java/com/twelvemonkeys/servlet/GenericFilter.java servlet/src/main/java/com/twelvemonkeys/servlet/GenericServlet.java servlet/src/main/java/com/twelvemonkeys/servlet/HttpServlet.java servlet/src/main/java/com/twelvemonkeys/servlet/ServletResponseStreamDelegate.java servlet/src/main/java/com/twelvemonkeys/servlet/cache/CacheResponseWrapper.java servlet/src/main/java/com/twelvemonkeys/servlet/cache/HTTPCache.java servlet/src/main/java/com/twelvemonkeys/servlet/cache/SerlvetCacheResponseWrapper.java servlet/src/main/java/com/twelvemonkeys/servlet/cache/WritableCachedResponseImpl.java servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageServletResponseImpl.java
This commit is contained in:
@@ -50,10 +50,10 @@ import java.util.Enumeration;
|
||||
* <p/
|
||||
* {@code GenericFilter} has an auto-init system, that automatically invokes
|
||||
* the method matching the signature {@code void setX(<Type>)},
|
||||
* for every init-parameter {@code x}. Both camelCase and lisp-style paramter
|
||||
* for every init-parameter {@code x}. Both camelCase and lisp-style parameter
|
||||
* naming is supported, lisp-style names will be converted to camelCase.
|
||||
* Parameter values are automatically converted from string represenation to
|
||||
* most basic types, if neccessary.
|
||||
* Parameter values are automatically converted from string representation to
|
||||
* most basic types, if necessary.
|
||||
* <p/>
|
||||
* To write a generic filter, you need only override the abstract
|
||||
* {@link #doFilterImpl(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)} doFilterImpl} method.
|
||||
@@ -67,6 +67,7 @@ import java.util.Enumeration;
|
||||
* @see FilterConfig
|
||||
*/
|
||||
public abstract class GenericFilter implements Filter, FilterConfig, Serializable {
|
||||
// TODO: Rewrite to use ServletConfigurator instead of BeanUtil
|
||||
|
||||
/**
|
||||
* The filter config.
|
||||
@@ -76,32 +77,29 @@ public abstract class GenericFilter implements Filter, FilterConfig, Serializabl
|
||||
/**
|
||||
* Makes sure the filter runs once per request
|
||||
* <p/>
|
||||
* see #isRunOnce
|
||||
*
|
||||
* @see #isRunOnce
|
||||
* @see #ATTRIB_RUN_ONCE_VALUE
|
||||
* @see #oncePerRequest
|
||||
* see #ATTRIB_RUN_ONCE_VALUE
|
||||
*/
|
||||
private final static String ATTRIB_RUN_ONCE_EXT = ".REQUEST_HANDLED";
|
||||
|
||||
/**
|
||||
* Makes sure the filter runs once per request.
|
||||
* Must be configured through init method, as the filter name is not
|
||||
* available before we have a FitlerConfig object.
|
||||
* available before we have a {@code FilterConfig} object.
|
||||
* <p/>
|
||||
* see #isRunOnce
|
||||
*
|
||||
* @see #isRunOnce
|
||||
* @see #ATTRIB_RUN_ONCE_VALUE
|
||||
* @see #oncePerRequest
|
||||
* see #ATTRIB_RUN_ONCE_VALUE
|
||||
*/
|
||||
private String attribRunOnce = null;
|
||||
|
||||
/**
|
||||
* Makes sure the filter runs once per request
|
||||
* <p/>
|
||||
* see #isRunOnce
|
||||
*
|
||||
* @see #isRunOnce
|
||||
* @see #ATTRIB_RUN_ONCE_EXT
|
||||
* @see #oncePerRequest
|
||||
* see #ATTRIB_RUN_ONCE_EXT
|
||||
*/
|
||||
private static final Object ATTRIB_RUN_ONCE_VALUE = new Object();
|
||||
|
||||
@@ -213,16 +211,16 @@ public abstract class GenericFilter implements Filter, FilterConfig, Serializabl
|
||||
* and returns false.
|
||||
* A return value of false, indicates that the filter has not yet run.
|
||||
* A return value of true, indicates that the filter has run for this
|
||||
* request, and processing should not contine.
|
||||
* request, and processing should not continue.
|
||||
* <P/>
|
||||
* Note that the method will mark the request as filtered on first
|
||||
* invocation.
|
||||
* <p/>
|
||||
* see #ATTRIB_RUN_ONCE_EXT
|
||||
* see #ATTRIB_RUN_ONCE_VALUE
|
||||
* @see #ATTRIB_RUN_ONCE_EXT
|
||||
* @see #ATTRIB_RUN_ONCE_VALUE
|
||||
*
|
||||
* @param pRequest the servlet request
|
||||
* @return {@code true} if the request is allready filtered, otherwise
|
||||
* @return {@code true} if the request is already filtered, otherwise
|
||||
* {@code false}.
|
||||
*/
|
||||
private boolean isRunOnce(final ServletRequest pRequest) {
|
||||
@@ -233,6 +231,7 @@ public abstract class GenericFilter implements Filter, FilterConfig, Serializabl
|
||||
|
||||
// Set attribute and return false (continue)
|
||||
pRequest.setAttribute(attribRunOnce, ATTRIB_RUN_ONCE_VALUE);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -286,7 +285,6 @@ public abstract class GenericFilter implements Filter, FilterConfig, Serializabl
|
||||
* @see ServletContext
|
||||
*/
|
||||
public ServletContext getServletContext() {
|
||||
// TODO: Create a servlet context wrapper that lets you log to a log4j appender?
|
||||
return filterConfig.getServletContext();
|
||||
}
|
||||
|
||||
@@ -347,6 +345,7 @@ public abstract class GenericFilter implements Filter, FilterConfig, Serializabl
|
||||
*
|
||||
* @deprecated For compatibility only, use {@link #init init} instead.
|
||||
*/
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
public void setFilterConfig(final FilterConfig pFilterConfig) {
|
||||
try {
|
||||
init(pFilterConfig);
|
||||
|
||||
@@ -39,7 +39,7 @@ import java.lang.reflect.InvocationTargetException;
|
||||
* <p/>
|
||||
* {@code GenericServlet} has an auto-init system, that automatically invokes
|
||||
* the method matching the signature {@code void setX(<Type>)},
|
||||
* for every init-parameter {@code x}. Both camelCase and lisp-style paramter
|
||||
* for every init-parameter {@code x}. Both camelCase and lisp-style parameter
|
||||
* naming is supported, lisp-style names will be converted to camelCase.
|
||||
* Parameter values are automatically converted from string representation to
|
||||
* most basic types, if necessary.
|
||||
@@ -50,6 +50,7 @@ import java.lang.reflect.InvocationTargetException;
|
||||
* @version $Id: GenericServlet.java#1 $
|
||||
*/
|
||||
public abstract class GenericServlet extends javax.servlet.GenericServlet {
|
||||
// TODO: Rewrite to use ServletConfigurator instead of BeanUtil
|
||||
|
||||
/**
|
||||
* Called by the web container to indicate to a servlet that it is being
|
||||
|
||||
@@ -39,7 +39,7 @@ import java.lang.reflect.InvocationTargetException;
|
||||
* <p/>
|
||||
* {@code HttpServlet} has an auto-init system, that automatically invokes
|
||||
* the method matching the signature {@code void setX(<Type>)},
|
||||
* for every init-parameter {@code x}. Both camelCase and lisp-style paramter
|
||||
* for every init-parameter {@code x}. Both camelCase and lisp-style parameter
|
||||
* naming is supported, lisp-style names will be converted to camelCase.
|
||||
* Parameter values are automatically converted from string representation to
|
||||
* most basic types, if necessary.
|
||||
@@ -50,6 +50,7 @@ import java.lang.reflect.InvocationTargetException;
|
||||
* @version $Id: HttpServlet.java#1 $
|
||||
*/
|
||||
public abstract class HttpServlet extends javax.servlet.http.HttpServlet {
|
||||
// TODO: Rewrite to use ServletConfigurator instead of BeanUtil
|
||||
|
||||
/**
|
||||
* Called by the web container to indicate to a servlet that it is being
|
||||
|
||||
@@ -28,17 +28,20 @@
|
||||
|
||||
package com.twelvemonkeys.servlet;
|
||||
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.ServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import static com.twelvemonkeys.lang.Validate.notNull;
|
||||
|
||||
/**
|
||||
* A delegate for handling stream support in wrapped servlet responses.
|
||||
* <p/>
|
||||
* Client code should delegate {@code getOutputStream}, {@code getWriter},
|
||||
* {@code flushBuffer} and {@code resetBuffer} methods from the servlet response.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haku $
|
||||
@@ -48,35 +51,33 @@ public class ServletResponseStreamDelegate {
|
||||
private Object out = null;
|
||||
protected final ServletResponse response;
|
||||
|
||||
public ServletResponseStreamDelegate(ServletResponse pResponse) {
|
||||
response = Validate.notNull(pResponse, "response");
|
||||
public ServletResponseStreamDelegate(final ServletResponse pResponse) {
|
||||
response = notNull(pResponse, "response");
|
||||
}
|
||||
|
||||
// NOTE: Intentionally NOT threadsafe, as one request/response should be
|
||||
// handled by one thread ONLY.
|
||||
// NOTE: Intentionally NOT thread safe, as one request/response should be handled by one thread ONLY.
|
||||
public final ServletOutputStream getOutputStream() throws IOException {
|
||||
if (out == null) {
|
||||
OutputStream out = createOutputStream();
|
||||
this.out = out instanceof ServletOutputStream ? out : new OutputStreamAdapter(out);
|
||||
}
|
||||
else if (out instanceof PrintWriter) {
|
||||
throw new IllegalStateException("getWriter() allready called.");
|
||||
throw new IllegalStateException("getWriter() already called.");
|
||||
}
|
||||
|
||||
return (ServletOutputStream) out;
|
||||
}
|
||||
|
||||
// NOTE: Intentionally NOT threadsafe, as one request/response should be
|
||||
// handled by one thread ONLY.
|
||||
// NOTE: Intentionally NOT thread safe, as one request/response should be handled by one thread ONLY.
|
||||
public final PrintWriter getWriter() throws IOException {
|
||||
if (out == null) {
|
||||
// NOTE: getCharacterEncoding may should not return null
|
||||
// NOTE: getCharacterEncoding may/should not return null
|
||||
OutputStream out = createOutputStream();
|
||||
String charEncoding = response.getCharacterEncoding();
|
||||
this.out = new PrintWriter(charEncoding != null ? new OutputStreamWriter(out, charEncoding) : new OutputStreamWriter(out));
|
||||
}
|
||||
else if (out instanceof ServletOutputStream) {
|
||||
throw new IllegalStateException("getOutputStream() allready called.");
|
||||
throw new IllegalStateException("getOutputStream() already called.");
|
||||
}
|
||||
|
||||
return (PrintWriter) out;
|
||||
@@ -84,8 +85,9 @@ public class ServletResponseStreamDelegate {
|
||||
|
||||
/**
|
||||
* Returns the {@code OutputStream}.
|
||||
* Override this method to provide a decoreated outputstream.
|
||||
* This method is guaranteed to be invoked only once for a request/response.
|
||||
* Subclasses should override this method to provide a decorated output stream.
|
||||
* This method is guaranteed to be invoked only once for a request/response
|
||||
* (unless {@code resetBuffer} is invoked).
|
||||
* <P/>
|
||||
* This implementation simply returns the output stream from the wrapped
|
||||
* response.
|
||||
@@ -107,7 +109,6 @@ public class ServletResponseStreamDelegate {
|
||||
}
|
||||
|
||||
public void resetBuffer() {
|
||||
// TODO: Is this okay? Probably not... :-)
|
||||
out = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
package com.twelvemonkeys.servlet.cache;
|
||||
|
||||
import com.twelvemonkeys.lang.StringUtil;
|
||||
import com.twelvemonkeys.net.NetUtil;
|
||||
import com.twelvemonkeys.net.HTTPUtil;
|
||||
import com.twelvemonkeys.servlet.ServletResponseStreamDelegate;
|
||||
|
||||
import javax.servlet.ServletOutputStream;
|
||||
@@ -212,7 +212,7 @@ class CacheResponseWrapper extends HttpServletResponseWrapper {
|
||||
if (Boolean.FALSE.equals(cacheable)) {
|
||||
super.setDateHeader(pName, pValue);
|
||||
}
|
||||
cachedResponse.setHeader(pName, NetUtil.formatHTTPDate(pValue));
|
||||
cachedResponse.setHeader(pName, HTTPUtil.formatHTTPDate(pValue));
|
||||
}
|
||||
|
||||
public void addDateHeader(String pName, long pValue) {
|
||||
@@ -220,7 +220,7 @@ class CacheResponseWrapper extends HttpServletResponseWrapper {
|
||||
if (Boolean.FALSE.equals(cacheable)) {
|
||||
super.addDateHeader(pName, pValue);
|
||||
}
|
||||
cachedResponse.addHeader(pName, NetUtil.formatHTTPDate(pValue));
|
||||
cachedResponse.addHeader(pName, HTTPUtil.formatHTTPDate(pValue));
|
||||
}
|
||||
|
||||
public void setHeader(String pName, String pValue) {
|
||||
|
||||
@@ -32,7 +32,7 @@ import com.twelvemonkeys.io.FileUtil;
|
||||
import com.twelvemonkeys.lang.StringUtil;
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
import com.twelvemonkeys.net.MIMEUtil;
|
||||
import com.twelvemonkeys.net.NetUtil;
|
||||
import com.twelvemonkeys.net.HTTPUtil;
|
||||
import com.twelvemonkeys.util.LRUHashMap;
|
||||
import com.twelvemonkeys.util.NullMap;
|
||||
|
||||
@@ -972,7 +972,7 @@ public class HTTPCache {
|
||||
File cached = getCachedFile(pCacheURI, pRequest);
|
||||
if (cached != null && cached.exists()) {
|
||||
lastModified = cached.lastModified();
|
||||
//// System.out.println(" ## HTTPCache ## Last-Modified is " + NetUtil.formatHTTPDate(lastModified) + ", using cachedFile.lastModified()");
|
||||
//// System.out.println(" ## HTTPCache ## Last-Modified is " + HTTPUtil.formatHTTPDate(lastModified) + ", using cachedFile.lastModified()");
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -981,11 +981,11 @@ public class HTTPCache {
|
||||
int maxAge = getIntHeader(response, HEADER_CACHE_CONTROL, "max-age");
|
||||
if (maxAge == -1) {
|
||||
expires = lastModified + defaultExpiryTime;
|
||||
//// System.out.println(" ## HTTPCache ## Expires is " + NetUtil.formatHTTPDate(expires) + ", using lastModified + defaultExpiry");
|
||||
//// System.out.println(" ## HTTPCache ## Expires is " + HTTPUtil.formatHTTPDate(expires) + ", using lastModified + defaultExpiry");
|
||||
}
|
||||
else {
|
||||
expires = lastModified + (maxAge * 1000L); // max-age is seconds
|
||||
//// System.out.println(" ## HTTPCache ## Expires is " + NetUtil.formatHTTPDate(expires) + ", using lastModified + maxAge");
|
||||
//// System.out.println(" ## HTTPCache ## Expires is " + HTTPUtil.formatHTTPDate(expires) + ", using lastModified + maxAge");
|
||||
}
|
||||
}
|
||||
/*
|
||||
@@ -997,7 +997,7 @@ public class HTTPCache {
|
||||
// Expired?
|
||||
if (expires < now) {
|
||||
// System.out.println(" ## HTTPCache ## Content is stale (content expired: "
|
||||
// + NetUtil.formatHTTPDate(expires) + " before " + NetUtil.formatHTTPDate(now) + ").");
|
||||
// + HTTPUtil.formatHTTPDate(expires) + " before " + HTTPUtil.formatHTTPDate(now) + ").");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1008,7 +1008,7 @@ public class HTTPCache {
|
||||
File cached = getCachedFile(pCacheURI, pRequest);
|
||||
if (cached != null && cached.exists()) {
|
||||
lastModified = cached.lastModified();
|
||||
//// System.out.println(" ## HTTPCache ## Last-Modified is " + NetUtil.formatHTTPDate(lastModified) + ", using cachedFile.lastModified()");
|
||||
//// System.out.println(" ## HTTPCache ## Last-Modified is " + HTTPUtil.formatHTTPDate(lastModified) + ", using cachedFile.lastModified()");
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -1018,7 +1018,7 @@ public class HTTPCache {
|
||||
//noinspection RedundantIfStatement
|
||||
if (real != null && real.exists() && real.lastModified() > lastModified) {
|
||||
// System.out.println(" ## HTTPCache ## Content is stale (new content"
|
||||
// + NetUtil.formatHTTPDate(lastModified) + " before " + NetUtil.formatHTTPDate(real.lastModified()) + ").");
|
||||
// + HTTPUtil.formatHTTPDate(lastModified) + " before " + HTTPUtil.formatHTTPDate(real.lastModified()) + ").");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1082,7 +1082,7 @@ public class HTTPCache {
|
||||
static long getDateHeader(final String pHeaderValue) {
|
||||
long date = -1L;
|
||||
if (pHeaderValue != null) {
|
||||
date = NetUtil.parseHTTPDate(pHeaderValue);
|
||||
date = HTTPUtil.parseHTTPDate(pHeaderValue);
|
||||
}
|
||||
return date;
|
||||
}
|
||||
@@ -1147,4 +1147,4 @@ public class HTTPCache {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@
|
||||
package com.twelvemonkeys.servlet.cache;
|
||||
|
||||
import com.twelvemonkeys.lang.StringUtil;
|
||||
import com.twelvemonkeys.net.NetUtil;
|
||||
import com.twelvemonkeys.net.HTTPUtil;
|
||||
import com.twelvemonkeys.servlet.ServletResponseStreamDelegate;
|
||||
|
||||
import javax.servlet.ServletOutputStream;
|
||||
@@ -224,7 +224,7 @@ class SerlvetCacheResponseWrapper extends HttpServletResponseWrapper {
|
||||
if (Boolean.FALSE.equals(cacheable)) {
|
||||
super.setDateHeader(pName, pValue);
|
||||
}
|
||||
cacheResponse.setHeader(pName, NetUtil.formatHTTPDate(pValue));
|
||||
cacheResponse.setHeader(pName, HTTPUtil.formatHTTPDate(pValue));
|
||||
}
|
||||
|
||||
public void addDateHeader(String pName, long pValue) {
|
||||
@@ -232,7 +232,7 @@ class SerlvetCacheResponseWrapper extends HttpServletResponseWrapper {
|
||||
if (Boolean.FALSE.equals(cacheable)) {
|
||||
super.addDateHeader(pName, pValue);
|
||||
}
|
||||
cacheResponse.addHeader(pName, NetUtil.formatHTTPDate(pValue));
|
||||
cacheResponse.addHeader(pName, HTTPUtil.formatHTTPDate(pValue));
|
||||
}
|
||||
|
||||
public void setHeader(String pName, String pValue) {
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
package com.twelvemonkeys.servlet.cache;
|
||||
|
||||
import com.twelvemonkeys.io.FastByteArrayOutputStream;
|
||||
import com.twelvemonkeys.net.NetUtil;
|
||||
import com.twelvemonkeys.net.HTTPUtil;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
@@ -53,7 +53,7 @@ class WritableCachedResponseImpl implements WritableCachedResponse {
|
||||
protected WritableCachedResponseImpl() {
|
||||
cachedResponse = new CachedResponseImpl();
|
||||
// Hmmm..
|
||||
setHeader(HTTPCache.HEADER_CACHED_TIME, NetUtil.formatHTTPDate(System.currentTimeMillis()));
|
||||
setHeader(HTTPCache.HEADER_CACHED_TIME, HTTPUtil.formatHTTPDate(System.currentTimeMillis()));
|
||||
}
|
||||
|
||||
public CachedResponse getCachedResponse() {
|
||||
|
||||
@@ -33,7 +33,9 @@ import javax.imageio.spi.IIORegistry;
|
||||
import javax.imageio.spi.ServiceRegistry;
|
||||
import javax.servlet.ServletContextEvent;
|
||||
import javax.servlet.ServletContextListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Takes care of registering and de-registering local ImageIO plugins (service providers) for the servlet context.
|
||||
@@ -66,8 +68,13 @@ public final class IIOProviderContextListener implements ServletContextListener
|
||||
Class<?> category = categories.next();
|
||||
Iterator<?> providers = registry.getServiceProviders(category, localFilter, false);
|
||||
|
||||
// Copy the providers, as de-registering while iterating over providers will lead to ConcurrentModificationExceptions.
|
||||
List<Object> providersCopy = new ArrayList<Object>();
|
||||
while (providers.hasNext()) {
|
||||
Object provider = providers.next();
|
||||
providersCopy.add(providers.next());
|
||||
}
|
||||
|
||||
for (Object provider : providersCopy) {
|
||||
registry.deregisterServiceProvider(provider);
|
||||
event.getServletContext().log(String.format("Unregistered locally installed provider class: %s", provider.getClass()));
|
||||
}
|
||||
|
||||
@@ -63,17 +63,15 @@ import java.util.Iterator;
|
||||
* The response also automatically handles writing the image back to the underlying response stream
|
||||
* in the preferred format, when the response is flushed.
|
||||
* <p>
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @version $Id: ImageServletResponseImpl.java#10 $
|
||||
*
|
||||
*/
|
||||
// TODO: Refactor out HTTP specifics (if possible).
|
||||
// TODO: Is it a good ide to throw IIOException?
|
||||
// TODO: This implementation has a problem if two filters does scaling, as the second will overwrite the SIZE attribute
|
||||
// TODO: Allow different scaling algorithm based on input image (use case: IndexColorModel does not scale well using default, smooth may be slow for large images)
|
||||
// TODO: Support pluggable pre- and post-processing steps
|
||||
class ImageServletResponseImpl extends HttpServletResponseWrapper implements ImageServletResponse {
|
||||
|
||||
private ServletRequest originalRequest;
|
||||
private final ServletContext context;
|
||||
private final ServletResponseStreamDelegate streamDelegate;
|
||||
@@ -223,6 +221,9 @@ class ImageServletResponseImpl extends HttpServletResponseWrapper implements Ima
|
||||
|
||||
// The default JPEG quality is not good enough, so always adjust compression/quality
|
||||
if ((requestQuality != null || "jpeg".equalsIgnoreCase(getFormatNameSafe(writer))) && param.canWriteCompressed()) {
|
||||
// TODO: See http://blog.apokalyptik.com/2009/09/16/quality-time-with-your-jpegs/ for better adjusting the (default) JPEG quality
|
||||
// OR: Use the metadata of the original image
|
||||
|
||||
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
|
||||
|
||||
// WORKAROUND: Known bug in GIFImageWriter in certain JDK versions, compression type is not set by default
|
||||
@@ -234,10 +235,13 @@ class ImageServletResponseImpl extends HttpServletResponseWrapper implements Ima
|
||||
}
|
||||
|
||||
if ("gif".equalsIgnoreCase(getFormatNameSafe(writer)) && !(image.getColorModel() instanceof IndexColorModel)
|
||||
&& image.getColorModel().getTransparency() != Transparency.OPAQUE) {
|
||||
/*&& image.getColorModel().getTransparency() != Transparency.OPAQUE*/) {
|
||||
// WORKAROUND: Bug in GIFImageWriter may throw NPE if transparent pixels
|
||||
// See: http://bugs.sun.com/view_bug.do?bug_id=6287936
|
||||
image = ImageUtil.createIndexed(ImageUtil.toBuffered(image), 256, null, ImageUtil.TRANSPARENCY_BITMASK | ImageUtil.DITHER_DIFFUSION_ALTSCANS);
|
||||
image = ImageUtil.createIndexed(
|
||||
ImageUtil.toBuffered(image), 256, null,
|
||||
(image.getColorModel().getTransparency() == Transparency.OPAQUE ? ImageUtil.TRANSPARENCY_OPAQUE : ImageUtil.TRANSPARENCY_BITMASK) | ImageUtil.DITHER_DIFFUSION_ALTSCANS
|
||||
);
|
||||
}
|
||||
//////////////////
|
||||
ImageOutputStream stream = ImageIO.createImageOutputStream(out);
|
||||
@@ -425,18 +429,29 @@ class ImageServletResponseImpl extends HttpServletResponseWrapper implements Ima
|
||||
if (image != null && size != null && (image.getWidth() != size.width || image.getHeight() != size.height)) {
|
||||
int resampleAlgorithm = getResampleAlgorithmFromRequest();
|
||||
|
||||
// TODO: One possibility is to NOT handle index color here, and only handle it later, IF NEEDED (read: GIF,
|
||||
// possibly also for PNG) when we know the output format (flush method).
|
||||
// This will make the filter faster (and better quality, possibly at the expense of more bytes being sent
|
||||
// over the wire) in the general case. Who uses GIF nowadays anyway?
|
||||
// Also, this means we could either keep the original IndexColorModel in the filter, or go through the
|
||||
// expensive operation of re-calculating the optimal palette for the new image (the latter might improve quality).
|
||||
|
||||
// NOTE: Only use createScaled if IndexColorModel, as it's more expensive due to color conversion
|
||||
if (image.getColorModel() instanceof IndexColorModel) {
|
||||
return ImageUtil.createScaled(image, size.width, size.height, resampleAlgorithm);
|
||||
/* if (image.getColorModel() instanceof IndexColorModel) {
|
||||
// return ImageUtil.createScaled(image, size.width, size.height, resampleAlgorithm);
|
||||
BufferedImage resampled = ImageUtil.createResampled(image, size.width, size.height, resampleAlgorithm);
|
||||
return ImageUtil.createIndexed(resampled, (IndexColorModel) image.getColorModel(), null, ImageUtil.DITHER_NONE | ImageUtil.TRANSPARENCY_BITMASK);
|
||||
// return ImageUtil.createIndexed(resampled, 256, null, ImageUtil.COLOR_SELECTION_QUALITY | ImageUtil.DITHER_NONE | ImageUtil.TRANSPARENCY_BITMASK);
|
||||
}
|
||||
else {
|
||||
*/
|
||||
return ImageUtil.createResampled(image, size.width, size.height, resampleAlgorithm);
|
||||
}
|
||||
// }
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
private int getResampleAlgorithmFromRequest() {
|
||||
int getResampleAlgorithmFromRequest() {
|
||||
Object algorithm = originalRequest.getAttribute(ATTRIB_IMAGE_RESAMPLE_ALGORITHM);
|
||||
if (algorithm instanceof Integer && ((Integer) algorithm == Image.SCALE_SMOOTH || (Integer) algorithm == Image.SCALE_FAST || (Integer) algorithm == Image.SCALE_DEFAULT)) {
|
||||
return (Integer) algorithm;
|
||||
@@ -445,6 +460,7 @@ class ImageServletResponseImpl extends HttpServletResponseWrapper implements Ima
|
||||
if (algorithm != null) {
|
||||
context.log("WARN: Illegal image resampling algorithm: " + algorithm);
|
||||
}
|
||||
|
||||
return BufferedImage.SCALE_DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user