Compare commits

...

14 Commits

Author SHA1 Message Date
Harald Kuhr 48f82a159f [maven-release-plugin] prepare release twelvemonkeys-3.8.1 2021-12-27 09:53:40 +01:00
Harald Kuhr 7105738811 #651: Fix ExtraSamplesColorModel equals + hashcode to behave nicely with ImageTypeSpecifier comparison.
(cherry picked from commit 98e4b76206)
2021-12-24 13:00:15 +01:00
Harald Kuhr 10aa4ba41e Minor clean-up.
(cherry picked from commit aa4b5db054)
2021-12-24 13:00:15 +01:00
Harald Kuhr 6fb06da4d7 #651: Fix ExtraSamplesColorModel to create correct length elements array.
(cherry picked from commit 433311c10d)
2021-12-24 13:00:15 +01:00
Harald Kuhr a963e1c355 Alternative fix for #650: Allow usage in OSGi environment.
(cherry picked from commit f50178bc78)
2021-12-23 11:02:46 +01:00
Snyk bot 966a9da45d fix: upgrade commons-io:commons-io from 2.9.0 to 2.11.0 (#647)
Snyk has created this PR to upgrade commons-io:commons-io from 2.9.0 to 2.11.0.

See this package in Maven Repository:
https://mvnrepository.com/artifact/commons-io/commons-io/

See this project in Snyk:
https://app.snyk.io/org/haraldk/project/9a1f6304-68e0-49c5-af4f-db1f87bd4f90?utm_source=github&utm_medium=referral&page=upgrade-pr

(cherry picked from commit e016e970e5)
2021-12-16 08:48:35 +01:00
Harald Kuhr 319b2c4e18 Overriding transitive dependency.
(cherry picked from commit 444aeabf21)
2021-12-15 17:01:38 +01:00
Harald Kuhr e9bf7d080c Getting rid of the dependencies too.
(cherry picked from commit 05507a59d6)
2021-12-15 16:29:53 +01:00
Harald Kuhr fb3691e2ee Delete deprecated servlet classes
(cherry picked from commit c4c89a0a25)
2021-12-15 16:24:41 +01:00
Harald Kuhr 25f9cc5c55 Delete deprecated Servlet classes
(cherry picked from commit b0ad6b2a4b)
2021-12-15 16:24:41 +01:00
Harald Kuhr 94777ddc96 #646: Spi now recognizes VP8 encoded images in VP8X ("extended format").
(cherry picked from commit 25c703f4b2)
2021-12-15 16:09:33 +01:00
Oleh Astappiev a4c12d0d64 Create jakartified package on build (#636)
* feat(servlet): create jakartified package on build

* feat(servlet): update README to include Jakarta classifier

(cherry picked from commit 529c59f93f)
2021-12-15 16:09:33 +01:00
Harald Kuhr 08a69886b1 Updated with the latest versions.
(cherry picked from commit 584b1d9b21)
2021-12-15 16:09:33 +01:00
Harald Kuhr ab85ff0ec8 Prepare for next version. 2021-12-15 16:09:06 +01:00
126 changed files with 280 additions and 17773 deletions
+46 -36
View File
@@ -31,19 +31,19 @@ As there is lots of legacy data out there, we see the need for open implementati
| [PCX](https://github.com/haraldk/TwelveMonkeys/wiki/PCX-Plugin) | PCX | ZSoft Paintbrush Format | âś” | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| | DCX | Multi-page PCX fax document | âś” | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| [PICT](https://github.com/haraldk/TwelveMonkeys/wiki/PICT-Plugin) | PICT | Apple QuickTime Picture Format | âś” | âś” | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| | PNTG | Apple MacPaint Picture Format | âś” | | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| | PNTG | Apple MacPaint Picture Format | âś” | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| [PNM](https://github.com/haraldk/TwelveMonkeys/wiki/PNM-Plugin) | PAM | NetPBM Portable Any Map | âś” | âś” | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| | PBM | NetPBM Portable Bit Map | âś” | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| | PGM | NetPBM Portable Grey Map | âś” | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| | PPM | NetPBM Portable Pix Map | âś” | âś” | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| | PFM | Portable Float Map | âś” | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| [PSD](https://github.com/haraldk/TwelveMonkeys/wiki/PSD-Plugin) | **PSD** | Adobe Photoshop Document | âś” | - | Native & [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| [PSD](https://github.com/haraldk/TwelveMonkeys/wiki/PSD-Plugin) | **PSD** | Adobe Photoshop Document | âś” | (âś”) | Native & [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| | PSB | Adobe Photoshop Large Document | âś” | - | Native & [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| [SGI](https://github.com/haraldk/TwelveMonkeys/wiki/SGI-Plugin) | SGI | Silicon Graphics Image Format | âś” | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| [TGA](https://github.com/haraldk/TwelveMonkeys/wiki/TGA-Plugin) | TGA | Truevision TGA Image Format | âś” | âś” | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|ThumbsDB| Thumbs.db| MS Windows Thumbs DB | âś” | - | - | OLE2 Compound Document based format only
| [TIFF](https://github.com/haraldk/TwelveMonkeys/wiki/TIFF-Plugin) | **TIFF** | Aldus/Adobe Tagged Image File Format | âś” | âś” | [Native](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/tiff_metadata.html#ImageMetadata) & [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| | BigTIFF | | âś” | - | [Native](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/tiff_metadata.html#ImageMetadata) & [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| | BigTIFF | | âś” | âś” | [Native](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/tiff_metadata.html#ImageMetadata) & [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| [WebP](https://github.com/haraldk/TwelveMonkeys/wiki/WebP-Plugin) | **WebP** | Google WebP Format | âś” | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| XWD | XWD | X11 Window Dump Format | âś” | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
@@ -271,22 +271,32 @@ To depend on the JPEG and TIFF plugin using Maven, add the following to your POM
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-jpeg</artifactId>
<version>3.7.0</version>
<version>3.8.0</version>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-tiff</artifactId>
<version>3.7.0</version>
<version>3.8.0</version>
</dependency>
<!--
Optional dependency. Needed only if you deploy ImageIO plugins as part of a web app.
Make sure you add the IIOProviderContextListener to your web.xml, see above.
-->
<dependency>
<groupId>com.twelvemonkeys.servlet</groupId>
<artifactId>servlet</artifactId>
<version>3.8.0</version>
</dependency>
<!--
Or Jakarta version, for Servlet API 5.0
-->
<dependency>
<groupId>com.twelvemonkeys.servlet</groupId>
<artifactId>servlet</artifactId>
<version>3.7.0</version>
<classifier>jakarta</classifier>
</dependency>
</dependencies>
```
@@ -295,13 +305,13 @@ To depend on the JPEG and TIFF plugin using Maven, add the following to your POM
To depend on the JPEG and TIFF plugin in your IDE or program, add all of the following JARs to your class path:
twelvemonkeys-common-lang-3.7.0.jar
twelvemonkeys-common-io-3.7.0.jar
twelvemonkeys-common-image-3.7.0.jar
twelvemonkeys-imageio-core-3.7.0.jar
twelvemonkeys-imageio-metadata-3.7.0.jar
twelvemonkeys-imageio-jpeg-3.7.0.jar
twelvemonkeys-imageio-tiff-3.7.0.jar
twelvemonkeys-common-lang-3.8.0.jar
twelvemonkeys-common-io-3.8.0.jar
twelvemonkeys-common-image-3.8.0.jar
twelvemonkeys-imageio-core-3.8.0.jar
twelvemonkeys-imageio-metadata-3.8.0.jar
twelvemonkeys-imageio-jpeg-3.8.0.jar
twelvemonkeys-imageio-tiff-3.8.0.jar
#### Deploying the plugins in a web app
@@ -367,44 +377,44 @@ Other "fat" JAR bundlers will probably have similar mechanisms to merge entries
### Links to prebuilt binaries
##### Latest version (3.7.0)
##### Latest version (3.8.0)
Requires Java 7 or later.
Common dependencies
* [common-lang-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-lang/3.7.0/common-lang-3.7.0.jar)
* [common-io-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-io/3.7.0/common-io-3.7.0.jar)
* [common-image-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-image/3.7.0/common-image-3.7.0.jar)
* [common-lang-3.8.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-lang/3.8.0/common-lang-3.8.0.jar)
* [common-io-3.8.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-io/3.8.0/common-io-3.8.0.jar)
* [common-image-3.8.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-image/3.8.0/common-image-3.8.0.jar)
ImageIO dependencies
* [imageio-core-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-core/3.7.0/imageio-core-3.7.0.jar)
* [imageio-metadata-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-metadata/3.7.0/imageio-metadata-3.7.0.jar)
* [imageio-core-3.8.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-core/3.8.0/imageio-core-3.8.0.jar)
* [imageio-metadata-3.8.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-metadata/3.8.0/imageio-metadata-3.8.0.jar)
ImageIO plugins
* [imageio-bmp-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-bmp/3.7.0/imageio-bmp-3.7.0.jar)
* [imageio-hdr-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-hdr/3.7.0/imageio-hdr-3.7.0.jar)
* [imageio-icns-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-icns/3.7.0/imageio-icns-3.7.0.jar)
* [imageio-iff-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-iff/3.7.0/imageio-iff-3.7.0.jar)
* [imageio-jpeg-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-jpeg/3.7.0/imageio-jpeg-3.7.0.jar)
* [imageio-pcx-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pcx/3.7.0/imageio-pcx-3.7.0.jar)
* [imageio-pict-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pict/3.7.0/imageio-pict-3.7.0.jar)
* [imageio-pnm-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pnm/3.7.0/imageio-pnm-3.7.0.jar)
* [imageio-psd-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-psd/3.7.0/imageio-psd-3.7.0.jar)
* [imageio-sgi-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-sgi/3.7.0/imageio-sgi-3.7.0.jar)
* [imageio-tga-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tga/3.7.0/imageio-tga-3.7.0.jar)
* [imageio-thumbsdb-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-thumbsdb/3.7.0/imageio-thumbsdb-3.7.0.jar)
* [imageio-tiff-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tiff/3.7.0/imageio-tiff-3.7.0.jar)
* [imageio-webp-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-webp/3.7.0/imageio-webp-3.7.0.jar)
* [imageio-xwd-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-xwd/3.7.0/imageio-xwd-3.7.0.jar)
* [imageio-bmp-3.8.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-bmp/3.8.0/imageio-bmp-3.8.0.jar)
* [imageio-hdr-3.8.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-hdr/3.8.0/imageio-hdr-3.8.0.jar)
* [imageio-icns-3.8.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-icns/3.8.0/imageio-icns-3.8.0.jar)
* [imageio-iff-3.8.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-iff/3.8.0/imageio-iff-3.8.0.jar)
* [imageio-jpeg-3.8.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-jpeg/3.8.0/imageio-jpeg-3.8.0.jar)
* [imageio-pcx-3.8.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pcx/3.8.0/imageio-pcx-3.8.0.jar)
* [imageio-pict-3.8.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pict/3.8.0/imageio-pict-3.8.0.jar)
* [imageio-pnm-3.8.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pnm/3.8.0/imageio-pnm-3.8.0.jar)
* [imageio-psd-3.8.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-psd/3.8.0/imageio-psd-3.8.0.jar)
* [imageio-sgi-3.8.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-sgi/3.8.0/imageio-sgi-3.8.0.jar)
* [imageio-tga-3.8.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tga/3.8.0/imageio-tga-3.8.0.jar)
* [imageio-thumbsdb-3.8.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-thumbsdb/3.8.0/imageio-thumbsdb-3.8.0.jar)
* [imageio-tiff-3.8.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tiff/3.8.0/imageio-tiff-3.8.0.jar)
* [imageio-webp-3.8.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-webp/3.8.0/imageio-webp-3.8.0.jar)
* [imageio-xwd-3.8.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-xwd/3.8.0/imageio-xwd-3.8.0.jar)
ImageIO plugins requiring 3rd party libs
* [imageio-batik-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-batik/3.7.0/imageio-batik-3.7.0.jar)
* [imageio-batik-3.8.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-batik/3.8.0/imageio-batik-3.8.0.jar)
Photoshop Path support for ImageIO
* [imageio-clippath-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-clippath/3.7.0/imageio-clippath-3.7.0.jar)
* [imageio-clippath-3.8.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-clippath/3.8.0/imageio-clippath-3.8.0.jar)
Servlet support
* [servlet-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/servlet/servlet/3.7.0/servlet-3.7.0.jar)
* [servlet-3.8.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/servlet/servlet/3.8.0/servlet-3.8.0.jar)
##### Old version (3.0.x)
+1 -1
View File
@@ -5,7 +5,7 @@
<parent>
<groupId>com.twelvemonkeys</groupId>
<artifactId>twelvemonkeys</artifactId>
<version>3.8.0</version>
<version>3.8.1</version>
</parent>
<groupId>com.twelvemonkeys.bom</groupId>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.common</groupId>
<artifactId>common</artifactId>
<version>3.8.0</version>
<version>3.8.1</version>
</parent>
<artifactId>common-image</artifactId>
<packaging>jar</packaging>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.common</groupId>
<artifactId>common</artifactId>
<version>3.8.0</version>
<version>3.8.1</version>
</parent>
<artifactId>common-io</artifactId>
<packaging>jar</packaging>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.common</groupId>
<artifactId>common</artifactId>
<version>3.8.0</version>
<version>3.8.1</version>
</parent>
<artifactId>common-lang</artifactId>
<packaging>jar</packaging>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys</groupId>
<artifactId>twelvemonkeys</artifactId>
<version>3.8.0</version>
<version>3.8.1</version>
</parent>
<groupId>com.twelvemonkeys.common</groupId>
<artifactId>common</artifactId>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys</groupId>
<artifactId>twelvemonkeys</artifactId>
<version>3.8.0</version>
<version>3.8.1</version>
</parent>
<groupId>com.twelvemonkeys.contrib</groupId>
<artifactId>contrib</artifactId>
+8 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.8.0</version>
<version>3.8.1</version>
</parent>
<artifactId>imageio-batik</artifactId>
<name>TwelveMonkeys :: ImageIO :: Batik Plugin</name>
@@ -48,6 +48,13 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-rasterizer-ext</artifactId>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.8.0</version>
<version>3.8.1</version>
</parent>
<artifactId>imageio-bmp</artifactId>
<name>TwelveMonkeys :: ImageIO :: BMP plugin</name>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.8.0</version>
<version>3.8.1</version>
</parent>
<artifactId>imageio-clippath</artifactId>
<name>TwelveMonkeys :: ImageIO :: Photoshop Path Support</name>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.8.0</version>
<version>3.8.1</version>
</parent>
<artifactId>imageio-core</artifactId>
<name>TwelveMonkeys :: ImageIO :: Core</name>
@@ -45,6 +45,7 @@ import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.SortedSet;
import java.util.TreeSet;
@@ -175,15 +176,22 @@ public final class IIOUtil {
* @param providerClassName name of the provider class.
* @param category provider category
*
* @return the provider instance, or {@code null}.
* @return the provider instance, or {@code null} if not found
*/
public static <T> T lookupProviderByName(final ServiceRegistry registry, final String providerClassName, Class<T> category) {
try {
return category.cast(registry.getServiceProviderByClass(Class.forName(providerClassName)));
}
catch (ClassNotFoundException ignore) {
return null;
// NOTE: While more verbose, this is more OSGi-friendly than using
// registry.getServiceProviderByClass(Class.forName(providerClassName))
Iterator<T> providers = registry.getServiceProviders(category, true);
while (providers.hasNext()) {
T provider = providers.next();
if (provider.getClass().getName().equals(providerClassName)) {
return provider;
}
}
return null;
}
/**
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.8.0</version>
<version>3.8.1</version>
</parent>
<artifactId>imageio-hdr</artifactId>
<name>TwelveMonkeys :: ImageIO :: HDR plugin</name>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.8.0</version>
<version>3.8.1</version>
</parent>
<artifactId>imageio-icns</artifactId>
<name>TwelveMonkeys :: ImageIO :: ICNS plugin</name>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.8.0</version>
<version>3.8.1</version>
</parent>
<artifactId>imageio-iff</artifactId>
<name>TwelveMonkeys :: ImageIO :: IFF plugin</name>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.8.0</version>
<version>3.8.1</version>
</parent>
<artifactId>imageio-jpeg-jai-interop</artifactId>
<name>TwelveMonkeys :: ImageIO :: JPEG/JAI TIFF Interop</name>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.8.0</version>
<version>3.8.1</version>
</parent>
<artifactId>imageio-jpeg-jep262-interop</artifactId>
<name>TwelveMonkeys :: ImageIO :: JPEG/JEP-262 Interop</name>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.8.0</version>
<version>3.8.1</version>
</parent>
<artifactId>imageio-jpeg</artifactId>
<name>TwelveMonkeys :: ImageIO :: JPEG plugin</name>
+1 -1
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.8.0</version>
<version>3.8.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>imageio-metadata</artifactId>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.8.0</version>
<version>3.8.1</version>
</parent>
<artifactId>imageio-pcx</artifactId>
<name>TwelveMonkeys :: ImageIO :: PCX plugin</name>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.8.0</version>
<version>3.8.1</version>
</parent>
<artifactId>imageio-pdf</artifactId>
<name>TwelveMonkeys :: ImageIO :: PDF plugin</name>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.8.0</version>
<version>3.8.1</version>
</parent>
<artifactId>imageio-pict</artifactId>
<name>TwelveMonkeys :: ImageIO :: PICT plugin</name>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.8.0</version>
<version>3.8.1</version>
</parent>
<artifactId>imageio-pnm</artifactId>
<name>TwelveMonkeys :: ImageIO :: PNM plugin</name>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.8.0</version>
<version>3.8.1</version>
</parent>
<artifactId>imageio-psd</artifactId>
<name>TwelveMonkeys :: ImageIO :: PSD plugin</name>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.8.0</version>
<version>3.8.1</version>
</parent>
<artifactId>imageio-reference</artifactId>
<name>TwelveMonkeys :: ImageIO :: JDK Reference Tests</name>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.8.0</version>
<version>3.8.1</version>
</parent>
<artifactId>imageio-sgi</artifactId>
<name>TwelveMonkeys :: ImageIO :: SGI plugin</name>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.8.0</version>
<version>3.8.1</version>
</parent>
<artifactId>imageio-tga</artifactId>
<name>TwelveMonkeys :: ImageIO :: TGA plugin</name>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.8.0</version>
<version>3.8.1</version>
</parent>
<artifactId>imageio-thumbsdb</artifactId>
<name>TwelveMonkeys :: ImageIO :: Thumbs.db plugin</name>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.8.0</version>
<version>3.8.1</version>
</parent>
<artifactId>imageio-tiff-jai-interop</artifactId>
<name>TwelveMonkeys :: ImageIO :: TIFF/JAI Metadata Interop</name>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.8.0</version>
<version>3.8.1</version>
</parent>
<artifactId>imageio-tiff-jdk-interop</artifactId>
<name>TwelveMonkeys :: ImageIO :: TIFF/JDK JPEG Interop</name>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.8.0</version>
<version>3.8.1</version>
</parent>
<artifactId>imageio-tiff</artifactId>
<name>TwelveMonkeys :: ImageIO :: TIFF plugin</name>
@@ -34,10 +34,8 @@ import com.twelvemonkeys.lang.Validate;
import java.awt.*;
import java.awt.color.ColorSpace;
import java.awt.image.ComponentColorModel;
import java.awt.image.ComponentSampleModel;
import java.awt.image.SampleModel;
import java.awt.image.WritableRaster;
import java.awt.image.*;
import java.util.Objects;
import static java.awt.image.DataBuffer.getDataTypeSize;
@@ -99,4 +97,67 @@ final class ExtraSamplesColorModel extends ComponentColorModel {
private int getAlphaComponent() {
return super.getNumComponents() - 1;
}
@Override
public Object getDataElements(final int rgb, final Object pixel) {
return super.getDataElements(rgb, pixel == null ? createDataArray() : pixel);
}
private Object createDataArray() {
switch (transferType) {
case DataBuffer.TYPE_BYTE:
return new byte[numComponents];
case DataBuffer.TYPE_SHORT:
case DataBuffer.TYPE_USHORT:
return new short[numComponents];
case DataBuffer.TYPE_INT:
return new int[numComponents];
case DataBuffer.TYPE_FLOAT:
return new float[numComponents];
case DataBuffer.TYPE_DOUBLE:
return new double[numComponents];
}
throw new IllegalArgumentException("This method has not been implemented for transferType " + transferType);
}
@Override
public boolean equals(final Object other) {
if (this == other) {
return true;
}
if (other == null || getClass() != other.getClass()) {
return false;
}
ExtraSamplesColorModel that = (ExtraSamplesColorModel) other;
if (hasAlpha() != that.hasAlpha() ||
isAlphaPremultiplied() != that.isAlphaPremultiplied() ||
getPixelSize() != that.getPixelSize() ||
getTransparency() != that.getTransparency() ||
numComponents != that.numComponents) {
return false;
}
int[] nBits = getComponentSize();
int[] nb = that.getComponentSize();
if ((nBits == null) || (nb == null)) {
return ((nBits == null) && (nb == null));
}
for (int i = 0; i < nBits.length; i++) {
if (nBits[i] != nb[i]) {
return false;
}
}
return true;
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), numComponents, componentSize);
}
}
@@ -32,6 +32,7 @@ package com.twelvemonkeys.imageio.plugins.tiff;
import com.twelvemonkeys.image.ResampleOp;
import com.twelvemonkeys.imageio.color.ColorSpaces;
import org.junit.Test;
import java.awt.*;
@@ -39,20 +40,23 @@ import java.awt.color.ColorSpace;
import java.awt.image.*;
import java.util.Hashtable;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.*;
public class ExtraSamplesColorModelTest {
private BufferedImage createExtraSamplesImage(int w, int h, ColorSpace cs, boolean hasAlpha, int extraComponents) {
int samplesPerPixel = cs.getNumComponents() + (hasAlpha ? 1 : 0) + extraComponents;
ExtraSamplesColorModel colorModel = new ExtraSamplesColorModel(cs, hasAlpha, true, DataBuffer.TYPE_BYTE, extraComponents);
ExtraSamplesColorModel colorModel = createExtraSamplesColorModel(cs, hasAlpha, extraComponents);
SampleModel sampleModel = new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE, w, h, samplesPerPixel, samplesPerPixel * w, createOffsets(samplesPerPixel));
WritableRaster raster = Raster.createWritableRaster(sampleModel, new Point(0, 0));
return new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), new Hashtable());
return new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), new Hashtable<>());
}
private ExtraSamplesColorModel createExtraSamplesColorModel(ColorSpace cs, boolean hasAlpha, int extraComponents) {
return new ExtraSamplesColorModel(cs, hasAlpha, true, DataBuffer.TYPE_BYTE, extraComponents);
}
private static int[] createOffsets(int samplesPerPixel) {
@@ -66,8 +70,8 @@ public class ExtraSamplesColorModelTest {
@Test
public void testImageWithExtraSamplesCanBeResampledGray() {
for (int i = 1; i < 8; i++) {
BufferedImage bufferedImage = createExtraSamplesImage(10, 10, ColorSpaces.getColorSpace(ColorSpace.CS_GRAY), false, i);
BufferedImage resampled = new ResampleOp(5, 5, ResampleOp.FILTER_LANCZOS).filter(bufferedImage, null);
BufferedImage image = createExtraSamplesImage(10, 10, ColorSpaces.getColorSpace(ColorSpace.CS_GRAY), false, i);
BufferedImage resampled = new ResampleOp(5, 5, ResampleOp.FILTER_LANCZOS).filter(image, null);
assertNotNull(resampled);
assertEquals(5, resampled.getWidth());
@@ -78,8 +82,8 @@ public class ExtraSamplesColorModelTest {
@Test
public void testImageWithExtraSamplesCanBeResampledGrayAlpha() {
for (int i = 1; i < 8; i++) {
BufferedImage bufferedImage = createExtraSamplesImage(10, 10, ColorSpaces.getColorSpace(ColorSpace.CS_GRAY), true, i);
BufferedImage resampled = new ResampleOp(5, 5, ResampleOp.FILTER_LANCZOS).filter(bufferedImage, null);
BufferedImage image = createExtraSamplesImage(10, 10, ColorSpaces.getColorSpace(ColorSpace.CS_GRAY), true, i);
BufferedImage resampled = new ResampleOp(5, 5, ResampleOp.FILTER_LANCZOS).filter(image, null);
assertNotNull(resampled);
assertEquals(5, resampled.getWidth());
@@ -90,8 +94,8 @@ public class ExtraSamplesColorModelTest {
@Test
public void testImageWithExtraSamplesCanBeResampledRGB() {
for (int i = 1; i < 8; i++) {
BufferedImage bufferedImage = createExtraSamplesImage(10, 10, ColorSpaces.getColorSpace(ColorSpace.CS_sRGB), false, i);
BufferedImage resampled = new ResampleOp(5, 5, ResampleOp.FILTER_LANCZOS).filter(bufferedImage, null);
BufferedImage image = createExtraSamplesImage(10, 10, ColorSpaces.getColorSpace(ColorSpace.CS_sRGB), false, i);
BufferedImage resampled = new ResampleOp(5, 5, ResampleOp.FILTER_LANCZOS).filter(image, null);
assertNotNull(resampled);
assertEquals(5, resampled.getWidth());
@@ -102,8 +106,8 @@ public class ExtraSamplesColorModelTest {
@Test
public void testImageWithExtraSamplesCanBeResampledRGBAlpha() {
for (int i = 1; i < 8; i++) {
BufferedImage bufferedImage = createExtraSamplesImage(10, 10, ColorSpaces.getColorSpace(ColorSpace.CS_sRGB), true, i);
BufferedImage resampled = new ResampleOp(5, 5, ResampleOp.FILTER_LANCZOS).filter(bufferedImage, null);
BufferedImage image = createExtraSamplesImage(10, 10, ColorSpaces.getColorSpace(ColorSpace.CS_sRGB), true, i);
BufferedImage resampled = new ResampleOp(5, 5, ResampleOp.FILTER_LANCZOS).filter(image, null);
assertNotNull(resampled);
assertEquals(5, resampled.getWidth());
@@ -114,8 +118,8 @@ public class ExtraSamplesColorModelTest {
@Test
public void testImageWithExtraSamplesCanBeResampledCMYK() {
for (int i = 1; i < 8; i++) {
BufferedImage bufferedImage = createExtraSamplesImage(10, 10, ColorSpaces.getColorSpace(ColorSpaces.CS_GENERIC_CMYK), false, i);
BufferedImage resampled = new ResampleOp(5, 5, ResampleOp.FILTER_LANCZOS).filter(bufferedImage, null);
BufferedImage image = createExtraSamplesImage(10, 10, ColorSpaces.getColorSpace(ColorSpaces.CS_GENERIC_CMYK), false, i);
BufferedImage resampled = new ResampleOp(5, 5, ResampleOp.FILTER_LANCZOS).filter(image, null);
assertNotNull(resampled);
assertEquals(5, resampled.getWidth());
@@ -126,12 +130,43 @@ public class ExtraSamplesColorModelTest {
@Test
public void testImageWithExtraSamplesCanBeResampledCMYKAlpha() {
for (int i = 1; i < 8; i++) {
BufferedImage bufferedImage = createExtraSamplesImage(10, 10, ColorSpaces.getColorSpace(ColorSpaces.CS_GENERIC_CMYK), true, i);
BufferedImage resampled = new ResampleOp(5, 5, ResampleOp.FILTER_LANCZOS).filter(bufferedImage, null);
BufferedImage image = createExtraSamplesImage(10, 10, ColorSpaces.getColorSpace(ColorSpaces.CS_GENERIC_CMYK), true, i);
BufferedImage resampled = new ResampleOp(5, 5, ResampleOp.FILTER_LANCZOS).filter(image, null);
assertNotNull(resampled);
assertEquals(5, resampled.getWidth());
assertEquals(5, resampled.getHeight());
}
}
@Test
public void testSetRGB() {
BufferedImage image = createExtraSamplesImage(1, 1, ColorSpaces.getColorSpace(ColorSpace.CS_sRGB), false, 1);
image.setRGB(0, 0, Color.BLACK.getRGB());
assertEquals(Color.BLACK.getRGB(), image.getRGB(0, 0));
}
@Test
public void testSetRGBs() {
BufferedImage image = createExtraSamplesImage(2, 2, ColorSpaces.getColorSpace(ColorSpace.CS_sRGB), false, 1);
image.setRGB(0, 0, 2, 1, new int[]{Color.BLACK.getRGB(), Color.WHITE.getRGB()}, 0, 2);
assertEquals(Color.BLACK.getRGB(), image.getRGB(0, 0));
assertEquals(Color.WHITE.getRGB(), image.getRGB(1, 0));
}
@Test
public void testEquals() {
ExtraSamplesColorModel original = createExtraSamplesColorModel(ColorSpaces.getColorSpace(ColorSpace.CS_sRGB), true, 1);
ExtraSamplesColorModel equal = createExtraSamplesColorModel(ColorSpaces.getColorSpace(ColorSpace.CS_sRGB), true, 1);
assertEquals(original, equal);
assertEquals(equal, original);
ExtraSamplesColorModel different = createExtraSamplesColorModel(ColorSpaces.getColorSpace(ColorSpace.CS_sRGB), true, 2);
ExtraSamplesColorModel differentToo = createExtraSamplesColorModel(ColorSpaces.getColorSpace(ColorSpace.CS_sRGB), false, 1);
assertNotEquals(original, different);
assertNotEquals(original, differentToo);
}
}
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.8.0</version>
<version>3.8.1</version>
</parent>
<artifactId>imageio-webp</artifactId>
<name>TwelveMonkeys :: ImageIO :: WebP plugin</name>
@@ -35,6 +35,7 @@ import com.twelvemonkeys.imageio.spi.ImageReaderSpiBase;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.nio.ByteOrder;
import java.util.Locale;
@@ -76,7 +77,8 @@ public final class WebPImageReaderSpi extends ImageReaderSpiBase {
switch (chunk) {
// TODO. Support lossless
// case WebP.CHUNK_VP8L:
// case WebP.CHUNK_VP8X:
case WebP.CHUNK_VP8X:
return containsSupportedChunk(stream, chunk);
case WebP.CHUNK_VP8_:
return true;
default:
@@ -89,6 +91,30 @@ public final class WebPImageReaderSpi extends ImageReaderSpiBase {
}
}
private static boolean containsSupportedChunk(ImageInputStream stream, int chunk) throws IOException {
// Temporary: Seek for VP8_, either first or second (after ICCP), or inside ANMF...
try {
while (chunk != WebP.CHUNK_VP8L && chunk != WebP.CHUNK_ALPH) {
long length = stream.readUnsignedInt();
stream.seek(stream.getStreamPosition() + length);
chunk = stream.readInt();
// Look inside ANMF chunks...
if (chunk == WebP.CHUNK_ANMF) {
stream.seek(stream.getStreamPosition() + 4 + 16);
chunk = stream.readInt();
}
if (chunk == WebP.CHUNK_VP8_) {
return true;
}
}
}
catch (EOFException ignore) {}
return false;
}
@Override
public ImageReader createReaderInstance(final Object extension) {
return new WebPImageReader(this);
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.8.0</version>
<version>3.8.1</version>
</parent>
<artifactId>imageio-xwd</artifactId>
<name>TwelveMonkeys :: ImageIO :: XWD plugin</name>
+1 -1
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>com.twelvemonkeys</groupId>
<artifactId>twelvemonkeys</artifactId>
<version>3.8.0</version>
<version>3.8.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.twelvemonkeys.imageio</groupId>
+2 -2
View File
@@ -9,7 +9,7 @@
<groupId>com.twelvemonkeys</groupId>
<artifactId>twelvemonkeys</artifactId>
<version>3.8.0</version>
<version>3.8.1</version>
<packaging>pom</packaging>
<name>Twelvemonkeys</name>
@@ -86,7 +86,7 @@
<connection>scm:git:https://github.com/haraldk/TwelveMonkeys</connection>
<developerConnection>scm:git:ssh://git@github.com/haraldk/TwelveMonkeys</developerConnection>
<url>https://github.com/haraldk/TwelveMonkeys</url>
<tag>twelvemonkeys-3.8.0</tag>
<tag>twelvemonkeys-3.8.1</tag>
</scm>
<properties>
+33 -48
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>com.twelvemonkeys</groupId>
<artifactId>twelvemonkeys</artifactId>
<version>3.8.0</version>
<version>3.8.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -12,37 +12,6 @@
<name>TwelveMonkeys :: Servlet</name>
<dependencies>
<dependency>
<groupId>com.twelvemonkeys.common</groupId>
<artifactId>common-lang</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.common</groupId>
<artifactId>common-io</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.common</groupId>
<artifactId>common-image</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.common</groupId>
<artifactId>common-lang</artifactId>
<version>${project.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.common</groupId>
<artifactId>common-io</artifactId>
<version>${project.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
@@ -50,31 +19,17 @@
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.12.4</version>
<version>4.1.0</version>
<scope>test</scope>
</dependency>
</dependencies>
@@ -107,6 +62,36 @@
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.2</version>
<executions>
<execution>
<id>jakarta</id>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<finalName>twelvemonkeys-${project.artifactId}-${project.version}-jakarta</finalName>
<shadedArtifactAttached>true</shadedArtifactAttached>
<createDependencyReducedPom>false</createDependencyReducedPom>
<artifactSet>
<includes>
<include>${project.groupId}:${project.artifactId}</include>
</includes>
</artifactSet>
<relocations>
<relocation>
<pattern>javax.servlet</pattern>
<shadedPattern>jakarta.servlet</shadedPattern>
</relocation>
</relocations>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
@@ -1,149 +0,0 @@
/*
* Copyright (c) 2009, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Iterator;
import java.util.Set;
/**
* AbstractServletMapAdapter
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: AbstractServletMapAdapter.java#1 $
*/
abstract class AbstractServletMapAdapter<T> extends AbstractMap<String, T> {
// TODO: This map is now a little too lazy.. Should cache entries!
private transient Set<Entry<String, T>> entries;
protected abstract Iterator<String> keysImpl();
protected abstract T valueImpl(String pName);
@Override
public T get(final Object pKey) {
if (pKey instanceof String) {
return valueImpl((String) pKey);
}
return null;
}
@Override
public int size() {
// Avoid creating expensive entry set for computing size
int size = 0;
for (Iterator<String> names = keysImpl(); names.hasNext(); names.next()) {
size++;
}
return size;
}
public Set<Entry<String, T>> entrySet() {
if (entries == null) {
entries = new AbstractSet<Entry<String, T>>() {
public Iterator<Entry<String, T>> iterator() {
return new Iterator<Entry<String, T>>() {
Iterator<String> keys = keysImpl();
public boolean hasNext() {
return keys.hasNext();
}
public Entry<String, T> next() {
// TODO: Replace with cached lookup
return new HeaderEntry(keys.next());
}
public void remove() {
keys.remove();
}
};
}
public int size() {
return AbstractServletMapAdapter.this.size();
}
};
}
return entries;
}
private class HeaderEntry implements Entry<String, T> {
final String key;
public HeaderEntry(final String pKey) {
key = pKey;
}
public String getKey() {
return key;
}
public T getValue() {
return get(key);
}
public T setValue(final T pValue) {
// Write-through if supported
return put(key, pValue);
}
@Override
public int hashCode() {
T value = getValue();
return (key == null ? 0 : key.hashCode()) ^
(value == null ? 0 : value.hashCode());
}
@Override
public boolean equals(final Object pOther) {
if (pOther == this) {
return true;
}
if (pOther instanceof Entry) {
Entry other = (Entry) pOther;
return ((other.getKey() == null && getKey() == null) ||
(getKey() != null && getKey().equals(other.getKey()))) &&
((other.getValue() == null && getValue() == null) ||
(getValue() != null && getValue().equals(other.getValue())));
}
return false;
}
}
}
@@ -1,168 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import com.twelvemonkeys.lang.StringUtil;
/**
* BrowserHelperFilter
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: BrowserHelperFilter.java#1 $
*/
@Deprecated
public class BrowserHelperFilter extends GenericFilter {
private static final String HTTP_HEADER_ACCEPT = "Accept";
protected static final String HTTP_HEADER_USER_AGENT = "User-Agent";
// TODO: Consider using unmodifiable LinkedHashMap<Pattern, String> instead
private Pattern[] knownAgentPatterns;
private String[] knownAgentAccepts;
/**
* Sets the accept-mappings for this filter
* @param pPropertiesFile name of accept-mappings properties files
* @throws ServletConfigException if the accept-mappings properties
* file cannot be read.
*/
@InitParam(name = "accept-mappings-file")
public void setAcceptMappingsFile(String pPropertiesFile) throws ServletConfigException {
// NOTE: Format is:
// <agent-name>=<reg-exp>
// <agent-name>.accept=<http-accept-header>
Properties mappings = new Properties();
try {
log("Reading Accept-mappings properties file: " + pPropertiesFile);
mappings.load(getServletContext().getResourceAsStream(pPropertiesFile));
//System.out.println("--> Loaded file: " + pPropertiesFile);
}
catch (IOException e) {
throw new ServletConfigException("Could not read Accept-mappings properties file: " + pPropertiesFile, e);
}
parseMappings(mappings);
}
private void parseMappings(Properties mappings) {
List<Pattern> patterns = new ArrayList<Pattern>();
List<String> accepts = new ArrayList<String>();
for (Object key : mappings.keySet()) {
String agent = (String) key;
if (agent.endsWith(".accept")) {
continue;
}
//System.out.println("--> Adding accept-mapping for User-Agent: " + agent);
try {
String accept = (String) mappings.get(agent + ".accept");
if (!StringUtil.isEmpty(accept)) {
patterns.add(Pattern.compile((String) mappings.get(agent)));
accepts.add(accept);
//System.out.println("--> " + agent + " accepts: " + accept);
}
else {
log("Missing Accept mapping for User-Agent: " + agent);
}
}
catch (PatternSyntaxException e) {
log("Could not parse User-Agent identification for " + agent, e);
}
knownAgentPatterns = patterns.toArray(new Pattern[patterns.size()]);
knownAgentAccepts = accepts.toArray(new String[accepts.size()]);
}
}
public void init() throws ServletException {
if (knownAgentAccepts == null || knownAgentAccepts.length == 0) {
throw new ServletConfigException("No User-Agent/Accept mappings for filter: " + getFilterName());
}
}
protected void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain) throws IOException, ServletException {
if (pRequest instanceof HttpServletRequest) {
//System.out.println("--> Trying to find User-Agent/Accept headers...");
HttpServletRequest request = (HttpServletRequest) pRequest;
// Check if User-Agent is in list of known agents
if (knownAgentPatterns != null && knownAgentPatterns.length > 0) {
String agent = request.getHeader(HTTP_HEADER_USER_AGENT);
//System.out.println("--> User-Agent: " + agent);
for (int i = 0; i < knownAgentPatterns.length; i++) {
Pattern pattern = knownAgentPatterns[i];
//System.out.println("--> Pattern: " + pattern);
if (pattern.matcher(agent).matches()) {
// TODO: Consider merge known with real accept, in case plugins add extra capabilities?
final String fakeAccept = knownAgentAccepts[i];
//System.out.println("--> User-Agent: " + agent + " accepts: " + fakeAccept);
pRequest = new HttpServletRequestWrapper(request) {
public String getHeader(String pName) {
if (HTTP_HEADER_ACCEPT.equals(pName)) {
return fakeAccept;
}
return super.getHeader(pName);
}
};
break;
}
}
}
}
pChain.doFilter(pRequest, pResponse);
}
}
@@ -1,122 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet;
import java.io.IOException;
import java.util.Enumeration;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* DebugServlet class description.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: DebugServlet.java#1 $
*/
@Deprecated
public class DebugServlet extends GenericServlet {
private long dateModified;
public final void service(ServletRequest pRequest, ServletResponse pResponse) throws ServletException, IOException {
service((HttpServletRequest) pRequest, (HttpServletResponse) pResponse);
}
public void init() throws ServletException {
super.init();
dateModified = System.currentTimeMillis();
}
public void service(HttpServletRequest pRequest, HttpServletResponse pResponse) throws ServletException, IOException {
pResponse.setContentType("text/plain");
// Include these to allow browser caching
pResponse.setDateHeader("Last-Modified", dateModified);
pResponse.setHeader("ETag", getServletName());
ServletOutputStream out = pResponse.getOutputStream();
out.println("Remote address: " + pRequest.getRemoteAddr());
out.println("Remote host name: " + pRequest.getRemoteHost());
out.println("Remote user: " + pRequest.getRemoteUser());
out.println();
out.println("Request Method: " + pRequest.getMethod());
out.println("Request Scheme: " + pRequest.getScheme());
out.println("Request URI: " + pRequest.getRequestURI());
out.println("Request URL: " + pRequest.getRequestURL().toString());
out.println("Request PathInfo: " + pRequest.getPathInfo());
out.println("Request ContentLength: " + pRequest.getContentLength());
out.println();
out.println("Request Headers:");
Enumeration headerNames = pRequest.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = (String) headerNames.nextElement();
Enumeration headerValues = pRequest.getHeaders(headerName);
if (headerName != null) {
while (headerValues.hasMoreElements()) {
String value = (String) headerValues.nextElement();
out.println(" " + headerName + ": " + value);
}
}
}
out.println();
out.println("Request parameters:");
Enumeration paramNames = pRequest.getParameterNames();
while (paramNames.hasMoreElements()) {
String name = (String) paramNames.nextElement();
String[] values = pRequest.getParameterValues(name);
for (String value : values) {
out.println(" " + name + ": " + value);
}
}
out.println();
out.println("Request attributes:");
Enumeration attribNames = pRequest.getAttributeNames();
while (attribNames.hasMoreElements()) {
String name = (String) attribNames.nextElement();
Object value = pRequest.getAttribute(name);
out.println(" " + name + ": " + value);
}
out.flush();
}
}
@@ -1,403 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.util.Enumeration;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import com.twelvemonkeys.lang.BeanUtil;
/**
* Defines a generic, protocol-independent filter.
* <p>
* {@code GenericFilter} is inspired by {@link GenericServlet}, and
* implements the {@code Filter} and {@code FilterConfig} interfaces.
* </p>
* <p>
* {@code GenericFilter} makes writing filters easier. It provides simple
* versions of the lifecycle methods {@code init} and {@code destroy}
* and of the methods in the {@code FilterConfig} interface.
* {@code GenericFilter} also implements the {@code log} methods,
* declared in the {@code ServletContext} interface.
* </p>
* <p>
* {@code GenericFilter} has an auto-init system, that automatically invokes
* the method matching the signature {@code void setX(&lt;Type&gt;)},
* 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.
* </p>
* <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.
* </p>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
*
* @version $Id: GenericFilter.java#1 $
*
* @see Filter
* @see FilterConfig
*/
@Deprecated
public abstract class GenericFilter implements Filter, FilterConfig, Serializable {
// TODO: Rewrite to use ServletConfigurator instead of BeanUtil
/**
* The filter config.
*/
private transient FilterConfig filterConfig = null;
/**
* Makes sure the filter runs once per request
*
* @see #isRunOnce
* @see #ATTRIB_RUN_ONCE_VALUE
* @see #oncePerRequest
*/
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 {@code FilterConfig} object.
*
* @see #isRunOnce
* @see #ATTRIB_RUN_ONCE_VALUE
* @see #oncePerRequest
*/
private String attribRunOnce = null;
/**
* Makes sure the filter runs once per request
*
* @see #isRunOnce
* @see #ATTRIB_RUN_ONCE_EXT
* @see #oncePerRequest
*/
private static final Object ATTRIB_RUN_ONCE_VALUE = new Object();
/**
* Indicates if this filter should run once per request ({@code true}),
* or for each forward/include resource ({@code false}).
* <p>
* Set this variable to true, to make sure the filter runs once per request.
* </p>
* <p>
* <em>NOTE: As of Servlet 2.4, this field
* should always be left to it's default value ({@code false}).
* <br>
* To run the filter once per request, the {@code filter-mapping} element
* of the web-descriptor should include a {@code dispatcher} element:
* </em>
* </p>
* <pre>&lt;dispatcher&gt;REQUEST&lt;/dispatcher&gt;</pre>
*/
protected boolean oncePerRequest = false;
/**
* Does nothing.
*/
public GenericFilter() {}
/**
* Called by the web container to indicate to a filter that it is being
* placed into service.
* <p>
* This implementation stores the {@code FilterConfig} object it
* receives from the servlet container for later use.
* Generally, there's no reason to override this method, override the
* no-argument {@code init} instead. However, <em>if</em> you are
* overriding this form of the method,
* always call {@code super.init(config)}.
* </p>
* <p>
* This implementation will also set all configured key/value pairs, that
* have a matching setter method annotated with {@link InitParam}.
* </p>
*
* @param pConfig the filter config
* @throws ServletException if an error occurs during init
*
* @see Filter#init(javax.servlet.FilterConfig)
* @see #init() init
* @see BeanUtil#configure(Object, java.util.Map, boolean)
*/
public void init(final FilterConfig pConfig) throws ServletException {
if (pConfig == null) {
throw new ServletConfigException("filter config == null");
}
// Store filter config
filterConfig = pConfig;
// Configure this
try {
BeanUtil.configure(this, ServletUtil.asMap(pConfig), true);
}
catch (InvocationTargetException e) {
throw new ServletConfigException("Could not configure " + getFilterName(), e.getCause());
}
// Create run-once attribute name
attribRunOnce = pConfig.getFilterName() + ATTRIB_RUN_ONCE_EXT;
log("init (oncePerRequest=" + oncePerRequest + ", attribRunOnce=" + attribRunOnce + ")");
init();
}
/**
* A convenience method which can be overridden so that there's no need to
* call {@code super.init(config)}.
*
* @see #init(FilterConfig)
*
* @throws ServletException if an error occurs during init
*/
public void init() throws ServletException {}
/**
* The {@code doFilter} method of the Filter is called by the container
* each time a request/response pair is passed through the chain due to a
* client request for a resource at the end of the chain.
* <p>
* Subclasses <em>should not override this method</em>, but rather the
* abstract {@link #doFilterImpl(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)} doFilterImpl} method.
* </p>
*
* @param pRequest the servlet request
* @param pResponse the servlet response
* @param pFilterChain the filter chain
*
* @throws IOException
* @throws ServletException
*
* @see Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) Filter.doFilter
* @see #doFilterImpl(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) doFilterImpl
*/
public final void doFilter(final ServletRequest pRequest, final ServletResponse pResponse, final FilterChain pFilterChain) throws IOException, ServletException {
// If request filter and already run, continue chain and return fast
if (oncePerRequest && isRunOnce(pRequest)) {
pFilterChain.doFilter(pRequest, pResponse);
return;
}
// Do real filter
doFilterImpl(pRequest, pResponse, pFilterChain);
}
/**
* If request is filtered, returns true, otherwise marks request as filtered
* 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 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
*
* @param pRequest the servlet request
* @return {@code true} if the request is already filtered, otherwise
* {@code false}.
*/
private boolean isRunOnce(final ServletRequest pRequest) {
// If request already filtered, return true (skip)
if (pRequest.getAttribute(attribRunOnce) == ATTRIB_RUN_ONCE_VALUE) {
return true;
}
// Set attribute and return false (continue)
pRequest.setAttribute(attribRunOnce, ATTRIB_RUN_ONCE_VALUE);
return false;
}
/**
* Invoked once, or each time a request/response pair is passed through the
* chain, depending on the {@link #oncePerRequest} member variable.
*
* @param pRequest the servlet request
* @param pResponse the servlet response
* @param pChain the filter chain
*
* @throws IOException if an I/O error occurs
* @throws ServletException if an exception occurs during the filter process
*
* @see #oncePerRequest
* @see #doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) doFilter
* @see Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) Filter.doFilter
*/
protected abstract void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain)
throws IOException, ServletException;
/**
* Called by the web container to indicate to a filter that it is being
* taken out of service.
*
* @see Filter#destroy
*/
public void destroy() {
log("destroy");
filterConfig = null;
}
/**
* Returns the filter-name of this filter as defined in the deployment
* descriptor.
*
* @return the filter-name
* @see FilterConfig#getFilterName
*/
public String getFilterName() {
return filterConfig.getFilterName();
}
/**
* Returns a reference to the {@link ServletContext} in which the caller is
* executing.
*
* @return the {@code ServletContext} object, used by the caller to
* interact with its servlet container
* @see FilterConfig#getServletContext
* @see ServletContext
*/
public ServletContext getServletContext() {
return filterConfig.getServletContext();
}
/**
* Returns a {@code String} containing the value of the named
* initialization parameter, or null if the parameter does not exist.
*
* @param pKey a {@code String} specifying the name of the
* initialization parameter
* @return a {@code String} containing the value of the initialization
* parameter
*/
public String getInitParameter(final String pKey) {
return filterConfig.getInitParameter(pKey);
}
/**
* Returns the names of the servlet's initialization parameters as an
* {@code Enumeration} of {@code String} objects, or an empty
* {@code Enumeration} if the servlet has no initialization parameters.
*
* @return an {@code Enumeration} of {@code String} objects
* containing the mNames of the servlet's initialization parameters
*/
public Enumeration getInitParameterNames() {
return filterConfig.getInitParameterNames();
}
/**
* Writes the specified message to a servlet log file, prepended by the
* filter's name.
*
* @param pMessage the log message
* @see ServletContext#log(String)
*/
protected void log(final String pMessage) {
getServletContext().log(getFilterName() + ": " + pMessage);
}
/**
* Writes an explanatory message and a stack trace for a given
* {@code Throwable} to the servlet log file, prepended by the
* filter's name.
*
* @param pMessage the log message
* @param pThrowable the exception
* @see ServletContext#log(String,Throwable)
*/
protected void log(final String pMessage, final Throwable pThrowable) {
getServletContext().log(getFilterName() + ": " + pMessage, pThrowable);
}
/**
* Initializes the filter.
*
* @param pFilterConfig the filter config
* @see #init init
*
* @deprecated For compatibility only, use {@link #init init} instead.
*/
@Deprecated
@SuppressWarnings("UnusedDeclaration")
public void setFilterConfig(final FilterConfig pFilterConfig) {
try {
init(pFilterConfig);
}
catch (ServletException e) {
log("Error in init(), see stack trace for details.", e);
}
}
/**
* Gets the {@code FilterConfig} for this filter.
*
* @return the {@code FilterConfig} for this filter
* @see FilterConfig
*/
public FilterConfig getFilterConfig() {
return filterConfig;
}
/**
* Specifies if this filter should run once per request ({@code true}),
* or for each forward/include resource ({@code false}).
* Called automatically from the {@code init}-method, with settings
* from web.xml.
*
* @param pOncePerRequest {@code true} if the filter should run only
* once per request
* @see #oncePerRequest
*/
@InitParam(name = "once-per-request")
public void setOncePerRequest(final boolean pOncePerRequest) {
oncePerRequest = pOncePerRequest;
}
}
@@ -1,95 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet;
import java.lang.reflect.InvocationTargetException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import com.twelvemonkeys.lang.BeanUtil;
/**
* Defines a generic, protocol-independent servlet.
* <p>
* {@code GenericServlet} has an auto-init system, that automatically invokes
* the method matching the signature {@code void setX(&lt;Type&gt;)},
* 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.
* </p>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
*
* @version $Id: GenericServlet.java#1 $
*/
@Deprecated
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
* placed into service.
* <p>
* This implementation stores the {@code ServletConfig} object it
* receives from the servlet container for later use. When overriding this
* form of the method, call {@code super.init(config)}.
* </p>
* <p>
* This implementation will also set all configured key/value pairs, that
* have a matching setter method annotated with {@link InitParam}.
* </p>
*
* @param pConfig the servlet config
* @throws ServletException if the servlet could not be initialized.
*
* @see javax.servlet.GenericServlet#init
* @see #init() init
* @see BeanUtil#configure(Object, java.util.Map, boolean)
*/
@Override
public void init(final ServletConfig pConfig) throws ServletException {
if (pConfig == null) {
throw new ServletConfigException("servlet config == null");
}
try {
BeanUtil.configure(this, ServletUtil.asMap(pConfig), true);
}
catch (InvocationTargetException e) {
throw new ServletConfigException("Could not configure " + getServletName(), e.getCause());
}
super.init(pConfig);
}
}
@@ -1,95 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet;
import java.lang.reflect.InvocationTargetException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import com.twelvemonkeys.lang.BeanUtil;
/**
* Defines a generic, HTTP specific servlet.
* <p>
* {@code HttpServlet} has an auto-init system, that automatically invokes
* the method matching the signature {@code void setX(&lt;Type&gt;)},
* 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.
* </p>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
*
* @version $Id: HttpServlet.java#1 $
*/
@Deprecated
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
* placed into service.
* <p>
* This implementation stores the {@code ServletConfig} object it
* receives from the servlet container for later use. When overriding this
* form of the method, call {@code super.init(config)}.
* </p>
* <p>
* This implementation will also set all configured key/value pairs, that
* have a matching setter method annotated with {@link InitParam}.
* </p>
*
* @param pConfig the servlet config
* @throws ServletException if an error occurred during init
*
* @see javax.servlet.GenericServlet#init
* @see #init() init
* @see BeanUtil#configure(Object, java.util.Map, boolean)
*/
@Override
public void init(ServletConfig pConfig) throws ServletException {
if (pConfig == null) {
throw new ServletConfigException("servlet config == null");
}
try {
BeanUtil.configure(this, ServletUtil.asMap(pConfig), true);
}
catch (InvocationTargetException e) {
throw new ServletConfigException("Could not configure " + getServletName(), e.getCause());
}
super.init(pConfig);
}
}
@@ -1,63 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation to be used by servlets/filters, to have their {@code init}-method
* automatically convert and set values from their respective configuration.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: InitParam.java#1 $
* @see com.twelvemonkeys.servlet.ServletConfigurator
* @see com.twelvemonkeys.servlet.GenericFilter#init(javax.servlet.FilterConfig)
* @see com.twelvemonkeys.servlet.GenericServlet#init(javax.servlet.ServletConfig)
* @see com.twelvemonkeys.servlet.HttpServlet#init(javax.servlet.ServletConfig)
*/
// TODO: Actually implement for version 3.0!
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD/*, TODO: ElementType.FIELD*/})
@Deprecated
public @interface InitParam {
static final String UNDEFINED = "";
String name() default UNDEFINED;
String defaultValue() default UNDEFINED; // TODO: Consider separate annotation?
boolean required() default false; // TODO: Consider separate annotation?
}
@@ -1,127 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet;
import java.io.IOException;
import java.io.OutputStream;
import javax.servlet.ServletOutputStream;
import com.twelvemonkeys.lang.Validate;
/**
* A {@code ServletOutputStream} implementation backed by a
* {@link java.io.OutputStream}. For filters that need to buffer the
* response and do post filtering, it may be used like this:<pre>
* ByteArrayOutputStream buffer = new ByteArraOutputStream();
* ServletOutputStream adapter = new OutputStreamAdapter(buffer);
* </pre>
* <p>
* As a {@code ServletOutputStream} is itself an {@code OutputStream}, this
* class may also be used as a superclass for wrappers of other
* {@code ServletOutputStream}s, like this:
* </p>
* <pre>
* class FilterServletOutputStream extends OutputStreamAdapter {
* public FilterServletOutputStream(ServletOutputStream out) {
* super(out);
* }
*
* public void write(int abyte) {
* // do filtering...
* super.write(...);
* }
* }
*
* ...
*
* ServletOutputStream original = response.getOutputStream();
* ServletOutputStream wrapper = new FilterServletOutputStream(original);
* </pre>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author $Author: haku $
* @version $Id: OutputStreamAdapter.java#1 $
*
*/
@Deprecated
public class OutputStreamAdapter extends ServletOutputStream {
/** The wrapped {@code OutputStream}. */
protected final OutputStream out;
/**
* Creates an {@code OutputStreamAdapter}.
*
* @param pOut the wrapped {@code OutputStream}
*
* @throws IllegalArgumentException if {@code pOut} is {@code null}.
*/
public OutputStreamAdapter(final OutputStream pOut) {
Validate.notNull(pOut, "out");
out = pOut;
}
/**
* Returns the wrapped {@code OutputStream}.
*
* @return the wrapped {@code OutputStream}.
*/
public OutputStream getOutputStream() {
return out;
}
@Override
public String toString() {
return "ServletOutputStream adapted from " + out.toString();
}
/**
* Writes a byte to the underlying stream.
*
* @param pByte the byte to write.
*
* @throws IOException if an error occurs during writing
*/
public void write(final int pByte) throws IOException {
out.write(pByte);
}
// Overide for efficiency
public void write(final byte pBytes[]) throws IOException {
out.write(pBytes);
}
// Overide for efficiency
public void write(final byte pBytes[], final int pOff, final int pLen) throws IOException {
out.write(pBytes, pOff, pLen);
}
}
@@ -1,136 +0,0 @@
/*
* Copyright (c) 2013, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletRequest;
import java.util.Enumeration;
import java.util.Iterator;
import static com.twelvemonkeys.lang.Validate.notNull;
/**
* ServletAttributesMap
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: ServletAttributesMap.java,v 1.0 01.03.13 10:34 haraldk Exp$
*/
class ServletAttributesMapAdapter extends AbstractServletMapAdapter<Object> {
private final ServletContext context;
private final ServletRequest request;
ServletAttributesMapAdapter(final ServletContext context) {
this(notNull(context), null);
}
ServletAttributesMapAdapter(final ServletRequest request) {
this(null, notNull(request));
}
private ServletAttributesMapAdapter(final ServletContext context, final ServletRequest request) {
this.context = context;
this.request = request;
}
@SuppressWarnings("unchecked")
private Enumeration<String> getAttributeNames() {
return context != null ? context.getAttributeNames() : request.getAttributeNames();
}
private Object getAttribute(final String name) {
return context != null ? context.getAttribute(name) : request.getAttribute(name);
}
private Object setAttribute(String name, Object value) {
Object oldValue = getAttribute(name);
if (context != null) {
context.setAttribute(name, value);
}
else {
request.setAttribute(name, value);
}
return oldValue;
}
private Object removeAttribute(String name) {
Object oldValue = getAttribute(name);
if (context != null) {
context.removeAttribute(name);
}
else {
request.removeAttribute(name);
}
return oldValue;
}
@Override
protected Iterator<String> keysImpl() {
final Enumeration<String> keys = getAttributeNames();
return new Iterator<String>() {
private String key;
public boolean hasNext() {
return keys.hasMoreElements();
}
public String next() {
key = keys.nextElement();
return key;
}
public void remove() {
// Support removal of attribute through key iterator
removeAttribute(key);
}
};
}
@Override
protected Object valueImpl(String pName) {
return getAttribute(pName);
}
@Override
public Object put(String key, Object value) {
return setAttribute(key, value);
}
@Override
public Object remove(Object key) {
return key instanceof String ? removeAttribute((String) key) : null;
}
}
@@ -1,84 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet;
import javax.servlet.ServletException;
/**
* ServletConfigException.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: ServletConfigException.java#2 $
*/
@Deprecated
public class ServletConfigException extends ServletException {
// TODO: Parameters for init-param at fault, and possibly servlet name?
/**
* Creates a {@code ServletConfigException} with the given message.
*
* @param pMessage the exception message
*/
public ServletConfigException(String pMessage) {
super(pMessage);
}
/**
* Creates a {@code ServletConfigException} with the given message and cause.
*
* @param pMessage the exception message
* @param pCause the exception cause
*/
public ServletConfigException(final String pMessage, final Throwable pCause) {
super(pMessage, pCause);
maybeInitCause(pCause);
}
/**
* Creates a {@code ServletConfigException} with the cause.
*
* @param pCause the exception cause
*/
public ServletConfigException(final Throwable pCause) {
super(String.format("Error in Servlet configuration: %s", pCause.getMessage()), pCause);
maybeInitCause(pCause);
}
private void maybeInitCause(Throwable pCause) {
// Workaround for ServletExceptions that does not do proper exception chaining
if (getCause() == null) {
initCause(pCause);
}
}
}
@@ -1,284 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet;
import com.twelvemonkeys.lang.StringUtil;
import com.twelvemonkeys.lang.Validate;
import javax.servlet.FilterConfig;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import java.io.Serializable;
import java.util.*;
/**
* {@code ServletConfig} or {@code FilterConfig} adapter, that implements
* the {@code Map} interface for interoperability with collection-based API's.
* <p>
* This {@code Map} is not synchronized.
* </p>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: ServletConfigMapAdapter.java#2 $
*/
class ServletConfigMapAdapter extends AbstractMap<String, String> implements Map<String, String>, Serializable, Cloneable {
enum ConfigType {
ServletConfig, FilterConfig, ServletContext
}
private final ConfigType type;
private final ServletConfig servletConfig;
private final FilterConfig filterConfig;
private final ServletContext servletContext;
// Cache the entry set
private transient Set<Entry<String, String>> entrySet;
public ServletConfigMapAdapter(final ServletConfig pConfig) {
this(pConfig, ConfigType.ServletConfig);
}
public ServletConfigMapAdapter(final FilterConfig pConfig) {
this(pConfig, ConfigType.FilterConfig);
}
public ServletConfigMapAdapter(final ServletContext pContext) {
this(pContext, ConfigType.ServletContext);
}
private ServletConfigMapAdapter(final Object pConfig, final ConfigType pType) {
// Could happen if client code invokes with null reference
Validate.notNull(pConfig, "config");
type = pType;
switch (type) {
case ServletConfig:
servletConfig = (ServletConfig) pConfig;
filterConfig = null;
servletContext = null;
break;
case FilterConfig:
servletConfig = null;
filterConfig = (FilterConfig) pConfig;
servletContext = null;
break;
case ServletContext:
servletConfig = null;
filterConfig = null;
servletContext = (ServletContext) pConfig;
break;
default:
throw new IllegalArgumentException("Wrong type: " + pType);
}
}
/**
* Gets the servlet or filter name from the config.
*
* @return the servlet or filter name
*/
public final String getName() {
switch (type) {
case ServletConfig:
return servletConfig.getServletName();
case FilterConfig:
return filterConfig.getFilterName();
case ServletContext:
return servletContext.getServletContextName();
default:
throw new IllegalStateException();
}
}
/**
* Gets the servlet context from the config.
*
* @return the servlet context
*/
public final ServletContext getServletContext() {
switch (type) {
case ServletConfig:
return servletConfig.getServletContext();
case FilterConfig:
return filterConfig.getServletContext();
case ServletContext:
return servletContext;
default:
throw new IllegalStateException();
}
}
public final Enumeration getInitParameterNames() {
switch (type) {
case ServletConfig:
return servletConfig.getInitParameterNames();
case FilterConfig:
return filterConfig.getInitParameterNames();
case ServletContext:
return servletContext.getInitParameterNames();
default:
throw new IllegalStateException();
}
}
public final String getInitParameter(final String pName) {
switch (type) {
case ServletConfig:
return servletConfig.getInitParameter(pName);
case FilterConfig:
return filterConfig.getInitParameter(pName);
case ServletContext:
return servletContext.getInitParameter(pName);
default:
throw new IllegalStateException();
}
}
public Set<Entry<String, String>> entrySet() {
if (entrySet == null) {
entrySet = createEntrySet();
}
return entrySet;
}
private Set<Entry<String, String>> createEntrySet() {
return new AbstractSet<Entry<String, String>>() {
// Cache size, if requested, -1 means not calculated
private int size = -1;
public Iterator<Entry<String, String>> iterator() {
return new Iterator<Entry<String, String>>() {
// Iterator is backed by initParameterNames enumeration
final Enumeration names = getInitParameterNames();
public boolean hasNext() {
return names.hasMoreElements();
}
public Entry<String, String> next() {
final String key = (String) names.nextElement();
return new Entry<String, String>() {
public String getKey() {
return key;
}
public String getValue() {
return get(key);
}
public String setValue(String pValue) {
throw new UnsupportedOperationException();
}
// NOTE: Override equals
public boolean equals(Object pOther) {
if (!(pOther instanceof Map.Entry)) {
return false;
}
Map.Entry e = (Map.Entry) pOther;
Object value = get(key);
Object rKey = e.getKey();
Object rValue = e.getValue();
return (key == null ? rKey == null : key.equals(rKey))
&& (value == null ? rValue == null : value.equals(rValue));
}
// NOTE: Override hashCode to keep the map's
// hashCode constant and compatible
public int hashCode() {
Object value = get(key);
return ((key == null) ? 0 : key.hashCode()) ^
((value == null) ? 0 : value.hashCode());
}
public String toString() {
return key + "=" + get(key);
}
};
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
public int size() {
if (size < 0) {
size = calculateSize();
}
return size;
}
private int calculateSize() {
final Enumeration names = getInitParameterNames();
int size = 0;
while (names.hasMoreElements()) {
size++;
names.nextElement();
}
return size;
}
};
}
public String get(Object pKey) {
return getInitParameter(StringUtil.valueOf(pKey));
}
/// Unsupported Map methods
@Override
public String put(String pKey, String pValue) {
throw new UnsupportedOperationException();
}
@Override
public String remove(Object pKey) {
throw new UnsupportedOperationException();
}
@Override
public void putAll(Map pMap) {
throw new UnsupportedOperationException();
}
@Override
public void clear() {
throw new UnsupportedOperationException();
}
}
@@ -1,271 +0,0 @@
/*
* Copyright (c) 2011, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet;
import com.twelvemonkeys.lang.StringUtil;
import com.twelvemonkeys.util.FilterIterator;
import com.twelvemonkeys.util.convert.ConversionException;
import com.twelvemonkeys.util.convert.Converter;
import javax.servlet.Filter;
import javax.servlet.FilterConfig;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
/**
* ServletConfigurator
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: ServletConfigurator.java,v 1.0 Apr 30, 2010 2:51:38 PM haraldk Exp$
* @see com.twelvemonkeys.servlet.InitParam
*/
final class ServletConfigurator {
// TODO: Rethink @InitParam? Allow annotation of method parameters instead? Allows setLocation(@InitParam int x, @InitParam int y)
// TODO: At least allow field injection
// TODO: defaultValue, required
private ServletConfigurator() {
}
public static void configure(final Servlet pServlet, final ServletConfig pConfig) throws ServletConfigException {
new Configurator(pServlet, pConfig.getServletName()).configure(ServletUtil.asMap(pConfig));
}
public static void configure(final Filter pFilter, final FilterConfig pConfig) throws ServletConfigException {
new Configurator(pFilter, pConfig.getFilterName()).configure(ServletUtil.asMap(pConfig));
}
private static class Configurator {
private final Object servletOrFilter;
private final String name;
private Configurator(final Object servletOrFilter, final String name) {
this.servletOrFilter = servletOrFilter;
this.name = name;
}
private void configure(final Map<String, String> pMapping) throws ServletConfigException {
// Loop over methods with InitParam annotations
for (Method method : annotatedMethods(servletOrFilter.getClass(), InitParam.class)) {
assertAcceptableMethod(method);
// Get value or default, throw exception if missing required value
Object value = getConfiguredValue(method, pMapping);
if (value != null) {
// Inject value to this method
try {
method.invoke(servletOrFilter, value);
}
catch (IllegalAccessException e) {
// We know the method is accessible, so this should never happen
throw new Error(e);
}
catch (InvocationTargetException e) {
throw new ServletConfigException(String.format("Could not configure %s: %s", name, e.getCause().getMessage()), e.getCause());
}
}
}
// TODO: Loop over fields with InitParam annotations
// TODO: Log warning for mappings not present among InitParam annotated methods?
}
private Object getConfiguredValue(final Method method, final Map<String, String> mapping) throws ServletConfigException {
InitParam initParam = method.getAnnotation(InitParam.class);
String paramName = getParameterName(method, initParam);
// Get parameter value
String stringValue = mapping.get(paramName);
if (stringValue == null && initParam.name().equals(InitParam.UNDEFINED)) {
stringValue = mapping.get(StringUtil.camelToLisp(paramName));
}
if (stringValue == null) {
// InitParam support required = true and throw exception if not present in map
if (initParam.required()) {
throw new ServletConfigException(
String.format(
"Could not configure %s: Required init-parameter \"%s\" of type %s is missing",
name, paramName, method.getParameterTypes()[0]
)
);
}
else if (!initParam.defaultValue().equals(InitParam.UNDEFINED)) {
// Support default values
stringValue = initParam.defaultValue();
}
}
// Convert value based on method arguments...
return stringValue == null ? null : convertValue(method, stringValue);
}
private Object convertValue(final Method method, final String stringValue) throws ServletConfigException {
// We know it's a single parameter method
Class<?> type = method.getParameterTypes()[0];
try {
return String.class.equals(type) ? stringValue : Converter.getInstance().toObject(stringValue, type);
}
catch (ConversionException e) {
throw new ServletConfigException(e);
}
}
private String getParameterName(final Method method, final InitParam initParam) throws ServletConfigException {
String paramName = initParam.name();
if (paramName.equals(InitParam.UNDEFINED)) {
String methodName = method.getName();
if (methodName.startsWith("set") && methodName.length() > 3) {
paramName = Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4);
}
else {
throw new ServletConfigException(
String.format(
"Could not configure %s: InitParam annotated method must either specify name or follow Bean standard for properties (ie. setFoo => 'foo'): %s",
name, method
)
);
}
}
return paramName;
}
private void assertAcceptableMethod(final Method method) throws ServletConfigException {
// Try to use setAccessible, if not public
boolean isAccessible = Modifier.isPublic(method.getModifiers());
if (!isAccessible) {
try {
method.setAccessible(true);
isAccessible = true;
}
catch (SecurityException ignore) {
// Won't be accessible, we'll fail below
}
}
if (!isAccessible || method.getReturnType() != Void.TYPE || method.getParameterTypes().length != 1) {
throw new ServletConfigException(
String.format(
"Could not configure %s: InitParam annotated method must be public void and have a single parameter argument list: %s",
name, method
)
);
}
}
/**
* Gets all methods annotated with the given annotations.
*
* @param pClass the class to get annotated methods from
* @param pAnnotations the annotations to test for
* @return an iterable that allows iterating over all methods with the given annotations.
*/
private Iterable<Method> annotatedMethods(final Class<?> pClass, final Class<? extends Annotation>... pAnnotations) {
return new Iterable<Method>() {
public Iterator<Method> iterator() {
Set<Method> methods = new LinkedHashSet<Method>();
Class<?> cl = pClass;
while (cl.getSuperclass() != null) { // There's no annotations of interest on java.lang.Object
methods.addAll(Arrays.asList(cl.getDeclaredMethods()));
// TODO: What about interface methods? Do we really want them?
Class<?>[] interfaces = cl.getInterfaces();
for (Class<?> i : interfaces) {
methods.addAll(Arrays.asList(i.getDeclaredMethods()));
}
cl = cl.getSuperclass();
}
return new FilterIterator<Method>(methods.iterator(), new FilterIterator.Filter<Method>() {
public boolean accept(final Method pMethod) {
for (Class<? extends Annotation> annotation : pAnnotations) {
if (!pMethod.isAnnotationPresent(annotation) || isOverriddenWithAnnotation(pMethod, annotation)) {
return false;
}
}
return true;
}
/**
* @param pMethod the method to test for override
* @param pAnnotation the annotation that must be present
* @return {@code true} iff the method is overridden in a subclass, and has annotation
* @see <a href="http://java.sun.com/docs/books/jls/third_edition/html/classes.html#8.4.8">The Java Language Specification: Classes: Inheritance, Overriding, and Hiding</a>
*/
private boolean isOverriddenWithAnnotation(final Method pMethod, final Class<? extends Annotation> pAnnotation) {
if (Modifier.isPrivate(pMethod.getModifiers())) {
return false;
}
Class cl = pClass;
// Loop down up from subclass to superclass declaring the method
while (cl != null && !pMethod.getDeclaringClass().equals(cl)) {
try {
Method override = cl.getDeclaredMethod(pMethod.getName(), pMethod.getParameterTypes());
// Overridden, test if it has the annotation present
if (override.isAnnotationPresent(pAnnotation)) {
return true;
}
}
catch (NoSuchMethodException ignore) {
}
cl = cl.getSuperclass();
}
return false;
}
});
}
};
}
}
}
@@ -1,72 +0,0 @@
/*
* Copyright (c) 2009, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet;
import com.twelvemonkeys.util.CollectionUtil;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import static com.twelvemonkeys.lang.Validate.notNull;
/**
* ServletHeadersMapAdapter
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: ServletHeadersMapAdapter.java#1 $
*/
class ServletHeadersMapAdapter extends AbstractServletMapAdapter<List<String>> {
protected final HttpServletRequest request;
public ServletHeadersMapAdapter(final HttpServletRequest pRequest) {
request = notNull(pRequest, "request");
}
protected List<String> valueImpl(final String pName) {
@SuppressWarnings("unchecked")
Enumeration<String> headers = request.getHeaders(pName);
return headers == null ? null : toList(CollectionUtil.iterator(headers));
}
private static List<String> toList(final Iterator<String> pValues) {
List<String> list = new ArrayList<String>();
CollectionUtil.addAll(list, pValues);
return Collections.unmodifiableList(list);
}
protected Iterator<String> keysImpl() {
@SuppressWarnings("unchecked")
Enumeration<String> headerNames = request.getHeaderNames();
return headerNames == null ? null : CollectionUtil.iterator(headerNames);
}
}
@@ -1,69 +0,0 @@
/*
* Copyright (c) 2009, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet;
import com.twelvemonkeys.util.CollectionUtil;
import javax.servlet.ServletRequest;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import static com.twelvemonkeys.lang.Validate.notNull;
/**
* ServletParametersMapAdapter
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: ServletParametersMapAdapter.java#1 $
*/
class ServletParametersMapAdapter extends AbstractServletMapAdapter<List<String>> {
// TODO: Be able to piggyback on HttpServletRequest.getParameterMap when available?
protected final ServletRequest request;
public ServletParametersMapAdapter(final ServletRequest pRequest) {
request = notNull(pRequest, "request");
}
protected List<String> valueImpl(String pName) {
String[] values = request.getParameterValues(pName);
return values == null ? null : Arrays.asList(values);
}
protected Iterator<String> keysImpl() {
@SuppressWarnings("unchecked")
Enumeration<String> names = request.getParameterNames();
return names == null ? null : CollectionUtil.iterator(names);
}
}
@@ -1,120 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet;
import static com.twelvemonkeys.lang.Validate.notNull;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletResponse;
/**
* 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.
* </p>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: ServletResponseStreamDelegate.java#2 $
*/
@Deprecated
public class ServletResponseStreamDelegate {
private Object out = null;
protected final ServletResponse response;
public ServletResponseStreamDelegate(final ServletResponse pResponse) {
response = notNull(pResponse, "response");
}
// 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() already called.");
}
return (ServletOutputStream) out;
}
// 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
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() already called.");
}
return (PrintWriter) out;
}
/**
* Returns the {@code OutputStream}.
* 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.
* </p>
*
* @return the {@code OutputStream} to use for the response
* @throws IOException if an I/O exception occurs
*/
protected OutputStream createOutputStream() throws IOException {
return response.getOutputStream();
}
public void flushBuffer() throws IOException {
if (out instanceof ServletOutputStream) {
((ServletOutputStream) out).flush();
}
else if (out != null) {
((PrintWriter) out).flush();
}
}
public void resetBuffer() {
out = null;
}
}
@@ -1,785 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet;
import java.io.File;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import java.util.Map;
import javax.servlet.FilterConfig;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletRequest;
import javax.servlet.ServletRequestWrapper;
import javax.servlet.ServletResponse;
import javax.servlet.ServletResponseWrapper;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import com.twelvemonkeys.lang.StringUtil;
import com.twelvemonkeys.util.convert.ConversionException;
import com.twelvemonkeys.util.convert.Converter;
/**
* Various servlet related helper methods.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author Eirik Torske
* @author last modified by $Author: haku $
* @version $Id: ServletUtil.java#3 $
*/
@Deprecated
public final class ServletUtil {
/**
* {@code "javax.servlet.include.request_uri"}
*/
private final static String ATTRIB_INC_REQUEST_URI = "javax.servlet.include.request_uri";
/**
* {@code "javax.servlet.include.context_path"}
*/
private final static String ATTRIB_INC_CONTEXT_PATH = "javax.servlet.include.context_path";
/**
* {@code "javax.servlet.include.servlet_path"}
*/
private final static String ATTRIB_INC_SERVLET_PATH = "javax.servlet.include.servlet_path";
/**
* {@code "javax.servlet.include.path_info"}
*/
private final static String ATTRIB_INC_PATH_INFO = "javax.servlet.include.path_info";
/**
* {@code "javax.servlet.include.query_string"}
*/
private final static String ATTRIB_INC_QUERY_STRING = "javax.servlet.include.query_string";
/**
* {@code "javax.servlet.forward.request_uri"}
*/
private final static String ATTRIB_FWD_REQUEST_URI = "javax.servlet.forward.request_uri";
/**
* {@code "javax.servlet.forward.context_path"}
*/
private final static String ATTRIB_FWD_CONTEXT_PATH = "javax.servlet.forward.context_path";
/**
* {@code "javax.servlet.forward.servlet_path"}
*/
private final static String ATTRIB_FWD_SERVLET_PATH = "javax.servlet.forward.servlet_path";
/**
* {@code "javax.servlet.forward.path_info"}
*/
private final static String ATTRIB_FWD_PATH_INFO = "javax.servlet.forward.path_info";
/**
* {@code "javax.servlet.forward.query_string"}
*/
private final static String ATTRIB_FWD_QUERY_STRING = "javax.servlet.forward.query_string";
/**
* Don't create, static methods only
*/
private ServletUtil() {
}
/**
* Gets the value of the given parameter from the request, or if the
* parameter is not set, the default value.
*
* @param pReq the servlet request
* @param pName the parameter name
* @param pDefault the default value
* @return the value of the parameter, or the default value, if the
* parameter is not set.
*/
public static String getParameter(final ServletRequest pReq, final String pName, final String pDefault) {
String str = pReq.getParameter(pName);
return str != null ? str : pDefault;
}
/**
* Gets the value of the given parameter from the request converted to
* an Object. If the parameter is not set or not parseable, the default
* value is returned.
*
* @param pReq the servlet request
* @param pName the parameter name
* @param pType the type of object (class) to return
* @param pFormat the format to use (might be {@code null} in many cases)
* @param pDefault the default value
* @return the value of the parameter converted to a boolean, or the
* default value, if the parameter is not set.
* @throws IllegalArgumentException if {@code pDefault} is
* non-{@code null} and not an instance of {@code pType}
* @throws NullPointerException if {@code pReq}, {@code pName} or
* {@code pType} is {@code null}.
* @see Converter#toObject
*/
// TODO: Well, it's done. Need some thinking... We probably don't want default if conversion fails...
static <T> T getParameter(final ServletRequest pReq, final String pName, final Class<T> pType, final String pFormat, final T pDefault) {
// Test if pDefault is either null or instance of pType
if (pDefault != null && !pType.isInstance(pDefault)) {
throw new IllegalArgumentException("default value not instance of " + pType + ": " + pDefault.getClass());
}
String str = pReq.getParameter(pName);
if (str == null) {
return pDefault;
}
try {
return pType.cast(Converter.getInstance().toObject(str, pType, pFormat));
}
catch (ConversionException ce) {
return pDefault;
}
}
/**
* Gets the value of the given parameter from the request converted to
* a {@code boolean}.&nbsp;If the parameter is not set or not parseable, the default
* value is returned.
*
* @param pReq the servlet request
* @param pName the parameter name
* @param pDefault the default value
* @return the value of the parameter converted to a {@code boolean}, or the
* default value, if the parameter is not set.
*/
public static boolean getBooleanParameter(final ServletRequest pReq, final String pName, final boolean pDefault) {
String str = pReq.getParameter(pName);
try {
return str != null ? Boolean.valueOf(str) : pDefault;
}
catch (NumberFormatException nfe) {
return pDefault;
}
}
/**
* Gets the value of the given parameter from the request converted to
* an {@code int}.&nbsp;If the parameter is not set or not parseable, the default
* value is returned.
*
* @param pReq the servlet request
* @param pName the parameter name
* @param pDefault the default value
* @return the value of the parameter converted to an {@code int}, or the default
* value, if the parameter is not set.
*/
public static int getIntParameter(final ServletRequest pReq, final String pName, final int pDefault) {
String str = pReq.getParameter(pName);
try {
return str != null ? Integer.parseInt(str) : pDefault;
}
catch (NumberFormatException nfe) {
return pDefault;
}
}
/**
* Gets the value of the given parameter from the request converted to
* an {@code long}.&nbsp;If the parameter is not set or not parseable, the default
* value is returned.
*
* @param pReq the servlet request
* @param pName the parameter name
* @param pDefault the default value
* @return the value of the parameter converted to an {@code long}, or the default
* value, if the parameter is not set.
*/
public static long getLongParameter(final ServletRequest pReq, final String pName, final long pDefault) {
String str = pReq.getParameter(pName);
try {
return str != null ? Long.parseLong(str) : pDefault;
}
catch (NumberFormatException nfe) {
return pDefault;
}
}
/**
* Gets the value of the given parameter from the request converted to
* a {@code float}.&nbsp;If the parameter is not set or not parseable, the default
* value is returned.
*
* @param pReq the servlet request
* @param pName the parameter name
* @param pDefault the default value
* @return the value of the parameter converted to a {@code float}, or the default
* value, if the parameter is not set.
*/
public static float getFloatParameter(final ServletRequest pReq, final String pName, final float pDefault) {
String str = pReq.getParameter(pName);
try {
return str != null ? Float.parseFloat(str) : pDefault;
}
catch (NumberFormatException nfe) {
return pDefault;
}
}
/**
* Gets the value of the given parameter from the request converted to
* a {@code double}.&nbsp;If the parameter is not set or not parseable, the default
* value is returned.
*
* @param pReq the servlet request
* @param pName the parameter name
* @param pDefault the default value
* @return the value of the parameter converted to n {@code double}, or the default
* value, if the parameter is not set.
*/
public static double getDoubleParameter(final ServletRequest pReq, final String pName, final double pDefault) {
String str = pReq.getParameter(pName);
try {
return str != null ? Double.parseDouble(str) : pDefault;
}
catch (NumberFormatException nfe) {
return pDefault;
}
}
/**
* Gets the value of the given parameter from the request converted to
* a {@code Date}.&nbsp;If the parameter is not set or not parseable, the
* default value is returned.
*
* @param pReq the servlet request
* @param pName the parameter name
* @param pDefault the default value
* @return the value of the parameter converted to a {@code Date}, or the
* default value, if the parameter is not set.
* @see com.twelvemonkeys.lang.StringUtil#toDate(String)
*/
public static long getDateParameter(final ServletRequest pReq, final String pName, final long pDefault) {
String str = pReq.getParameter(pName);
try {
return str != null ? StringUtil.toDate(str).getTime() : pDefault;
}
catch (IllegalArgumentException iae) {
return pDefault;
}
}
/**
* Gets the value of the given parameter from the request converted to
* a Date.&nbsp;If the parameter is not set or not parseable, the
* default value is returned.
*
* @param pReq the servlet request
* @param pName the parameter name
* @param pFormat the date format to use
* @param pDefault the default value
* @return the value of the parameter converted to a Date, or the
* default value, if the parameter is not set.
* @see com.twelvemonkeys.lang.StringUtil#toDate(String,String)
*/
/*
public static long getDateParameter(ServletRequest pReq, String pName, String pFormat, long pDefault) {
String str = pReq.getParameter(pName);
try {
return ((str != null) ? StringUtil.toDate(str, pFormat).getTime() : pDefault);
}
catch (IllegalArgumentException iae) {
return pDefault;
}
}
*/
/**
* Builds a full-blown HTTP/HTTPS URL from a
* {@code javax.servlet.http.HttpServletRequest} object.
*
* @param pRequest The HTTP servlet request object.
* @return the reproduced URL
* @deprecated Use {@link javax.servlet.http.HttpServletRequest#getRequestURL()}
* instead.
*/
@Deprecated
static StringBuffer buildHTTPURL(final HttpServletRequest pRequest) {
StringBuffer resultURL = new StringBuffer();
// Scheme, as in http, https, ftp etc
String scheme = pRequest.getScheme();
resultURL.append(scheme);
resultURL.append("://");
resultURL.append(pRequest.getServerName());
// Append port only if not default port
int port = pRequest.getServerPort();
if (port > 0 &&
!(("http".equals(scheme) && port == 80) ||
("https".equals(scheme) && port == 443))) {
resultURL.append(":");
resultURL.append(port);
}
// Append URI
resultURL.append(pRequest.getRequestURI());
// If present, append extra path info
String pathInfo = pRequest.getPathInfo();
if (pathInfo != null) {
resultURL.append(pathInfo);
}
return resultURL;
}
/**
* Gets the URI of the resource currently included.
* The value is read from the request attribute
* {@code "javax.servlet.include.request_uri"}
*
* @param pRequest the servlet request
* @return the URI of the included resource, or {@code null} if no include
* @see HttpServletRequest#getRequestURI
* @since Servlet 2.2
*/
public static String getIncludeRequestURI(final ServletRequest pRequest) {
return (String) pRequest.getAttribute(ATTRIB_INC_REQUEST_URI);
}
/**
* Gets the context path of the resource currently included.
* The value is read from the request attribute
* {@code "javax.servlet.include.context_path"}
*
* @param pRequest the servlet request
* @return the context path of the included resource, or {@code null} if no include
* @see HttpServletRequest#getContextPath
* @since Servlet 2.2
*/
public static String getIncludeContextPath(final ServletRequest pRequest) {
return (String) pRequest.getAttribute(ATTRIB_INC_CONTEXT_PATH);
}
/**
* Gets the servlet path of the resource currently included.
* The value is read from the request attribute
* {@code "javax.servlet.include.servlet_path"}
*
* @param pRequest the servlet request
* @return the servlet path of the included resource, or {@code null} if no include
* @see HttpServletRequest#getServletPath
* @since Servlet 2.2
*/
public static String getIncludeServletPath(final ServletRequest pRequest) {
return (String) pRequest.getAttribute(ATTRIB_INC_SERVLET_PATH);
}
/**
* Gets the path info of the resource currently included.
* The value is read from the request attribute
* {@code "javax.servlet.include.path_info"}
*
* @param pRequest the servlet request
* @return the path info of the included resource, or {@code null} if no include
* @see HttpServletRequest#getPathInfo
* @since Servlet 2.2
*/
public static String getIncludePathInfo(final ServletRequest pRequest) {
return (String) pRequest.getAttribute(ATTRIB_INC_PATH_INFO);
}
/**
* Gets the query string of the resource currently included.
* The value is read from the request attribute
* {@code "javax.servlet.include.query_string"}
*
* @param pRequest the servlet request
* @return the query string of the included resource, or {@code null} if no include
* @see HttpServletRequest#getQueryString
* @since Servlet 2.2
*/
public static String getIncludeQueryString(final ServletRequest pRequest) {
return (String) pRequest.getAttribute(ATTRIB_INC_QUERY_STRING);
}
/**
* Gets the URI of the resource this request was forwarded from.
* The value is read from the request attribute
* {@code "javax.servlet.forward.request_uri"}
*
* @param pRequest the servlet request
* @return the URI of the resource, or {@code null} if not forwarded
* @see HttpServletRequest#getRequestURI
* @since Servlet 2.4
*/
public static String getForwardRequestURI(final ServletRequest pRequest) {
return (String) pRequest.getAttribute(ATTRIB_FWD_REQUEST_URI);
}
/**
* Gets the context path of the resource this request was forwarded from.
* The value is read from the request attribute
* {@code "javax.servlet.forward.context_path"}
*
* @param pRequest the servlet request
* @return the context path of the resource, or {@code null} if not forwarded
* @see HttpServletRequest#getContextPath
* @since Servlet 2.4
*/
public static String getForwardContextPath(final ServletRequest pRequest) {
return (String) pRequest.getAttribute(ATTRIB_FWD_CONTEXT_PATH);
}
/**
* Gets the servlet path of the resource this request was forwarded from.
* The value is read from the request attribute
* {@code "javax.servlet.forward.servlet_path"}
*
* @param pRequest the servlet request
* @return the servlet path of the resource, or {@code null} if not forwarded
* @see HttpServletRequest#getServletPath
* @since Servlet 2.4
*/
public static String getForwardServletPath(final ServletRequest pRequest) {
return (String) pRequest.getAttribute(ATTRIB_FWD_SERVLET_PATH);
}
/**
* Gets the path info of the resource this request was forwarded from.
* The value is read from the request attribute
* {@code "javax.servlet.forward.path_info"}
*
* @param pRequest the servlet request
* @return the path info of the resource, or {@code null} if not forwarded
* @see HttpServletRequest#getPathInfo
* @since Servlet 2.4
*/
public static String getForwardPathInfo(final ServletRequest pRequest) {
return (String) pRequest.getAttribute(ATTRIB_FWD_PATH_INFO);
}
/**
* Gets the query string of the resource this request was forwarded from.
* The value is read from the request attribute
* {@code "javax.servlet.forward.query_string"}
*
* @param pRequest the servlet request
* @return the query string of the resource, or {@code null} if not forwarded
* @see HttpServletRequest#getQueryString
* @since Servlet 2.4
*/
public static String getForwardQueryString(final ServletRequest pRequest) {
return (String) pRequest.getAttribute(ATTRIB_FWD_QUERY_STRING);
}
/**
* Gets the name of the servlet or the script that generated the servlet.
*
* @param pRequest The HTTP servlet request object.
* @return the script name.
* @see javax.servlet.http.HttpServletRequest#getServletPath()
*/
// TODO: Read the spec, seems to be a mismatch with the Servlet API...
static String getScriptName(final HttpServletRequest pRequest) {
String requestURI = pRequest.getRequestURI();
return StringUtil.getLastElement(requestURI, "/");
}
/**
* Gets the request URI relative to the current context path.
* <p>
* As an example:
* </p>
* <pre>
* requestURI = "/webapp/index.jsp"
* contextPath = "/webapp"
* </pre>
* The method will return {@code "/index.jsp"}.
*
* @param pRequest the current HTTP request
* @return the request URI relative to the current context path.
*/
public static String getContextRelativeURI(final HttpServletRequest pRequest) {
String context = pRequest.getContextPath();
if (!StringUtil.isEmpty(context)) { // "" for root context
return pRequest.getRequestURI().substring(context.length());
}
return pRequest.getRequestURI();
}
/**
* Returns a {@code URL} containing the real path for a given virtual
* path, on URL form.
* Note that this method will return {@code null} for all the same reasons
* as {@code ServletContext.getRealPath(java.lang.String)} does.
*
* @param pContext the servlet context
* @param pPath the virtual path
* @return a {@code URL} object containing the path, or {@code null}.
* @throws MalformedURLException if the path refers to a malformed URL
* @see ServletContext#getRealPath(java.lang.String)
* @see ServletContext#getResource(java.lang.String)
*/
public static URL getRealURL(final ServletContext pContext, final String pPath) throws MalformedURLException {
String realPath = pContext.getRealPath(pPath);
if (realPath != null) {
// NOTE: First convert to URI, as of Java 6 File.toURL is deprecated
return new File(realPath).toURI().toURL();
}
return null;
}
/**
* Gets the temp directory for the given {@code ServletContext} (web app).
*
* @param pContext the servlet context
* @return the temp directory
*/
public static File getTempDir(final ServletContext pContext) {
return (File) pContext.getAttribute("javax.servlet.context.tempdir");
}
/**
* Gets the unique identifier assigned to this session.
* The identifier is assigned by the servlet container and is implementation
* dependent.
*
* @param pRequest The HTTP servlet request object.
* @return the session Id
*/
public static String getSessionId(final HttpServletRequest pRequest) {
HttpSession session = pRequest.getSession();
return (session != null) ? session.getId() : null;
}
/**
* Creates an unmodifiable {@code Map} view of the given
* {@code ServletConfig}s init-parameters.
* <small>Note: The returned {@code Map} is optimized for {@code get}
* operations and iterating over it's {@code keySet}.
* For other operations it may not perform well.</small>
*
* @param pConfig the servlet configuration
* @return a {@code Map} view of the config
* @throws IllegalArgumentException if {@code pConfig} is {@code null}
*/
public static Map<String, String> asMap(final ServletConfig pConfig) {
return new ServletConfigMapAdapter(pConfig);
}
/**
* Creates an unmodifiable {@code Map} view of the given
* {@code FilterConfig}s init-parameters.
* <small>Note: The returned {@code Map} is optimized for {@code get}
* operations and iterating over it's {@code keySet}.
* For other operations it may not perform well.</small>
*
* @param pConfig the servlet filter configuration
* @return a {@code Map} view of the config
* @throws IllegalArgumentException if {@code pConfig} is {@code null}
*/
public static Map<String, String> asMap(final FilterConfig pConfig) {
return new ServletConfigMapAdapter(pConfig);
}
/**
* Creates an unmodifiable {@code Map} view of the given
* {@code ServletContext}s init-parameters.
* <small>Note: The returned {@code Map} is optimized for {@code get}
* operations and iterating over it's {@code keySet}.
* For other operations it may not perform well.</small>
*
* @param pContext the servlet context
* @return a {@code Map} view of the init parameters
* @throws IllegalArgumentException if {@code pContext} is {@code null}
*/
public static Map<String, String> initParamsAsMap(final ServletContext pContext) {
return new ServletConfigMapAdapter(pContext);
}
/**
* Creates an <em>modifiable</em> {@code Map} view of the given
* {@code ServletContext}s attributes.
*
* @param pContext the servlet context
* @return a {@code Map} view of the attributes
* @throws IllegalArgumentException if {@code pContext} is {@code null}
*/
public static Map<String, Object> attributesAsMap(final ServletContext pContext) {
return new ServletAttributesMapAdapter(pContext);
}
/**
* Creates an <em>modifiable</em> {@code Map} view of the given
* {@code ServletRequest}s attributes.
*
* @param pRequest the servlet request
* @return a {@code Map} view of the attributes
* @throws IllegalArgumentException if {@code pContext} is {@code null}
*/
public static Map<String, Object> attributesAsMap(final ServletRequest pRequest) {
return new ServletAttributesMapAdapter(pRequest);
}
/**
* Creates an unmodifiable {@code Map} view of the given
* {@code HttpServletRequest}s request parameters.
*
* @param pRequest the request
* @return a {@code Map} view of the request parameters
* @throws IllegalArgumentException if {@code pRequest} is {@code null}
*/
public static Map<String, List<String>> parametersAsMap(final ServletRequest pRequest) {
return new ServletParametersMapAdapter(pRequest);
}
/**
* Creates an unmodifiable {@code Map} view of the given
* {@code HttpServletRequest}s request headers.
*
* @param pRequest the request
* @return a {@code Map} view of the request headers
* @throws IllegalArgumentException if {@code pRequest} is {@code null}
*/
public static Map<String, List<String>> headersAsMap(final HttpServletRequest pRequest) {
return new ServletHeadersMapAdapter(pRequest);
}
/**
* Creates a wrapper that implements either {@code ServletResponse} or
* {@code HttpServletResponse}, depending on the type of
* {@code pImplementation.getResponse()}.
*
* @param pImplementation the servlet response to create a wrapper for
* @return a {@code ServletResponse} or
* {@code HttpServletResponse}, depending on the type of
* {@code pImplementation.getResponse()}
*/
public static ServletResponse createWrapper(final ServletResponseWrapper pImplementation) {
// TODO: Get all interfaces from implementation
if (pImplementation.getResponse() instanceof HttpServletResponse) {
return (HttpServletResponse) Proxy.newProxyInstance(pImplementation.getClass().getClassLoader(),
new Class[]{HttpServletResponse.class, ServletResponse.class},
new HttpServletResponseHandler(pImplementation));
}
return pImplementation;
}
/**
* Creates a wrapper that implements either {@code ServletRequest} or
* {@code HttpServletRequest}, depending on the type of
* {@code pImplementation.getRequest()}.
*
* @param pImplementation the servlet request to create a wrapper for
* @return a {@code ServletResponse} or
* {@code HttpServletResponse}, depending on the type of
* {@code pImplementation.getResponse()}
*/
public static ServletRequest createWrapper(final ServletRequestWrapper pImplementation) {
// TODO: Get all interfaces from implementation
if (pImplementation.getRequest() instanceof HttpServletRequest) {
return (HttpServletRequest) Proxy.newProxyInstance(pImplementation.getClass().getClassLoader(),
new Class[]{HttpServletRequest.class, ServletRequest.class},
new HttpServletRequestHandler(pImplementation));
}
return pImplementation;
}
private static class HttpServletResponseHandler implements InvocationHandler {
private final ServletResponseWrapper response;
HttpServletResponseHandler(final ServletResponseWrapper pResponse) {
response = pResponse;
}
public Object invoke(final Object pProxy, final Method pMethod, final Object[] pArgs) throws Throwable {
try {
// TODO: Allow partial implementing?
if (pMethod.getDeclaringClass().isInstance(response)) {
return pMethod.invoke(response, pArgs);
}
// Method is not implemented in wrapper
return pMethod.invoke(response.getResponse(), pArgs);
}
catch (InvocationTargetException e) {
// Unwrap, to avoid UndeclaredThrowableException...
throw e.getTargetException();
}
}
}
private static class HttpServletRequestHandler implements InvocationHandler {
private final ServletRequestWrapper request;
HttpServletRequestHandler(final ServletRequestWrapper pRequest) {
request = pRequest;
}
public Object invoke(final Object pProxy, final Method pMethod, final Object[] pArgs) throws Throwable {
try {
// TODO: Allow partial implementing?
if (pMethod.getDeclaringClass().isInstance(request)) {
return pMethod.invoke(request, pArgs);
}
// Method is not implemented in wrapper
return pMethod.invoke(request.getRequest(), pArgs);
}
catch (InvocationTargetException e) {
// Unwrap, to avoid UndeclaredThrowableException...
throw e.getTargetException();
}
}
}
}
@@ -1,311 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.twelvemonkeys.io.FileUtil;
import com.twelvemonkeys.lang.StringUtil;
/**
* ThrottleFilter, a filter for easing server during heavy load.
* <p>
* Intercepts requests, and returns HTTP response code {@code 503 (Service Unavailable)},
* if there are more than a given number of concurrent
* requests, to avoid large backlogs. The number of concurrent requests and the
* response messages sent to the user agent, is configurable from the web
* descriptor.
* </p>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: ThrottleFilter.java#1 $
* @see #setMaxConcurrentThreadCount
* @see #setResponseMessages
*/
@Deprecated
public class ThrottleFilter extends GenericFilter {
/**
* Minimum free thread count, defaults to {@code 10}
*/
protected int maxConcurrentThreadCount = 10;
/**
* The number of running request threads
*/
private int runningThreads = 0;
private final Object runningThreadsLock = new Object();
/**
* Default response message sent to user agents, if the request is rejected
*/
protected final static String DEFUALT_RESPONSE_MESSAGE =
"Service temporarily unavailable, please try again later.";
/**
* Default response content type
*/
protected static final String DEFAULT_TYPE = "text/html";
/**
* The reposne message sent to user agenta, if the request is rejected
*/
private Map<String, String> responseMessageNames = new HashMap<String, String>(10);
/**
* The reposne message sent to user agents, if the request is rejected
*/
private String[] responseMessageTypes = null;
/**
* Cache for response messages
*/
private Map<String, CacheEntry> responseCache = new HashMap<String, CacheEntry>(10);
/**
* Sets the minimum free thread count.
*
* @param pMaxConcurrentThreadCount
*/
public void setMaxConcurrentThreadCount(String pMaxConcurrentThreadCount) {
if (!StringUtil.isEmpty(pMaxConcurrentThreadCount)) {
try {
maxConcurrentThreadCount = Integer.parseInt(pMaxConcurrentThreadCount);
}
catch (NumberFormatException nfe) {
// Use default
}
}
}
/**
* Sets the response message sent to the user agent, if the request is
* rejected.
* <br>
* The format is {@code &lt;mime-type&gt;=&lt;filename&gt;,
* &lt;mime-type&gt;=&lt;filename&gt;}.
* <br>
* Example: {@code &lt;text/vnd.wap.wmlgt;=&lt;/errors/503.wml&gt;,
* &lt;text/html&gt;=&lt;/errors/503.html&gt;}
*
* @param pResponseMessages
*/
public void setResponseMessages(String pResponseMessages) {
// Split string in type=filename pairs
String[] mappings = StringUtil.toStringArray(pResponseMessages, ", \r\n\t");
List<String> types = new ArrayList<String>();
for (String pair : mappings) {
// Split pairs on '='
String[] mapping = StringUtil.toStringArray(pair, "= ");
// Test for wrong mapping
if ((mapping == null) || (mapping.length < 2)) {
log("Error in init param \"responseMessages\": " + pResponseMessages);
continue;
}
types.add(mapping[0]);
responseMessageNames.put(mapping[0], mapping[1]);
}
// Create arrays
responseMessageTypes = types.toArray(new String[types.size()]);
}
/**
* @param pRequest
* @param pResponse
* @param pChain
* @throws IOException
* @throws ServletException
*/
protected void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain) throws IOException, ServletException {
try {
if (beginRequest()) {
// Continue request
pChain.doFilter(pRequest, pResponse);
}
else {
// Send error and end request
// Get HTTP specific versions
HttpServletRequest request = (HttpServletRequest) pRequest;
HttpServletResponse response = (HttpServletResponse) pResponse;
// Get content type
String contentType = getContentType(request);
// Note: This is not the way the spec says you should do it.
// However, we handle error response this way for preformace reasons.
// The "correct" way would be to use sendError() and register a servlet
// that does the content negotiation as errorpage in the web descriptor.
response.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
response.setContentType(contentType);
response.getWriter().println(getMessage(contentType));
// Log warning, as this shouldn't happen too often
log("Request denied, no more available threads for requestURI=" + request.getRequestURI());
}
}
finally {
doneRequest();
}
}
/**
* Marks the beginning of a request
*
* @return {@code true} if the request should be handled.
*/
private boolean beginRequest() {
synchronized (runningThreadsLock) {
runningThreads++;
}
return (runningThreads <= maxConcurrentThreadCount);
}
/**
* Marks the end of the request
*/
private void doneRequest() {
synchronized (runningThreadsLock) {
runningThreads--;
}
}
/**
* Gets the content type for the response, suitable for the requesting user agent.
*
* @param pRequest
* @return the content type
*/
private String getContentType(HttpServletRequest pRequest) {
if (responseMessageTypes != null) {
String accept = pRequest.getHeader("Accept");
for (String type : responseMessageTypes) {
// Note: This is not 100% correct way of doing content negotiation
// But we just want a compatible result, quick, so this is okay
if (StringUtil.contains(accept, type)) {
return type;
}
}
}
// If none found, return default
return DEFAULT_TYPE;
}
/**
* Gets the response message for the given content type.
*
* @param pContentType
* @return the message
*/
private String getMessage(String pContentType) {
String fileName = responseMessageNames.get(pContentType);
// Get cached value
CacheEntry entry = responseCache.get(fileName);
if ((entry == null) || entry.isExpired()) {
// Create and add or replace cached value
entry = new CacheEntry(readMessage(fileName));
responseCache.put(fileName, entry);
}
// Return value
return (entry.getValue() != null)
? (String) entry.getValue()
: DEFUALT_RESPONSE_MESSAGE;
}
/**
* Reads the response message from a file in the current web app.
*
* @param pFileName
* @return the message
*/
private String readMessage(String pFileName) {
try {
// Read resource from web app
InputStream is = getServletContext().getResourceAsStream(pFileName);
if (is != null) {
return new String(FileUtil.read(is));
}
else {
log("File not found: " + pFileName);
}
}
catch (IOException ioe) {
log("Error reading file: " + pFileName + " (" + ioe.getMessage() + ")");
}
return null;
}
/**
* Keeps track of Cached objects
*/
private static class CacheEntry {
private Object value;
private long timestamp = -1;
CacheEntry(Object pValue) {
value = pValue;
timestamp = System.currentTimeMillis();
}
Object getValue() {
return value;
}
boolean isExpired() {
return (System.currentTimeMillis() - timestamp) > 60000; // Cache 1 minute
}
}
}
@@ -1,116 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
/**
* TimingFilter class description.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: TimingFilter.java#1 $
*/
@Deprecated
public class TimingFilter extends GenericFilter {
private String attribUsage = null;
/**
* Method init
*
* @throws ServletException
*/
public void init() throws ServletException {
attribUsage = getFilterName() + ".timerDelta";
}
/**
*
* @param pRequest
* @param pResponse
* @param pChain
* @throws IOException
* @throws ServletException
*/
protected void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain)
throws IOException, ServletException {
// Get total usage of earlier filters on same level
Object usageAttrib = pRequest.getAttribute(attribUsage);
long total = 0;
if (usageAttrib instanceof Long) {
// If set, get value, and remove attribute for nested resources
total = (Long) usageAttrib;
pRequest.removeAttribute(attribUsage);
}
// Start timing
long start = System.currentTimeMillis();
try {
// Continue chain
pChain.doFilter(pRequest, pResponse);
}
finally {
// Stop timing
long end = System.currentTimeMillis();
// Get time usage of included resources, add to total usage
usageAttrib = pRequest.getAttribute(attribUsage);
long usage = 0;
if (usageAttrib instanceof Long) {
usage = (Long) usageAttrib;
}
// Get the name of the included resource
String resourceURI = ServletUtil.getIncludeRequestURI(pRequest);
// If none, this is probably the parent page itself
if (resourceURI == null) {
resourceURI = ((HttpServletRequest) pRequest).getRequestURI();
}
long delta = end - start;
log(String.format("Request processing time for resource \"%s\": %d ms (accumulated: %d ms).", resourceURI, (delta - usage), delta));
// Store total usage
total += delta;
pRequest.setAttribute(attribUsage, total);
}
}
}
@@ -1,246 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.ServletResponseWrapper;
/**
* Removes extra unneccessary white space from a servlet response.
* White space is defined as per {@link Character#isWhitespace(char)}.
* <p>
* This filter has no understanding of the content in the reponse, and will
* remove repeated white space anywhere in the stream. It is intended for
* removing white space from HTML or XML streams, but this limitation makes it
* less suited for filtering HTML/XHTML with embedded CSS or JavaScript,
* in case white space should be significant here. It is strongly reccommended
* you keep CSS and JavaScript in separate files (this will have the added
* benefit of further reducing the ammount of data communicated between
* server and client).
* </p>
* <p>
* <em>At the moment this filter has no concept of encoding</em>.
* This means, that if some multi-byte escape sequence contains one or more
* bytes that <em>individually</em> is treated as a white space, these bytes
* may be skipped.
* As <a href="http://en.wikipedia.org/wiki/UTF-8">UTF-8</a>
* guarantees that no bytes are repeated in this way, this filter can safely
* filter UTF-8.
* Simple 8 bit character encodings, like the
* <a href="http://en.wikipedia.org/wiki/ISO/IEC_8859">ISO/IEC 8859</a> standard, or
* <a href="http://en.wikipedia.org/wiki/Windows-1252">Windows-1252"</a>
* are always safe.
* </p>
* <p>
* <b>Configuration</b>
* <br>
* To use {@code TrimWhiteSpaceFilter} in your web-application, you simply need
* to add it to your web descriptor ({@code web.xml}).
* If using a servlet container that supports the Servlet 2.4 spec, the new
* {@code dispatcher} element should be used, and set to
* {@code REQUEST/FORWARD}, to make sure the filter is invoked only once for
* requests.
* If using an older web descriptor, set the {@code init-param}
* {@code "once-per-request"} to {@code "true"} (this will have the same effect,
* but might perform slightly worse than the 2.4 version).
* Please see the examples below.
* </p>
* <p>
* <b>Servlet 2.4 version, filter section:</b>
* </p>
* <pre>
* &lt;!-- TrimWS Filter Configuration --&gt;
* &lt;filter&gt;
* &lt;filter-name&gt;trimws&lt;/filter-name&gt;
* &lt;filter-class&gt;com.twelvemonkeys.servlet.TrimWhiteSpaceFilter&lt;/filter-class&gt;
* &lt;!-- auto-flush=true is the default, may be omitted --&gt;
* &lt;init-param&gt;
* &lt;param-name&gt;auto-flush&lt;/param-name&gt;
* &lt;param-value&gt;true&lt;/param-value&gt;
* &lt;/init-param&gt;
* &lt;/filter&gt;
* </pre>
* <b>Filter-mapping section:</b><br>
* <pre>
* &lt;!-- TimWS Filter Mapping --&gt;
* &lt;filter-mapping&gt;
* &lt;filter-name&gt;trimws&lt;/filter-name&gt;
* &lt;url-pattern&gt;*.html&lt;/url-pattern&gt;
* &lt;dispatcher&gt;REQUEST&lt;/dispatcher&gt;
* &lt;dispatcher&gt;FORWARD&lt;/dispatcher&gt;
* &lt;/filter-mapping&gt;
* &lt;filter-mapping&gt;
* &lt;filter-name&gt;trimws&lt;/filter-name&gt;
* &lt;url-pattern&gt;*.jsp&lt;/url-pattern&gt;
* &lt;dispatcher&gt;REQUEST&lt;/dispatcher&gt;
* &lt;dispatcher&gt;FORWARD&lt;/dispatcher&gt;
* &lt;/filter-mapping&gt;
* </pre>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: TrimWhiteSpaceFilter.java#2 $
*/
@Deprecated
public class TrimWhiteSpaceFilter extends GenericFilter {
private boolean autoFlush = true;
@InitParam
public void setAutoFlush(final boolean pAutoFlush) {
autoFlush = pAutoFlush;
}
public void init() throws ServletException {
super.init();
log("Automatic flushing is " + (autoFlush ? "enabled" : "disabled"));
}
protected void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain) throws IOException, ServletException {
ServletResponseWrapper wrapped = new TrimWSServletResponseWrapper(pResponse);
pChain.doFilter(pRequest, ServletUtil.createWrapper(wrapped));
if (autoFlush) {
wrapped.flushBuffer();
}
}
static final class TrimWSFilterOutputStream extends FilterOutputStream {
boolean lastWasWS = true; // Avoids leading WS by init to true
public TrimWSFilterOutputStream(OutputStream pOut) {
super(pOut);
}
// Override this, in case the wrapped outputstream overrides...
public final void write(byte pBytes[]) throws IOException {
write(pBytes, 0, pBytes.length);
}
// Override this, in case the wrapped outputstream overrides...
public final void write(byte pBytes[], int pOff, int pLen) throws IOException {
if (pBytes == null) {
throw new NullPointerException("bytes == null");
}
else if (pOff < 0 || pLen < 0 || (pOff + pLen > pBytes.length)) {
throw new IndexOutOfBoundsException("Bytes: " + pBytes.length + " Offset: " + pOff + " Length: " + pLen);
}
for (int i = 0; i < pLen ; i++) {
write(pBytes[pOff + i]);
}
}
public void write(int pByte) throws IOException {
// TODO: Is this good enough for multi-byte encodings like UTF-16?
// Consider writing through a Writer that does that for us, and
// also buffer whitespace, so we write a linefeed every time there's
// one in the original...
// According to http://en.wikipedia.org/wiki/UTF-8:
// "[...] US-ASCII octet values do not appear otherwise in a UTF-8
// encoded character stream. This provides compatibility with file
// systems or other software (e.g., the printf() function in
// C libraries) that parse based on US-ASCII values but are
// transparent to other values."
if (!Character.isWhitespace((char) pByte)) {
// If char is not WS, just store
super.write(pByte);
lastWasWS = false;
}
else {
// TODO: Consider writing only 0x0a (LF) and 0x20 (space)
// Else, if char is WS, store first, skip the rest
if (!lastWasWS) {
if (pByte == 0x0d) { // Convert all CR/LF's to 0x0a
super.write(0x0a);
}
else {
super.write(pByte);
}
}
lastWasWS = true;
}
}
}
private static class TrimWSStreamDelegate extends ServletResponseStreamDelegate {
public TrimWSStreamDelegate(ServletResponse pResponse) {
super(pResponse);
}
protected OutputStream createOutputStream() throws IOException {
return new TrimWSFilterOutputStream(response.getOutputStream());
}
}
static class TrimWSServletResponseWrapper extends ServletResponseWrapper {
private final ServletResponseStreamDelegate streamDelegate = new TrimWSStreamDelegate(getResponse());
public TrimWSServletResponseWrapper(ServletResponse pResponse) {
super(pResponse);
}
public ServletOutputStream getOutputStream() throws IOException {
return streamDelegate.getOutputStream();
}
public PrintWriter getWriter() throws IOException {
return streamDelegate.getWriter();
}
public void setContentLength(int pLength) {
// Will be changed by filter, so don't set.
}
@Override
public void flushBuffer() throws IOException {
streamDelegate.flushBuffer();
}
@Override
public void resetBuffer() {
streamDelegate.resetBuffer();
}
// TODO: Consider picking up content-type/encoding, as we can only
// filter US-ASCII, UTF-8 and other compatible encodings?
}
}
@@ -1,72 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet.cache;
import java.net.URI;
import com.twelvemonkeys.lang.Validate;
/**
* AbstractCacheRequest
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: AbstractCacheRequest.java#1 $
*/
@Deprecated
public abstract class AbstractCacheRequest implements CacheRequest {
private final URI requestURI;
private final String method;
protected AbstractCacheRequest(final URI pRequestURI, final String pMethod) {
requestURI = Validate.notNull(pRequestURI, "requestURI");
method = Validate.notNull(pMethod, "method");
}
public URI getRequestURI() {
return requestURI;
}
public String getMethod() {
return method;
}
// TODO: Consider overriding equals/hashcode
@Override
public String toString() {
return new StringBuilder(getClass().getSimpleName())
.append("[URI=").append(requestURI)
.append(", parameters=").append(getParameters())
.append(", headers=").append(getHeaders())
.append("]").toString();
}
}
@@ -1,82 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet.cache;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* AbstractCacheResponse
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: AbstractCacheResponse.java#1 $
*/
@Deprecated
public abstract class AbstractCacheResponse implements CacheResponse {
private int status;
private final Map<String, List<String>> headers = new LinkedHashMap<String, List<String>>(); // Insertion order
private final Map<String, List<String>> readableHeaders = Collections.unmodifiableMap(headers);
public int getStatus() {
return status;
}
public void setStatus(int pStatusCode) {
status = pStatusCode;
}
public void addHeader(String pHeaderName, String pHeaderValue) {
setHeader(pHeaderName, pHeaderValue, true);
}
public void setHeader(String pHeaderName, String pHeaderValue) {
setHeader(pHeaderName, pHeaderValue, false);
}
private void setHeader(String pHeaderName, String pHeaderValue, boolean pAdd) {
List<String> values = pAdd ? headers.get(pHeaderName) : null;
if (values == null) {
values = new ArrayList<String>();
headers.put(pHeaderName, values);
}
values.add(pHeaderValue);
}
public Map<String, List<String>> getHeaders() {
return readableHeaders;
}
}
@@ -1,45 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet.cache;
/**
* CacheException
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: CacheException.java#1 $
*/
@Deprecated
public class CacheException extends Exception {
public CacheException(Throwable pCause) {
super(pCause);
}
}
@@ -1,210 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet.cache;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.twelvemonkeys.lang.StringUtil;
import com.twelvemonkeys.servlet.GenericFilter;
import com.twelvemonkeys.servlet.ServletConfigException;
import com.twelvemonkeys.servlet.ServletUtil;
/**
* A Filter that provides response caching, for HTTP {@code GET} requests.
* <p>
* Originally based on ideas and code found in the ONJava article
* <a href="http://www.onjava.com/pub/a/onjava/2003/11/19/filters.html">Two
* Servlet Filters Every Web Application Should Have</a>
* by Jayson Falkner.
* </p>
*
* @author Jayson Falkner
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: CacheFilter.java#4 $
*
*/
@Deprecated
public class CacheFilter extends GenericFilter {
HTTPCache cache;
/**
* Initializes the filter
*
* @throws javax.servlet.ServletException
*/
public void init() throws ServletException {
FilterConfig config = getFilterConfig();
// Default don't delete cache files on exit (persistent cache)
boolean deleteCacheOnExit = "TRUE".equalsIgnoreCase(config.getInitParameter("deleteCacheOnExit"));
// Default expiry time 10 minutes
int expiryTime = 10 * 60 * 1000;
String expiryTimeStr = config.getInitParameter("expiryTime");
if (!StringUtil.isEmpty(expiryTimeStr)) {
try {
// TODO: This is insane.. :-P Let the expiry time be in minutes or seconds..
expiryTime = Integer.parseInt(expiryTimeStr);
}
catch (NumberFormatException e) {
throw new ServletConfigException("Could not parse expiryTime: " + e.toString(), e);
}
}
// Default max mem cache size 10 MB
int memCacheSize = 10;
String memCacheSizeStr = config.getInitParameter("memCacheSize");
if (!StringUtil.isEmpty(memCacheSizeStr)) {
try {
memCacheSize = Integer.parseInt(memCacheSizeStr);
}
catch (NumberFormatException e) {
throw new ServletConfigException("Could not parse memCacheSize: " + e.toString(), e);
}
}
int maxCachedEntites = 10000;
try {
cache = new HTTPCache(
getTempFolder(),
expiryTime,
memCacheSize * 1024 * 1024,
maxCachedEntites,
deleteCacheOnExit,
new ServletContextLoggerAdapter(getFilterName(), getServletContext())
) {
@Override
protected File getRealFile(CacheRequest pRequest) {
String contextRelativeURI = ServletUtil.getContextRelativeURI(((ServletCacheRequest) pRequest).getRequest());
String path = getServletContext().getRealPath(contextRelativeURI);
if (path != null) {
return new File(path);
}
return null;
}
};
log("Created cache: " + cache);
}
catch (IllegalArgumentException e) {
throw new ServletConfigException("Could not create cache: " + e.toString(), e);
}
}
private File getTempFolder() {
File tempRoot = (File) getServletContext().getAttribute("javax.servlet.context.tempdir");
if (tempRoot == null) {
throw new IllegalStateException("Missing context attribute \"javax.servlet.context.tempdir\"");
}
return new File(tempRoot, getFilterName());
}
public void destroy() {
log("Destroying cache: " + cache);
cache = null;
super.destroy();
}
protected void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain) throws IOException, ServletException {
// We can only cache HTTP GET/HEAD requests
if (!(pRequest instanceof HttpServletRequest
&& pResponse instanceof HttpServletResponse
&& isCachable((HttpServletRequest) pRequest))) {
pChain.doFilter(pRequest, pResponse); // Continue chain
}
else {
ServletCacheRequest cacheRequest = new ServletCacheRequest((HttpServletRequest) pRequest);
ServletCacheResponse cacheResponse = new ServletCacheResponse((HttpServletResponse) pResponse);
ServletResponseResolver resolver = new ServletResponseResolver(cacheRequest, cacheResponse, pChain);
// Render fast
try {
cache.doCached(cacheRequest, cacheResponse, resolver);
}
catch (CacheException e) {
if (e.getCause() instanceof ServletException) {
throw (ServletException) e.getCause();
}
else {
throw new ServletException(e);
}
}
finally {
pResponse.flushBuffer();
}
}
}
private boolean isCachable(HttpServletRequest pRequest) {
// TODO: Get Cache-Control: no-cache/max-age=0 and Pragma: no-cache from REQUEST too?
return "GET".equals(pRequest.getMethod()) || "HEAD".equals(pRequest.getMethod());
}
// TODO: Extract, complete and document this class, might be useful in other cases
// Maybe add it to the ServletUtil class
static class ServletContextLoggerAdapter extends Logger {
private final ServletContext context;
public ServletContextLoggerAdapter(String pName, ServletContext pContext) {
super(pName, null);
context = pContext;
}
@Override
public void log(Level pLevel, String pMessage) {
context.log(pMessage);
}
@Override
public void log(Level pLevel, String pMessage, Throwable pThrowable) {
context.log(pMessage, pThrowable);
}
}
}
@@ -1,57 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet.cache;
import java.net.URI;
import java.util.List;
import java.util.Map;
/**
* CacheRequest
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: CacheRequest.java#1 $
*/
@Deprecated
public interface CacheRequest {
URI getRequestURI();
String getMethod();
Map<String, List<String>> getHeaders();
Map<String, List<String>> getParameters();
String getServerName();
int getServerPort();
}
@@ -1,58 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet.cache;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import java.util.Map;
/**
* CacheResponse
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: CacheResponse.java#1 $
*/
@Deprecated
public interface CacheResponse {
OutputStream getOutputStream() throws IOException;
void setStatus(int pStatusCode);
int getStatus();
void addHeader(String pHeaderName, String pHeaderValue);
void setHeader(String pHeaderName, String pHeaderValue);
Map<String, List<String>> getHeaders();
}
@@ -1,266 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet.cache;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponseWrapper;
import com.twelvemonkeys.lang.StringUtil;
import com.twelvemonkeys.net.HTTPUtil;
import com.twelvemonkeys.servlet.ServletResponseStreamDelegate;
/**
* CacheResponseWrapper class description.
* <p>
* Based on ideas and code found in the ONJava article
* <a href="http://www.onjava.com/pub/a/onjava/2003/11/19/filters.html">Two
* Servlet Filters Every Web Application Should Have</a>
* by Jayson Falkner.
* </p>
*
* @author Jayson Falkner
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: CacheResponseWrapper.java#3 $
*/
@Deprecated
class CacheResponseWrapper extends HttpServletResponseWrapper {
private ServletResponseStreamDelegate streamDelegate;
private CacheResponse response;
private CachedEntity cached;
private WritableCachedResponse cachedResponse;
private Boolean cacheable;
private int status;
public CacheResponseWrapper(final ServletCacheResponse pResponse, final CachedEntity pCached) {
super(pResponse.getResponse());
response = pResponse;
cached = pCached;
init();
}
/*
NOTE: This class defers determining if a response is cacheable until the
output stream is needed.
This it the reason for the somewhat complicated logic in the add/setHeader
methods below.
*/
private void init() {
cacheable = null;
status = SC_OK;
cachedResponse = cached.createCachedResponse();
streamDelegate = new ServletResponseStreamDelegate(this) {
protected OutputStream createOutputStream() throws IOException {
// Test if this request is really cacheable, otherwise,
// just write through to underlying response, and don't cache
if (isCacheable()) {
return cachedResponse.getOutputStream();
}
else {
cachedResponse.setStatus(status);
cachedResponse.writeHeadersTo(CacheResponseWrapper.this.response);
return super.getOutputStream();
}
}
};
}
CachedResponse getCachedResponse() {
return cachedResponse.getCachedResponse();
}
public boolean isCacheable() {
// NOTE: Intentionally not synchronized
if (cacheable == null) {
cacheable = isCacheableImpl();
}
return cacheable;
}
private boolean isCacheableImpl() {
if (status != SC_OK) {
return false;
}
// Vary: *
String[] values = cachedResponse.getHeaderValues(HTTPCache.HEADER_VARY);
if (values != null) {
for (String value : values) {
if ("*".equals(value)) {
return false;
}
}
}
// Cache-Control: no-cache, no-store, must-revalidate
values = cachedResponse.getHeaderValues(HTTPCache.HEADER_CACHE_CONTROL);
if (values != null) {
for (String value : values) {
if (StringUtil.contains(value, "no-cache")
|| StringUtil.contains(value, "no-store")
|| StringUtil.contains(value, "must-revalidate")) {
return false;
}
}
}
// Pragma: no-cache
values = cachedResponse.getHeaderValues(HTTPCache.HEADER_PRAGMA);
if (values != null) {
for (String value : values) {
if (StringUtil.contains(value, "no-cache")) {
return false;
}
}
}
return true;
}
public void flushBuffer() throws IOException {
streamDelegate.flushBuffer();
}
public void resetBuffer() {
// Servlet 2.3
streamDelegate.resetBuffer();
}
public void reset() {
if (Boolean.FALSE.equals(cacheable)) {
super.reset();
}
// No else, might be cacheable after all..
init();
}
public ServletOutputStream getOutputStream() throws IOException {
return streamDelegate.getOutputStream();
}
public PrintWriter getWriter() throws IOException {
return streamDelegate.getWriter();
}
public boolean containsHeader(String name) {
return cachedResponse.getHeaderValues(name) != null;
}
public void sendError(int pStatusCode, String msg) throws IOException {
// NOT cacheable
status = pStatusCode;
super.sendError(pStatusCode, msg);
}
public void sendError(int pStatusCode) throws IOException {
// NOT cacheable
status = pStatusCode;
super.sendError(pStatusCode);
}
public void setStatus(int pStatusCode, String sm) {
// NOTE: This method is deprecated
setStatus(pStatusCode);
}
public void setStatus(int pStatusCode) {
// NOT cacheable unless pStatusCode == 200 (or a FEW others?)
if (pStatusCode != SC_OK) {
status = pStatusCode;
super.setStatus(pStatusCode);
}
}
public void sendRedirect(String pLocation) throws IOException {
// NOT cacheable
status = SC_MOVED_TEMPORARILY;
super.sendRedirect(pLocation);
}
public void setDateHeader(String pName, long pValue) {
// If in write-trough-mode, set headers directly
if (Boolean.FALSE.equals(cacheable)) {
super.setDateHeader(pName, pValue);
}
cachedResponse.setHeader(pName, HTTPUtil.formatHTTPDate(pValue));
}
public void addDateHeader(String pName, long pValue) {
// If in write-trough-mode, set headers directly
if (Boolean.FALSE.equals(cacheable)) {
super.addDateHeader(pName, pValue);
}
cachedResponse.addHeader(pName, HTTPUtil.formatHTTPDate(pValue));
}
public void setHeader(String pName, String pValue) {
// If in write-trough-mode, set headers directly
if (Boolean.FALSE.equals(cacheable)) {
super.setHeader(pName, pValue);
}
cachedResponse.setHeader(pName, pValue);
}
public void addHeader(String pName, String pValue) {
// If in write-trough-mode, set headers directly
if (Boolean.FALSE.equals(cacheable)) {
super.addHeader(pName, pValue);
}
cachedResponse.addHeader(pName, pValue);
}
public void setIntHeader(String pName, int pValue) {
// If in write-trough-mode, set headers directly
if (Boolean.FALSE.equals(cacheable)) {
super.setIntHeader(pName, pValue);
}
cachedResponse.setHeader(pName, String.valueOf(pValue));
}
public void addIntHeader(String pName, int pValue) {
// If in write-trough-mode, set headers directly
if (Boolean.FALSE.equals(cacheable)) {
super.addIntHeader(pName, pValue);
}
cachedResponse.addHeader(pName, String.valueOf(pValue));
}
public final void setContentType(String type) {
setHeader(HTTPCache.HEADER_CONTENT_TYPE, type);
}
}
@@ -1,78 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet.cache;
import java.io.IOException;
/**
* CachedEntity
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: CachedEntity.java#3 $
*/
@Deprecated
interface CachedEntity {
/**
* Renders the cached entity to the response.
*
* @param pRequest the request
* @param pResponse the response
* @throws java.io.IOException if an I/O exception occurs
*/
void render(CacheRequest pRequest, CacheResponse pResponse) throws IOException;
/**
* Captures (caches) the response for the given request.
*
* @param pRequest the request
* @param pResponse the response
* @throws java.io.IOException if an I/O exception occurs
*
* @see #createCachedResponse()
*/
void capture(CacheRequest pRequest, CachedResponse pResponse) throws IOException;
/**
* Tests if the content of this entity is stale for the given request.
*
* @param pRequest the request
* @return {@code true} if content is stale
*/
boolean isStale(CacheRequest pRequest);
/**
* Creates a {@code WritableCachedResponse} to use to capture the response.
*
* @return a {@code WritableCachedResponse}
*/
WritableCachedResponse createCachedResponse();
}
@@ -1,174 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet.cache;
import java.io.IOException;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import com.twelvemonkeys.lang.Validate;
/**
* CachedEntity
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: CachedEntityImpl.java#3 $
*/
@Deprecated
class CachedEntityImpl implements CachedEntity {
private String cacheURI;
private HTTPCache cache;
CachedEntityImpl(String pCacheURI, HTTPCache pCache) {
cacheURI = Validate.notNull(pCacheURI, "cacheURI");
cache = pCache;
}
public void render(CacheRequest pRequest, CacheResponse pResponse) throws IOException {
// Get cached content
CachedResponse cached = cache.getContent(cacheURI, pRequest);
// Sanity check
if (cached == null) {
throw new IllegalStateException("Tried to render non-cached response (cache == null).");
}
// If the cached entity is not modified since the date of the browsers
// version, then simply send a "304 Not Modified" response
// Otherwise send the full response.
// TODO: WHY DID I COMMENT OUT THIS LINE AND REPLACE IT WITH THE ONE BELOW??
//long lastModified = HTTPCache.getDateHeader(cached.getHeaderValue(HTTPCache.HEADER_LAST_MODIFIED));
long lastModified = HTTPCache.getDateHeader(cached.getHeaderValue(HTTPCache.HEADER_CACHED_TIME));
// TODO: Consider handling time skews between server "now" and client "now"?
// NOTE: The If-Modified-Since is probably right according to the server
// even in a time skew situation, as the client should use either the
// Date or Last-Modifed dates from the response headers (server generated)
long ifModifiedSince = -1L;
try {
List<String> ifmh = pRequest.getHeaders().get(HTTPCache.HEADER_IF_MODIFIED_SINCE);
ifModifiedSince = ifmh != null ? HTTPCache.getDateHeader(ifmh.get(0)) : -1L;
if (ifModifiedSince != -1L) {
/*
long serverTime = DateUtil.currentTimeMinute();
long clientTime = DateUtil.roundToMinute(pRequest.getDateHeader(HTTPCache.HEADER_DATE));
// Test if time skew is greater than time skew threshold (currently 1 minute)
if (Math.abs(serverTime - clientTime) > 1) {
// TODO: Correct error in ifModifiedSince?
}
*/
// System.out.println(" << CachedEntity >> If-Modified-Since present: " + ifModifiedSince + " --> " + NetUtil.formatHTTPDate(ifModifiedSince) + "==" + pRequest.getHeader(HTTPCache.HEADER_IF_MODIFIED_SINCE));
// System.out.println(" << CachedEntity >> Last-Modified for entity: " + lastModified + " --> " + NetUtil.formatHTTPDate(lastModified));
}
}
catch (IllegalArgumentException e) {
// Seems to be a bug in FireFox 1.0.2..?!
cache.log("Error in date header from user-agent. User-Agent: " + pRequest.getHeaders().get("User-Agent"), e);
}
if (lastModified == -1L || (ifModifiedSince < (lastModified / 1000L) * 1000L)) {
pResponse.setStatus(cached.getStatus());
cached.writeHeadersTo(pResponse);
if (isStale(pRequest)) {
// Add warning header
// Warning: 110 <server>:<port> Content is stale
pResponse.addHeader(HTTPCache.HEADER_WARNING, "110 " + getHost(pRequest) + " Content is stale.");
}
// NOTE: At the moment we only ever try to cache HEAD and GET requests
if (!"HEAD".equals(pRequest.getMethod())) {
cached.writeContentsTo(pResponse.getOutputStream());
}
}
else {
pResponse.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
// System.out.println(" << CachedEntity >> Not modified: " + toString());
if (isStale(pRequest)) {
// Add warning header
// Warning: 110 <server>:<port> Content is stale
pResponse.addHeader(HTTPCache.HEADER_WARNING, "110 " + getHost(pRequest) + " Content is stale.");
}
}
}
/* Utility method to get Host header */
private static String getHost(CacheRequest pRequest) {
return pRequest.getServerName() + ":" + pRequest.getServerPort();
}
public void capture(CacheRequest pRequest, CachedResponse pResponse) throws IOException {
// if (!(pResponse instanceof CacheResponseWrapper)) {
// throw new IllegalArgumentException("Response must be created by CachedEntity.createResponseWrapper()");
// }
//
// CacheResponseWrapper response = (CacheResponseWrapper) pResponse;
// if (response.isCacheable()) {
cache.registerContent(
cacheURI,
pRequest,
pResponse instanceof WritableCachedResponse ? ((WritableCachedResponse) pResponse).getCachedResponse() : pResponse
);
// }
// else {
// Else store that the response for this request is not cachable
// pRequest.setAttribute(ATTRIB_NOT_CACHEABLE, Boolean.TRUE);
// TODO: Store this in HTTPCache, for subsequent requests to same resource?
// }
}
public boolean isStale(CacheRequest pRequest) {
return cache.isContentStale(cacheURI, pRequest);
}
public WritableCachedResponse createCachedResponse() {
return new WritableCachedResponseImpl();
}
public int hashCode() {
return (cacheURI != null ? cacheURI.hashCode() : 0) + 1397;
}
public boolean equals(Object pOther) {
return pOther instanceof CachedEntityImpl &&
((cacheURI == null && ((CachedEntityImpl) pOther).cacheURI == null) ||
cacheURI != null && cacheURI.equals(((CachedEntityImpl) pOther).cacheURI));
}
public String toString() {
return "CachedEntity[URI=" + cacheURI + "]";
}
}
@@ -1,98 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet.cache;
import java.io.IOException;
import java.io.OutputStream;
/**
* CachedResponse
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: CachedResponse.java#3 $
*/
@Deprecated
interface CachedResponse {
/**
* Writes the cached headers to the response
*
* @param pResponse the servlet response
*/
void writeHeadersTo(CacheResponse pResponse);
/**
* Writes the cahced content to the response
*
* @param pStream the response output stream
* @throws IOException if an I/O exception occurs during write
*/
void writeContentsTo(OutputStream pStream) throws IOException;
int getStatus();
// TODO: Map<String, List<String>> getHeaders()
/**
* Gets the header names of all headers set in this response.
*
* @return an array of {@code String}s
*/
String[] getHeaderNames();
/**
* Gets all header values set for the given header in this response. If the
* header is not set, {@code null} is returned.
*
* @param pHeaderName the header name
* @return an array of {@code String}s, or {@code null} if there is no
* such header in this response.
*/
String[] getHeaderValues(String pHeaderName);
/**
* Gets the first header value set for the given header in this response.
* If the header is not set, {@code null} is returned.
* Useful for headers that don't have multiple values, like
* {@code "Content-Type"} or {@code "Content-Length"}.
*
* @param pHeaderName the header name
* @return a {@code String}, or {@code null} if there is no
* such header in this response.
*/
String getHeaderValue(String pHeaderName);
/**
* Returns the size of this cached response in bytes.
*
* @return the size
*/
int size();
}
@@ -1,220 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet.cache;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.twelvemonkeys.io.FastByteArrayOutputStream;
import com.twelvemonkeys.lang.Validate;
/**
* CachedResponseImpl
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: CachedResponseImpl.java#4 $
*/
@Deprecated
class CachedResponseImpl implements CachedResponse {
final protected Map<String, List<String>> headers;
protected int headersSize;
protected ByteArrayOutputStream content = null;
int status;
protected CachedResponseImpl() {
headers = new LinkedHashMap<String, List<String>>(); // Keep headers in insertion order
}
// For use by HTTPCache, when recreating CachedResponses from disk cache
CachedResponseImpl(final int pStatus, final LinkedHashMap<String, List<String>> pHeaders, final int pHeaderSize, final byte[] pContent) {
status = pStatus;
headers = Validate.notNull(pHeaders, "headers");
headersSize = pHeaderSize;
content = new FastByteArrayOutputStream(pContent);
}
public int getStatus() {
return status;
}
/**
* Writes the cached headers to the response
*
* @param pResponse the response
*/
public void writeHeadersTo(final CacheResponse pResponse) {
String[] headers = getHeaderNames();
for (String header : headers) {
// HACK...
// Strip away internal headers
if (HTTPCache.HEADER_CACHED_TIME.equals(header)) {
continue;
}
// TODO: Replace Last-Modified with X-Cached-At? See CachedEntityImpl
String[] headerValues = getHeaderValues(header);
for (int i = 0; i < headerValues.length; i++) {
String headerValue = headerValues[i];
if (i == 0) {
pResponse.setHeader(header, headerValue);
}
else {
pResponse.addHeader(header, headerValue);
}
}
}
}
/**
* Writes the cached content to the response
*
* @param pStream the response stream
* @throws java.io.IOException
*/
public void writeContentsTo(final OutputStream pStream) throws IOException {
if (content == null) {
throw new IOException("Cache is null, no content to write.");
}
content.writeTo(pStream);
}
/**
* Gets the header names of all headers set in this response.
*
* @return an array of {@code String}s
*/
public String[] getHeaderNames() {
Set<String> headers = this.headers.keySet();
return headers.toArray(new String[headers.size()]);
}
/**
* Gets all header values set for the given header in this response. If the
* header is not set, {@code null} is returned.
*
* @param pHeaderName the header name
* @return an array of {@code String}s, or {@code null} if there is no
* such header in this response.
*/
public String[] getHeaderValues(final String pHeaderName) {
List<String> values = headers.get(pHeaderName);
return values == null ? null : values.toArray(new String[values.size()]);
}
/**
* Gets the first header value set for the given header in this response.
* If the header is not set, {@code null} is returned.
* Useful for headers that don't have multiple values, like
* {@code "Content-Type"} or {@code "Content-Length"}.
*
* @param pHeaderName the header name
* @return a {@code String}, or {@code null} if there is no
* such header in this response.
*/
public String getHeaderValue(final String pHeaderName) {
List<String> values = headers.get(pHeaderName);
return (values != null && values.size() > 0) ? values.get(0) : null;
}
public int size() {
// content.size() is exact size in bytes, headersSize is an estimate
return (content != null ? content.size() : 0) + headersSize;
}
public boolean equals(final Object pOther) {
if (this == pOther) {
return true;
}
if (pOther instanceof CachedResponseImpl) {
// "Fast"
return equalsImpl((CachedResponseImpl) pOther);
}
else if (pOther instanceof CachedResponse) {
// Slow
return equalsGeneric((CachedResponse) pOther);
}
return false;
}
private boolean equalsImpl(final CachedResponseImpl pOther) {
return headersSize == pOther.headersSize &&
(content == null ? pOther.content == null : content.equals(pOther.content)) &&
headers.equals(pOther.headers);
}
private boolean equalsGeneric(final CachedResponse pOther) {
if (size() != pOther.size()) {
return false;
}
String[] headers = getHeaderNames();
String[] otherHeaders = pOther.getHeaderNames();
if (!Arrays.equals(headers, otherHeaders)) {
return false;
}
if (headers != null) {
for (String header : headers) {
String[] values = getHeaderValues(header);
String[] otherValues = pOther.getHeaderValues(header);
if (!Arrays.equals(values, otherValues)) {
return false;
}
}
}
return true;
}
public int hashCode() {
int result;
result = headers.hashCode();
result = 29 * result + headersSize;
result = 37 * result + (content != null ? content.hashCode() : 0);
return result;
}
}
@@ -1,75 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet.cache;
import java.net.URI;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* ClientCacheRequest
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: ClientCacheRequest.java#1 $
*/
@Deprecated
public final class ClientCacheRequest extends AbstractCacheRequest {
private Map<String, List<String>> parameters;
private Map<String, List<String>> headers;
public ClientCacheRequest(final URI pRequestURI,final Map<String, List<String>> pParameters, final Map<String, List<String>> pHeaders) {
super(pRequestURI, "GET"); // TODO: Consider supporting more than get? At least HEAD and OPTIONS...
parameters = normalizeMap(pParameters);
headers = normalizeMap(pHeaders);
}
private <K, V> Map<K, V> normalizeMap(Map<K, V> pMap) {
return pMap == null ? Collections.<K, V>emptyMap() : Collections.unmodifiableMap(pMap);
}
public Map<String, List<String>> getParameters() {
return parameters;
}
public Map<String, List<String>> getHeaders() {
return headers;
}
public String getServerName() {
return getRequestURI().getAuthority();
}
public int getServerPort() {
return getRequestURI().getPort();
}
}
@@ -1,56 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet.cache;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* ClientCacheResponse
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: ClientCacheResponse.java#2 $
*/
@Deprecated
public final class ClientCacheResponse extends AbstractCacheResponse {
// It's quite useless to cache the data either on disk or in memory, as it already is cached in the client's cache...
// It would be nice if we could bypass that...
public OutputStream getOutputStream() throws IOException {
throw new UnsupportedOperationException("Method getOutputStream not implemented"); // TODO: Implement
}
public InputStream getInputStream() {
throw new UnsupportedOperationException("Method getInputStream not implemented"); // TODO: Implement
}
}
File diff suppressed because it is too large Load Diff
@@ -1,45 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet.cache;
import java.io.IOException;
/**
* ResponseResolver
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: ResponseResolver.java#2 $
*/
@Deprecated
public interface ResponseResolver {
void resolve(CacheRequest pRequest, CacheResponse pResponse) throws IOException, CacheException;
}
@@ -1,278 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet.cache;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import com.twelvemonkeys.lang.StringUtil;
import com.twelvemonkeys.net.HTTPUtil;
import com.twelvemonkeys.servlet.ServletResponseStreamDelegate;
/**
* CacheResponseWrapper class description.
* <p>
* Based on ideas and code found in the ONJava article
* <a href="http://www.onjava.com/pub/a/onjava/2003/11/19/filters.html">Two
* Servlet Filters Every Web Application Should Have</a>
* by Jayson Falkner.
* </p>
*
* @author Jayson Falkner
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: SerlvetCacheResponseWrapper.java#2 $
*/
@Deprecated
class SerlvetCacheResponseWrapper extends HttpServletResponseWrapper {
private ServletResponseStreamDelegate streamDelegate;
private CacheResponse cacheResponse;
private Boolean cacheable;
private int status;
public SerlvetCacheResponseWrapper(final HttpServletResponse pServletResponse, final CacheResponse pResponse) {
super(pServletResponse);
cacheResponse = pResponse;
init();
}
/*
NOTE: This class defers determining if a response is cacheable until the
output stream is needed.
This it the reason for the somewhat complicated logic in the add/setHeader
methods below.
*/
private void init() {
cacheable = null;
status = SC_OK;
streamDelegate = new ServletResponseStreamDelegate(this) {
protected OutputStream createOutputStream() throws IOException {
// Test if this request is really cacheable, otherwise,
// just write through to underlying response, and don't cache
if (isCacheable()) {
return cacheResponse.getOutputStream();
}
else {
// TODO: We need to tell the cache about this, somehow...
writeHeaders(cacheResponse, (HttpServletResponse) getResponse());
return super.getOutputStream();
}
}
};
}
private void writeHeaders(final CacheResponse pResponse, final HttpServletResponse pServletResponse) {
Map<String,List<String>> headers = pResponse.getHeaders();
for (Map.Entry<String, List<String>> header : headers.entrySet()) {
for (int i = 0; i < header.getValue().size(); i++) {
String value = header.getValue().get(i);
if (i == 0) {
pServletResponse.setHeader(header.getKey(), value);
}
else {
pServletResponse.addHeader(header.getKey(), value);
}
}
}
}
public boolean isCacheable() {
// NOTE: Intentionally not synchronized
if (cacheable == null) {
cacheable = isCacheableImpl();
}
return cacheable;
}
private boolean isCacheableImpl() {
// TODO: This code is duped in the cache...
if (status != SC_OK) {
return false;
}
// Vary: *
List<String> values = cacheResponse.getHeaders().get(HTTPCache.HEADER_VARY);
if (values != null) {
for (String value : values) {
if ("*".equals(value)) {
return false;
}
}
}
// Cache-Control: no-cache, no-store, must-revalidate
values = cacheResponse.getHeaders().get(HTTPCache.HEADER_CACHE_CONTROL);
if (values != null) {
for (String value : values) {
if (StringUtil.contains(value, "no-cache")
|| StringUtil.contains(value, "no-store")
|| StringUtil.contains(value, "must-revalidate")) {
return false;
}
}
}
// Pragma: no-cache
values = cacheResponse.getHeaders().get(HTTPCache.HEADER_PRAGMA);
if (values != null) {
for (String value : values) {
if (StringUtil.contains(value, "no-cache")) {
return false;
}
}
}
return true;
}
public void flushBuffer() throws IOException {
streamDelegate.flushBuffer();
}
public void resetBuffer() {
// Servlet 2.3
streamDelegate.resetBuffer();
}
public void reset() {
if (Boolean.FALSE.equals(cacheable)) {
super.reset();
}
// No else, might be cacheable after all..
init();
}
public ServletOutputStream getOutputStream() throws IOException {
return streamDelegate.getOutputStream();
}
public PrintWriter getWriter() throws IOException {
return streamDelegate.getWriter();
}
public boolean containsHeader(String name) {
return cacheResponse.getHeaders().get(name) != null;
}
public void sendError(int pStatusCode, String msg) throws IOException {
// NOT cacheable
status = pStatusCode;
super.sendError(pStatusCode, msg);
}
public void sendError(int pStatusCode) throws IOException {
// NOT cacheable
status = pStatusCode;
super.sendError(pStatusCode);
}
public void setStatus(int pStatusCode, String sm) {
// NOTE: This method is deprecated
setStatus(pStatusCode);
}
public void setStatus(int pStatusCode) {
// NOT cacheable unless pStatusCode == 200 (or a FEW others?)
if (pStatusCode != SC_OK) {
status = pStatusCode;
super.setStatus(pStatusCode);
}
}
public void sendRedirect(String pLocation) throws IOException {
// NOT cacheable
status = SC_MOVED_TEMPORARILY;
super.sendRedirect(pLocation);
}
public void setDateHeader(String pName, long pValue) {
// If in write-trough-mode, set headers directly
if (Boolean.FALSE.equals(cacheable)) {
super.setDateHeader(pName, pValue);
}
cacheResponse.setHeader(pName, HTTPUtil.formatHTTPDate(pValue));
}
public void addDateHeader(String pName, long pValue) {
// If in write-trough-mode, set headers directly
if (Boolean.FALSE.equals(cacheable)) {
super.addDateHeader(pName, pValue);
}
cacheResponse.addHeader(pName, HTTPUtil.formatHTTPDate(pValue));
}
public void setHeader(String pName, String pValue) {
// If in write-trough-mode, set headers directly
if (Boolean.FALSE.equals(cacheable)) {
super.setHeader(pName, pValue);
}
cacheResponse.setHeader(pName, pValue);
}
public void addHeader(String pName, String pValue) {
// If in write-trough-mode, set headers directly
if (Boolean.FALSE.equals(cacheable)) {
super.addHeader(pName, pValue);
}
cacheResponse.addHeader(pName, pValue);
}
public void setIntHeader(String pName, int pValue) {
// If in write-trough-mode, set headers directly
if (Boolean.FALSE.equals(cacheable)) {
super.setIntHeader(pName, pValue);
}
cacheResponse.setHeader(pName, String.valueOf(pValue));
}
public void addIntHeader(String pName, int pValue) {
// If in write-trough-mode, set headers directly
if (Boolean.FALSE.equals(cacheable)) {
super.addIntHeader(pName, pValue);
}
cacheResponse.addHeader(pName, String.valueOf(pValue));
}
public final void setContentType(String type) {
setHeader(HTTPCache.HEADER_CONTENT_TYPE, type);
}
}
@@ -1,88 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet.cache;
import java.net.URI;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import com.twelvemonkeys.servlet.ServletUtil;
/**
* ServletCacheRequest
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: ServletCacheRequest.java#1 $
*/
@Deprecated
public final class ServletCacheRequest extends AbstractCacheRequest {
private final HttpServletRequest request;
private Map<String, List<String>> headers;
private Map<String, List<String>> parameters;
protected ServletCacheRequest(final HttpServletRequest pRequest) {
super(URI.create(pRequest.getRequestURI()), pRequest.getMethod());
request = pRequest;
}
public Map<String, List<String>> getHeaders() {
if (headers == null) {
headers = ServletUtil.headersAsMap(request);
}
return headers;
}
public Map<String, List<String>> getParameters() {
if (parameters == null) {
parameters = ServletUtil.parametersAsMap(request);
}
return parameters;
}
public String getServerName() {
return request.getServerName();
}
public int getServerPort() {
return request.getServerPort();
}
HttpServletRequest getRequest() {
return request;
}
}
@@ -1,78 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet.cache;
import java.io.IOException;
import java.io.OutputStream;
import javax.servlet.http.HttpServletResponse;
/**
* ServletCacheResponse
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: ServletCacheResponse.java#2 $
*/
@Deprecated
public final class ServletCacheResponse extends AbstractCacheResponse {
private HttpServletResponse response;
public ServletCacheResponse(HttpServletResponse pResponse) {
response = pResponse;
}
public OutputStream getOutputStream() throws IOException {
return response.getOutputStream();
}
@Override
public void setStatus(int pStatusCode) {
response.setStatus(pStatusCode);
super.setStatus(pStatusCode);
}
@Override
public void addHeader(String pHeaderName, String pHeaderValue) {
response.addHeader(pHeaderName, pHeaderValue);
super.addHeader(pHeaderName, pHeaderValue);
}
@Override
public void setHeader(String pHeaderName, String pHeaderValue) {
response.setHeader(pHeaderName, pHeaderValue);
super.setHeader(pHeaderName, pHeaderValue);
}
HttpServletResponse getResponse() {
return response;
}
}
@@ -1,72 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet.cache;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
/**
* ServletResponseResolver
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: ServletResponseResolver.java#2 $
*/
@Deprecated
final class ServletResponseResolver implements ResponseResolver {
final private ServletCacheRequest request;
final private ServletCacheResponse response;
final private FilterChain chain;
ServletResponseResolver(final ServletCacheRequest pRequest, final ServletCacheResponse pResponse, final FilterChain pChain) {
request = pRequest;
response = pResponse;
chain = pChain;
}
public void resolve(final CacheRequest pRequest, final CacheResponse pResponse) throws IOException, CacheException {
// Need only wrap if pResponse is not response...
HttpServletResponse response = pResponse == this.response ? this.response.getResponse() : new SerlvetCacheResponseWrapper(this.response.getResponse(), pResponse);
try {
chain.doFilter(request.getRequest(), response);
}
catch (ServletException e) {
throw new CacheException(e);
}
finally {
response.flushBuffer();
}
}
}
@@ -1,80 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet.cache;
import java.io.OutputStream;
/**
* WritableCachedResponse
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: WritableCachedResponse.java#2 $
*/
@Deprecated
public interface WritableCachedResponse extends CachedResponse, CacheResponse {
/**
* Gets the {@code OutputStream} for this cached response.
* This allows a client to write to the cached response.
*
* @return the {@code OutputStream} for this response.
*/
OutputStream getOutputStream();
/**
* Sets a header key/value pair for this response.
* Any prior header value for the given header key will be overwritten.
*
* @see #addHeader(String, String)
*
* @param pName the header name
* @param pValue the header value
*/
void setHeader(String pName, String pValue);
/**
* Adds a header key/value pair for this response.
* If a value allready exists for the given key, the value will be appended.
*
* @see #setHeader(String, String)
*
* @param pName the header name
* @param pValue the header value
*/
void addHeader(String pName, String pValue);
/**
* Returns the final (immutable) {@code CachedResponse} created by this
* {@code WritableCachedResponse}.
*
* @return the {@code CachedResponse}
*/
CachedResponse getCachedResponse();
}
@@ -1,189 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet.cache;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.twelvemonkeys.io.FastByteArrayOutputStream;
import com.twelvemonkeys.net.HTTPUtil;
/**
* WritableCachedResponseImpl
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: WritableCachedResponseImpl.java#3 $
*/
@Deprecated
class WritableCachedResponseImpl implements WritableCachedResponse {
private final CachedResponseImpl cachedResponse;
/**
* Creates a {@code WritableCachedResponseImpl}.
*/
protected WritableCachedResponseImpl() {
cachedResponse = new CachedResponseImpl();
// Hmmm..
setHeader(HTTPCache.HEADER_CACHED_TIME, HTTPUtil.formatHTTPDate(System.currentTimeMillis()));
}
public CachedResponse getCachedResponse() {
return cachedResponse;
}
public void setHeader(String pName, String pValue) {
setHeader(pName, pValue, false);
}
public void addHeader(String pName, String pValue) {
setHeader(pName, pValue, true);
}
public Map<String, List<String>> getHeaders() {
return cachedResponse.headers;
}
/**
*
* @param pName the header name
* @param pValue the new header value
* @param pAdd {@code true} if the value should add to the list of values, not replace existing value
*/
private void setHeader(String pName, String pValue, boolean pAdd) {
// System.out.println(" ++ CachedResponse ++ " + (pAdd ? "addHeader(" : "setHeader(") + pName + ", " + pValue + ")");
// If adding, get list and append, otherwise replace list
List<String> values = pAdd ? cachedResponse.headers.get(pName) : null;
if (values == null) {
values = new ArrayList<String>();
if (pAdd) {
// Add length of pName
cachedResponse.headersSize += (pName != null ? pName.length() : 0);
}
else {
// Remove length of potential replaced old values + pName
String[] oldValues = getHeaderValues(pName);
if (oldValues != null) {
for (String oldValue : oldValues) {
cachedResponse.headersSize -= oldValue.length();
}
}
else {
cachedResponse.headersSize += (pName != null ? pName.length() : 0);
}
}
}
// Add value, if not null
if (pValue != null) {
values.add(pValue);
// Add length of pValue
cachedResponse.headersSize += pValue.length();
}
// Always add to headers
cachedResponse.headers.put(pName, values);
}
public OutputStream getOutputStream() {
// TODO: Hmm.. Smells like DCL..?
if (cachedResponse.content == null) {
createOutputStream();
}
return cachedResponse.content;
}
public void setStatus(int pStatusCode) {
cachedResponse.status = pStatusCode;
}
public int getStatus() {
return cachedResponse.getStatus();
}
private synchronized void createOutputStream() {
ByteArrayOutputStream cache = cachedResponse.content;
if (cache == null) {
String contentLengthStr = getHeaderValue("Content-Length");
if (contentLengthStr != null) {
int contentLength = Integer.parseInt(contentLengthStr);
cache = new FastByteArrayOutputStream(contentLength);
}
else {
cache = new FastByteArrayOutputStream(1024);
}
cachedResponse.content = cache;
}
}
public void writeHeadersTo(CacheResponse pResponse) {
cachedResponse.writeHeadersTo(pResponse);
}
public void writeContentsTo(OutputStream pStream) throws IOException {
cachedResponse.writeContentsTo(pStream);
}
public String[] getHeaderNames() {
return cachedResponse.getHeaderNames();
}
public String[] getHeaderValues(String pHeaderName) {
return cachedResponse.getHeaderValues(pHeaderName);
}
public String getHeaderValue(String pHeaderName) {
return cachedResponse.getHeaderValue(pHeaderName);
}
public int size() {
return cachedResponse.size();
}
public boolean equals(Object pOther) {
if (pOther instanceof WritableCachedResponse) {
// Take advantage of faster implementation
return cachedResponse.equals(((WritableCachedResponse) pOther).getCachedResponse());
}
return cachedResponse.equals(pOther);
}
public int hashCode() {
return cachedResponse.hashCode();
}
}
@@ -1,3 +0,0 @@
- Keep filter and servlet specific implementations in servlet module
- Move most of the implementation out of servlet module (HTTPCache + interfaces + abstract impl)
- Move client cache implementation classes out of servlet module, and to separate package
@@ -1,44 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet.fileupload;
/**
* FileSizeExceededException
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: FileSizeExceededException.java#1 $
*/
@Deprecated
public class FileSizeExceededException extends FileUploadException {
public FileSizeExceededException(Throwable pCause) {
super(pCause.getMessage(), pCause);
}
}
@@ -1,54 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet.fileupload;
import javax.servlet.ServletException;
/**
* FileUploadException
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: FileUploadException.java#1 $
*/
@Deprecated
public class FileUploadException extends ServletException {
public FileUploadException(String pMessage) {
super(pMessage);
}
public FileUploadException(String pMessage, Throwable pCause) {
super(pMessage, pCause);
}
public FileUploadException(Throwable pCause) {
super(pCause.getMessage(), pCause);
}
}
@@ -1,129 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet.fileupload;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import com.twelvemonkeys.io.FileUtil;
import com.twelvemonkeys.lang.StringUtil;
import com.twelvemonkeys.servlet.GenericFilter;
import com.twelvemonkeys.servlet.ServletUtil;
/**
* A servlet {@code Filter} for processing HTTP file upload requests, as
* specified by
* <a href="http://www.ietf.org/rfc/rfc1867.txt">Form-based File Upload in HTML (RFC1867)</a>.
*
* @see HttpFileUploadRequest
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: FileUploadFilter.java#1 $
*/
@Deprecated
public class FileUploadFilter extends GenericFilter {
private File uploadDir;
private long maxFileSize = 1024 * 1024; // 1 MByte
/**
* This method is called by the server before the filter goes into service,
* and here it determines the file upload directory.
*
* @throws ServletException
*/
public void init() throws ServletException {
// Get the name of the upload directory.
String uploadDirParam = getInitParameter("uploadDir");
if (!StringUtil.isEmpty(uploadDirParam)) {
try {
URL uploadDirURL = getServletContext().getResource(uploadDirParam);
uploadDir = FileUtil.toFile(uploadDirURL);
}
catch (MalformedURLException e) {
throw new ServletException(e.getMessage(), e);
}
}
if (uploadDir == null) {
uploadDir = ServletUtil.getTempDir(getServletContext());
}
}
/**
* Sets max filesize allowed for upload.
* <!-- used by automagic init -->
*
* @param pMaxSize
*/
public void setMaxFileSize(long pMaxSize) {
log("maxFileSize=" + pMaxSize);
maxFileSize = pMaxSize;
}
/**
* Examines the request content type, and if it is a
* {@code multipart/*} request, wraps the request with a
* {@code HttpFileUploadRequest}.
*
* @param pRequest The servlet request
* @param pResponse The servlet response
* @param pChain The filter chain
*
* @throws ServletException
* @throws IOException
*/
public void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) pRequest;
// Get the content type from the request
String contentType = request.getContentType();
// If the content type is multipart, wrap
if (isMultipartFileUpload(contentType)) {
pRequest = new HttpFileUploadRequestWrapper(request, uploadDir, maxFileSize);
}
pChain.doFilter(pRequest, pResponse);
}
private boolean isMultipartFileUpload(String pContentType) {
return pContentType != null && pContentType.startsWith("multipart/");
}
}
@@ -1,66 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet.fileupload;
import javax.servlet.http.HttpServletRequest;
/**
* This interface represents an HTTP file upload request, as specified by
* <a href="http://www.ietf.org/rfc/rfc1867.txt">Form-based File Upload in HTML (RFC1867)</a>.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: HttpFileUploadRequest.java#1 $
*/
@Deprecated
public interface HttpFileUploadRequest extends HttpServletRequest {
/**
* Returns the value of a request parameter as an {@code UploadedFile},
* or {@code null} if the parameter does not exist.
* You should only use this method when you are sure the parameter has only
* one value.
*
* @param pName the name of the requested parameter
* @return a {@code UoploadedFile} or {@code null}
*
* @see #getUploadedFiles(String)
*/
UploadedFile getUploadedFile(String pName);
/**
* Returns an array of {@code UploadedFile} objects containing all the
* values for the given request parameter,
* or {@code null} if the parameter does not exist.
*
* @param pName the name of the requested parameter
* @return an array of {@code UoploadedFile}s or {@code null}
*/
UploadedFile[] getUploadedFiles(String pName);
}
@@ -1,164 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet.fileupload;
import java.io.File;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUpload;
import org.apache.commons.fileupload.FileUploadBase;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletRequestContext;
/**
* An {@code HttpFileUploadRequest} implementation, based on
* <a href="http://jakarta.apache.org/commons/fileupload/">Jakarta Commons FileUpload</a>.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: HttpFileUploadRequestWrapper.java#1 $
*/
@Deprecated
class HttpFileUploadRequestWrapper extends HttpServletRequestWrapper implements HttpFileUploadRequest {
private final Map<String, String[]> parameters = new HashMap<String, String[]>();
private final Map<String, UploadedFile[]> files = new HashMap<String, UploadedFile[]>();
public HttpFileUploadRequestWrapper(HttpServletRequest pRequest, File pUploadDir, long pMaxSize) throws ServletException {
super(pRequest);
DiskFileItemFactory factory = new DiskFileItemFactory(
128 * 1024, // 128 KByte
new File(pUploadDir.getAbsolutePath())
);
FileUpload upload = new FileUpload(factory);
upload.setSizeMax(pMaxSize);
// TODO: Defer request parsing??
try {
//noinspection unchecked
List<FileItem> items = upload.parseRequest(new ServletRequestContext(pRequest));
for (FileItem item : items) {
if (item.isFormField()) {
processFormField(item.getFieldName(), item.getString());
}
else {
processeFile(item);
}
}
}
catch (FileUploadBase.SizeLimitExceededException e) {
throw new FileSizeExceededException(e);
}
catch (org.apache.commons.fileupload.FileUploadException e) {
throw new FileUploadException(e);
}
}
private void processeFile(final FileItem pItem) {
UploadedFile value = new UploadedFileImpl(pItem);
String name = pItem.getFieldName();
UploadedFile[] values;
UploadedFile[] oldValues = files.get(name);
if (oldValues != null) {
values = new UploadedFile[oldValues.length + 1];
System.arraycopy(oldValues, 0, values, 0, oldValues.length);
values[oldValues.length] = value;
}
else {
values = new UploadedFile[] {value};
}
files.put(name, values);
// Also add to normal fields
processFormField(name, value.getName());
}
private void processFormField(String pName, String pValue) {
// Multiple parameter values are not that common, so it's
// probably faster to just use arrays...
// TODO: Research and document...
String[] values;
String[] oldValues = parameters.get(pName);
if (oldValues != null) {
values = new String[oldValues.length + 1];
System.arraycopy(oldValues, 0, values, 0, oldValues.length);
values[oldValues.length] = pValue;
}
else {
values = new String[] {pValue};
}
parameters.put(pName, values);
}
public Map getParameterMap() {
// TODO: The spec dicates immutable map, but what about the value arrays?!
// Probably just leave as-is, for performance
return Collections.unmodifiableMap(parameters);
}
public Enumeration getParameterNames() {
return Collections.enumeration(parameters.keySet());
}
public String getParameter(String pString) {
String[] values = getParameterValues(pString);
return values != null ? values[0] : null;
}
public String[] getParameterValues(String pString) {
// TODO: Optimize?
return parameters.get(pString).clone();
}
public UploadedFile getUploadedFile(String pName) {
UploadedFile[] files = getUploadedFiles(pName);
return files != null ? files[0] : null;
}
public UploadedFile[] getUploadedFiles(String pName) {
// TODO: Optimize?
return files.get(pName).clone();
}
}
@@ -1,89 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet.fileupload;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
/**
* This class represents an uploaded file.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: UploadedFile.java#1 $
*/
@Deprecated
public interface UploadedFile {
/**
* Returns the length of file, in bytes.
*
* @return length of file
*/
long length();
/**
* Returns the original file name (from client).
*
* @return original name
*/
String getName();
/**
* Returns the content type of the file.
*
* @return the content type
*/
String getContentType();
/**
* Returns the file data, as an {@code InputStream}.
* The file data may be read from disk, or from an in-memory source,
* depending on implementation.
*
* @return an {@code InputStream} containing the file data
* @throws IOException
* @throws RuntimeException
*/
InputStream getInputStream() throws IOException;
/**
* Writes the file data to the given {@code File}.
* Note that implementations are free to optimize this to a rename
* operation, if the file is allready cached to disk.
*
* @param pFile the {@code File} (file name) to write to.
* @throws IOException
* @throws RuntimeException
*/
void writeTo(File pFile) throws IOException;
// TODO: void delete()?
}
@@ -1,94 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet.fileupload;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
/**
* An {@code UploadedFile} implementation, based on
* <a href="http://jakarta.apache.org/commons/fileupload/">Jakarta Commons FileUpload</a>.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: UploadedFileImpl.java#1 $
*/
@Deprecated
class UploadedFileImpl implements UploadedFile {
private final FileItem item;
public UploadedFileImpl(FileItem pItem) {
if (pItem == null) {
throw new IllegalArgumentException("fileitem == null");
}
item = pItem;
}
public String getContentType() {
return item.getContentType();
}
public InputStream getInputStream() throws IOException {
return item.getInputStream();
}
public String getName() {
return item.getName();
}
public long length() {
return item.getSize();
}
public void writeTo(File pFile) throws IOException {
try {
item.write(pFile);
}
catch(RuntimeException e) {
throw e;
}
catch (IOException e) {
throw e;
}
catch (FileUploadException e) {
// We deliberately change this exception to an IOException, as it really is
throw (IOException) new IOException(e.getMessage()).initCause(e);
}
catch (Exception e) {
// Should not really happen, ever
throw new RuntimeException(e.getMessage(), e);
}
}
}
@@ -1,151 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet.gzip;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.twelvemonkeys.servlet.GenericFilter;
/**
* A filter to reduce the output size of web resources.
* <p>
* The HTTP protocol supports compression of the content to reduce network
* bandwidth. The important headers involved, are the {@code Accept-Encoding}
* request header, and the {@code Content-Encoding} response header.
* This feature can be used to further reduce the number of bytes transferred
* over the network, at the cost of some extra processing time at both endpoints.
* Most modern browsers supports compression in GZIP format, which is fairly
* efficient in cost/compression ratio.
* </p>
* <p>
* The filter tests for the presence of an {@code Accept-Encoding} header with a
* value of {@code "gzip"} (several different encoding header values are
* possible in one header). If not present, the filter simply passes the
* request/response pair through, leaving it untouched. If present, the
* {@code Content-Encoding} header is set, with the value {@code "gzip"},
* and the response is wrapped.
* The response output stream is wrapped in a
* {@link java.util.zip.GZIPOutputStream} which performs the GZIP encoding.
* For efficiency, the filter does not buffer the response, but writes through
* the gzipped output stream.
* </p>
* <p>
* <b>Configuration</b>
* <br>
* To use {@code GZIPFilter} in your web-application, you simply need to add it
* to your web descriptor ({@code web.xml}). If using a servlet container that
* supports the Servlet 2.4 spec, the new {@code dispatcher} element should be
* used, and set to {@code REQUEST/FORWARD}, to make sure the filter is invoked
* only once for requests.
* If using an older web descriptor, set the {@code init-param}
* {@code "once-per-request"} to {@code "true"} (this will have the same effect,
* but might perform slightly worse than the 2.4 version).
* Please see the examples below.
* <b>Servlet 2.4 version, filter section:</b>
* </p>
* <br>
* <pre>
* &lt;!-- GZIP Filter Configuration --&gt;
* &lt;filter&gt;
* &lt;filter-name&gt;gzip&lt;/filter-name&gt;
* &lt;filter-class&gt;com.twelvemonkeys.servlet.GZIPFilter&lt;/filter-class&gt;
* &lt;/filter&gt;
* </pre>
* <b>Filter-mapping section:</b>
* <br>
* <pre>
* &lt;!-- GZIP Filter Mapping --&gt;
* &lt;filter-mapping&gt;
* &lt;filter-name&gt;gzip&lt;/filter-name&gt;
* &lt;url-pattern&gt;*.html&lt;/url-pattern&gt;
* &lt;dispatcher&gt;REQUEST&lt;/dispatcher&gt;
* &lt;dispatcher&gt;FORWARD&lt;/dispatcher&gt;
* &lt;/filter-mapping&gt;
* &lt;filter-mapping&gt;
* &lt;filter-name&gt;gzip&lt;/filter-name&gt;
* &lt;url-pattern&gt;*.jsp&lt; /url-pattern&gt;
* &lt;dispatcher&gt;REQUEST&lt;/dispatcher&gt;
* &lt;dispatcher&gt;FORWARD&lt;/dispatcher&gt;
* &lt;/filter-mapping&gt;
* </pre>
* <p>
* Based on ideas and code found in the ONJava article
* <a href="http://www.onjava.com/pub/a/onjava/2003/11/19/filters.html">Two
* Servlet Filters Every Web Application Should Have</a>
* by Jayson Falkner.
* </p>
*
* @author Jayson Falkner
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: GZIPFilter.java#1 $
*/
@Deprecated
public class GZIPFilter extends GenericFilter {
{
oncePerRequest = true;
}
protected void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain) throws IOException, ServletException {
// Can only filter HTTP responses
if (pRequest instanceof HttpServletRequest) {
HttpServletRequest request = (HttpServletRequest) pRequest;
HttpServletResponse response = (HttpServletResponse) pResponse;
// If GZIP is supported, use compression
String accept = request.getHeader("Accept-Encoding");
if (accept != null && accept.contains("gzip")) {
//System.out.println("GZIP supported, compressing.");
GZIPResponseWrapper wrapped = new GZIPResponseWrapper(response);
try {
pChain.doFilter(pRequest, wrapped);
}
finally {
wrapped.flushResponse();
}
return;
}
}
// Else, continue chain
pChain.doFilter(pRequest, pResponse);
}
}
@@ -1,152 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet.gzip;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.zip.GZIPOutputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import com.twelvemonkeys.servlet.OutputStreamAdapter;
/**
* GZIPResponseWrapper class description.
* <p>
* Based on ideas and code found in the ONJava article
* <a href="http://www.onjava.com/pub/a/onjava/2003/11/19/filters.html">Two Servlet Filters Every Web Application Should Have</a>
* by Jayson Falkner.
* </p>
*
* @author Jayson Falkner
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: GZIPResponseWrapper.java#1 $
*/
@Deprecated
public class GZIPResponseWrapper extends HttpServletResponseWrapper {
// TODO: Remove/update ETags if needed? Read the spec (RFC 2616) on Vary/ETag for caching
protected ServletOutputStream out;
protected PrintWriter writer;
protected GZIPOutputStream gzipOut;
protected int contentLength = -1;
public GZIPResponseWrapper(final HttpServletResponse response) {
super(response);
response.addHeader("Content-Encoding", "gzip");
response.addHeader("Vary", "Accept");
}
public ServletOutputStream createOutputStream() throws IOException {
// FIX: Write directly to servlet output stream, for faster responses.
// Relies on chunked streams, or buffering in the servlet engine.
if (contentLength >= 0) {
gzipOut = new GZIPOutputStream(getResponse().getOutputStream(), contentLength);
}
else {
gzipOut = new GZIPOutputStream(getResponse().getOutputStream());
}
// Wrap in ServletOutputStream and return
return new OutputStreamAdapter(gzipOut);
}
// TODO: Move this to flushbuffer or something? Hmmm..
public void flushResponse() throws IOException {
try {
// Finish GZIP encodig
if (gzipOut != null) {
gzipOut.finish();
}
flushBuffer();
}
finally {
// Close stream
if (writer != null) {
writer.close();
}
else {
if (out != null) {
out.close();
}
}
}
}
public void flushBuffer() throws IOException {
if (writer != null) {
writer.flush();
}
else if (out != null) {
out.flush();
}
}
public ServletOutputStream getOutputStream() throws IOException {
if (writer != null) {
throw new IllegalStateException("getWriter() has already been called!");
}
if (out == null) {
out = createOutputStream();
}
return out;
}
public PrintWriter getWriter() throws IOException {
if (writer != null) {
return (writer);
}
if (out != null) {
throw new IllegalStateException("getOutputStream() has already been called!");
}
out = createOutputStream();
// TODO: This is wrong. Should use getCharacterEncoding() or "ISO-8859-1" if getCE returns null.
writer = new PrintWriter(new OutputStreamWriter(out, "UTF-8"));
return writer;
}
public void setContentLength(int pLength) {
// NOTE: Do not call super, as we will shrink the size.
contentLength = pLength;
}
}
@@ -1,76 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet.image;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import javax.servlet.ServletRequest;
import com.twelvemonkeys.image.ImageUtil;
/**
* AWTImageFilterAdapter
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: AWTImageFilterAdapter.java#1 $
*
*/
@Deprecated
public class AWTImageFilterAdapter extends ImageFilter {
private java.awt.image.ImageFilter imageFilter = null;
public void setImageFilter(String pFilterClass) {
try {
Class filterClass = Class.forName(pFilterClass);
imageFilter = (java.awt.image.ImageFilter) filterClass.newInstance();
}
catch (ClassNotFoundException e) {
log("Could not load filter class.", e);
}
catch (InstantiationException e) {
log("Could not instantiate filter.", e);
}
catch (IllegalAccessException e) {
log("Could not access filter class.", e);
}
}
protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) {
// Filter
Image img = ImageUtil.filter(pImage, imageFilter);
// Create BufferedImage & return
return ImageUtil.toBuffered(img, BufferedImage.TYPE_INT_RGB); // TODO: This is ok for JPEG only...
}
}
@@ -1,71 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet.image;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.awt.image.RenderedImage;
import javax.servlet.ServletRequest;
/**
* BufferedImageOpAdapter
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: BufferedImageOpAdapter.java#1 $
*
*/
@Deprecated
public class BufferedImageOpAdapter extends ImageFilter {
private BufferedImageOp filter = null;
public void setImageFilter(String pFilterClass) {
try {
Class filterClass = Class.forName(pFilterClass);
filter = (BufferedImageOp) filterClass.newInstance();
}
catch (ClassNotFoundException e) {
log("Could not instantiate filter class.", e);
}
catch (InstantiationException e) {
log("Could not instantiate filter.", e);
}
catch (IllegalAccessException e) {
log("Could not access filter class.", e);
}
}
protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) {
// Filter & return
return filter.filter(pImage, null);
}
}
@@ -1,216 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet.image;
import java.io.IOException;
import java.util.zip.CRC32;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import com.twelvemonkeys.servlet.GenericServlet;
/**
* Creates a minimal 1 x 1 pixel PNG image, in a color specified by the
* {@code "color"} parameter. The color is HTML-style #RRGGBB, with two
* digits hex number for red, green and blue (the hash, '#', is optional).
* <p>
* The class does only byte manipulation, there is no server-side image
* processing involving AWT ({@code Toolkit} class) of any kind.
* </p>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: ColorServlet.java#2 $
*/
@Deprecated
public class ColorServlet extends GenericServlet {
private final static String RGB_PARAME = "color";
// A minimal, one color indexed PNG
private final static byte[] PNG_IMG = new byte[]{
(byte) 0x89, (byte) 'P', (byte) 'N', (byte) 'G', // PNG signature (8 bytes)
0x0d, 0x0a, 0x1a, 0x0a,
0x00, 0x00, 0x00, 0x0d, // IHDR length (13)
(byte) 'I', (byte) 'H', (byte) 'D', (byte) 'R', // Image header
0x00, 0x00, 0x00, 0x01, // width
0x00, 0x00, 0x00, 0x01, // height
0x01, 0x03, 0x00, 0x00, 0x00, // bits, color type, compression, filter, interlace
0x25, (byte) 0xdb, 0x56, (byte) 0xca, // IHDR CRC
0x00, 0x00, 0x00, 0x03, // PLTE length (3)
(byte) 'P', (byte) 'L', (byte) 'T', (byte) 'E', // Palette
0x00, 0x00, (byte) 0xff, // red, green, blue (updated by this servlet)
(byte) 0x8a, (byte) 0x78, (byte) 0xd2, 0x57, // PLTE CRC
0x00, 0x00, 0x00, 0x0a, // IDAT length (10)
(byte) 'I', (byte) 'D', (byte) 'A', (byte) 'T', // Image data
0x78, (byte) 0xda, 0x63, 0x60, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01,
(byte) 0xe5, 0x27, (byte) 0xde, (byte) 0xfc, // IDAT CRC
0x00, 0x00, 0x00, 0x00, // IEND length (0)
(byte) 'I', (byte) 'E', (byte) 'N', (byte) 'D', // Image end
(byte) 0xae, (byte) 0x42, (byte) 0x60, (byte) 0x82 // IEND CRC
};
private final static int PLTE_CHUNK_START = 37; // after chunk length
private final static int PLTE_CHUNK_LENGTH = 7; // chunk name & data
private final static int RED_IDX = 4;
private final static int GREEN_IDX = RED_IDX + 1;
private final static int BLUE_IDX = GREEN_IDX + 1;
private final CRC32 crc = new CRC32();
/**
* Creates a ColorDroplet.
*/
public ColorServlet() {
super();
}
/**
* Renders the 1 x 1 single color PNG to the response.
*
* @see ColorServlet class description
*
* @param pRequest the request
* @param pResponse the response
*
* @throws IOException
* @throws ServletException
*/
public void service(ServletRequest pRequest, ServletResponse pResponse) throws IOException, ServletException {
int red = 0;
int green = 0;
int blue = 0;
// Get color parameter and parse color
String rgb = pRequest.getParameter(RGB_PARAME);
if (rgb != null && rgb.length() >= 6 && rgb.length() <= 7) {
int index = 0;
// If the hash ('#') character is included, skip it.
if (rgb.length() == 7) {
index++;
}
try {
// Two digit hex for each color
String r = rgb.substring(index, index += 2);
red = Integer.parseInt(r, 0x10);
String g = rgb.substring(index, index += 2);
green = Integer.parseInt(g, 0x10);
String b = rgb.substring(index, index += 2);
blue = Integer.parseInt(b, 0x10);
}
catch (NumberFormatException nfe) {
log("Wrong color format for ColorDroplet: " + rgb + ". Must be RRGGBB.");
}
}
// Set MIME type for PNG
pResponse.setContentType("image/png");
ServletOutputStream out = pResponse.getOutputStream();
try {
// Write header (and palette chunk length)
out.write(PNG_IMG, 0, PLTE_CHUNK_START);
// Create palette chunk, excl lenght, and write
byte[] palette = makePalette(red, green, blue);
out.write(palette);
// Write image data until end
int pos = PLTE_CHUNK_START + PLTE_CHUNK_LENGTH + 4;
out.write(PNG_IMG, pos, PNG_IMG.length - pos);
}
finally {
out.flush();
}
}
/**
* Updates the CRC for a byte array. Note that the byte array must be at
* least {@code pOff + pLen + 4} bytes long, as the CRC is stored in the
* 4 last bytes.
*
* @param pBytes the bytes to create CRC for
* @param pOff the offset into the byte array to create CRC for
* @param pLen the length of the byte array to create CRC for
*/
private void updateCRC(byte[] pBytes, int pOff, int pLen) {
int value;
synchronized (crc) {
crc.reset();
crc.update(pBytes, pOff, pLen);
value = (int) crc.getValue();
}
pBytes[pOff + pLen ] = (byte) ((value >> 24) & 0xff);
pBytes[pOff + pLen + 1] = (byte) ((value >> 16) & 0xff);
pBytes[pOff + pLen + 2] = (byte) ((value >> 8) & 0xff);
pBytes[pOff + pLen + 3] = (byte) ( value & 0xff);
}
/**
* Creates a PNG palette (PLTE) chunk with one color.
* The palette chunk data is always 3 bytes in length (one byte per color
* component).
* The returned byte array is then {@code 4 + 3 + 4 = 11} bytes,
* including chunk header, data and CRC.
*
* @param pRed the red component
* @param pGreen the reen component
* @param pBlue the blue component
*
* @return the bytes for the PLTE chunk, including CRC (but not length)
*/
private byte[] makePalette(int pRed, int pGreen, int pBlue) {
byte[] palette = new byte[PLTE_CHUNK_LENGTH + 4];
System.arraycopy(PNG_IMG, PLTE_CHUNK_START, palette, 0, PLTE_CHUNK_LENGTH);
palette[RED_IDX] = (byte) pRed;
palette[GREEN_IDX] = (byte) pGreen;
palette[BLUE_IDX] = (byte) pBlue;
updateCRC(palette, 0, PLTE_CHUNK_LENGTH);
return palette;
}
}
@@ -1,62 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet.image;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.IOException;
import javax.servlet.ServletRequest;
/**
* ComposeFilter
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: ComposeFilter.java#1 $
*/
@Deprecated
public class ComposeFilter extends ImageFilter {
protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) throws IOException {
// 1. Load different image, locally (using ServletContext.getResource)
// - Allow loading other filtered sources, or servlets? For example to
// apply filename or timestamps?
// - Allow applying text directly? Variables?
// 2. Apply transformations from config
// - Relative positioning
// - Relative scaling
// - Repeat (fill-pattern)?
// - Rotation?
// - Transparency?
// - Background or foreground (layers)?
// 3. Apply loaded image to original image (or vice versa?).
return pImage;
}
}
@@ -1,443 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet.image;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.IndexColorModel;
import java.awt.image.RenderedImage;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.util.HashMap;
import java.util.Map;
import javax.imageio.ImageIO;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.twelvemonkeys.image.ImageUtil;
import com.twelvemonkeys.lang.StringUtil;
/**
* This filter implements server side content negotiation and transcoding for
* images.
**
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: ContentNegotiationFilter.java#1 $
*/
// TODO: Add support for automatic recognition of known browsers, to avoid
// unneccessary conversion (as IE supports PNG, the latests FireFox supports
// JPEG and GIF, etc. even though they both don't explicitly list these formats
// in their Accept headers).
@Deprecated
public class ContentNegotiationFilter extends ImageFilter {
private final static String MIME_TYPE_IMAGE_PREFIX = "image/";
private static final String MIME_TYPE_IMAGE_ANY = MIME_TYPE_IMAGE_PREFIX + "*";
private static final String MIME_TYPE_ANY = "*/*";
private static final String HTTP_HEADER_ACCEPT = "Accept";
private static final String HTTP_HEADER_VARY = "Vary";
protected static final String HTTP_HEADER_USER_AGENT = "User-Agent";
private static final String FORMAT_JPEG = "image/jpeg";
private static final String FORMAT_WBMP = "image/wbmp";
private static final String FORMAT_GIF = "image/gif";
private static final String FORMAT_PNG = "image/png";
private final static String[] sKnownFormats = new String[] {
FORMAT_JPEG, FORMAT_PNG, FORMAT_GIF, FORMAT_WBMP
};
private float[] knownFormatQuality = new float[] {
1f, 1f, 0.99f, 0.5f
};
private HashMap<String, Float> formatQuality; // HashMap, as I need to clone this for each request
private final Object lock = new Object();
/*
private Pattern[] mKnownAgentPatterns;
private String[] mKnownAgentAccpets;
*/
{
// Hack: Make sure the filter don't trigger all the time
// See: super.trigger(ServletRequest)
triggerParams = new String[] {};
}
/*
public void setAcceptMappings(String pPropertiesFile) {
// NOTE: Supposed to be:
// <agent-name>=<reg-exp>
// <agent-name>.accept=<http-accept-header>
Properties mappings = new Properties();
try {
mappings.load(getServletContext().getResourceAsStream(pPropertiesFile));
List patterns = new ArrayList();
List accepts = new ArrayList();
for (Iterator iterator = mappings.keySet().iterator(); iterator.hasNext();) {
String agent = (String) iterator.next();
if (agent.endsWith(".accept")) {
continue;
}
try {
patterns.add(Pattern.compile((String) mappings.get(agent)));
// TODO: Consider preparsing ACCEPT header??
accepts.add(mappings.get(agent + ".accept"));
}
catch (PatternSyntaxException e) {
log("Could not parse User-Agent identification for " + agent, e);
}
mKnownAgentPatterns = (Pattern[]) patterns.toArray(new Pattern[patterns.size()]);
mKnownAgentAccpets = (String[]) accepts.toArray(new String[accepts.size()]);
}
}
catch (IOException e) {
log("Could not read accetp-mappings properties file: " + pPropertiesFile, e);
}
}
*/
protected void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain) throws IOException, ServletException {
// NOTE: super invokes trigger() and image specific doFilter() if needed
super.doFilterImpl(pRequest, pResponse, pChain);
if (pResponse instanceof HttpServletResponse) {
// Update the Vary HTTP header field
((HttpServletResponse) pResponse).addHeader(HTTP_HEADER_VARY, HTTP_HEADER_ACCEPT);
//((HttpServletResponse) pResponse).addHeader(HTTP_HEADER_VARY, HTTP_HEADER_USER_AGENT);
}
}
/**
* Makes sure the filter triggers for unknown file formats.
*
* @param pRequest the request
* @return {@code true} if the filter should execute, {@code false}
* otherwise
*/
protected boolean trigger(ServletRequest pRequest) {
boolean trigger = false;
if (pRequest instanceof HttpServletRequest) {
HttpServletRequest request = (HttpServletRequest) pRequest;
String accept = getAcceptedFormats(request);
String originalFormat = getServletContext().getMimeType(request.getRequestURI());
//System.out.println("Accept: " + accept);
//System.out.println("Original format: " + originalFormat);
// Only override original format if it is not accpeted by the client
// Note: Only explicit matches are okay, */* or image/* is not.
if (!StringUtil.contains(accept, originalFormat)) {
trigger = true;
}
}
// Call super, to allow content negotiation even though format is supported
return trigger || super.trigger(pRequest);
}
private String getAcceptedFormats(HttpServletRequest pRequest) {
return pRequest.getHeader(HTTP_HEADER_ACCEPT);
}
/*
private String getAcceptedFormats(HttpServletRequest pRequest) {
String accept = pRequest.getHeader(HTTP_HEADER_ACCEPT);
// Check if User-Agent is in list of known agents
if (mKnownAgentPatterns != null) {
String agent = pRequest.getHeader(HTTP_HEADER_USER_AGENT);
for (int i = 0; i < mKnownAgentPatterns.length; i++) {
Pattern pattern = mKnownAgentPatterns[i];
if (pattern.matcher(agent).matches()) {
// Merge known with real accpet, in case plugins add extra capabilities
accept = mergeAccept(mKnownAgentAccpets[i], accept);
System.out.println("--> User-Agent: " + agent + " accepts: " + accept);
return accept;
}
}
}
System.out.println("No agent match, defaulting to Accept header: " + accept);
return accept;
}
private String mergeAccept(String pKnown, String pAccept) {
// TODO: Make sure there are no duplicates...
return pKnown + ", " + pAccept;
}
*/
protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) throws IOException {
if (pRequest instanceof HttpServletRequest) {
HttpServletRequest request = (HttpServletRequest) pRequest;
Map<String, Float> formatQuality = getFormatQualityMapping();
// TODO: Consider adding original format, and use as fallback in worst case?
// TODO: Original format should have some boost, to avoid unneccesary convertsion?
// Update source quality settings from image properties
adjustQualityFromImage(formatQuality, pImage);
//System.out.println("Source quality mapping: " + formatQuality);
adjustQualityFromAccept(formatQuality, request);
//System.out.println("Final media scores: " + formatQuality);
// Find the formats with the highest quality factor, and use the first (predictable)
String acceptable = findBestFormat(formatQuality);
//System.out.println("Acceptable: " + acceptable);
// Send HTTP 406 Not Acceptable
if (acceptable == null) {
if (pResponse instanceof HttpServletResponse) {
((HttpServletResponse) pResponse).sendError(HttpURLConnection.HTTP_NOT_ACCEPTABLE);
}
return null;
}
else {
// TODO: Only if the format was changed!
// Let other filters/caches/proxies know we changed the image
}
// Set format
pResponse.setOutputContentType(acceptable);
//System.out.println("Set format: " + acceptable);
}
return pImage;
}
private Map<String, Float> getFormatQualityMapping() {
synchronized(lock) {
if (formatQuality == null) {
formatQuality = new HashMap<String, Float>();
// Use ImageIO to find formats we can actually write
String[] formats = ImageIO.getWriterMIMETypes();
// All known formats qs are initially 1.0
// Others should be 0.1 or something like that...
for (String format : formats) {
formatQuality.put(format, getKnownFormatQuality(format));
}
}
}
//noinspection unchecked
return (Map<String, Float>) formatQuality.clone();
}
/**
* Finds the best available format.
*
* @param pFormatQuality the format to quality mapping
* @return the mime type of the best available format
*/
private static String findBestFormat(Map<String, Float> pFormatQuality) {
String acceptable = null;
float acceptQuality = 0.0f;
for (Map.Entry<String, Float> entry : pFormatQuality.entrySet()) {
float qValue = entry.getValue();
if (qValue > acceptQuality) {
acceptQuality = qValue;
acceptable = entry.getKey();
}
}
//System.out.println("Accepted format: " + acceptable);
//System.out.println("Accepted quality: " + acceptQuality);
return acceptable;
}
/**
* Adjust quality from HTTP Accept header
*
* @param pFormatQuality the format to quality mapping
* @param pRequest the request
*/
private void adjustQualityFromAccept(Map<String, Float> pFormatQuality, HttpServletRequest pRequest) {
// Multiply all q factors with qs factors
// No q=.. should be interpreted as q=1.0
// Apache does some extras; if both explicit types and wildcards
// (without qaulity factor) are present, */* is interpreted as
// */*;q=0.01 and image/* is interpreted as image/*;q=0.02
// See: http://httpd.apache.org/docs-2.0/content-negotiation.html
String accept = getAcceptedFormats(pRequest);
//System.out.println("Accept: " + accept);
float anyImageFactor = getQualityFactor(accept, MIME_TYPE_IMAGE_ANY);
anyImageFactor = (anyImageFactor == 1) ? 0.02f : anyImageFactor;
float anyFactor = getQualityFactor(accept, MIME_TYPE_ANY);
anyFactor = (anyFactor == 1) ? 0.01f : anyFactor;
for (String format : pFormatQuality.keySet()) {
//System.out.println("Trying format: " + format);
String formatMIME = MIME_TYPE_IMAGE_PREFIX + format;
float qFactor = getQualityFactor(accept, formatMIME);
qFactor = (qFactor == 0f) ? Math.max(anyFactor, anyImageFactor) : qFactor;
adjustQuality(pFormatQuality, format, qFactor);
}
}
/**
*
* @param pAccept the accpet header value
* @param pContentType the content type to get the quality factor for
* @return the q factor of the given format, according to the accept header
*/
private static float getQualityFactor(String pAccept, String pContentType) {
float qFactor = 0;
int foundIndex = pAccept.indexOf(pContentType);
if (foundIndex >= 0) {
int startQIndex = foundIndex + pContentType.length();
if (startQIndex < pAccept.length() && pAccept.charAt(startQIndex) == ';') {
while (startQIndex < pAccept.length() && pAccept.charAt(startQIndex++) == ' ') {
// Skip over whitespace
}
if (pAccept.charAt(startQIndex++) == 'q' && pAccept.charAt(startQIndex++) == '=') {
int endQIndex = pAccept.indexOf(',', startQIndex);
if (endQIndex < 0) {
endQIndex = pAccept.length();
}
try {
qFactor = Float.parseFloat(pAccept.substring(startQIndex, endQIndex));
//System.out.println("Found qFactor " + qFactor);
}
catch (NumberFormatException e) {
// TODO: Determine what to do here.. Maybe use a very low value?
// Ahem.. The specs don't say anything about how to interpret a wrong q factor..
//System.out.println("Unparseable q setting; " + e.getMessage());
}
}
// TODO: Determine what to do here.. Maybe use a very low value?
// Unparseable q value, use 0
}
else {
// Else, assume quality is 1.0
qFactor = 1;
}
}
return qFactor;
}
/**
* Adjusts source quality settings from image properties.
*
* @param pFormatQuality the format to quality mapping
* @param pImage the image
*/
private static void adjustQualityFromImage(Map<String, Float> pFormatQuality, BufferedImage pImage) {
// NOTE: The values are all made-up. May need tuning.
// If pImage.getColorModel() instanceof IndexColorModel
// JPEG qs*=0.6
// If NOT binary or 2 color index
// WBMP qs*=0.5
// Else
// GIF qs*=0.02
// PNG qs*=0.9 // JPEG is smaller/faster
if (pImage.getColorModel() instanceof IndexColorModel) {
adjustQuality(pFormatQuality, FORMAT_JPEG, 0.6f);
if (pImage.getType() != BufferedImage.TYPE_BYTE_BINARY || ((IndexColorModel) pImage.getColorModel()).getMapSize() != 2) {
adjustQuality(pFormatQuality, FORMAT_WBMP, 0.5f);
}
}
else {
adjustQuality(pFormatQuality, FORMAT_GIF, 0.01f);
adjustQuality(pFormatQuality, FORMAT_PNG, 0.99f); // JPEG is smaller/faster
}
// If pImage.getColorModel().hasTransparentPixels()
// JPEG qs*=0.05
// WBMP qs*=0.05
// If NOT transparency == BITMASK
// GIF qs*=0.8
if (ImageUtil.hasTransparentPixels(pImage, true)) {
adjustQuality(pFormatQuality, FORMAT_JPEG, 0.009f);
adjustQuality(pFormatQuality, FORMAT_WBMP, 0.009f);
if (pImage.getColorModel().getTransparency() != Transparency.BITMASK) {
adjustQuality(pFormatQuality, FORMAT_GIF, 0.8f);
}
}
}
/**
* Updates the quality in the map.
*
* @param pFormatQuality Map<String,Float>
* @param pFormat the format
* @param pFactor the quality factor
*/
private static void adjustQuality(Map<String, Float> pFormatQuality, String pFormat, float pFactor) {
Float oldValue = pFormatQuality.get(pFormat);
if (oldValue != null) {
pFormatQuality.put(pFormat, oldValue * pFactor);
//System.out.println("New vallue after multiplying with " + pFactor + " is " + pFormatQuality.get(pFormat));
}
}
/**
* Gets the initial quality if this is a known format, otherwise 0.1
*
* @param pFormat the format name
* @return the q factor of the given format
*/
private float getKnownFormatQuality(String pFormat) {
for (int i = 0; i < sKnownFormats.length; i++) {
if (pFormat.equals(sKnownFormats[i])) {
return knownFormatQuality[i];
}
}
return 0.1f;
}
}
@@ -1,237 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet.image;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import javax.servlet.ServletRequest;
import com.twelvemonkeys.servlet.ServletUtil;
/**
* This Servlet is able to render a cropped part of an image.
*
* <HR>
*
* <A name="parameters"></A><STRONG>Parameters:</STRONG><BR>
* <DL>
* <DT>{@code cropX}</DT>
* <DD>integer, the new left edge of the image.
* <DT>{@code cropY}</DT>
* <DD>integer, the new top of the image.
* <DT>{@code cropWidth}</DT>
* <DD>integer, the new width of the image.
* <DT>{@code cropHeight}</DT>
* <DD>integer, the new height of the image.
* <DT>{@code cropUniform}</DT>
* <DD>boolean, wether or not uniform scalnig should be used. Default is
* {@code true}.
* <DT>{@code cropUnits}</DT>
* <DD>string, one of {@code PIXELS}, {@code PERCENT}.
* {@code PIXELS} is default.
*
* <!-- inherited from ScaleImage below: -->
*
* <DT>{@code image}</DT>
* <DD>string, the URL of the image to scale.
*
* <DT>{@code scaleX}</DT>
* <DD>integer, the new width of the image.
*
* <DT>{@code scaleY}</DT>
* <DD>integer, the new height of the image.
*
* <DT>{@code scaleUniform}</DT>
* <DD>boolean, wether or not uniform scalnig should be used. Default is
* {@code true}.
*
* <DT>{@code scaleUnits}</DT>
* <DD>string, one of {@code PIXELS}, {@code PERCENT}.
* {@code PIXELS} is default.
*
* <DT>{@code scaleQuality}</DT>
* <DD>string, one of {@code SCALE_SMOOTH}, {@code SCALE_FAST},
* {@code SCALE_REPLICATE}, {@code SCALE_AREA_AVERAGING}.
* {@code SCALE_DEFAULT} is default.
*
* </DL>
* <p>
* Examples:
* <br>
* &lt;IMG src="/crop/test.jpg?image=http://www.iconmedialab.com/images/random/home_image_12.jpg&amp;cropWidth=500&amp;cropUniform=true"&gt;
* <br>
* &lt;IMG src="/crop/test.png?cache=false&amp;image=http://www.iconmedialab.com/images/random/home_image_12.jpg&amp;cropWidth=50&amp;cropUnits=PERCENT"&gt;
* </p>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: CropFilter.java#1 $
*/
@Deprecated
public class CropFilter extends ScaleFilter {
/** {@code cropX}*/
protected final static String PARAM_CROP_X = "cropX";
/** {@code cropY}*/
protected final static String PARAM_CROP_Y = "cropY";
/** {@code cropWidth}*/
protected final static String PARAM_CROP_WIDTH = "cropWidth";
/** {@code cropHeight}*/
protected final static String PARAM_CROP_HEIGHT = "cropHeight";
/** {@code cropUniform}*/
protected final static String PARAM_CROP_UNIFORM = "cropUniform";
/** {@code cropUnits}*/
protected final static String PARAM_CROP_UNITS = "cropUnits";
/**
* Reads the image from the requested URL, scales it, crops it, and returns
* it in the
* Servlet stream. See above for details on parameters.
*/
protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) {
// Get crop coordinates
int x = ServletUtil.getIntParameter(pRequest, PARAM_CROP_X, -1);
int y = ServletUtil.getIntParameter(pRequest, PARAM_CROP_Y, -1);
int width = ServletUtil.getIntParameter(pRequest, PARAM_CROP_WIDTH, -1);
int height = ServletUtil.getIntParameter(pRequest, PARAM_CROP_HEIGHT, -1);
boolean uniform =
ServletUtil.getBooleanParameter(pRequest, PARAM_CROP_UNIFORM, false);
int units = getUnits(ServletUtil.getParameter(pRequest, PARAM_CROP_UNITS, null));
// Get crop bounds
Rectangle bounds =
getBounds(x, y, width, height, units, uniform, pImage);
// Return cropped version
return pImage.getSubimage((int) bounds.getX(), (int) bounds.getY(),
(int) bounds.getWidth(),
(int) bounds.getHeight());
//return scaled.getSubimage(x, y, width, height);
}
protected Rectangle getBounds(int pX, int pY, int pWidth, int pHeight,
int pUnits, boolean pUniform,
BufferedImage pImg) {
// Algoritm:
// Try to get x and y (default 0,0).
// Try to get width and height (default width-x, height-y)
//
// If percent, get ratio
//
// If uniform
//
int oldWidth = pImg.getWidth();
int oldHeight = pImg.getHeight();
float ratio;
if (pUnits == UNITS_PERCENT) {
if (pWidth >= 0 && pHeight >= 0) {
// Non-uniform
pWidth = (int) ((float) oldWidth * (float) pWidth / 100f);
pHeight = (int) ((float) oldHeight * (float) pHeight / 100f);
}
else if (pWidth >= 0) {
// Find ratio from pWidth
ratio = (float) pWidth / 100f;
pWidth = (int) ((float) oldWidth * ratio);
pHeight = (int) ((float) oldHeight * ratio);
}
else if (pHeight >= 0) {
// Find ratio from pHeight
ratio = (float) pHeight / 100f;
pWidth = (int) ((float) oldWidth * ratio);
pHeight = (int) ((float) oldHeight * ratio);
}
// Else: No crop
}
//else if (UNITS_PIXELS.equalsIgnoreCase(pUnits)) {
else if (pUnits == UNITS_PIXELS) {
// Uniform
if (pUniform) {
if (pWidth >= 0 && pHeight >= 0) {
// Compute both ratios
ratio = (float) pWidth / (float) oldWidth;
float heightRatio = (float) pHeight / (float) oldHeight;
// Find the largest ratio, and use that for both
if (heightRatio < ratio) {
ratio = heightRatio;
pWidth = (int) ((float) oldWidth * ratio);
}
else {
pHeight = (int) ((float) oldHeight * ratio);
}
}
else if (pWidth >= 0) {
// Find ratio from pWidth
ratio = (float) pWidth / (float) oldWidth;
pHeight = (int) ((float) oldHeight * ratio);
}
else if (pHeight >= 0) {
// Find ratio from pHeight
ratio = (float) pHeight / (float) oldHeight;
pWidth = (int) ((float) oldWidth * ratio);
}
// Else: No crop
}
}
// Else: No crop
// Not specified, or outside bounds: Use original dimensions
if (pWidth < 0 || (pX < 0 && pWidth > oldWidth)
|| (pX >= 0 && (pX + pWidth) > oldWidth)) {
pWidth = (pX >= 0 ? oldWidth - pX : oldWidth);
}
if (pHeight < 0 || (pY < 0 && pHeight > oldHeight)
|| (pY >= 0 && (pY + pHeight) > oldHeight)) {
pHeight = (pY >= 0 ? oldHeight - pY : oldHeight);
}
// Center
if (pX < 0) {
pX = (pImg.getWidth() - pWidth) / 2;
}
if (pY < 0) {
pY = (pImg.getHeight() - pHeight) / 2;
}
//System.out.println("x: " + pX + " y: " + pY
// + " w: " + pWidth + " h " + pHeight);
return new Rectangle(pX, pY, pWidth, pHeight);
}
}
@@ -1,225 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet.image;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import com.twelvemonkeys.image.ImageUtil;
import com.twelvemonkeys.lang.StringUtil;
import com.twelvemonkeys.servlet.GenericFilter;
/**
* Abstract base class for image filters. Automatically decoding and encoding of
* the image is handled in the {@code doFilterImpl} method.
*
* @see #doFilter(java.awt.image.BufferedImage,javax.servlet.ServletRequest,ImageServletResponse)
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: ImageFilter.java#2 $
*
*/
@Deprecated
public abstract class ImageFilter extends GenericFilter {
// TODO: Take the design back to the drawing board (see ImageServletResponseImpl)
// - Allow multiple filters to set size attribute
// - Allow a later filter to reset, to get pass-through given certain criteria...
// - Or better yet, allow a filter to decide if it wants to decode, based on image metadata on the original image (ie: width/height)
protected String[] triggerParams = null;
/**
* The {@code doFilterImpl} method is called once, or each time a
* request/response pair is passed through the chain, depending on the
* {@link #oncePerRequest} member variable.
*
* @see #oncePerRequest
* @see com.twelvemonkeys.servlet.GenericFilter#doFilterImpl(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) doFilter
* @see Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) Filter.doFilter
*
* @param pRequest the servlet request
* @param pResponse the servlet response
* @param pChain the filter chain
*
* @throws IOException
* @throws ServletException
*/
protected void doFilterImpl(final ServletRequest pRequest, final ServletResponse pResponse, final FilterChain pChain)
throws IOException, ServletException {
//System.out.println("Starting filtering...");
// Test for trigger params
if (!trigger(pRequest)) {
//System.out.println("Passing request on to next in chain (skipping " + getFilterName() + ")...");
// Pass the request on
pChain.doFilter(pRequest, pResponse);
}
else {
// If already wrapped, the image will be encoded later in the chain
// Or, if this is first filter in chain, we must encode when done
boolean encode = !(pResponse instanceof ImageServletResponse);
// For images, we do post filtering only and need to wrap the response
ImageServletResponse imageResponse = createImageServletResponse(pRequest, pResponse);
//System.out.println("Passing request on to next in chain...");
// Pass the request on
pChain.doFilter(pRequest, imageResponse);
//System.out.println("Post filtering...");
// Get image
//System.out.println("Getting image from ImageServletResponse...");
// Get the image from the wrapped response
RenderedImage image = imageResponse.getImage();
//System.out.println("Got image: " + image);
// Note: Image will be null if this is a HEAD request, the
// If-Modified-Since header is present, or similar.
if (image != null) {
// Do the image filtering
//System.out.println("Filtering image (" + getFilterName() + ")...");
image = doFilter(ImageUtil.toBuffered(image), pRequest, imageResponse);
//System.out.println("Done filtering.");
//System.out.println("Making image available...");
// Make image available to other filters (avoid unnecessary serializing/deserializing)
imageResponse.setImage(image);
//System.out.println("Done.");
}
if (encode) {
//System.out.println("Encoding image...");
// Encode image to original response
if (image != null) {
// TODO: Be smarter than this...
// TODO: Make sure ETag is same, if image content is the same...
// Use ETag of original response (or derived from)
// Use last modified of original response? Or keep original resource's, don't set at all?
// TODO: Why weak ETag?
String etag = "W/\"" + Integer.toHexString(hashCode()) + "-" + Integer.toHexString(image.hashCode()) + "\"";
// TODO: This breaks for wrapped instances, need to either unwrap or test for HttpSR...
((HttpServletResponse) pResponse).setHeader("ETag", etag);
((HttpServletResponse) pResponse).setDateHeader("Last-Modified", (System.currentTimeMillis() / 1000) * 1000);
}
imageResponse.flush();
//System.out.println("Done encoding.");
}
}
//System.out.println("Filtering done.");
}
/**
* Creates the image servlet response for this response.
*
* @param pResponse the original response
* @param pRequest the original request
* @return the new response, or {@code pResponse} if the response is already wrapped
*
* @see com.twelvemonkeys.servlet.image.ImageServletResponseImpl
*/
private ImageServletResponse createImageServletResponse(final ServletRequest pRequest, final ServletResponse pResponse) {
if (pResponse instanceof ImageServletResponseImpl) {
ImageServletResponseImpl response = (ImageServletResponseImpl) pResponse;
// response.setRequest(pRequest);
return response;
}
return new ImageServletResponseImpl(pRequest, pResponse, getServletContext());
}
/**
* Tests if the filter should do image filtering/processing.
* <p>
* This default implementation uses {@link #triggerParams} to test if:
* </p>
* <dl>
* <dt>{@code mTriggerParams == null}</dt>
* <dd>{@code return true}</dd>
* <dt>{@code mTriggerParams != null}, loop through parameters, and test
* if {@code pRequest} contains the parameter. If match</dt>
* <dd>{@code return true}</dd>
* <dt>Otherwise</dt>
* <dd>{@code return false}</dd>
* </dl>
*
* @param pRequest the servlet request
* @return {@code true} if the filter should do image filtering
*/
protected boolean trigger(final ServletRequest pRequest) {
// If triggerParams not set, assume always trigger
if (triggerParams == null) {
return true;
}
// Trigger only for certain request parameters
for (String triggerParam : triggerParams) {
if (pRequest.getParameter(triggerParam) != null) {
return true;
}
}
// Didn't trigger
return false;
}
/**
* Sets the trigger parameters.
* The parameter is supposed to be a comma-separated string of parameter
* names.
*
* @param pTriggerParams a comma-separated string of parameter names.
*/
// TODO: Make it an @InitParam, and make sure we may set String[]/Collection<String> as parameter?
public void setTriggerParams(final String pTriggerParams) {
triggerParams = StringUtil.toStringArray(pTriggerParams);
}
/**
* Filters the image for this request.
*
* @param pImage the image to filter
* @param pRequest the servlet request
* @param pResponse the servlet response
*
* @return the filtered image
* @throws java.io.IOException if an I/O error occurs during filtering
*/
protected abstract RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) throws IOException;
}
@@ -1,58 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet.image;
import javax.servlet.ServletException;
/**
* This exception is a subclass of ServletException, and acts just as a marker
* for exceptions thrown by the ImageServlet API.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
*
* @version $Id: ImageServletException.java#2 $
*/
@Deprecated
public class ImageServletException extends ServletException {
public ImageServletException(String pMessage) {
super(pMessage);
}
public ImageServletException(Throwable pThrowable) {
super(pThrowable);
}
public ImageServletException(String pMessage, Throwable pThrowable) {
super(pMessage, pThrowable);
}
}
@@ -1,211 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet.image;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.IOException;
import javax.servlet.ServletResponse;
/**
* ImageServletResponse.
* <p>
* The request attributes regarding image size and source region (AOI) are used
* in the decoding process, and must be set before the first invocation of
* {@link #getImage()} to have any effect.
* </p>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: ImageServletResponse.java#4 $
*/
@Deprecated
public interface ImageServletResponse extends ServletResponse {
/**
* Request attribute of type {@link java.awt.Dimension} controlling image
* size.
* If either {@code width} or {@code height} is negative, the size is
* computed, using uniform scaling.
* Else, if {@code SIZE_UNIFORM} is {@code true}, the size will be
* computed to the largest possible area (with correct aspect ratio)
* fitting inside the target area.
* Otherwise, the image is scaled to the given size, with no regard to
* aspect ratio.
* <p>
* Defaults to {@code null} (original image size).
* </p>
*/
String ATTRIB_SIZE = "com.twelvemonkeys.servlet.image.ImageServletResponse.SIZE";
/**
* Request attribute of type {@link Boolean} controlling image sizing.
* <p>
* Defaults to {@code Boolean.TRUE}.
* </p>
*/
String ATTRIB_SIZE_UNIFORM = "com.twelvemonkeys.servlet.image.ImageServletResponse.SIZE_UNIFORM";
/**
* Request attribute of type {@link Boolean} controlling image sizing.
* <p>
* Defaults to {@code Boolean.FALSE}.
* </p>
*/
String ATTRIB_SIZE_PERCENT = "com.twelvemonkeys.servlet.image.ImageServletResponse.SIZE_PERCENT";
/**
* Request attribute of type {@link java.awt.Rectangle} controlling image
* source region (area of interest).
* <p>
* Defaults to {@code null} (the entire image).
* </p>
*/
String ATTRIB_AOI = "com.twelvemonkeys.servlet.image.ImageServletResponse.AOI";
/**
* Request attribute of type {@link Boolean} controlling image AOI.
* <p>
* Defaults to {@code Boolean.FALSE}.
* </p>
*/
String ATTRIB_AOI_UNIFORM = "com.twelvemonkeys.servlet.image.ImageServletResponse.AOI_UNIFORM";
/**
* Request attribute of type {@link Boolean} controlling image AOI.
* <p>
* Defaults to {@code Boolean.FALSE}.
* </p>
*/
String ATTRIB_AOI_PERCENT = "com.twelvemonkeys.servlet.image.ImageServletResponse.AOI_PERCENT";
/**
* Request attribute of type {@link java.awt.Color} controlling background
* color for any transparent/translucent areas of the image.
* <p>
* Defaults to {@code null} (keeps the transparent areas transparent).
* </p>
*/
String ATTRIB_BG_COLOR = "com.twelvemonkeys.servlet.image.ImageServletResponse.BG_COLOR";
/**
* Request attribute of type {@link Float} controlling image output compression/quality.
* Used for formats that accepts compression or quality settings,
* like JPEG (quality), PNG (compression only) etc.
* <p>
* Defaults to {@code 0.8f} for JPEG.
* </p>
*/
String ATTRIB_OUTPUT_QUALITY = "com.twelvemonkeys.servlet.image.ImageServletResponse.OUTPUT_QUALITY";
/**
* Request attribute of type {@link Double} controlling image read
* subsampling factor. Controls the maximum sample pixels in each direction,
* that is read per pixel in the output image, if the result will be
* downscaled.
* Larger values will result in better quality, at the expense of higher
* memory consumption and CPU usage.
* However, using values above {@code 3.0} will usually not improve image
* quality.
* Legal values are in the range {@code [1.0 .. positive infinity&gt;}.
* <p>
* Defaults to {@code 2.0}.
* </p>
*/
String ATTRIB_READ_SUBSAMPLING_FACTOR = "com.twelvemonkeys.servlet.image.ImageServletResponse.READ_SUBSAMPLING_FACTOR";
/**
* Request attribute of type {@link Integer} controlling image resample
* algorithm.
* Legal values are {@link java.awt.Image#SCALE_DEFAULT SCALE_DEFAULT},
* {@link java.awt.Image#SCALE_FAST SCALE_FAST} or
* {@link java.awt.Image#SCALE_SMOOTH SCALE_SMOOTH}.
* <p>
* Note: When using a value of {@code SCALE_FAST}, you should also use a
* subsampling factor of {@code 1.0}, for fast read/scale.
* Otherwise, use a subsampling factor of {@code 2.0} for better quality.
* </p>
* <p>
* Defaults to {@code SCALE_DEFAULT}.
* </p>
*/
String ATTRIB_IMAGE_RESAMPLE_ALGORITHM = "com.twelvemonkeys.servlet.image.ImageServletResponse.IMAGE_RESAMPLE_ALGORITHM";
/**
* Gets the image format for this response, such as "image/gif" or "image/jpeg".
* If not set, the default format is that of the original image.
*
* @return the image format for this response.
* @see #setOutputContentType(String)
*/
String getOutputContentType();
/**
* Sets the image format for this response, such as "image/gif" or "image/jpeg".
* <p>
* As an example, a custom filter could do content negotiation based on the
* request header fields and write the image back in an appropriate format.
* </p>
* <p>
* If not set, the default format is that of the original image.
* </p>
*
* @param pImageFormat the image format for this response.
*/
void setOutputContentType(String pImageFormat);
//TODO: ?? void setCompressionQuality(float pQualityFactor);
//TODO: ?? float getCompressionQuality();
/**
* Writes the image to the original {@code ServletOutputStream}.
* If no format is {@linkplain #setOutputContentType(String) set} in this response,
* the image is encoded in the same format as the original image.
*
* @throws java.io.IOException if an I/O exception occurs during writing
*/
void flush() throws IOException;
/**
* Gets the decoded image from the response.
*
* @return a {@code BufferedImage} or {@code null} if the image could not be read.
*
* @throws java.io.IOException if an I/O exception occurs during reading
*/
BufferedImage getImage() throws IOException;
/**
* Sets the image for this response.
*
* @param pImage the new response image.
*/
void setImage(RenderedImage pImage);
}
@@ -1,815 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet.image;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.IndexColorModel;
import java.awt.image.RenderedImage;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.Iterator;
import javax.imageio.IIOException;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
import javax.servlet.ServletContext;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import com.twelvemonkeys.image.ImageUtil;
import com.twelvemonkeys.io.FastByteArrayOutputStream;
import com.twelvemonkeys.lang.StringUtil;
import com.twelvemonkeys.servlet.ServletResponseStreamDelegate;
import com.twelvemonkeys.servlet.ServletUtil;
import com.twelvemonkeys.servlet.image.aoi.AreaOfInterest;
import com.twelvemonkeys.servlet.image.aoi.AreaOfInterestFactory;
/**
* This {@link ImageServletResponse} implementation can be used with image
* requests, to have the image immediately decoded to a {@code BufferedImage}.
* The image may be optionally subsampled, scaled and/or cropped.
* 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
@Deprecated
class ImageServletResponseImpl extends HttpServletResponseWrapper implements ImageServletResponse {
private ServletRequest originalRequest;
private final ServletContext context;
private final ServletResponseStreamDelegate streamDelegate;
private FastByteArrayOutputStream bufferedOut;
private RenderedImage image;
private String outputContentType;
private String originalContentType;
private int originalContentLength = -1;
/**
* Creates an {@code ImageServletResponseImpl}.
*
* @param pRequest the request
* @param pResponse the response
* @param pContext the servlet context
*/
public ImageServletResponseImpl(final HttpServletRequest pRequest, final HttpServletResponse pResponse, final ServletContext pContext) {
super(pResponse);
originalRequest = pRequest;
streamDelegate = new ServletResponseStreamDelegate(pResponse) {
@Override
protected OutputStream createOutputStream() throws IOException {
if (originalContentLength >= 0) {
bufferedOut = new FastByteArrayOutputStream(originalContentLength);
}
else {
bufferedOut = new FastByteArrayOutputStream(0);
}
return bufferedOut;
}
};
context = pContext;
}
/**
* Creates an {@code ImageServletResponseImpl}.
*
* @param pRequest the request
* @param pResponse the response
* @param pContext the servlet context
*
* @throws ClassCastException if {@code pRequest} is not an {@link javax.servlet.http.HttpServletRequest} or
* {@code pResponse} is not an {@link javax.servlet.http.HttpServletResponse}.
*/
public ImageServletResponseImpl(final ServletRequest pRequest, final ServletResponse pResponse, final ServletContext pContext) {
// Cheat for now...
this((HttpServletRequest) pRequest, (HttpServletResponse) pResponse, pContext);
}
/**
* Called by the container, do not invoke.
*
* @param pMimeType the content (MIME) type
*/
public void setContentType(final String pMimeType) {
// Throw exception is already set
if (originalContentType != null) {
throw new IllegalStateException("ContentType already set.");
}
originalContentType = pMimeType;
}
/**
* Called by the container. Do not invoke.
*
* @return the response's {@code OutputStream}
* @throws IOException
*/
public ServletOutputStream getOutputStream() throws IOException {
return streamDelegate.getOutputStream();
}
/**
* Called by the container. Do not invoke.
*
* @return the response's {@code PrintWriter}
* @throws IOException
*/
public PrintWriter getWriter() throws IOException {
return streamDelegate.getWriter();
}
/**
* Called by the container. Do not invoke.
*
* @param pLength the content length
*/
public void setContentLength(final int pLength) {
if (originalContentLength != -1) {
throw new IllegalStateException("ContentLength already set.");
}
originalContentLength = pLength;
}
@Override
public void setHeader(String name, String value) {
// NOTE: Clients could also specify content type/content length using the setHeader method, special handling
if (name != null && name.equals("Content-Length")) {
setContentLength(Integer.valueOf(value)); // Value might be too large, but we don't support that anyway
}
else if (name != null && name.equals("Content-Type")) {
setContentType(value);
}
else {
super.setHeader(name, value);
}
}
/**
* Writes the image to the original {@code ServletOutputStream}.
* If no format is set in this response, the image is encoded in the same
* format as the original image.
*
* @throws IOException if an I/O exception occurs during writing
*/
public void flush() throws IOException {
String outputType = getOutputContentType();
// Force transcoding, if no other filtering is done
if (outputType != null && !outputType.equals(originalContentType)) {
getImage();
}
if (image != null) {
Iterator writers = ImageIO.getImageWritersByMIMEType(outputType);
if (writers.hasNext()) {
super.setContentType(outputType);
OutputStream out = super.getOutputStream();
try {
ImageWriter writer = (ImageWriter) writers.next();
try {
ImageWriteParam param = writer.getDefaultWriteParam();
///////////////////
// POST-PROCESS
// For known formats that don't support transparency, convert to opaque
if (isNonAlphaFormat(outputType) && image.getColorModel().getTransparency() != Transparency.OPAQUE) {
image = ImageUtil.toBuffered(image, BufferedImage.TYPE_INT_RGB);
}
Float requestQuality = (Float) originalRequest.getAttribute(ImageServletResponse.ATTRIB_OUTPUT_QUALITY);
// 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
if (param.getCompressionTypes() != null && param.getCompressionType() == null) {
param.setCompressionType(param.getCompressionTypes()[0]); // Just choose any, to keep param happy
}
param.setCompressionQuality(requestQuality != null ? requestQuality : 0.8f);
}
if ("gif".equalsIgnoreCase(getFormatNameSafe(writer)) && !(image.getColorModel() instanceof IndexColorModel)
/*&& 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,
(image.getColorModel().getTransparency() == Transparency.OPAQUE ? ImageUtil.TRANSPARENCY_OPAQUE : ImageUtil.TRANSPARENCY_BITMASK) | ImageUtil.DITHER_DIFFUSION_ALTSCANS
);
}
//////////////////
ImageOutputStream stream = ImageIO.createImageOutputStream(out);
writer.setOutput(stream);
try {
writer.write(null, new IIOImage(image, null, null), param);
}
finally {
stream.close();
}
}
finally {
writer.dispose();
}
}
finally {
out.flush();
}
}
else {
context.log("ERROR: No writer for content-type: " + outputType);
throw new IIOException("Unable to transcode image: No suitable image writer found (content-type: " + outputType + ").");
}
}
else {
super.setContentType(originalContentType);
ServletOutputStream out = super.getOutputStream();
try {
if (bufferedOut != null) {
bufferedOut.writeTo(out);
}
}
finally {
out.flush();
}
}
}
private boolean isNonAlphaFormat(String outputType) {
return "image/jpeg".equals(outputType) || "image/jpg".equals(outputType) ||
"image/bmp".equals(outputType) || "image/x-bmp".equals(outputType);
}
private String getFormatNameSafe(final ImageWriter pWriter) {
try {
return pWriter.getOriginatingProvider().getFormatNames()[0];
}
catch (RuntimeException e) {
// NPE, AIOOBE, etc..
return null;
}
}
public String getOutputContentType() {
return outputContentType != null ? outputContentType : originalContentType;
}
public void setOutputContentType(final String pImageFormat) {
outputContentType = pImageFormat;
}
/**
* Sets the image for this response.
*
* @param pImage the {@code RenderedImage} that will be written to the
* response stream
*/
public void setImage(final RenderedImage pImage) {
image = pImage;
}
/**
* Gets the decoded image from the response.
*
* @return a {@code BufferedImage} or {@code null} if the image could
* not be read.
*
* @throws java.io.IOException if an I/O exception occurs during reading
*/
public BufferedImage getImage() throws IOException {
if (image == null) {
// No content, no image
if (bufferedOut == null) {
return null;
}
// Read from the byte buffer
InputStream byteStream = bufferedOut.createInputStream();
ImageInputStream input = null;
try {
input = ImageIO.createImageInputStream(byteStream);
Iterator readers = ImageIO.getImageReaders(input);
if (readers.hasNext()) {
// Get the correct reader
ImageReader reader = (ImageReader) readers.next();
try {
reader.setInput(input);
ImageReadParam param = reader.getDefaultReadParam();
// Get default size
int originalWidth = reader.getWidth(0);
int originalHeight = reader.getHeight(0);
//////////////////
// PRE-PROCESS (prepare): param, size, format?, request, response?
// TODO: AOI strategy?
// Extract AOI from request
Rectangle aoi = extractAOIFromRequest(originalWidth, originalHeight, originalRequest);
if (aoi != null) {
param.setSourceRegion(aoi);
originalWidth = aoi.width;
originalHeight = aoi.height;
}
// TODO: Size and subsampling strategy?
// If possible, extract size from request
Dimension size = extractSizeFromRequest(originalWidth, originalHeight, originalRequest);
double readSubSamplingFactor = getReadSubsampleFactorFromRequest(originalRequest);
if (size != null) {
//System.out.println("Size: " + size);
if (param.canSetSourceRenderSize()) {
param.setSourceRenderSize(size);
}
else {
int subX = (int) Math.max(originalWidth / (size.width * readSubSamplingFactor), 1.0);
int subY = (int) Math.max(originalHeight / (size.height * readSubSamplingFactor), 1.0);
if (subX > 1 || subY > 1) {
param.setSourceSubsampling(subX, subY, subX > 1 ? subX / 2 : 0, subY > 1 ? subY / 2 : 0);
}
}
}
// Need base URI for SVG with links/stylesheets etc
maybeSetBaseURIFromRequest(param);
/////////////////////
// Finally, read the image using the supplied parameter
BufferedImage image = reader.read(0, param);
// TODO: If we sub-sampled, it would be a good idea to blur before resampling,
// to avoid jagged lines artifacts
// If reader doesn't support dynamic sizing, scale now
image = resampleImage(image, size);
// Fill bgcolor behind image, if transparent
extractAndSetBackgroundColor(image); // TODO: Move to flush/POST-PROCESS
// Set image
this.image = image;
}
finally {
reader.dispose();
}
}
else {
context.log("ERROR: No suitable image reader found (content-type: " + originalContentType + ").");
context.log("ERROR: Available formats: " + getFormatsString());
throw new IIOException("Unable to transcode image: No suitable image reader found (content-type: " + originalContentType + ").");
}
// Free resources, as the image is now either read, or unreadable
bufferedOut = null;
}
finally {
if (input != null) {
input.close();
}
}
}
// Image is usually a BufferedImage, but may also be a RenderedImage
return image != null ? ImageUtil.toBuffered(image) : null;
}
private BufferedImage resampleImage(final BufferedImage image, final Dimension size) {
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);
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;
}
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;
}
else {
if (algorithm != null) {
context.log("WARN: Illegal image resampling algorithm: " + algorithm);
}
return BufferedImage.SCALE_DEFAULT;
}
}
private double getReadSubsampleFactorFromRequest(final ServletRequest pOriginalRequest) {
double subsampleFactor;
Object factor = pOriginalRequest.getAttribute(ATTRIB_READ_SUBSAMPLING_FACTOR);
if (factor instanceof Number && ((Number) factor).doubleValue() >= 1.0) {
subsampleFactor = ((Number) factor).doubleValue();
}
else {
if (factor != null) {
context.log("WARN: Illegal read subsampling factor: " + factor);
}
subsampleFactor = 2.0;
}
return subsampleFactor;
}
private void extractAndSetBackgroundColor(final BufferedImage pImage) {
// TODO: bgColor request attribute instead of parameter?
if (pImage.getColorModel().hasAlpha()) {
String bgColor = originalRequest.getParameter("bg.color");
if (bgColor != null) {
Color color = StringUtil.toColor(bgColor);
Graphics2D g = pImage.createGraphics();
try {
g.setColor(color);
g.setComposite(AlphaComposite.DstOver);
g.fillRect(0, 0, pImage.getWidth(), pImage.getHeight());
}
finally {
g.dispose();
}
}
}
}
private static String getFormatsString() {
String[] formats = ImageIO.getReaderFormatNames();
StringBuilder buf = new StringBuilder();
for (int i = 0; i < formats.length; i++) {
String format = formats[i];
if (i > 0) {
buf.append(", ");
}
buf.append(format);
}
return buf.toString();
}
private void maybeSetBaseURIFromRequest(final ImageReadParam pParam) {
if (originalRequest instanceof HttpServletRequest) {
try {
// If there's a setBaseURI method, we'll try to use that (uses reflection, to avoid dependency on plugins)
Method setBaseURI;
try {
setBaseURI = pParam.getClass().getMethod("setBaseURI", String.class);
}
catch (NoSuchMethodException ignore) {
return;
}
// Get URL for resource and set as base
String baseURI = ServletUtil.getContextRelativeURI((HttpServletRequest) originalRequest);
URL resourceURL = context.getResource(baseURI);
if (resourceURL == null) {
resourceURL = ServletUtil.getRealURL(context, baseURI);
}
if (resourceURL != null) {
setBaseURI.invoke(pParam, resourceURL.toExternalForm());
}
else {
context.log("WARN: Resource URL not found for URI: " + baseURI);
}
}
catch (Exception e) {
context.log("WARN: Could not set base URI: ", e);
}
}
}
private Dimension extractSizeFromRequest(final int pDefaultWidth, final int pDefaultHeight, final ServletRequest pOriginalRequest) {
// TODO: Allow extraction from request parameters
/*
int sizeW = ServletUtil.getIntParameter(originalRequest, "size.w", -1);
int sizeH = ServletUtil.getIntParameter(originalRequest, "size.h", -1);
boolean sizePercent = ServletUtil.getBooleanParameter(originalRequest, "size.percent", false);
boolean sizeUniform = ServletUtil.getBooleanParameter(originalRequest, "size.uniform", true);
*/
Dimension size = (Dimension) pOriginalRequest.getAttribute(ATTRIB_SIZE);
int sizeW = size != null ? size.width : -1;
int sizeH = size != null ? size.height : -1;
Boolean b = (Boolean) pOriginalRequest.getAttribute(ATTRIB_SIZE_PERCENT);
boolean sizePercent = b != null && b; // default: false
b = (Boolean) pOriginalRequest.getAttribute(ATTRIB_SIZE_UNIFORM);
boolean sizeUniform = b == null || b; // default: true
if (sizeW >= 0 || sizeH >= 0) {
size = getSize(pDefaultWidth, pDefaultHeight, sizeW, sizeH, sizePercent, sizeUniform);
}
return size;
}
private Rectangle extractAOIFromRequest(final int pDefaultWidth, final int pDefaultHeight, final ServletRequest pOriginalRequest) {
// TODO: Allow extraction from request parameters
/*
int aoiX = ServletUtil.getIntParameter(originalRequest, "aoi.x", -1);
int aoiY = ServletUtil.getIntParameter(originalRequest, "aoi.y", -1);
int aoiW = ServletUtil.getIntParameter(originalRequest, "aoi.w", -1);
int aoiH = ServletUtil.getIntParameter(originalRequest, "aoi.h", -1);
boolean aoiPercent = ServletUtil.getBooleanParameter(originalRequest, "aoi.percent", false);
boolean aoiUniform = ServletUtil.getBooleanParameter(originalRequest, "aoi.uniform", false);
*/
Rectangle aoi = (Rectangle) pOriginalRequest.getAttribute(ATTRIB_AOI);
int aoiX = aoi != null ? aoi.x : -1;
int aoiY = aoi != null ? aoi.y : -1;
int aoiW = aoi != null ? aoi.width : -1;
int aoiH = aoi != null ? aoi.height : -1;
Boolean b = (Boolean) pOriginalRequest.getAttribute(ATTRIB_AOI_PERCENT);
boolean aoiPercent = b != null && b; // default: false
b = (Boolean) pOriginalRequest.getAttribute(ATTRIB_AOI_UNIFORM);
boolean aoiUniform = b != null && b; // default: false
if (aoiX >= 0 || aoiY >= 0 || aoiW >= 0 || aoiH >= 0) {
AreaOfInterest areaOfInterest = AreaOfInterestFactory.getDefault().
createAreaOfInterest(pDefaultWidth, pDefaultHeight, aoiPercent, aoiUniform);
aoi = areaOfInterest.getAOI(new Rectangle(aoiX, aoiY, aoiW, aoiH));
return aoi;
}
return null;
}
// TODO: Move these to ImageUtil or similar, as they are often used...
// TODO: Consider separate methods for percent and pixels
/**
* Gets the dimensions (height and width) of the scaled image. The
* dimensions are computed based on the old image's dimensions, the units
* used for specifying new dimensions and whether or not uniform scaling
* should be used (se algorithm below).
*
* @param pOriginalWidth the original width of the image
* @param pOriginalHeight the original height of the image
* @param pWidth the new width of the image, or -1 if unknown
* @param pHeight the new height of the image, or -1 if unknown
* @param pPercent the constant specifying units for width and height
* parameter (UNITS_PIXELS or UNITS_PERCENT)
* @param pUniform boolean specifying uniform scale or not
* @return a Dimension object, with the correct width and heigth
* in pixels, for the scaled version of the image.
*/
static Dimension getSize(int pOriginalWidth, int pOriginalHeight,
int pWidth, int pHeight,
boolean pPercent, boolean pUniform) {
// If uniform, make sure width and height are scaled the same amount
// (use ONLY height or ONLY width).
//
// Algorithm:
// if uniform
// if newHeight not set
// find ratio newWidth / oldWidth
// oldHeight *= ratio
// else if newWidth not set
// find ratio newWidth / oldWidth
// oldHeight *= ratio
// else
// find both ratios and use the smallest one
// (this will be the largest version of the image that fits
// inside the rectangle given)
// (if PERCENT, just use smallest percentage).
//
// If units is percent, we only need old height and width
float ratio;
if (pPercent) {
if (pWidth >= 0 && pHeight >= 0) {
// Non-uniform
pWidth = Math.round((float) pOriginalWidth * (float) pWidth / 100f);
pHeight = Math.round((float) pOriginalHeight * (float) pHeight / 100f);
}
else if (pWidth >= 0) {
// Find ratio from pWidth
ratio = (float) pWidth / 100f;
pWidth = Math.round((float) pOriginalWidth * ratio);
pHeight = Math.round((float) pOriginalHeight * ratio);
}
else if (pHeight >= 0) {
// Find ratio from pHeight
ratio = (float) pHeight / 100f;
pWidth = Math.round((float) pOriginalWidth * ratio);
pHeight = Math.round((float) pOriginalHeight * ratio);
}
// Else: No scale
}
else {
if (pUniform) {
if (pWidth >= 0 && pHeight >= 0) {
// Compute both ratios
ratio = (float) pWidth / (float) pOriginalWidth;
float heightRatio = (float) pHeight / (float) pOriginalHeight;
// Find the largest ratio, and use that for both
if (heightRatio < ratio) {
ratio = heightRatio;
pWidth = Math.round((float) pOriginalWidth * ratio);
}
else {
pHeight = Math.round((float) pOriginalHeight * ratio);
}
}
else if (pWidth >= 0) {
// Find ratio from pWidth
ratio = (float) pWidth / (float) pOriginalWidth;
pHeight = Math.round((float) pOriginalHeight * ratio);
}
else if (pHeight >= 0) {
// Find ratio from pHeight
ratio = (float) pHeight / (float) pOriginalHeight;
pWidth = Math.round((float) pOriginalWidth * ratio);
}
// Else: No scale
}
}
// Default is no scale, just work as a proxy
if (pWidth < 0) {
pWidth = pOriginalWidth;
}
if (pHeight < 0) {
pHeight = pOriginalHeight;
}
// Create new Dimension object and return
return new Dimension(pWidth, pHeight);
}
static Rectangle getAOI(int pOriginalWidth, int pOriginalHeight,
int pX, int pY, int pWidth, int pHeight,
boolean pPercent, boolean pMaximizeToAspect) {
// Algorithm:
// Try to get x and y (default 0,0).
// Try to get width and height (default width-x, height-y)
//
// If percent, get ratio
//
// If uniform
//
float ratio;
if (pPercent) {
if (pWidth >= 0 && pHeight >= 0) {
// Non-uniform
pWidth = Math.round((float) pOriginalWidth * (float) pWidth / 100f);
pHeight = Math.round((float) pOriginalHeight * (float) pHeight / 100f);
}
else if (pWidth >= 0) {
// Find ratio from pWidth
ratio = (float) pWidth / 100f;
pWidth = Math.round((float) pOriginalWidth * ratio);
pHeight = Math.round((float) pOriginalHeight * ratio);
}
else if (pHeight >= 0) {
// Find ratio from pHeight
ratio = (float) pHeight / 100f;
pWidth = Math.round((float) pOriginalWidth * ratio);
pHeight = Math.round((float) pOriginalHeight * ratio);
}
// Else: No crop
}
else {
// Uniform
if (pMaximizeToAspect) {
if (pWidth >= 0 && pHeight >= 0) {
// Compute both ratios
ratio = (float) pWidth / (float) pHeight;
float originalRatio = (float) pOriginalWidth / (float) pOriginalHeight;
if (ratio > originalRatio) {
pWidth = pOriginalWidth;
pHeight = Math.round((float) pOriginalWidth / ratio);
}
else {
pHeight = pOriginalHeight;
pWidth = Math.round((float) pOriginalHeight * ratio);
}
}
else if (pWidth >= 0) {
// Find ratio from pWidth
ratio = (float) pWidth / (float) pOriginalWidth;
pHeight = Math.round((float) pOriginalHeight * ratio);
}
else if (pHeight >= 0) {
// Find ratio from pHeight
ratio = (float) pHeight / (float) pOriginalHeight;
pWidth = Math.round((float) pOriginalWidth * ratio);
}
// Else: No crop
}
}
// Not specified, or outside bounds: Use original dimensions
if (pWidth < 0 || (pX < 0 && pWidth > pOriginalWidth)
|| (pX >= 0 && (pX + pWidth) > pOriginalWidth)) {
pWidth = (pX >= 0 ? pOriginalWidth - pX : pOriginalWidth);
}
if (pHeight < 0 || (pY < 0 && pHeight > pOriginalHeight)
|| (pY >= 0 && (pY + pHeight) > pOriginalHeight)) {
pHeight = (pY >= 0 ? pOriginalHeight - pY : pOriginalHeight);
}
// Center
if (pX < 0) {
pX = (pOriginalWidth - pWidth) / 2;
}
if (pY < 0) {
pY = (pOriginalHeight - pHeight) / 2;
}
// System.out.println("x: " + pX + " y: " + pY
// + " w: " + pWidth + " h " + pHeight);
return new Rectangle(pX, pY, pWidth, pHeight);
}
}
@@ -1,50 +0,0 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.servlet.image;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import javax.servlet.ServletRequest;
/**
* An {@code ImageFilter} that does nothing. Useful for debugging purposes.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: NullImageFilter.java $
*
*/
@Deprecated
public final class NullImageFilter extends ImageFilter {
protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) {
return pImage;
}
}

Some files were not shown because too many files have changed in this diff Show More