Re-added test case lost in large merge

- Removed imageio-jmagick (again)

- Removed internal Maven repo

- fixed line endings on a number of files to avoid a humongous merge
  diff when getting changes into upstream

- Re-enabled a few tests
This commit is contained in:
Torstein Krause Johansen
2016-06-28 14:24:25 +02:00
parent 1e3c8b26f6
commit 0db676f1be
89 changed files with 15925 additions and 15631 deletions

View File

@@ -1,38 +0,0 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.0-ece-1</version>
</parent>
<artifactId>imageio-jmagick</artifactId>
<name>TwelveMonkeys :: ImageIO :: JMagick Plugin</name>
<description>
<![CDATA[
ImageIO wrapper for JMagick.
See the <a href="http://www.yeo.id.au/jmagick/">JMagick Home page</a>
for more information.]]>
</description>
<dependencies>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-core</artifactId>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-core</artifactId>
<classifier>tests</classifier>
</dependency>
<dependency>
<groupId>jmagick</groupId>
<artifactId>jmagick</artifactId>
<version>6.2.4</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@@ -951,7 +951,6 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
} }
@Test @Test
@Ignore
public void testReadSubsamplingNotSkippingLines1028() throws IOException { public void testReadSubsamplingNotSkippingLines1028() throws IOException {
JPEGImageReader reader = createReader(); JPEGImageReader reader = createReader();
reader.setInput(ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/read-error1028.jpg"))); reader.setInput(ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/read-error1028.jpg")));
@@ -1016,7 +1015,6 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
} }
@Test @Test
@Ignore
public void testReadSubsamplingNotSkippingLines1025() throws IOException { public void testReadSubsamplingNotSkippingLines1025() throws IOException {
JPEGImageReader reader = createReader(); JPEGImageReader reader = createReader();
reader.setInput(ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/read-error1025.jpg"))); reader.setInput(ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/read-error1025.jpg")));

View File

@@ -234,11 +234,4 @@
</plugins> </plugins>
</reporting> </reporting>
<distributionManagement>
<snapshotRepository>
<id>central</id>
<url>http://repo.dev.escenic.com/content/repositories/thirdparty-snapshots</url>
<uniqueVersion>false</uniqueVersion>
</snapshotRepository>
</distributionManagement>
</project> </project>

View File

@@ -1,90 +1,90 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.io.enc; package com.twelvemonkeys.io.enc;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.zip.Deflater; import java.util.zip.Deflater;
/** /**
* {@code Encoder} implementation for standard DEFLATE encoding. * {@code Encoder} implementation for standard DEFLATE encoding.
* <p/> * <p/>
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/DeflateEncoder.java#2 $ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/DeflateEncoder.java#2 $
* *
* @see <a href="http://tools.ietf.org/html/rfc1951">RFC 1951</a> * @see <a href="http://tools.ietf.org/html/rfc1951">RFC 1951</a>
* @see Deflater * @see Deflater
* @see InflateDecoder * @see InflateDecoder
* @see java.util.zip.DeflaterOutputStream * @see java.util.zip.DeflaterOutputStream
*/ */
final class DeflateEncoder implements Encoder { final class DeflateEncoder implements Encoder {
private final Deflater deflater; private final Deflater deflater;
private final byte[] buffer = new byte[1024]; private final byte[] buffer = new byte[1024];
public DeflateEncoder() { public DeflateEncoder() {
this(new Deflater(Deflater.DEFAULT_COMPRESSION, true)); // TODO: Should we use "no wrap"? this(new Deflater(Deflater.DEFAULT_COMPRESSION, true)); // TODO: Should we use "no wrap"?
} }
public DeflateEncoder(final Deflater pDeflater) { public DeflateEncoder(final Deflater pDeflater) {
if (pDeflater == null) { if (pDeflater == null) {
throw new IllegalArgumentException("deflater == null"); throw new IllegalArgumentException("deflater == null");
} }
deflater = pDeflater; deflater = pDeflater;
} }
public void encode(final OutputStream stream, ByteBuffer buffer) public void encode(final OutputStream stream, ByteBuffer buffer)
throws IOException throws IOException
{ {
System.out.println("DeflateEncoder.encode"); System.out.println("DeflateEncoder.encode");
deflater.setInput(buffer.array(), buffer.arrayOffset() + buffer.position(), buffer.remaining()); deflater.setInput(buffer.array(), buffer.arrayOffset() + buffer.position(), buffer.remaining());
flushInputToStream(stream); flushInputToStream(stream);
} }
private void flushInputToStream(final OutputStream pStream) throws IOException { private void flushInputToStream(final OutputStream pStream) throws IOException {
System.out.println("DeflateEncoder.flushInputToStream"); System.out.println("DeflateEncoder.flushInputToStream");
if (deflater.needsInput()) { if (deflater.needsInput()) {
System.out.println("Foo"); System.out.println("Foo");
} }
while (!deflater.needsInput()) { while (!deflater.needsInput()) {
int deflated = deflater.deflate(buffer, 0, buffer.length); int deflated = deflater.deflate(buffer, 0, buffer.length);
pStream.write(buffer, 0, deflated); pStream.write(buffer, 0, deflated);
System.out.println("flushed " + deflated); System.out.println("flushed " + deflated);
} }
} }
// public void flush() { // public void flush() {
// deflater.finish(); // deflater.finish();
// } // }
} }

View File

@@ -1,110 +1,110 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.io.enc; package com.twelvemonkeys.io.enc;
import java.io.EOFException; import java.io.EOFException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.zip.DataFormatException; import java.util.zip.DataFormatException;
import java.util.zip.Inflater; import java.util.zip.Inflater;
/** /**
* {@code Decoder} implementation for standard DEFLATE encoding. * {@code Decoder} implementation for standard DEFLATE encoding.
* <p/> * <p/>
* *
* @see <a href="http://tools.ietf.org/html/rfc1951">RFC 1951</a> * @see <a href="http://tools.ietf.org/html/rfc1951">RFC 1951</a>
* *
* @see Inflater * @see Inflater
* @see DeflateEncoder * @see DeflateEncoder
* @see java.util.zip.InflaterInputStream * @see java.util.zip.InflaterInputStream
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/InflateDecoder.java#2 $ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/InflateDecoder.java#2 $
*/ */
final class InflateDecoder implements Decoder { final class InflateDecoder implements Decoder {
private final Inflater inflater; private final Inflater inflater;
private final byte[] buffer; private final byte[] buffer;
/** /**
* Creates an {@code InflateDecoder} * Creates an {@code InflateDecoder}
* *
*/ */
public InflateDecoder() { public InflateDecoder() {
this(new Inflater(true)); this(new Inflater(true));
} }
/** /**
* Creates an {@code InflateDecoder} * Creates an {@code InflateDecoder}
* *
* @param pInflater the inflater instance to use * @param pInflater the inflater instance to use
*/ */
public InflateDecoder(final Inflater pInflater) { public InflateDecoder(final Inflater pInflater) {
if (pInflater == null) { if (pInflater == null) {
throw new IllegalArgumentException("inflater == null"); throw new IllegalArgumentException("inflater == null");
} }
inflater = pInflater; inflater = pInflater;
buffer = new byte[1024]; buffer = new byte[1024];
} }
public int decode(final InputStream stream, final ByteBuffer buffer) throws IOException { public int decode(final InputStream stream, final ByteBuffer buffer) throws IOException {
try { try {
int decoded; int decoded;
while ((decoded = inflater.inflate(buffer.array(), buffer.arrayOffset(), buffer.capacity())) == 0) { while ((decoded = inflater.inflate(buffer.array(), buffer.arrayOffset(), buffer.capacity())) == 0) {
if (inflater.finished() || inflater.needsDictionary()) { if (inflater.finished() || inflater.needsDictionary()) {
return 0; return 0;
} }
if (inflater.needsInput()) { if (inflater.needsInput()) {
fill(stream); fill(stream);
} }
} }
return decoded; return decoded;
} }
catch (DataFormatException e) { catch (DataFormatException e) {
String message = e.getMessage(); String message = e.getMessage();
throw new DecodeException(message != null ? message : "Invalid ZLIB data format", e); throw new DecodeException(message != null ? message : "Invalid ZLIB data format", e);
} }
} }
private void fill(final InputStream pStream) throws IOException { private void fill(final InputStream pStream) throws IOException {
int available = pStream.read(buffer, 0, buffer.length); int available = pStream.read(buffer, 0, buffer.length);
if (available == -1) { if (available == -1) {
throw new EOFException("Unexpected end of ZLIB stream"); throw new EOFException("Unexpected end of ZLIB stream");
} }
inflater.setInput(buffer, 0, available); inflater.setInput(buffer, 0, available);
} }
} }

View File

@@ -1,401 +1,401 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.lang; package com.twelvemonkeys.lang;
import com.twelvemonkeys.io.FileUtil; import com.twelvemonkeys.io.FileUtil;
import com.twelvemonkeys.util.FilterIterator; import com.twelvemonkeys.util.FilterIterator;
import com.twelvemonkeys.util.service.ServiceRegistry; import com.twelvemonkeys.util.service.ServiceRegistry;
import java.io.*; import java.io.*;
import java.util.Collections; import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
/** /**
* NativeLoader * NativeLoader
* <p/> * <p/>
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/lang/NativeLoader.java#2 $ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/lang/NativeLoader.java#2 $
*/ */
final class NativeLoader { final class NativeLoader {
// TODO: Considerations: // TODO: Considerations:
// - Rename all libs like the current code, to <library>.(so|dll|dylib)? // - Rename all libs like the current code, to <library>.(so|dll|dylib)?
// - Keep library filename from jar, and rather store a separate // - Keep library filename from jar, and rather store a separate
// properties-file with the library->library-file mappings? // properties-file with the library->library-file mappings?
// - As all invocations are with library file name, we could probably skip // - As all invocations are with library file name, we could probably skip
// both renaming and properties-file altogether... // both renaming and properties-file altogether...
// TODO: The real trick here, is how to load the correct library for the // TODO: The real trick here, is how to load the correct library for the
// current platform... // current platform...
// - Change String pResource to String[] pResources? // - Change String pResource to String[] pResources?
// - NativeResource class, that has a list of multiple resources? // - NativeResource class, that has a list of multiple resources?
// NativeResource(Map<String, String>) os->native lib mapping // NativeResource(Map<String, String>) os->native lib mapping
// TODO: Consider exposing the method from SystemUtil // TODO: Consider exposing the method from SystemUtil
// TODO: How about a SPI based solution?! // TODO: How about a SPI based solution?!
// public interface com.twelvemonkeys.lang.NativeResourceProvider // public interface com.twelvemonkeys.lang.NativeResourceProvider
// //
// imlementations return a pointer to the correct resource for a given (by // imlementations return a pointer to the correct resource for a given (by
// this class) OS. // this class) OS.
// //
// String getResourceName(...) // String getResourceName(...)
// //
// See http://tolstoy.com/samizdat/sysprops.html // See http://tolstoy.com/samizdat/sysprops.html
// System properties: // System properties:
// "os.name" // "os.name"
// Windows, Linux, Solaris/SunOS, // Windows, Linux, Solaris/SunOS,
// Mac OS/Mac OS X/Rhapsody (aka Mac OS X Server) // Mac OS/Mac OS X/Rhapsody (aka Mac OS X Server)
// General Unix (AIX, Digital Unix, FreeBSD, HP-UX, Irix) // General Unix (AIX, Digital Unix, FreeBSD, HP-UX, Irix)
// OS/2 // OS/2
// "os.arch" // "os.arch"
// Windows: x86 // Windows: x86
// Linux: x86, i386, i686, x86_64, ia64, // Linux: x86, i386, i686, x86_64, ia64,
// Solaris: sparc, sparcv9, x86 // Solaris: sparc, sparcv9, x86
// Mac OS: PowerPC, ppc, i386 // Mac OS: PowerPC, ppc, i386
// AIX: x86, ppc // AIX: x86, ppc
// Digital Unix: alpha // Digital Unix: alpha
// FreeBSD: x86, sparc // FreeBSD: x86, sparc
// HP-UX: PA-RISC // HP-UX: PA-RISC
// Irix: mips // Irix: mips
// OS/2: x86 // OS/2: x86
// "os.version" // "os.version"
// Windows: 4.0 -> NT/95, 5.0 -> 2000, 5.1 -> XP (don't care about old versions, CE etc) // Windows: 4.0 -> NT/95, 5.0 -> 2000, 5.1 -> XP (don't care about old versions, CE etc)
// Mac OS: 8.0, 8.1, 10.0 -> OS X, 10.x.x -> OS X, 5.6 -> Rhapsody (!) // Mac OS: 8.0, 8.1, 10.0 -> OS X, 10.x.x -> OS X, 5.6 -> Rhapsody (!)
// //
// Normalize os.name, os.arch and os.version?! // Normalize os.name, os.arch and os.version?!
///** Normalized operating system constant */ ///** Normalized operating system constant */
//static final OperatingSystem OS_NAME = normalizeOperatingSystem(); //static final OperatingSystem OS_NAME = normalizeOperatingSystem();
// //
///** Normalized system architecture constant */ ///** Normalized system architecture constant */
//static final Architecture OS_ARCHITECTURE = normalizeArchitecture(); //static final Architecture OS_ARCHITECTURE = normalizeArchitecture();
// //
///** Unormalized operating system version constant (for completeness) */ ///** Unormalized operating system version constant (for completeness) */
//static final String OS_VERSION = System.getProperty("os.version"); //static final String OS_VERSION = System.getProperty("os.version");
static final NativeResourceRegistry sRegistry = new NativeResourceRegistry(); static final NativeResourceRegistry sRegistry = new NativeResourceRegistry();
private NativeLoader() { private NativeLoader() {
} }
/* /*
private static Architecture normalizeArchitecture() { private static Architecture normalizeArchitecture() {
String arch = System.getProperty("os.arch"); String arch = System.getProperty("os.arch");
if (arch == null) { if (arch == null) {
throw new IllegalStateException("System property \"os.arch\" == null"); throw new IllegalStateException("System property \"os.arch\" == null");
} }
arch = arch.toLowerCase(); arch = arch.toLowerCase();
if (OS_NAME == OperatingSystem.Windows if (OS_NAME == OperatingSystem.Windows
&& (arch.startsWith("x86") || arch.startsWith("i386"))) { && (arch.startsWith("x86") || arch.startsWith("i386"))) {
return Architecture.X86; return Architecture.X86;
// TODO: 64 bit // TODO: 64 bit
} }
else if (OS_NAME == OperatingSystem.Linux) { else if (OS_NAME == OperatingSystem.Linux) {
if (arch.startsWith("x86") || arch.startsWith("i386")) { if (arch.startsWith("x86") || arch.startsWith("i386")) {
return Architecture.I386; return Architecture.I386;
} }
else if (arch.startsWith("i686")) { else if (arch.startsWith("i686")) {
return Architecture.I686; return Architecture.I686;
} }
// TODO: More Linux options? // TODO: More Linux options?
// TODO: 64 bit // TODO: 64 bit
} }
else if (OS_NAME == OperatingSystem.MacOS) { else if (OS_NAME == OperatingSystem.MacOS) {
if (arch.startsWith("power") || arch.startsWith("ppc")) { if (arch.startsWith("power") || arch.startsWith("ppc")) {
return Architecture.PPC; return Architecture.PPC;
} }
else if (arch.startsWith("i386")) { else if (arch.startsWith("i386")) {
return Architecture.I386; return Architecture.I386;
} }
} }
else if (OS_NAME == OperatingSystem.Solaris) { else if (OS_NAME == OperatingSystem.Solaris) {
if (arch.startsWith("sparc")) { if (arch.startsWith("sparc")) {
return Architecture.SPARC; return Architecture.SPARC;
} }
if (arch.startsWith("x86")) { if (arch.startsWith("x86")) {
// TODO: Should we use i386 as Linux and Mac does? // TODO: Should we use i386 as Linux and Mac does?
return Architecture.X86; return Architecture.X86;
} }
// TODO: 64 bit // TODO: 64 bit
} }
return Architecture.Unknown; return Architecture.Unknown;
} }
*/ */
/* /*
private static OperatingSystem normalizeOperatingSystem() { private static OperatingSystem normalizeOperatingSystem() {
String os = System.getProperty("os.name"); String os = System.getProperty("os.name");
if (os == null) { if (os == null) {
throw new IllegalStateException("System property \"os.name\" == null"); throw new IllegalStateException("System property \"os.name\" == null");
} }
os = os.toLowerCase(); os = os.toLowerCase();
if (os.startsWith("windows")) { if (os.startsWith("windows")) {
return OperatingSystem.Windows; return OperatingSystem.Windows;
} }
else if (os.startsWith("linux")) { else if (os.startsWith("linux")) {
return OperatingSystem.Linux; return OperatingSystem.Linux;
} }
else if (os.startsWith("mac os")) { else if (os.startsWith("mac os")) {
return OperatingSystem.MacOS; return OperatingSystem.MacOS;
} }
else if (os.startsWith("solaris") || os.startsWith("sunos")) { else if (os.startsWith("solaris") || os.startsWith("sunos")) {
return OperatingSystem.Solaris; return OperatingSystem.Solaris;
} }
return OperatingSystem.Unknown; return OperatingSystem.Unknown;
} }
*/ */
// TODO: We could actually have more than one resource for each lib... // TODO: We could actually have more than one resource for each lib...
private static String getResourceFor(String pLibrary) { private static String getResourceFor(String pLibrary) {
Iterator<NativeResourceSPI> providers = sRegistry.providers(pLibrary); Iterator<NativeResourceSPI> providers = sRegistry.providers(pLibrary);
while (providers.hasNext()) { while (providers.hasNext()) {
NativeResourceSPI resourceSPI = providers.next(); NativeResourceSPI resourceSPI = providers.next();
try { try {
return resourceSPI.getClassPathResource(Platform.get()); return resourceSPI.getClassPathResource(Platform.get());
} }
catch (Throwable t) { catch (Throwable t) {
// Dergister and try next // Dergister and try next
sRegistry.deregister(resourceSPI); sRegistry.deregister(resourceSPI);
} }
} }
return null; return null;
} }
/** /**
* Loads a native library. * Loads a native library.
* *
* @param pLibrary name of the library * @param pLibrary name of the library
* *
* @throws UnsatisfiedLinkError * @throws UnsatisfiedLinkError
*/ */
public static void loadLibrary(String pLibrary) { public static void loadLibrary(String pLibrary) {
loadLibrary0(pLibrary, null, null); loadLibrary0(pLibrary, null, null);
} }
/** /**
* Loads a native library. * Loads a native library.
* *
* @param pLibrary name of the library * @param pLibrary name of the library
* @param pLoader the class loader to use * @param pLoader the class loader to use
* *
* @throws UnsatisfiedLinkError * @throws UnsatisfiedLinkError
*/ */
public static void loadLibrary(String pLibrary, ClassLoader pLoader) { public static void loadLibrary(String pLibrary, ClassLoader pLoader) {
loadLibrary0(pLibrary, null, pLoader); loadLibrary0(pLibrary, null, pLoader);
} }
/** /**
* Loads a native library. * Loads a native library.
* *
* @param pLibrary name of the library * @param pLibrary name of the library
* @param pResource name of the resource * @param pResource name of the resource
* @param pLoader the class loader to use * @param pLoader the class loader to use
* *
* @throws UnsatisfiedLinkError * @throws UnsatisfiedLinkError
*/ */
static void loadLibrary0(String pLibrary, String pResource, ClassLoader pLoader) { static void loadLibrary0(String pLibrary, String pResource, ClassLoader pLoader) {
if (pLibrary == null) { if (pLibrary == null) {
throw new IllegalArgumentException("library == null"); throw new IllegalArgumentException("library == null");
} }
// Try loading normal way // Try loading normal way
UnsatisfiedLinkError unsatisfied; UnsatisfiedLinkError unsatisfied;
try { try {
System.loadLibrary(pLibrary); System.loadLibrary(pLibrary);
return; return;
} }
catch (UnsatisfiedLinkError err) { catch (UnsatisfiedLinkError err) {
// Ignore // Ignore
unsatisfied = err; unsatisfied = err;
} }
final ClassLoader loader = pLoader != null ? pLoader : Thread.currentThread().getContextClassLoader(); final ClassLoader loader = pLoader != null ? pLoader : Thread.currentThread().getContextClassLoader();
final String resource = pResource != null ? pResource : getResourceFor(pLibrary); final String resource = pResource != null ? pResource : getResourceFor(pLibrary);
// TODO: resource may be null, and that MIGHT be okay, IFF the resource // TODO: resource may be null, and that MIGHT be okay, IFF the resource
// is allready unpacked to the user dir... However, we then need another // is allready unpacked to the user dir... However, we then need another
// way to resolve the library extension... // way to resolve the library extension...
// Right now we just fail in a predictable way (no NPE)! // Right now we just fail in a predictable way (no NPE)!
if (resource == null) { if (resource == null) {
throw unsatisfied; throw unsatisfied;
} }
// Default to load/store from user.home // Default to load/store from user.home
File dir = new File(System.getProperty("user.home") + "/.twelvemonkeys/lib"); File dir = new File(System.getProperty("user.home") + "/.twelvemonkeys/lib");
dir.mkdirs(); dir.mkdirs();
//File libraryFile = new File(dir.getAbsolutePath(), pLibrary + LIBRARY_EXTENSION); //File libraryFile = new File(dir.getAbsolutePath(), pLibrary + LIBRARY_EXTENSION);
File libraryFile = new File(dir.getAbsolutePath(), pLibrary + "." + FileUtil.getExtension(resource)); File libraryFile = new File(dir.getAbsolutePath(), pLibrary + "." + FileUtil.getExtension(resource));
if (!libraryFile.exists()) { if (!libraryFile.exists()) {
try { try {
extractToUserDir(resource, libraryFile, loader); extractToUserDir(resource, libraryFile, loader);
} }
catch (IOException ioe) { catch (IOException ioe) {
UnsatisfiedLinkError err = new UnsatisfiedLinkError("Unable to extract resource to dir: " + libraryFile.getAbsolutePath()); UnsatisfiedLinkError err = new UnsatisfiedLinkError("Unable to extract resource to dir: " + libraryFile.getAbsolutePath());
err.initCause(ioe); err.initCause(ioe);
throw err; throw err;
} }
} }
// Try to load the library from the file we just wrote // Try to load the library from the file we just wrote
System.load(libraryFile.getAbsolutePath()); System.load(libraryFile.getAbsolutePath());
} }
private static void extractToUserDir(String pResource, File pLibraryFile, ClassLoader pLoader) throws IOException { private static void extractToUserDir(String pResource, File pLibraryFile, ClassLoader pLoader) throws IOException {
// Get resource from classpath // Get resource from classpath
InputStream in = pLoader.getResourceAsStream(pResource); InputStream in = pLoader.getResourceAsStream(pResource);
if (in == null) { if (in == null) {
throw new FileNotFoundException("Unable to locate classpath resource: " + pResource); throw new FileNotFoundException("Unable to locate classpath resource: " + pResource);
} }
// Write to file in user dir // Write to file in user dir
FileOutputStream fileOut = null; FileOutputStream fileOut = null;
try { try {
fileOut = new FileOutputStream(pLibraryFile); fileOut = new FileOutputStream(pLibraryFile);
byte[] tmp = new byte[1024]; byte[] tmp = new byte[1024];
// copy the contents of our resource out to the destination // copy the contents of our resource out to the destination
// dir 1K at a time. 1K may seem arbitrary at first, but today // dir 1K at a time. 1K may seem arbitrary at first, but today
// is a Tuesday, so it makes perfect sense. // is a Tuesday, so it makes perfect sense.
int bytesRead = in.read(tmp); int bytesRead = in.read(tmp);
while (bytesRead != -1) { while (bytesRead != -1) {
fileOut.write(tmp, 0, bytesRead); fileOut.write(tmp, 0, bytesRead);
bytesRead = in.read(tmp); bytesRead = in.read(tmp);
} }
} }
finally { finally {
FileUtil.close(fileOut); FileUtil.close(fileOut);
FileUtil.close(in); FileUtil.close(in);
} }
} }
// TODO: Validate OS names? // TODO: Validate OS names?
// Windows // Windows
// Linux // Linux
// Solaris // Solaris
// Mac OS (OSX+) // Mac OS (OSX+)
// Generic Unix? // Generic Unix?
// Others? // Others?
// TODO: OSes that support different architectures might require different // TODO: OSes that support different architectures might require different
// resources for each architecture.. Need a namespace/flavour system // resources for each architecture.. Need a namespace/flavour system
// TODO: 32 bit/64 bit issues? // TODO: 32 bit/64 bit issues?
// Eg: Windows, Windows/32, Windows/64, Windows/Intel/64? // Eg: Windows, Windows/32, Windows/64, Windows/Intel/64?
// Solaris/Sparc, Solaris/Intel/64 // Solaris/Sparc, Solaris/Intel/64
// MacOS/PowerPC, MacOS/Intel // MacOS/PowerPC, MacOS/Intel
/* /*
public static class NativeResource { public static class NativeResource {
private Map mMap; private Map mMap;
public NativeResource(String[] pOSNames, String[] pReourceNames) { public NativeResource(String[] pOSNames, String[] pReourceNames) {
if (pOSNames == null) { if (pOSNames == null) {
throw new IllegalArgumentException("osNames == null"); throw new IllegalArgumentException("osNames == null");
} }
if (pReourceNames == null) { if (pReourceNames == null) {
throw new IllegalArgumentException("resourceNames == null"); throw new IllegalArgumentException("resourceNames == null");
} }
if (pOSNames.length != pReourceNames.length) { if (pOSNames.length != pReourceNames.length) {
throw new IllegalArgumentException("osNames.length != resourceNames.length"); throw new IllegalArgumentException("osNames.length != resourceNames.length");
} }
Map map = new HashMap(); Map map = new HashMap();
for (int i = 0; i < pOSNames.length; i++) { for (int i = 0; i < pOSNames.length; i++) {
map.put(pOSNames[i], pReourceNames[i]); map.put(pOSNames[i], pReourceNames[i]);
} }
mMap = Collections.unmodifiableMap(map); mMap = Collections.unmodifiableMap(map);
} }
public NativeResource(Map pMap) { public NativeResource(Map pMap) {
if (pMap == null) { if (pMap == null) {
throw new IllegalArgumentException("map == null"); throw new IllegalArgumentException("map == null");
} }
Map map = new HashMap(pMap); Map map = new HashMap(pMap);
Iterator it = map.keySet().iterator(); Iterator it = map.keySet().iterator();
while (it.hasNext()) { while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next(); Map.Entry entry = (Map.Entry) it.next();
if (!(entry.getKey() instanceof String && entry.getValue() instanceof String)) { if (!(entry.getKey() instanceof String && entry.getValue() instanceof String)) {
throw new IllegalArgumentException("map contains non-string entries: " + entry); throw new IllegalArgumentException("map contains non-string entries: " + entry);
} }
} }
mMap = Collections.unmodifiableMap(map); mMap = Collections.unmodifiableMap(map);
} }
protected NativeResource() { protected NativeResource() {
} }
public final String resourceForCurrentOS() { public final String resourceForCurrentOS() {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
protected String getResourceName(String pOSName) { protected String getResourceName(String pOSName) {
return (String) mMap.get(pOSName); return (String) mMap.get(pOSName);
} }
} }
*/ */
private static class NativeResourceRegistry extends ServiceRegistry { private static class NativeResourceRegistry extends ServiceRegistry {
public NativeResourceRegistry() { public NativeResourceRegistry() {
super(Collections.singletonList(NativeResourceSPI.class).iterator()); super(Collections.singletonList(NativeResourceSPI.class).iterator());
registerApplicationClasspathSPIs(); registerApplicationClasspathSPIs();
} }
Iterator<NativeResourceSPI> providers(final String nativeResource) { Iterator<NativeResourceSPI> providers(final String nativeResource) {
return new FilterIterator<NativeResourceSPI>( return new FilterIterator<NativeResourceSPI>(
providers(NativeResourceSPI.class), providers(NativeResourceSPI.class),
new NameFilter(nativeResource) new NameFilter(nativeResource)
); );
} }
} }
private static class NameFilter implements FilterIterator.Filter<NativeResourceSPI> { private static class NameFilter implements FilterIterator.Filter<NativeResourceSPI> {
private final String name; private final String name;
NameFilter(String pName) { NameFilter(String pName) {
if (pName == null) { if (pName == null) {
throw new IllegalArgumentException("name == null"); throw new IllegalArgumentException("name == null");
} }
name = pName; name = pName;
} }
public boolean accept(NativeResourceSPI pElement) { public boolean accept(NativeResourceSPI pElement) {
return name.equals(pElement.getResourceName()); return name.equals(pElement.getResourceName());
} }
} }
} }

View File

@@ -1,398 +1,398 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.util.regex; package com.twelvemonkeys.util.regex;
import com.twelvemonkeys.util.DebugUtil; import com.twelvemonkeys.util.DebugUtil;
import java.io.PrintStream; import java.io.PrintStream;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException; import java.util.regex.PatternSyntaxException;
/** /**
* This class parses arbitrary strings against a wildcard string mask provided. * This class parses arbitrary strings against a wildcard string mask provided.
* The wildcard characters are '*' and '?'. * The wildcard characters are '*' and '?'.
* <p> * <p>
* The string masks provided are treated as case sensitive.<br> * The string masks provided are treated as case sensitive.<br>
* Null-valued string masks as well as null valued strings to be parsed, will lead to rejection. * Null-valued string masks as well as null valued strings to be parsed, will lead to rejection.
* *
* <p><hr style="height=1"><p> * <p><hr style="height=1"><p>
* *
* This task is performed based on regular expression techniques. * This task is performed based on regular expression techniques.
* The possibilities of string generation with the well-known wildcard characters stated above, * The possibilities of string generation with the well-known wildcard characters stated above,
* represent a subset of the possibilities of string generation with regular expressions.<br> * represent a subset of the possibilities of string generation with regular expressions.<br>
* The '*' corresponds to ([Union of all characters in the alphabet])*<br> * The '*' corresponds to ([Union of all characters in the alphabet])*<br>
* The '?' corresponds to ([Union of all characters in the alphabet])<br> * The '?' corresponds to ([Union of all characters in the alphabet])<br>
* &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<small>These expressions are not suited for textual representation at all, I must say. Is there any math tags included in HTML?</small> * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<small>These expressions are not suited for textual representation at all, I must say. Is there any math tags included in HTML?</small>
* *
* <p> * <p>
* *
* This class uses the Regexp package from Apache's Jakarta Project, links below. * This class uses the Regexp package from Apache's Jakarta Project, links below.
* *
* <p><hr style="height=1"><p> * <p><hr style="height=1"><p>
* *
* Examples of usage:<br> * Examples of usage:<br>
* This example will return "Accepted!". * This example will return "Accepted!".
* <pre> * <pre>
* REWildcardStringParser parser = new REWildcardStringParser("*_28????.jp*"); * REWildcardStringParser parser = new REWildcardStringParser("*_28????.jp*");
* if (parser.parseString("gupu_280915.jpg")) { * if (parser.parseString("gupu_280915.jpg")) {
* System.out.println("Accepted!"); * System.out.println("Accepted!");
* } else { * } else {
* System.out.println("Not accepted!"); * System.out.println("Not accepted!");
* } * }
* </pre> * </pre>
* *
* <p><hr style="height=1"><p> * <p><hr style="height=1"><p>
* *
* @author <a href="mailto:eirik.torske@iconmedialab.no">Eirik Torske</a> * @author <a href="mailto:eirik.torske@iconmedialab.no">Eirik Torske</a>
* @see <a href="http://jakarta.apache.org/regexp/">Jakarta Regexp</a> * @see <a href="http://jakarta.apache.org/regexp/">Jakarta Regexp</a>
* @see <a href="http://jakarta.apache.org/regexp/apidocs/org/apache/regexp/RE.html">{@code org.apache.regexp.RE}</a> * @see <a href="http://jakarta.apache.org/regexp/apidocs/org/apache/regexp/RE.html">{@code org.apache.regexp.RE}</a>
* @see com.twelvemonkeys.util.regex.WildcardStringParser * @see com.twelvemonkeys.util.regex.WildcardStringParser
* *
* @todo Rewrite to use this regex package, and not Jakarta directly! * @todo Rewrite to use this regex package, and not Jakarta directly!
*/ */
public class REWildcardStringParser /*extends EntityObject*/ { public class REWildcardStringParser /*extends EntityObject*/ {
// Constants // Constants
/** Field ALPHABET */ /** Field ALPHABET */
public static final char[] ALPHABET = { public static final char[] ALPHABET = {
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '\u00e6', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '\u00e6',
'\u00f8', '\u00e5', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'N', 'M', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', '\u00f8', '\u00e5', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'N', 'M', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y',
'Z', '\u00c6', '\u00d8', '\u00c5', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', '_', '-' 'Z', '\u00c6', '\u00d8', '\u00c5', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', '_', '-'
}; };
/** Field FREE_RANGE_CHARACTER */ /** Field FREE_RANGE_CHARACTER */
public static final char FREE_RANGE_CHARACTER = '*'; public static final char FREE_RANGE_CHARACTER = '*';
/** Field FREE_PASS_CHARACTER */ /** Field FREE_PASS_CHARACTER */
public static final char FREE_PASS_CHARACTER = '?'; public static final char FREE_PASS_CHARACTER = '?';
// Members // Members
Pattern mRegexpParser; Pattern mRegexpParser;
String mStringMask; String mStringMask;
boolean mInitialized = false; boolean mInitialized = false;
int mTotalNumberOfStringsParsed; int mTotalNumberOfStringsParsed;
boolean mDebugging; boolean mDebugging;
PrintStream out; PrintStream out;
// Properties // Properties
// Constructors // Constructors
/** /**
* Creates a wildcard string parser. * Creates a wildcard string parser.
* <p> * <p>
* @param pStringMask the wildcard string mask. * @param pStringMask the wildcard string mask.
*/ */
public REWildcardStringParser(final String pStringMask) { public REWildcardStringParser(final String pStringMask) {
this(pStringMask, false); this(pStringMask, false);
} }
/** /**
* Creates a wildcard string parser. * Creates a wildcard string parser.
* <p> * <p>
* @param pStringMask the wildcard string mask. * @param pStringMask the wildcard string mask.
* @param pDebugging {@code true} will cause debug messages to be emitted to {@code System.out}. * @param pDebugging {@code true} will cause debug messages to be emitted to {@code System.out}.
*/ */
public REWildcardStringParser(final String pStringMask, final boolean pDebugging) { public REWildcardStringParser(final String pStringMask, final boolean pDebugging) {
this(pStringMask, pDebugging, System.out); this(pStringMask, pDebugging, System.out);
} }
/** /**
* Creates a wildcard string parser. * Creates a wildcard string parser.
* <p> * <p>
* @param pStringMask the wildcard string mask. * @param pStringMask the wildcard string mask.
* @param pDebugging {@code true} will cause debug messages to be emitted. * @param pDebugging {@code true} will cause debug messages to be emitted.
* @param pDebuggingPrintStream the {@code java.io.PrintStream} to which the debug messages will be emitted. * @param pDebuggingPrintStream the {@code java.io.PrintStream} to which the debug messages will be emitted.
*/ */
public REWildcardStringParser(final String pStringMask, final boolean pDebugging, final PrintStream pDebuggingPrintStream) { public REWildcardStringParser(final String pStringMask, final boolean pDebugging, final PrintStream pDebuggingPrintStream) {
this.mStringMask = pStringMask; this.mStringMask = pStringMask;
this.mDebugging = pDebugging; this.mDebugging = pDebugging;
this.out = pDebuggingPrintStream; this.out = pDebuggingPrintStream;
mInitialized = buildRegexpParser(); mInitialized = buildRegexpParser();
} }
// Methods // Methods
/** /**
* Converts wildcard string mask to regular expression. * Converts wildcard string mask to regular expression.
* This method should reside in som utility class, but I don't know how proprietary the regular expression format is... * This method should reside in som utility class, but I don't know how proprietary the regular expression format is...
* @return the corresponding regular expression or {@code null} if an error occurred. * @return the corresponding regular expression or {@code null} if an error occurred.
*/ */
private String convertWildcardExpressionToRegularExpression(final String pWildcardExpression) { private String convertWildcardExpressionToRegularExpression(final String pWildcardExpression) {
if (pWildcardExpression == null) { if (pWildcardExpression == null) {
if (mDebugging) { if (mDebugging) {
out.println(DebugUtil.getPrefixDebugMessage(this) + "wildcard expression is null - also returning null as regexp!"); out.println(DebugUtil.getPrefixDebugMessage(this) + "wildcard expression is null - also returning null as regexp!");
} }
return null; return null;
} }
StringBuilder regexpBuffer = new StringBuilder(); StringBuilder regexpBuffer = new StringBuilder();
boolean convertingError = false; boolean convertingError = false;
for (int i = 0; i < pWildcardExpression.length(); i++) { for (int i = 0; i < pWildcardExpression.length(); i++) {
if (convertingError) { if (convertingError) {
return null; return null;
} }
// Free-range character '*' // Free-range character '*'
char stringMaskChar = pWildcardExpression.charAt(i); char stringMaskChar = pWildcardExpression.charAt(i);
if (isFreeRangeCharacter(stringMaskChar)) { if (isFreeRangeCharacter(stringMaskChar)) {
regexpBuffer.append("(([a-<2D>A-<2D>0-9]|.|_|-)*)"); regexpBuffer.append("(([a-<2D>A-<2D>0-9]|.|_|-)*)");
} }
// Free-pass character '?' // Free-pass character '?'
else if (isFreePassCharacter(stringMaskChar)) { else if (isFreePassCharacter(stringMaskChar)) {
regexpBuffer.append("([a-<2D>A_<41>0-9]|.|_|-)"); regexpBuffer.append("([a-<2D>A_<41>0-9]|.|_|-)");
} }
// Valid characters // Valid characters
else if (isInAlphabet(stringMaskChar)) { else if (isInAlphabet(stringMaskChar)) {
regexpBuffer.append(stringMaskChar); regexpBuffer.append(stringMaskChar);
} }
// Invalid character - aborting // Invalid character - aborting
else { else {
if (mDebugging) { if (mDebugging) {
out.println(DebugUtil.getPrefixDebugMessage(this) out.println(DebugUtil.getPrefixDebugMessage(this)
+ "one or more characters in string mask are not legal characters - returning null as regexp!"); + "one or more characters in string mask are not legal characters - returning null as regexp!");
} }
convertingError = true; convertingError = true;
} }
} }
return regexpBuffer.toString(); return regexpBuffer.toString();
} }
/** /**
* Builds the regexp parser. * Builds the regexp parser.
*/ */
private boolean buildRegexpParser() { private boolean buildRegexpParser() {
// Convert wildcard string mask to regular expression // Convert wildcard string mask to regular expression
String regexp = convertWildcardExpressionToRegularExpression(mStringMask); String regexp = convertWildcardExpressionToRegularExpression(mStringMask);
if (regexp == null) { if (regexp == null) {
out.println(DebugUtil.getPrefixErrorMessage(this) out.println(DebugUtil.getPrefixErrorMessage(this)
+ "irregularity in regexp conversion - now not able to parse any strings, all strings will be rejected!"); + "irregularity in regexp conversion - now not able to parse any strings, all strings will be rejected!");
return false; return false;
} }
// Instantiate a regular expression parser // Instantiate a regular expression parser
try { try {
mRegexpParser = Pattern.compile(regexp); mRegexpParser = Pattern.compile(regexp);
} }
catch (PatternSyntaxException e) { catch (PatternSyntaxException e) {
if (mDebugging) { if (mDebugging) {
out.println(DebugUtil.getPrefixErrorMessage(this) + "RESyntaxException \"" + e.getMessage() out.println(DebugUtil.getPrefixErrorMessage(this) + "RESyntaxException \"" + e.getMessage()
+ "\" caught - now not able to parse any strings, all strings will be rejected!"); + "\" caught - now not able to parse any strings, all strings will be rejected!");
} }
if (mDebugging) { if (mDebugging) {
e.printStackTrace(System.err); e.printStackTrace(System.err);
} }
return false; return false;
} }
if (mDebugging) { if (mDebugging) {
out.println(DebugUtil.getPrefixDebugMessage(this) + "regular expression parser from regular expression " + regexp out.println(DebugUtil.getPrefixDebugMessage(this) + "regular expression parser from regular expression " + regexp
+ " extracted from wildcard string mask " + mStringMask + "."); + " extracted from wildcard string mask " + mStringMask + ".");
} }
return true; return true;
} }
/** /**
* Simple check of the string to be parsed. * Simple check of the string to be parsed.
*/ */
private boolean checkStringToBeParsed(final String pStringToBeParsed) { private boolean checkStringToBeParsed(final String pStringToBeParsed) {
// Check for nullness // Check for nullness
if (pStringToBeParsed == null) { if (pStringToBeParsed == null) {
if (mDebugging) { if (mDebugging) {
out.println(DebugUtil.getPrefixDebugMessage(this) + "string to be parsed is null - rejection!"); out.println(DebugUtil.getPrefixDebugMessage(this) + "string to be parsed is null - rejection!");
} }
return false; return false;
} }
// Check if valid character (element in alphabet) // Check if valid character (element in alphabet)
for (int i = 0; i < pStringToBeParsed.length(); i++) { for (int i = 0; i < pStringToBeParsed.length(); i++) {
if (!isInAlphabet(pStringToBeParsed.charAt(i))) { if (!isInAlphabet(pStringToBeParsed.charAt(i))) {
if (mDebugging) { if (mDebugging) {
out.println(DebugUtil.getPrefixDebugMessage(this) out.println(DebugUtil.getPrefixDebugMessage(this)
+ "one or more characters in string to be parsed are not legal characters - rejection!"); + "one or more characters in string to be parsed are not legal characters - rejection!");
} }
return false; return false;
} }
} }
return true; return true;
} }
/** /**
* Tests if a certain character is a valid character in the alphabet that is applying for this automaton. * Tests if a certain character is a valid character in the alphabet that is applying for this automaton.
*/ */
public static boolean isInAlphabet(final char pCharToCheck) { public static boolean isInAlphabet(final char pCharToCheck) {
for (int i = 0; i < ALPHABET.length; i++) { for (int i = 0; i < ALPHABET.length; i++) {
if (pCharToCheck == ALPHABET[i]) { if (pCharToCheck == ALPHABET[i]) {
return true; return true;
} }
} }
return false; return false;
} }
/** /**
* Tests if a certain character is the designated "free-range" character ('*'). * Tests if a certain character is the designated "free-range" character ('*').
*/ */
public static boolean isFreeRangeCharacter(final char pCharToCheck) { public static boolean isFreeRangeCharacter(final char pCharToCheck) {
return pCharToCheck == FREE_RANGE_CHARACTER; return pCharToCheck == FREE_RANGE_CHARACTER;
} }
/** /**
* Tests if a certain character is the designated "free-pass" character ('?'). * Tests if a certain character is the designated "free-pass" character ('?').
*/ */
public static boolean isFreePassCharacter(final char pCharToCheck) { public static boolean isFreePassCharacter(final char pCharToCheck) {
return pCharToCheck == FREE_PASS_CHARACTER; return pCharToCheck == FREE_PASS_CHARACTER;
} }
/** /**
* Tests if a certain character is a wildcard character ('*' or '?'). * Tests if a certain character is a wildcard character ('*' or '?').
*/ */
public static boolean isWildcardCharacter(final char pCharToCheck) { public static boolean isWildcardCharacter(final char pCharToCheck) {
return ((isFreeRangeCharacter(pCharToCheck)) || (isFreePassCharacter(pCharToCheck))); return ((isFreeRangeCharacter(pCharToCheck)) || (isFreePassCharacter(pCharToCheck)));
} }
/** /**
* Gets the string mask that was used when building the parser atomaton. * Gets the string mask that was used when building the parser atomaton.
* <p> * <p>
* @return the string mask used for building the parser automaton. * @return the string mask used for building the parser automaton.
*/ */
public String getStringMask() { public String getStringMask() {
return mStringMask; return mStringMask;
} }
/** /**
* Parses a string. * Parses a string.
* <p> * <p>
* *
* @param pStringToBeParsed * @param pStringToBeParsed
* @return {@code true} if and only if the string are accepted by the parser. * @return {@code true} if and only if the string are accepted by the parser.
*/ */
public boolean parseString(final String pStringToBeParsed) { public boolean parseString(final String pStringToBeParsed) {
if (mDebugging) { if (mDebugging) {
out.println(); out.println();
} }
if (mDebugging) { if (mDebugging) {
out.println(DebugUtil.getPrefixDebugMessage(this) + "parsing \"" + pStringToBeParsed + "\"..."); out.println(DebugUtil.getPrefixDebugMessage(this) + "parsing \"" + pStringToBeParsed + "\"...");
} }
// Update statistics // Update statistics
mTotalNumberOfStringsParsed++; mTotalNumberOfStringsParsed++;
// Check string to be parsed // Check string to be parsed
if (!checkStringToBeParsed(pStringToBeParsed)) { if (!checkStringToBeParsed(pStringToBeParsed)) {
return false; return false;
} }
// Perform parsing and return accetance/rejection flag // Perform parsing and return accetance/rejection flag
if (mInitialized) { if (mInitialized) {
return mRegexpParser.matcher(pStringToBeParsed).matches(); return mRegexpParser.matcher(pStringToBeParsed).matches();
} else { } else {
out.println(DebugUtil.getPrefixErrorMessage(this) + "trying to use non-initialized parser - string rejected!"); out.println(DebugUtil.getPrefixErrorMessage(this) + "trying to use non-initialized parser - string rejected!");
} }
return false; return false;
} }
/* /*
* Overriding mandatory methods from EntityObject's. * Overriding mandatory methods from EntityObject's.
*/ */
/** /**
* Method toString * Method toString
* *
* *
* @return * @return
* *
*/ */
public String toString() { public String toString() {
StringBuilder buffer = new StringBuilder(); StringBuilder buffer = new StringBuilder();
buffer.append(DebugUtil.getClassName(this)); buffer.append(DebugUtil.getClassName(this));
buffer.append(": String mask "); buffer.append(": String mask ");
buffer.append(mStringMask); buffer.append(mStringMask);
buffer.append("\n"); buffer.append("\n");
return buffer.toString(); return buffer.toString();
} }
// Just taking the lazy, easy and dangerous way out // Just taking the lazy, easy and dangerous way out
/** /**
* Method equals * Method equals
* *
* *
* @param pObject * @param pObject
* *
* @return * @return
* *
*/ */
public boolean equals(Object pObject) { public boolean equals(Object pObject) {
if (pObject instanceof REWildcardStringParser) { if (pObject instanceof REWildcardStringParser) {
REWildcardStringParser externalParser = (REWildcardStringParser) pObject; REWildcardStringParser externalParser = (REWildcardStringParser) pObject;
return (externalParser.mStringMask == this.mStringMask); return (externalParser.mStringMask == this.mStringMask);
} }
return ((Object) this).equals(pObject); return ((Object) this).equals(pObject);
} }
// Just taking the lazy, easy and dangerous way out // Just taking the lazy, easy and dangerous way out
/** /**
* Method hashCode * Method hashCode
* *
* *
* @return * @return
* *
*/ */
public int hashCode() { public int hashCode() {
return ((Object) this).hashCode(); return ((Object) this).hashCode();
} }
protected Object clone() throws CloneNotSupportedException { protected Object clone() throws CloneNotSupportedException {
return new REWildcardStringParser(mStringMask); return new REWildcardStringParser(mStringMask);
} }
// Just taking the lazy, easy and dangerous way out // Just taking the lazy, easy and dangerous way out
protected void finalize() throws Throwable {} protected void finalize() throws Throwable {}
} }
/*--- Formatted in Sun Java Convention Style on ma, des 1, '03 ---*/ /*--- Formatted in Sun Java Convention Style on ma, des 1, '03 ---*/
/*------ Formatted by Jindent 3.23 Basic 1.0 --- http://www.jindent.de ------*/ /*------ Formatted by Jindent 3.23 Basic 1.0 --- http://www.jindent.de ------*/

View File

@@ -1,336 +1,336 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.servlet.image; package com.twelvemonkeys.servlet.image;
import com.twelvemonkeys.lang.StringUtil; import com.twelvemonkeys.lang.StringUtil;
import com.twelvemonkeys.servlet.ServletUtil; import com.twelvemonkeys.servlet.ServletUtil;
import javax.servlet.ServletRequest; import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import java.awt.*; import java.awt.*;
import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D;
/** /**
* This servlet is capable of rendereing a text string and output it as an * This servlet is capable of rendereing a text string and output it as an
* image. The text can be rendered in any given font, size, * image. The text can be rendered in any given font, size,
* style or color, into an image, and output it as a GIF, JPEG or PNG image, * style or color, into an image, and output it as a GIF, JPEG or PNG image,
* with optional caching of the rendered image files. * with optional caching of the rendered image files.
* *
* <P><HR><P> * <P><HR><P>
* *
* <A name="parameters"></A><STRONG>Parameters:</STRONG><BR> * <A name="parameters"></A><STRONG>Parameters:</STRONG><BR>
* <DL> * <DL>
* <DT>{@code text}</DT> * <DT>{@code text}</DT>
* <DD>string, the text string to render. * <DD>string, the text string to render.
* <DT>{@code width}</DT> * <DT>{@code width}</DT>
* <DD>integer, the width of the image * <DD>integer, the width of the image
* <DT>{@code height}</DT> * <DT>{@code height}</DT>
* <DD>integer, the height of the image * <DD>integer, the height of the image
* <DT>{@code fontFamily}</DT> * <DT>{@code fontFamily}</DT>
* <DD>string, the name of the font family. * <DD>string, the name of the font family.
* Default is {@code "Helvetica"}. * Default is {@code "Helvetica"}.
* <DT>{@code fontSize}</DT> * <DT>{@code fontSize}</DT>
* <DD>integer, the size of the font. Default is {@code 12}. * <DD>integer, the size of the font. Default is {@code 12}.
* <DT>{@code fontStyle}</DT> * <DT>{@code fontStyle}</DT>
* <DD>string, the tyle of the font. Can be one of the constants * <DD>string, the tyle of the font. Can be one of the constants
* {@code plain} (default), {@code bold}, {@code italic} or * {@code plain} (default), {@code bold}, {@code italic} or
* {@code bolditalic}. Any other will result in {@code plain}. * {@code bolditalic}. Any other will result in {@code plain}.
* <DT>{@code fgcolor}</DT> * <DT>{@code fgcolor}</DT>
* <DD>color (HTML form, {@code #RRGGBB}), or color constant from * <DD>color (HTML form, {@code #RRGGBB}), or color constant from
* {@link java.awt.Color}, default is {@code "black"}. * {@link java.awt.Color}, default is {@code "black"}.
* <DT>{@code bgcolor}</DT> * <DT>{@code bgcolor}</DT>
* <DD>color (HTML form, {@code #RRGGBB}), or color constant from * <DD>color (HTML form, {@code #RRGGBB}), or color constant from
* {@link java.awt.Color}, default is {@code "transparent"}. * {@link java.awt.Color}, default is {@code "transparent"}.
* Note that the hash character ({@code "#"}) used in colors must be * Note that the hash character ({@code "#"}) used in colors must be
* escaped as {@code %23} in the query string. See * escaped as {@code %23} in the query string. See
* {@link StringUtil#toColor(String)}, <A href="#examples">examples</A>. * {@link StringUtil#toColor(String)}, <A href="#examples">examples</A>.
* *
* <!-- inherited from ImageServlet below: --> * <!-- inherited from ImageServlet below: -->
* *
* <DT>{@code cache}</DT> * <DT>{@code cache}</DT>
* <DD>boolean, {@code true} if you want to cache the result * <DD>boolean, {@code true} if you want to cache the result
* to disk (default). * to disk (default).
* *
* <DT>{@code compression}</DT> * <DT>{@code compression}</DT>
* <DD>float, the optional compression ratio for the output image. For JPEG * <DD>float, the optional compression ratio for the output image. For JPEG
* images, the quality is the inverse of the compression ratio. See * images, the quality is the inverse of the compression ratio. See
* {@link #JPEG_DEFAULT_COMPRESSION_LEVEL}, * {@link #JPEG_DEFAULT_COMPRESSION_LEVEL},
* {@link #PNG_DEFAULT_COMPRESSION_LEVEL}. * {@link #PNG_DEFAULT_COMPRESSION_LEVEL}.
* <DD>Applies to JPEG and PNG images only. * <DD>Applies to JPEG and PNG images only.
* *
* <DT>{@code dither}</DT> * <DT>{@code dither}</DT>
* <DD>enumerated, one of {@code NONE}, {@code DEFAULT} or * <DD>enumerated, one of {@code NONE}, {@code DEFAULT} or
* {@code FS}, if you want to dither the result ({@code DEFAULT} is * {@code FS}, if you want to dither the result ({@code DEFAULT} is
* default). * default).
* {@code FS} will produce the best results, but it's slower. * {@code FS} will produce the best results, but it's slower.
* <DD>Use in conjuction with {@code indexed}, {@code palette} * <DD>Use in conjuction with {@code indexed}, {@code palette}
* and {@code websafe}. * and {@code websafe}.
* <DD>Applies to GIF and PNG images only. * <DD>Applies to GIF and PNG images only.
* *
* <DT>{@code fileName}</DT> * <DT>{@code fileName}</DT>
* <DD>string, an optional filename. If not set, the path after the servlet * <DD>string, an optional filename. If not set, the path after the servlet
* ({@link HttpServletRequest#getPathInfo}) will be used for the cache * ({@link HttpServletRequest#getPathInfo}) will be used for the cache
* filename. See {@link #getCacheFile(ServletRequest)}, * filename. See {@link #getCacheFile(ServletRequest)},
* {@link #getCacheRoot}. * {@link #getCacheRoot}.
* *
* <DT>{@code height}</DT> * <DT>{@code height}</DT>
* <DD>integer, the height of the image. * <DD>integer, the height of the image.
* *
* <DT>{@code width}</DT> * <DT>{@code width}</DT>
* <DD>integer, the width of the image. * <DD>integer, the width of the image.
* *
* <DT>{@code indexed}</DT> * <DT>{@code indexed}</DT>
* <DD>integer, the number of colors in the resulting image, or -1 (default). * <DD>integer, the number of colors in the resulting image, or -1 (default).
* If the value is set and positive, the image will use an * If the value is set and positive, the image will use an
* {@code IndexColorModel} with * {@code IndexColorModel} with
* the number of colors specified. Otherwise the image will be true color. * the number of colors specified. Otherwise the image will be true color.
* <DD>Applies to GIF and PNG images only. * <DD>Applies to GIF and PNG images only.
* *
* <DT>{@code palette}</DT> * <DT>{@code palette}</DT>
* <DD>string, an optional filename. If set, the image will use IndexColorModel * <DD>string, an optional filename. If set, the image will use IndexColorModel
* with a palette read from the given file. * with a palette read from the given file.
* <DD>Applies to GIF and PNG images only. * <DD>Applies to GIF and PNG images only.
* *
* <DT>{@code websafe}</DT> * <DT>{@code websafe}</DT>
* <DD>boolean, {@code true} if you want the result to use the 216 color * <DD>boolean, {@code true} if you want the result to use the 216 color
* websafe palette (default is false). * websafe palette (default is false).
* <DD>Applies to GIF and PNG images only. * <DD>Applies to GIF and PNG images only.
* </DL> * </DL>
* *
* @example * @example
* &lt;IMG src="/text/test.gif?height=40&width=600 * &lt;IMG src="/text/test.gif?height=40&width=600
* &fontFamily=TimesRoman&fontSize=30&fontStyle=italic&fgcolor=%23990033 * &fontFamily=TimesRoman&fontSize=30&fontStyle=italic&fgcolor=%23990033
* &bgcolor=%23cccccc&text=the%20quick%20brown%20fox%20jumps%20over%20the * &bgcolor=%23cccccc&text=the%20quick%20brown%20fox%20jumps%20over%20the
* %20lazy%20dog&cache=false" /&gt; * %20lazy%20dog&cache=false" /&gt;
* *
* @example * @example
* &lt;IMG src="/text/test.jpg?height=40&width=600 * &lt;IMG src="/text/test.jpg?height=40&width=600
* &fontFamily=TimesRoman&fontSize=30&fontStyle=italic&fgcolor=black * &fontFamily=TimesRoman&fontSize=30&fontStyle=italic&fgcolor=black
* &bgcolor=%23cccccc&text=the%20quick%20brown%20fox%20jumps%20over%20the * &bgcolor=%23cccccc&text=the%20quick%20brown%20fox%20jumps%20over%20the
* %20lazy%20dog&compression=3&cache=false" /&gt; * %20lazy%20dog&compression=3&cache=false" /&gt;
* *
* @example * @example
* &lt;IMG src="/text/test.png?height=40&width=600 * &lt;IMG src="/text/test.png?height=40&width=600
* &fontFamily=TimesRoman&fontSize=30&fontStyle=italic&fgcolor=%23336699 * &fontFamily=TimesRoman&fontSize=30&fontStyle=italic&fgcolor=%23336699
* &bgcolor=%23cccccc&text=the%20quick%20brown%20fox%20jumps%20over%20the * &bgcolor=%23cccccc&text=the%20quick%20brown%20fox%20jumps%20over%20the
* %20lazy%20dog&cache=true" /&gt; * %20lazy%20dog&cache=true" /&gt;
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $ * @author last modified by $Author: haku $
* @version $Id: TextRenderer.java#2 $ * @version $Id: TextRenderer.java#2 $
*/ */
class TextRenderer /*extends ImageServlet implements ImagePainterServlet*/ { class TextRenderer /*extends ImageServlet implements ImagePainterServlet*/ {
// TODO: Create something usable out of this piece of old junk.. ;-) // TODO: Create something usable out of this piece of old junk.. ;-)
// It just needs a graphics object to write onto // It just needs a graphics object to write onto
// Alternatively, defer, and compute the size needed // Alternatively, defer, and compute the size needed
// Or, make it a filter... // Or, make it a filter...
/** {@code "italic"} */ /** {@code "italic"} */
public final static String FONT_STYLE_ITALIC = "italic"; public final static String FONT_STYLE_ITALIC = "italic";
/** {@code "plain"} */ /** {@code "plain"} */
public final static String FONT_STYLE_PLAIN = "plain"; public final static String FONT_STYLE_PLAIN = "plain";
/** {@code "bold"} */ /** {@code "bold"} */
public final static String FONT_STYLE_BOLD = "bold"; public final static String FONT_STYLE_BOLD = "bold";
/** {@code text} */ /** {@code text} */
public final static String PARAM_TEXT = "text"; public final static String PARAM_TEXT = "text";
/** {@code marginLeft} */ /** {@code marginLeft} */
public final static String PARAM_MARGIN_LEFT = "marginLeft"; public final static String PARAM_MARGIN_LEFT = "marginLeft";
/** {@code marginTop} */ /** {@code marginTop} */
public final static String PARAM_MARGIN_TOP = "marginTop"; public final static String PARAM_MARGIN_TOP = "marginTop";
/** {@code fontFamily} */ /** {@code fontFamily} */
public final static String PARAM_FONT_FAMILY = "fontFamily"; public final static String PARAM_FONT_FAMILY = "fontFamily";
/** {@code fontSize} */ /** {@code fontSize} */
public final static String PARAM_FONT_SIZE = "fontSize"; public final static String PARAM_FONT_SIZE = "fontSize";
/** {@code fontStyle} */ /** {@code fontStyle} */
public final static String PARAM_FONT_STYLE = "fontStyle"; public final static String PARAM_FONT_STYLE = "fontStyle";
/** {@code textRotation} */ /** {@code textRotation} */
public final static String PARAM_TEXT_ROTATION = "textRotation"; public final static String PARAM_TEXT_ROTATION = "textRotation";
/** {@code textRotation} */ /** {@code textRotation} */
public final static String PARAM_TEXT_ROTATION_UNITS = "textRotationUnits"; public final static String PARAM_TEXT_ROTATION_UNITS = "textRotationUnits";
/** {@code bgcolor} */ /** {@code bgcolor} */
public final static String PARAM_BGCOLOR = "bgcolor"; public final static String PARAM_BGCOLOR = "bgcolor";
/** {@code fgcolor} */ /** {@code fgcolor} */
public final static String PARAM_FGCOLOR = "fgcolor"; public final static String PARAM_FGCOLOR = "fgcolor";
protected final static String ROTATION_DEGREES = "DEGREES"; protected final static String ROTATION_DEGREES = "DEGREES";
protected final static String ROTATION_RADIANS = "RADIANS"; protected final static String ROTATION_RADIANS = "RADIANS";
/** /**
* Creates the TextRender servlet. * Creates the TextRender servlet.
*/ */
public TextRenderer() { public TextRenderer() {
} }
/** /**
* Renders the text string for this servlet request. * Renders the text string for this servlet request.
*/ */
private void paint(ServletRequest pReq, Graphics2D pRes, int pWidth, int pHeight) private void paint(ServletRequest pReq, Graphics2D pRes, int pWidth, int pHeight)
throws ImageServletException { throws ImageServletException {
// Get parameters // Get parameters
String text = pReq.getParameter(PARAM_TEXT); String text = pReq.getParameter(PARAM_TEXT);
String[] lines = StringUtil.toStringArray(text, "\n\r"); String[] lines = StringUtil.toStringArray(text, "\n\r");
String fontFamily = pReq.getParameter(PARAM_FONT_FAMILY); String fontFamily = pReq.getParameter(PARAM_FONT_FAMILY);
String fontSize = pReq.getParameter(PARAM_FONT_SIZE); String fontSize = pReq.getParameter(PARAM_FONT_SIZE);
String fontStyle = pReq.getParameter(PARAM_FONT_STYLE); String fontStyle = pReq.getParameter(PARAM_FONT_STYLE);
String bgcolor = pReq.getParameter(PARAM_BGCOLOR); String bgcolor = pReq.getParameter(PARAM_BGCOLOR);
String fgcolor = pReq.getParameter(PARAM_FGCOLOR); String fgcolor = pReq.getParameter(PARAM_FGCOLOR);
// TODO: Make them static.. // TODO: Make them static..
pRes.addRenderingHints(new RenderingHints(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON)); pRes.addRenderingHints(new RenderingHints(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON));
pRes.addRenderingHints(new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY)); pRes.addRenderingHints(new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY));
pRes.addRenderingHints(new RenderingHints(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY)); pRes.addRenderingHints(new RenderingHints(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY));
// pRes.addRenderingHints(new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)); // pRes.addRenderingHints(new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON));
//System.out.println(pRes.getBackground()); //System.out.println(pRes.getBackground());
// Clear area with bgcolor // Clear area with bgcolor
if (!StringUtil.isEmpty(bgcolor)) { if (!StringUtil.isEmpty(bgcolor)) {
pRes.setBackground(StringUtil.toColor(bgcolor)); pRes.setBackground(StringUtil.toColor(bgcolor));
pRes.clearRect(0, 0, pWidth, pHeight); pRes.clearRect(0, 0, pWidth, pHeight);
//System.out.println(pRes.getBackground()); //System.out.println(pRes.getBackground());
} }
// Create and set font // Create and set font
Font font = new Font( Font font = new Font(
fontFamily != null ? fontFamily : "Helvetica", fontFamily != null ? fontFamily : "Helvetica",
getFontStyle(fontStyle), getFontStyle(fontStyle),
fontSize != null ? Integer.parseInt(fontSize) : 12 fontSize != null ? Integer.parseInt(fontSize) : 12
); );
pRes.setFont(font); pRes.setFont(font);
// Set rotation // Set rotation
double angle = getAngle(pReq); double angle = getAngle(pReq);
pRes.rotate(angle, pWidth / 2.0, pHeight / 2.0); pRes.rotate(angle, pWidth / 2.0, pHeight / 2.0);
// Draw string in fgcolor // Draw string in fgcolor
pRes.setColor(fgcolor != null ? StringUtil.toColor(fgcolor) : Color.black); pRes.setColor(fgcolor != null ? StringUtil.toColor(fgcolor) : Color.black);
float x = ServletUtil.getFloatParameter(pReq, PARAM_MARGIN_LEFT, Float.MIN_VALUE); float x = ServletUtil.getFloatParameter(pReq, PARAM_MARGIN_LEFT, Float.MIN_VALUE);
Rectangle2D[] bounds = new Rectangle2D[lines.length]; Rectangle2D[] bounds = new Rectangle2D[lines.length];
if (x <= Float.MIN_VALUE) { if (x <= Float.MIN_VALUE) {
// Center // Center
float longest = 0f; float longest = 0f;
for (int i = 0; i < lines.length; i++) { for (int i = 0; i < lines.length; i++) {
bounds[i] = font.getStringBounds(lines[i], pRes.getFontRenderContext()); bounds[i] = font.getStringBounds(lines[i], pRes.getFontRenderContext());
if (bounds[i].getWidth() > longest) { if (bounds[i].getWidth() > longest) {
longest = (float) bounds[i].getWidth(); longest = (float) bounds[i].getWidth();
} }
} }
//x = (float) ((pWidth - bounds.getWidth()) / 2f); //x = (float) ((pWidth - bounds.getWidth()) / 2f);
x = (float) ((pWidth - longest) / 2f); x = (float) ((pWidth - longest) / 2f);
//System.out.println("marginLeft: " + x); //System.out.println("marginLeft: " + x);
} }
//else { //else {
//System.out.println("marginLeft (from param): " + x); //System.out.println("marginLeft (from param): " + x);
//} //}
float y = ServletUtil.getFloatParameter(pReq, PARAM_MARGIN_TOP, Float.MIN_VALUE); float y = ServletUtil.getFloatParameter(pReq, PARAM_MARGIN_TOP, Float.MIN_VALUE);
float lineHeight = (float) (bounds[0] != null ? float lineHeight = (float) (bounds[0] != null ?
bounds[0].getHeight() : font.getStringBounds(lines[0], pRes.getFontRenderContext()).getHeight()); bounds[0].getHeight() : font.getStringBounds(lines[0], pRes.getFontRenderContext()).getHeight());
if (y <= Float.MIN_VALUE) { if (y <= Float.MIN_VALUE) {
// Center // Center
y = (float) ((pHeight - lineHeight) / 2f) y = (float) ((pHeight - lineHeight) / 2f)
- (lineHeight * (lines.length - 2.5f) / 2f); - (lineHeight * (lines.length - 2.5f) / 2f);
//System.out.println("marginTop: " + y); //System.out.println("marginTop: " + y);
} }
else { else {
// Todo: Correct for font height? // Todo: Correct for font height?
y += font.getSize2D(); y += font.getSize2D();
//System.out.println("marginTop (from param):" + y); //System.out.println("marginTop (from param):" + y);
} }
//System.out.println("Font size: " + font.getSize2D()); //System.out.println("Font size: " + font.getSize2D());
//System.out.println("Line height: " + lineHeight); //System.out.println("Line height: " + lineHeight);
// Draw // Draw
for (int i = 0; i < lines.length; i++) { for (int i = 0; i < lines.length; i++) {
pRes.drawString(lines[i], x, y + lineHeight * i); pRes.drawString(lines[i], x, y + lineHeight * i);
} }
} }
/** /**
* Returns the font style constant. * Returns the font style constant.
* *
* @param pStyle a string containing either the word {@code "plain"} or one * @param pStyle a string containing either the word {@code "plain"} or one
* or more of {@code "bold"} and {@code italic}. * or more of {@code "bold"} and {@code italic}.
* @return the font style constant as defined in {@link Font}. * @return the font style constant as defined in {@link Font}.
* *
* @see Font#PLAIN * @see Font#PLAIN
* @see Font#BOLD * @see Font#BOLD
* @see Font#ITALIC * @see Font#ITALIC
*/ */
private int getFontStyle(String pStyle) { private int getFontStyle(String pStyle) {
if (pStyle == null || StringUtil.containsIgnoreCase(pStyle, FONT_STYLE_PLAIN)) { if (pStyle == null || StringUtil.containsIgnoreCase(pStyle, FONT_STYLE_PLAIN)) {
return Font.PLAIN; return Font.PLAIN;
} }
// Try to find bold/italic // Try to find bold/italic
int style = Font.PLAIN; int style = Font.PLAIN;
if (StringUtil.containsIgnoreCase(pStyle, FONT_STYLE_BOLD)) { if (StringUtil.containsIgnoreCase(pStyle, FONT_STYLE_BOLD)) {
style |= Font.BOLD; style |= Font.BOLD;
} }
if (StringUtil.containsIgnoreCase(pStyle, FONT_STYLE_ITALIC)) { if (StringUtil.containsIgnoreCase(pStyle, FONT_STYLE_ITALIC)) {
style |= Font.ITALIC; style |= Font.ITALIC;
} }
return style; return style;
} }
/** /**
* Gets the angle of rotation from the request. * Gets the angle of rotation from the request.
* *
* @param pRequest the servlet request to get parameters from * @param pRequest the servlet request to get parameters from
* @return the angle in radians. * @return the angle in radians.
*/ */
private double getAngle(ServletRequest pRequest) { private double getAngle(ServletRequest pRequest) {
// Get angle // Get angle
double angle = ServletUtil.getDoubleParameter(pRequest, PARAM_TEXT_ROTATION, 0.0); double angle = ServletUtil.getDoubleParameter(pRequest, PARAM_TEXT_ROTATION, 0.0);
// Convert to radians, if needed // Convert to radians, if needed
String units = pRequest.getParameter(PARAM_TEXT_ROTATION_UNITS); String units = pRequest.getParameter(PARAM_TEXT_ROTATION_UNITS);
if (!StringUtil.isEmpty(units) && ROTATION_DEGREES.equalsIgnoreCase(units)) { if (!StringUtil.isEmpty(units) && ROTATION_DEGREES.equalsIgnoreCase(units)) {
angle = Math.toRadians(angle); angle = Math.toRadians(angle);
} }
return angle; return angle;
} }
} }

View File

@@ -1,76 +1,76 @@
/* /*
* Copyright (c) 2002 TwelveMonkeys. * Copyright (c) 2002 TwelveMonkeys.
* All rights reserved. * All rights reserved.
* *
* $Log: Droplet.java,v $ * $Log: Droplet.java,v $
* Revision 1.3 2003/10/06 14:25:19 WMHAKUR * Revision 1.3 2003/10/06 14:25:19 WMHAKUR
* Code clean-up only. * Code clean-up only.
* *
* Revision 1.2 2002/10/18 14:12:16 WMHAKUR * Revision 1.2 2002/10/18 14:12:16 WMHAKUR
* Now, it even compiles. :-/ * Now, it even compiles. :-/
* *
* Revision 1.1 2002/10/18 14:02:16 WMHAKUR * Revision 1.1 2002/10/18 14:02:16 WMHAKUR
* Moved to com.twelvemonkeys.servlet.jsp.droplet * Moved to com.twelvemonkeys.servlet.jsp.droplet
* *
* *
*/ */
package com.twelvemonkeys.servlet.jsp.droplet; package com.twelvemonkeys.servlet.jsp.droplet;
import com.twelvemonkeys.servlet.jsp.droplet.taglib.IncludeTag; import com.twelvemonkeys.servlet.jsp.droplet.taglib.IncludeTag;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.PageContext; import javax.servlet.jsp.PageContext;
import java.io.IOException; import java.io.IOException;
/** /**
* Dynamo Droplet like Servlet. * Dynamo Droplet like Servlet.
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $ * @author last modified by $Author: haku $
* *
* @version $Revision: #1 $, ($Date: 2008/05/05 $) * @version $Revision: #1 $, ($Date: 2008/05/05 $)
* *
*/ */
public abstract class Droplet extends HttpServlet implements JspFragment { public abstract class Droplet extends HttpServlet implements JspFragment {
public abstract void service(PageContext pPageContext) public abstract void service(PageContext pPageContext)
throws ServletException, IOException; throws ServletException, IOException;
/** /**
* Services a parameter. Programatically equivalent to the * Services a parameter. Programatically equivalent to the
* <d:valueof param="pParameter"/> JSP tag. * <d:valueof param="pParameter"/> JSP tag.
*/ */
public void serviceParameter(String pParameter, PageContext pPageContext) throws ServletException, IOException { public void serviceParameter(String pParameter, PageContext pPageContext) throws ServletException, IOException {
Object param = pPageContext.getRequest().getAttribute(pParameter); Object param = pPageContext.getRequest().getAttribute(pParameter);
if (param != null) { if (param != null) {
if (param instanceof Param) { if (param instanceof Param) {
((Param) param).service(pPageContext); ((Param) param).service(pPageContext);
} }
else { else {
pPageContext.getOut().print(param); pPageContext.getOut().print(param);
} }
} }
else { else {
// Try to get value from parameters // Try to get value from parameters
Object obj = pPageContext.getRequest().getParameter(pParameter); Object obj = pPageContext.getRequest().getParameter(pParameter);
// Print parameter or default value // Print parameter or default value
pPageContext.getOut().print((obj != null) ? obj : ""); pPageContext.getOut().print((obj != null) ? obj : "");
} }
} }
/** /**
* "There's no need to override this method." :-) * "There's no need to override this method." :-)
*/ */
final public void service(HttpServletRequest pRequest, HttpServletResponse pResponse) throws ServletException, IOException { final public void service(HttpServletRequest pRequest, HttpServletResponse pResponse) throws ServletException, IOException {
PageContext pageContext = (PageContext) pRequest.getAttribute(IncludeTag.PAGE_CONTEXT); PageContext pageContext = (PageContext) pRequest.getAttribute(IncludeTag.PAGE_CONTEXT);
// TODO: What if pageContext == null // TODO: What if pageContext == null
service(pageContext); service(pageContext);
} }
} }

View File

@@ -1,42 +1,42 @@
/* /*
* Copyright (c) 2002 TwelveMonkeys. * Copyright (c) 2002 TwelveMonkeys.
* All rights reserved. * All rights reserved.
* *
* $Log: JspFragment.java,v $ * $Log: JspFragment.java,v $
* Revision 1.2 2003/10/06 14:25:36 WMHAKUR * Revision 1.2 2003/10/06 14:25:36 WMHAKUR
* Code clean-up only. * Code clean-up only.
* *
* Revision 1.1 2002/10/18 14:02:16 WMHAKUR * Revision 1.1 2002/10/18 14:02:16 WMHAKUR
* Moved to com.twelvemonkeys.servlet.jsp.droplet * Moved to com.twelvemonkeys.servlet.jsp.droplet
* *
* *
*/ */
package com.twelvemonkeys.servlet.jsp.droplet; package com.twelvemonkeys.servlet.jsp.droplet;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.jsp.PageContext; import javax.servlet.jsp.PageContext;
import java.io.IOException; import java.io.IOException;
/** /**
* Interface for JSP sub pages or page fragments to implement. * Interface for JSP sub pages or page fragments to implement.
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $ * @author last modified by $Author: haku $
* *
* @version $Revision: #1 $, ($Date: 2008/05/05 $) * @version $Revision: #1 $, ($Date: 2008/05/05 $)
*/ */
public interface JspFragment { public interface JspFragment {
/** /**
* Services a sub page or a page fragment inside another page * Services a sub page or a page fragment inside another page
* (or PageContext). * (or PageContext).
* *
* @param pContext the PageContext that is used to render the subpage. * @param pContext the PageContext that is used to render the subpage.
* *
* @throws ServletException if an exception occurs that interferes with the * @throws ServletException if an exception occurs that interferes with the
* subpage's normal operation * subpage's normal operation
* @throws IOException if an input or output exception occurs * @throws IOException if an input or output exception occurs
*/ */
public void service(PageContext pContext) throws ServletException, IOException; public void service(PageContext pContext) throws ServletException, IOException;
} }

View File

@@ -1,26 +1,26 @@
package com.twelvemonkeys.servlet.jsp.droplet; package com.twelvemonkeys.servlet.jsp.droplet;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.jsp.PageContext; import javax.servlet.jsp.PageContext;
import java.io.IOException; import java.io.IOException;
/** /**
* Oparam (Open parameter) * Oparam (Open parameter)
*/ */
public class Oparam extends Param implements JspFragment { public class Oparam extends Param implements JspFragment {
/** /**
* Creates an Oparam. * Creates an Oparam.
* *
* @param pValue the value of the parameter * @param pValue the value of the parameter
*/ */
public Oparam(String pValue) { public Oparam(String pValue) {
super(pValue); super(pValue);
} }
public void service(PageContext pContext) throws ServletException, IOException { public void service(PageContext pContext) throws ServletException, IOException {
pContext.getServletContext().log("Service subpage " + pContext.getServletContext().getRealPath(value)); pContext.getServletContext().log("Service subpage " + pContext.getServletContext().getRealPath(value));
pContext.include(value); pContext.include(value);
} }
} }

View File

@@ -1,41 +1,41 @@
package com.twelvemonkeys.servlet.jsp.droplet; package com.twelvemonkeys.servlet.jsp.droplet;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.jsp.JspWriter; import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.PageContext; import javax.servlet.jsp.PageContext;
import java.io.IOException; import java.io.IOException;
/** /**
* Param * Param
*/ */
public class Param implements JspFragment { public class Param implements JspFragment {
/** The value member field. */ /** The value member field. */
protected String value = null; protected String value = null;
/** /**
* Creates a Param. * Creates a Param.
* *
* @param pValue the value of the parameter * @param pValue the value of the parameter
*/ */
public Param(String pValue) { public Param(String pValue) {
value = pValue; value = pValue;
} }
/** /**
* Gets the value of the parameter. * Gets the value of the parameter.
*/ */
public String getValue() { public String getValue() {
return value; return value;
} }
/** /**
* Services the page fragment. This version simply prints the value of * Services the page fragment. This version simply prints the value of
* this parameter to teh PageContext's out. * this parameter to teh PageContext's out.
*/ */
public void service(PageContext pContext) public void service(PageContext pContext)
throws ServletException, IOException { throws ServletException, IOException {
JspWriter writer = pContext.getOut(); JspWriter writer = pContext.getOut();
writer.print(value); writer.print(value);
} }
} }

View File

@@ -1,214 +1,214 @@
/* /*
* Copyright (c) 2002 TwelveMonkeys. * Copyright (c) 2002 TwelveMonkeys.
* All rights reserved. * All rights reserved.
* *
* $Log: IncludeTag.java,v $ * $Log: IncludeTag.java,v $
* Revision 1.2 2003/10/06 14:25:36 WMHAKUR * Revision 1.2 2003/10/06 14:25:36 WMHAKUR
* Code clean-up only. * Code clean-up only.
* *
* Revision 1.1 2002/10/18 14:03:09 WMHAKUR * Revision 1.1 2002/10/18 14:03:09 WMHAKUR
* Moved to com.twelvemonkeys.servlet.jsp.droplet.taglib * Moved to com.twelvemonkeys.servlet.jsp.droplet.taglib
* *
* *
*/ */
package com.twelvemonkeys.servlet.jsp.droplet.taglib; package com.twelvemonkeys.servlet.jsp.droplet.taglib;
import com.twelvemonkeys.servlet.jsp.taglib.ExTagSupport; import com.twelvemonkeys.servlet.jsp.taglib.ExTagSupport;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspException;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
/** /**
* Include tag tag that emulates ATG Dynamo Droplet tag JHTML behaviour for * Include tag tag that emulates ATG Dynamo Droplet tag JHTML behaviour for
* JSP. * JSP.
* *
* @author Thomas Purcell (CSC Australia) * @author Thomas Purcell (CSC Australia)
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $ * @author last modified by $Author: haku $
* *
* @version $Revision: #1 $, ($Date: 2008/05/05 $) * @version $Revision: #1 $, ($Date: 2008/05/05 $)
* *
*/ */
public class IncludeTag extends ExTagSupport { public class IncludeTag extends ExTagSupport {
/** /**
* This will contain the names of all the parameters that have been * This will contain the names of all the parameters that have been
* added to the PageContext.REQUEST_SCOPE scope by this tag. * added to the PageContext.REQUEST_SCOPE scope by this tag.
*/ */
private ArrayList<String> parameterNames = null; private ArrayList<String> parameterNames = null;
/** /**
* If any of the parameters we insert for this tag already exist, then * If any of the parameters we insert for this tag already exist, then
* we back up the older parameter in this {@code HashMap} and * we back up the older parameter in this {@code HashMap} and
* restore them when the tag is finished. * restore them when the tag is finished.
*/ */
private HashMap<String, Object> oldParameters = null; private HashMap<String, Object> oldParameters = null;
/** /**
* This is the URL for the JSP page that the parameters contained in this * This is the URL for the JSP page that the parameters contained in this
* tag are to be inserted into. * tag are to be inserted into.
*/ */
private String page; private String page;
/** /**
* The name of the PageContext attribute * The name of the PageContext attribute
*/ */
public final static String PAGE_CONTEXT = "com.twelvemonkeys.servlet.jsp.PageContext"; public final static String PAGE_CONTEXT = "com.twelvemonkeys.servlet.jsp.PageContext";
/** /**
* Sets the value for the JSP page to insert the parameters into. This * Sets the value for the JSP page to insert the parameters into. This
* will be set by the tag attribute within the original JSP page. * will be set by the tag attribute within the original JSP page.
* *
* @param pPage The URL for the JSP page to insert parameters into. * @param pPage The URL for the JSP page to insert parameters into.
*/ */
public void setPage(String pPage) { public void setPage(String pPage) {
page = pPage; page = pPage;
} }
/** /**
* Adds a parameter to the {@code PageContext.REQUEST_SCOPE} scope. * Adds a parameter to the {@code PageContext.REQUEST_SCOPE} scope.
* If a parameter with the same name as {@code pName} already exists, * If a parameter with the same name as {@code pName} already exists,
* then the old parameter is first placed in the {@code OldParameters} * then the old parameter is first placed in the {@code OldParameters}
* member variable. When this tag is finished, the old value will be * member variable. When this tag is finished, the old value will be
* restored. * restored.
* *
* @param pName The name of the new parameter to be stored in the * @param pName The name of the new parameter to be stored in the
* {@code PageContext.REQUEST_SCOPE} scope. * {@code PageContext.REQUEST_SCOPE} scope.
* @param pValue The value for the parmeter to be stored in the {@code * @param pValue The value for the parmeter to be stored in the {@code
* PageContext.REQUEST_SCOPE} scope. * PageContext.REQUEST_SCOPE} scope.
*/ */
public void addParameter(String pName, Object pValue) { public void addParameter(String pName, Object pValue) {
// Check that we haven't already saved this parameter // Check that we haven't already saved this parameter
if (!parameterNames.contains(pName)) { if (!parameterNames.contains(pName)) {
parameterNames.add(pName); parameterNames.add(pName);
// Now check if this parameter already exists in the page. // Now check if this parameter already exists in the page.
Object obj = getRequest().getAttribute(pName); Object obj = getRequest().getAttribute(pName);
if (obj != null) { if (obj != null) {
oldParameters.put(pName, obj); oldParameters.put(pName, obj);
} }
} }
// Finally, insert the parameter in the request scope. // Finally, insert the parameter in the request scope.
getRequest().setAttribute(pName, pValue); getRequest().setAttribute(pName, pValue);
} }
/** /**
* This is the method called when the JSP interpreter first hits the tag * This is the method called when the JSP interpreter first hits the tag
* associated with this class. This method will firstly determine whether * associated with this class. This method will firstly determine whether
* the page referenced by the {@code page} attribute exists. If the * the page referenced by the {@code page} attribute exists. If the
* page doesn't exist, this method will throw a {@code JspException}. * page doesn't exist, this method will throw a {@code JspException}.
* If the page does exist, this method will hand control over to that JSP * If the page does exist, this method will hand control over to that JSP
* page. * page.
* *
* @exception JspException * @exception JspException
*/ */
public int doStartTag() throws JspException { public int doStartTag() throws JspException {
oldParameters = new HashMap<String, Object>(); oldParameters = new HashMap<String, Object>();
parameterNames = new ArrayList<String>(); parameterNames = new ArrayList<String>();
return EVAL_BODY_INCLUDE; return EVAL_BODY_INCLUDE;
} }
/** /**
* This method is called when the JSP page compiler hits the end tag. By * This method is called when the JSP page compiler hits the end tag. By
* now all the data should have been passed and parameters entered into * now all the data should have been passed and parameters entered into
* the {@code PageContext.REQUEST_SCOPE} scope. This method includes * the {@code PageContext.REQUEST_SCOPE} scope. This method includes
* the JSP page whose URL is stored in the {@code mPage} member * the JSP page whose URL is stored in the {@code mPage} member
* variable. * variable.
* *
* @exception JspException * @exception JspException
*/ */
public int doEndTag() throws JspException { public int doEndTag() throws JspException {
String msg; String msg;
try { try {
Iterator<String> iterator; Iterator<String> iterator;
String parameterName; String parameterName;
// -- Harald K 20020726 // -- Harald K 20020726
// Include the page, in place // Include the page, in place
//getDispatcher().include(getRequest(), getResponse()); //getDispatcher().include(getRequest(), getResponse());
addParameter(PAGE_CONTEXT, pageContext); // Will be cleared later addParameter(PAGE_CONTEXT, pageContext); // Will be cleared later
pageContext.include(page); pageContext.include(page);
// Remove all the parameters that were added to the request scope // Remove all the parameters that were added to the request scope
// for this insert tag. // for this insert tag.
iterator = parameterNames.iterator(); iterator = parameterNames.iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
parameterName = iterator.next(); parameterName = iterator.next();
getRequest().removeAttribute(parameterName); getRequest().removeAttribute(parameterName);
} }
iterator = oldParameters.keySet().iterator(); iterator = oldParameters.keySet().iterator();
// Restore the parameters we temporarily replaced (if any). // Restore the parameters we temporarily replaced (if any).
while (iterator.hasNext()) { while (iterator.hasNext()) {
parameterName = iterator.next(); parameterName = iterator.next();
getRequest().setAttribute(parameterName, oldParameters.get(parameterName)); getRequest().setAttribute(parameterName, oldParameters.get(parameterName));
} }
return super.doEndTag(); return super.doEndTag();
} }
catch (IOException ioe) { catch (IOException ioe) {
msg = "Caught an IOException while including " + page msg = "Caught an IOException while including " + page
+ "\n" + ioe.toString(); + "\n" + ioe.toString();
log(msg, ioe); log(msg, ioe);
throw new JspException(msg); throw new JspException(msg);
} }
catch (ServletException se) { catch (ServletException se) {
msg = "Caught a ServletException while including " + page msg = "Caught a ServletException while including " + page
+ "\n" + se.toString(); + "\n" + se.toString();
log(msg, se); log(msg, se);
throw new JspException(msg); throw new JspException(msg);
} }
} }
/** /**
* Free up the member variables that we've used throughout this tag. * Free up the member variables that we've used throughout this tag.
*/ */
protected void clearServiceState() { protected void clearServiceState() {
oldParameters = null; oldParameters = null;
parameterNames = null; parameterNames = null;
} }
/** /**
* Returns the request dispatcher for the JSP page whose URL is stored in * Returns the request dispatcher for the JSP page whose URL is stored in
* the {@code mPage} member variable. * the {@code mPage} member variable.
* *
* @return The RequestDispatcher for the JSP page whose URL is stored in * @return The RequestDispatcher for the JSP page whose URL is stored in
* the {@code mPage} member variable. * the {@code mPage} member variable.
*/ */
/* /*
private RequestDispatcher getDispatcher() { private RequestDispatcher getDispatcher() {
return getRequest().getRequestDispatcher(page); return getRequest().getRequestDispatcher(page);
} }
*/ */
/** /**
* Returns the HttpServletRequest object for the current user request. * Returns the HttpServletRequest object for the current user request.
* *
* @return The HttpServletRequest object for the current user request. * @return The HttpServletRequest object for the current user request.
*/ */
private HttpServletRequest getRequest() { private HttpServletRequest getRequest() {
return (HttpServletRequest) pageContext.getRequest(); return (HttpServletRequest) pageContext.getRequest();
} }
/** /**
* Returns the HttpServletResponse object for the current user request. * Returns the HttpServletResponse object for the current user request.
* *
* @return The HttpServletResponse object for the current user request. * @return The HttpServletResponse object for the current user request.
*/ */
private HttpServletResponse getResponse() { private HttpServletResponse getResponse() {
return (HttpServletResponse) pageContext.getResponse(); return (HttpServletResponse) pageContext.getResponse();
} }
} }

View File

@@ -1,183 +1,183 @@
/* /*
* Copyright (c) 2002 TwelveMonkeys. * Copyright (c) 2002 TwelveMonkeys.
* All rights reserved. * All rights reserved.
* *
* $Log: NestingHandler.java,v $ * $Log: NestingHandler.java,v $
* Revision 1.4 2003/10/06 14:25:44 WMHAKUR * Revision 1.4 2003/10/06 14:25:44 WMHAKUR
* Code clean-up only. * Code clean-up only.
* *
* Revision 1.3 2003/08/04 15:26:30 WMHAKUR * Revision 1.3 2003/08/04 15:26:30 WMHAKUR
* Code clean-up. * Code clean-up.
* *
* Revision 1.2 2002/10/18 14:28:07 WMHAKUR * Revision 1.2 2002/10/18 14:28:07 WMHAKUR
* Fixed package error. * Fixed package error.
* *
* Revision 1.1 2002/10/18 14:03:09 WMHAKUR * Revision 1.1 2002/10/18 14:03:09 WMHAKUR
* Moved to com.twelvemonkeys.servlet.jsp.droplet.taglib * Moved to com.twelvemonkeys.servlet.jsp.droplet.taglib
* *
* *
*/ */
package com.twelvemonkeys.servlet.jsp.droplet.taglib; package com.twelvemonkeys.servlet.jsp.droplet.taglib;
import com.twelvemonkeys.lang.StringUtil; import com.twelvemonkeys.lang.StringUtil;
import org.xml.sax.*; import org.xml.sax.*;
import org.xml.sax.helpers.DefaultHandler; import org.xml.sax.helpers.DefaultHandler;
/** /**
* A SAX handler that returns an exception if the nesting of * A SAX handler that returns an exception if the nesting of
* {@code param}, {@code oparam}, {@code droplet} and * {@code param}, {@code oparam}, {@code droplet} and
* {@code valueof} is not correct. * {@code valueof} is not correct.
* *
* Based on the NestingHandler.java, * Based on the NestingHandler.java,
* taken from More Servlets and JavaServer Pages * taken from More Servlets and JavaServer Pages
* from Prentice Hall and Sun Microsystems Press, * from Prentice Hall and Sun Microsystems Press,
* http://www.moreservlets.com/. * http://www.moreservlets.com/.
* &copy; 2002 Marty Hall; may be freely used or adapted. * &copy; 2002 Marty Hall; may be freely used or adapted.
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $ * @author last modified by $Author: haku $
* *
* @version $Revision: #1 $, ($Date: 2008/05/05 $) * @version $Revision: #1 $, ($Date: 2008/05/05 $)
*/ */
public class NestingHandler extends DefaultHandler { public class NestingHandler extends DefaultHandler {
private String includeTagName = "include"; private String includeTagName = "include";
private String paramTagName = "param"; private String paramTagName = "param";
private String openParamTagName = "oparam"; private String openParamTagName = "oparam";
//private Stack mParents = new Stack(); //private Stack mParents = new Stack();
private boolean inIncludeTag = false; private boolean inIncludeTag = false;
private String namespacePrefix = null; private String namespacePrefix = null;
private String namespaceURI = null; private String namespaceURI = null;
private NestingValidator validator = null; private NestingValidator validator = null;
public NestingHandler(String pNamespacePrefix, String pNameSpaceURI, public NestingHandler(String pNamespacePrefix, String pNameSpaceURI,
NestingValidator pValidator) { NestingValidator pValidator) {
namespacePrefix = pNamespacePrefix; namespacePrefix = pNamespacePrefix;
namespaceURI = pNameSpaceURI; namespaceURI = pNameSpaceURI;
validator = pValidator; validator = pValidator;
} }
public void startElement(String pNamespaceURI, String pLocalName, public void startElement(String pNamespaceURI, String pLocalName,
String pQualifiedName, Attributes pAttributes) String pQualifiedName, Attributes pAttributes)
throws SAXException { throws SAXException {
String namespacePrefix = !StringUtil.isEmpty(pNamespaceURI) String namespacePrefix = !StringUtil.isEmpty(pNamespaceURI)
? getNSPrefixFromURI(pNamespaceURI) ? getNSPrefixFromURI(pNamespaceURI)
: getNamespacePrefix(pQualifiedName); : getNamespacePrefix(pQualifiedName);
String localName = !StringUtil.isEmpty(pLocalName) String localName = !StringUtil.isEmpty(pLocalName)
? pLocalName : getLocalName(pQualifiedName); ? pLocalName : getLocalName(pQualifiedName);
/* /*
if (namespacePrefix.equals(namespacePrefix)) { if (namespacePrefix.equals(namespacePrefix)) {
System.out.println("startElement:\nnamespaceURI=" + pNamespaceURI System.out.println("startElement:\nnamespaceURI=" + pNamespaceURI
+ " namespacePrefix=" + namespacePrefix + " namespacePrefix=" + namespacePrefix
+ " localName=" + localName + " localName=" + localName
+ " qName=" + pQualifiedName + " qName=" + pQualifiedName
+ " attributes=" + pAttributes); + " attributes=" + pAttributes);
} }
*/ */
if (localName.equals(includeTagName)) { if (localName.equals(includeTagName)) {
// include // include
//System.out.println("<" + namespacePrefix + ":" //System.out.println("<" + namespacePrefix + ":"
// + includeTagName + ">"); // + includeTagName + ">");
if (inIncludeTag) { if (inIncludeTag) {
validator.reportError("Cannot nest " + namespacePrefix + ":" validator.reportError("Cannot nest " + namespacePrefix + ":"
+ includeTagName); + includeTagName);
} }
inIncludeTag = true; inIncludeTag = true;
} }
else if (localName.equals(paramTagName)) { else if (localName.equals(paramTagName)) {
// param // param
//System.out.println("<" + namespacePrefix + ":" //System.out.println("<" + namespacePrefix + ":"
// + paramTagName + "/>"); // + paramTagName + "/>");
if (!inIncludeTag) { if (!inIncludeTag) {
validator.reportError(this.namespacePrefix + ":" validator.reportError(this.namespacePrefix + ":"
+ paramTagName + paramTagName
+ " can only appear within " + " can only appear within "
+ this.namespacePrefix + ":" + this.namespacePrefix + ":"
+ includeTagName); + includeTagName);
} }
} }
else if (localName.equals(openParamTagName)) { else if (localName.equals(openParamTagName)) {
// oparam // oparam
//System.out.println("<" + namespacePrefix + ":" //System.out.println("<" + namespacePrefix + ":"
// + openParamTagName + ">"); // + openParamTagName + ">");
if (!inIncludeTag) { if (!inIncludeTag) {
validator.reportError(this.namespacePrefix + ":" validator.reportError(this.namespacePrefix + ":"
+ openParamTagName + openParamTagName
+ " can only appear within " + " can only appear within "
+ this.namespacePrefix + ":" + this.namespacePrefix + ":"
+ includeTagName); + includeTagName);
} }
inIncludeTag = false; inIncludeTag = false;
} }
else { else {
// Only jsp:text allowed inside include! // Only jsp:text allowed inside include!
if (inIncludeTag && !localName.equals("text")) { if (inIncludeTag && !localName.equals("text")) {
validator.reportError(namespacePrefix + ":" + localName validator.reportError(namespacePrefix + ":" + localName
+ " can not appear within " + " can not appear within "
+ this.namespacePrefix + ":" + this.namespacePrefix + ":"
+ includeTagName); + includeTagName);
} }
} }
} }
public void endElement(String pNamespaceURI, public void endElement(String pNamespaceURI,
String pLocalName, String pLocalName,
String pQualifiedName) String pQualifiedName)
throws SAXException { throws SAXException {
String namespacePrefix = !StringUtil.isEmpty(pNamespaceURI) String namespacePrefix = !StringUtil.isEmpty(pNamespaceURI)
? getNSPrefixFromURI(pNamespaceURI) ? getNSPrefixFromURI(pNamespaceURI)
: getNamespacePrefix(pQualifiedName); : getNamespacePrefix(pQualifiedName);
String localName = !StringUtil.isEmpty(pLocalName) String localName = !StringUtil.isEmpty(pLocalName)
? pLocalName : getLocalName(pQualifiedName); ? pLocalName : getLocalName(pQualifiedName);
/* /*
if (namespacePrefix.equals(namespacePrefix)) { if (namespacePrefix.equals(namespacePrefix)) {
System.out.println("endElement:\nnamespaceURI=" + pNamespaceURI System.out.println("endElement:\nnamespaceURI=" + pNamespaceURI
+ " namespacePrefix=" + namespacePrefix + " namespacePrefix=" + namespacePrefix
+ " localName=" + localName + " localName=" + localName
+ " qName=" + pQualifiedName); + " qName=" + pQualifiedName);
} }
*/ */
if (namespacePrefix.equals(this.namespacePrefix) if (namespacePrefix.equals(this.namespacePrefix)
&& localName.equals(includeTagName)) { && localName.equals(includeTagName)) {
//System.out.println("</" + namespacePrefix + ":" //System.out.println("</" + namespacePrefix + ":"
// + includeTagName + ">"); // + includeTagName + ">");
inIncludeTag = false; inIncludeTag = false;
} }
else if (namespacePrefix.equals(this.namespacePrefix) else if (namespacePrefix.equals(this.namespacePrefix)
&& localName.equals(openParamTagName)) { && localName.equals(openParamTagName)) {
//System.out.println("</" + namespacePrefix + ":" //System.out.println("</" + namespacePrefix + ":"
// + openParamTagName + ">"); // + openParamTagName + ">");
inIncludeTag = true; // assuming no errors before this... inIncludeTag = true; // assuming no errors before this...
} }
} }
/** /**
* Stupid broken namespace-support "fix".. * Stupid broken namespace-support "fix"..
*/ */
private String getNSPrefixFromURI(String pNamespaceURI) { private String getNSPrefixFromURI(String pNamespaceURI) {
return (pNamespaceURI.equals(namespaceURI) return (pNamespaceURI.equals(namespaceURI)
? namespacePrefix : ""); ? namespacePrefix : "");
} }
private String getNamespacePrefix(String pQualifiedName) { private String getNamespacePrefix(String pQualifiedName) {
return pQualifiedName.substring(0, pQualifiedName.indexOf(':')); return pQualifiedName.substring(0, pQualifiedName.indexOf(':'));
} }
private String getLocalName(String pQualifiedName) { private String getLocalName(String pQualifiedName) {
return pQualifiedName.substring(pQualifiedName.indexOf(':') + 1); return pQualifiedName.substring(pQualifiedName.indexOf(':') + 1);
} }
} }

View File

@@ -1,102 +1,102 @@
/* /*
* Copyright (c) 2002 TwelveMonkeys. * Copyright (c) 2002 TwelveMonkeys.
* All rights reserved. * All rights reserved.
* *
* $Log: NestingValidator.java,v $ * $Log: NestingValidator.java,v $
* Revision 1.4 2003/08/04 15:26:40 WMHAKUR * Revision 1.4 2003/08/04 15:26:40 WMHAKUR
* Code clean-up. * Code clean-up.
* *
* Revision 1.3 2002/11/18 14:12:43 WMHAKUR * Revision 1.3 2002/11/18 14:12:43 WMHAKUR
* *** empty log message *** * *** empty log message ***
* *
* Revision 1.2 2002/10/18 14:28:07 WMHAKUR * Revision 1.2 2002/10/18 14:28:07 WMHAKUR
* Fixed package error. * Fixed package error.
* *
* Revision 1.1 2002/10/18 14:03:09 WMHAKUR * Revision 1.1 2002/10/18 14:03:09 WMHAKUR
* Moved to com.twelvemonkeys.servlet.jsp.droplet.taglib * Moved to com.twelvemonkeys.servlet.jsp.droplet.taglib
* *
* *
*/ */
package com.twelvemonkeys.servlet.jsp.droplet.taglib; package com.twelvemonkeys.servlet.jsp.droplet.taglib;
import org.xml.sax.InputSource; import org.xml.sax.InputSource;
import org.xml.sax.helpers.DefaultHandler; import org.xml.sax.helpers.DefaultHandler;
import javax.servlet.jsp.tagext.PageData; import javax.servlet.jsp.tagext.PageData;
import javax.servlet.jsp.tagext.TagLibraryValidator; import javax.servlet.jsp.tagext.TagLibraryValidator;
import javax.servlet.jsp.tagext.ValidationMessage; import javax.servlet.jsp.tagext.ValidationMessage;
import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory; import javax.xml.parsers.SAXParserFactory;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
* A validator that verifies that tags follow * A validator that verifies that tags follow
* proper nesting order. * proper nesting order.
* <P> * <P>
* Based on NestingValidator.java, * Based on NestingValidator.java,
* taken from More Servlets and JavaServer Pages * taken from More Servlets and JavaServer Pages
* from Prentice Hall and Sun Microsystems Press, * from Prentice Hall and Sun Microsystems Press,
* http://www.moreservlets.com/. * http://www.moreservlets.com/.
* &copy; 2002 Marty Hall; may be freely used or adapted. * &copy; 2002 Marty Hall; may be freely used or adapted.
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $ * @author last modified by $Author: haku $
* *
* @version $Revision: #1 $, ($Date: 2008/05/05 $) * @version $Revision: #1 $, ($Date: 2008/05/05 $)
* *
*/ */
public class NestingValidator extends TagLibraryValidator { public class NestingValidator extends TagLibraryValidator {
private List<ValidationMessage> errors = new ArrayList<ValidationMessage>(); private List<ValidationMessage> errors = new ArrayList<ValidationMessage>();
/** /**
* *
*/ */
public ValidationMessage[] validate(String pPrefix, String pURI, PageData pPage) { public ValidationMessage[] validate(String pPrefix, String pURI, PageData pPage) {
//System.out.println("Validating " + pPrefix + " (" + pURI + ") for " //System.out.println("Validating " + pPrefix + " (" + pURI + ") for "
// + pPage + "."); // + pPage + ".");
// Pass the parser factory in on the command line with // Pass the parser factory in on the command line with
// -D to override the use of the Apache parser. // -D to override the use of the Apache parser.
DefaultHandler handler = new NestingHandler(pPrefix, pURI, this); DefaultHandler handler = new NestingHandler(pPrefix, pURI, this);
SAXParserFactory factory = SAXParserFactory.newInstance(); SAXParserFactory factory = SAXParserFactory.newInstance();
try { try {
// FileUtil.copy(pPage.getInputStream(), System.out); // FileUtil.copy(pPage.getInputStream(), System.out);
SAXParser parser = factory.newSAXParser(); SAXParser parser = factory.newSAXParser();
InputSource source = InputSource source =
new InputSource(pPage.getInputStream()); new InputSource(pPage.getInputStream());
// Parse, handler will use callback to report errors // Parse, handler will use callback to report errors
parser.parse(source, handler); parser.parse(source, handler);
} }
catch (Exception e) { catch (Exception e) {
String errorMessage = e.getMessage(); String errorMessage = e.getMessage();
reportError(errorMessage); reportError(errorMessage);
} }
// Return any errors and exceptions, empty array means okay // Return any errors and exceptions, empty array means okay
return errors.toArray(new ValidationMessage[errors.size()]); return errors.toArray(new ValidationMessage[errors.size()]);
} }
/** /**
* Callback method for the handler to report errors * Callback method for the handler to report errors
*/ */
public void reportError(String pMessage) { public void reportError(String pMessage) {
// The first argument to the ValidationMessage // The first argument to the ValidationMessage
// constructor can be a tag ID. Since tag IDs // constructor can be a tag ID. Since tag IDs
// are not universally supported, use null for // are not universally supported, use null for
// portability. The important part is the second // portability. The important part is the second
// argument: the error message. // argument: the error message.
errors.add(new ValidationMessage(null, pMessage)); errors.add(new ValidationMessage(null, pMessage));
} }
} }

View File

@@ -1,220 +1,220 @@
/* /*
* Copyright (c) 2002 TwelveMonkeys. * Copyright (c) 2002 TwelveMonkeys.
* All rights reserved. * All rights reserved.
* *
* $Log: OparamTag.java,v $ * $Log: OparamTag.java,v $
* Revision 1.4 2003/10/06 14:25:53 WMHAKUR * Revision 1.4 2003/10/06 14:25:53 WMHAKUR
* Code clean-up only. * Code clean-up only.
* *
* Revision 1.3 2002/11/18 14:12:43 WMHAKUR * Revision 1.3 2002/11/18 14:12:43 WMHAKUR
* *** empty log message *** * *** empty log message ***
* *
* Revision 1.2 2002/11/07 12:20:14 WMHAKUR * Revision 1.2 2002/11/07 12:20:14 WMHAKUR
* Updated to reflect changes in com.twelvemonkeys.util.*Util * Updated to reflect changes in com.twelvemonkeys.util.*Util
* *
* Revision 1.1 2002/10/18 14:03:09 WMHAKUR * Revision 1.1 2002/10/18 14:03:09 WMHAKUR
* Moved to com.twelvemonkeys.servlet.jsp.droplet.taglib * Moved to com.twelvemonkeys.servlet.jsp.droplet.taglib
* *
* *
*/ */
package com.twelvemonkeys.servlet.jsp.droplet.taglib; package com.twelvemonkeys.servlet.jsp.droplet.taglib;
import com.twelvemonkeys.io.FileUtil; import com.twelvemonkeys.io.FileUtil;
import com.twelvemonkeys.lang.StringUtil; import com.twelvemonkeys.lang.StringUtil;
import com.twelvemonkeys.servlet.jsp.droplet.Oparam; import com.twelvemonkeys.servlet.jsp.droplet.Oparam;
import com.twelvemonkeys.servlet.jsp.taglib.BodyReaderTag; import com.twelvemonkeys.servlet.jsp.taglib.BodyReaderTag;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspException;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
/** /**
* Open parameter tag that emulates ATG Dynamo JHTML behaviour for JSP. * Open parameter tag that emulates ATG Dynamo JHTML behaviour for JSP.
* *
* @author Thomas Purcell (CSC Australia) * @author Thomas Purcell (CSC Australia)
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $ * @author last modified by $Author: haku $
* @version $Id: jsp/droplet/taglib/OparamTag.java#1 $ * @version $Id: jsp/droplet/taglib/OparamTag.java#1 $
*/ */
public class OparamTag extends BodyReaderTag { public class OparamTag extends BodyReaderTag {
protected final static String COUNTER = "com.twelvemonkeys.servlet.jsp.taglib.OparamTag.counter"; protected final static String COUNTER = "com.twelvemonkeys.servlet.jsp.taglib.OparamTag.counter";
private File subpage = null; private File subpage = null;
/** /**
* This is the name of the parameter to be inserted into the {@code * This is the name of the parameter to be inserted into the {@code
* PageContext.REQUEST_SCOPE} scope. * PageContext.REQUEST_SCOPE} scope.
*/ */
private String parameterName = null; private String parameterName = null;
private String language = null; private String language = null;
private String prefix = null; private String prefix = null;
/** /**
* This method allows the JSP page to set the name for the parameter by * This method allows the JSP page to set the name for the parameter by
* using the {@code name} tag attribute. * using the {@code name} tag attribute.
* *
* @param pName The name for the parameter to insert into the {@code * @param pName The name for the parameter to insert into the {@code
* PageContext.REQUEST_SCOPE} scope. * PageContext.REQUEST_SCOPE} scope.
*/ */
public void setName(String pName) { public void setName(String pName) {
parameterName = pName; parameterName = pName;
} }
public void setLanguage(String pLanguage) { public void setLanguage(String pLanguage) {
//System.out.println("setLanguage:"+pLanguage); //System.out.println("setLanguage:"+pLanguage);
language = pLanguage; language = pLanguage;
} }
public void setPrefix(String pPrefix) { public void setPrefix(String pPrefix) {
//System.out.println("setPrefix:"+pPrefix); //System.out.println("setPrefix:"+pPrefix);
prefix = pPrefix; prefix = pPrefix;
} }
/** /**
* Ensure that the tag implemented by this class is enclosed by an {@code IncludeTag}. * Ensure that the tag implemented by this class is enclosed by an {@code IncludeTag}.
* If the tag is not enclosed by an {@code IncludeTag} then a {@code JspException} is thrown. * If the tag is not enclosed by an {@code IncludeTag} then a {@code JspException} is thrown.
* *
* @return If this tag is enclosed within an {@code IncludeTag}, then the default return value * @return If this tag is enclosed within an {@code IncludeTag}, then the default return value
* from this method is the {@code TagSupport.EVAL_BODY_TAG} value. * from this method is the {@code TagSupport.EVAL_BODY_TAG} value.
* *
* @throws JspException * @throws JspException
*/ */
public int doStartTag() throws JspException { public int doStartTag() throws JspException {
//checkEnclosedInIncludeTag(); // Moved to TagLibValidator //checkEnclosedInIncludeTag(); // Moved to TagLibValidator
// Get request // Get request
HttpServletRequest request = (HttpServletRequest) pageContext.getRequest(); HttpServletRequest request = (HttpServletRequest) pageContext.getRequest();
// Get filename // Get filename
subpage = createFileNameFromRequest(request); subpage = createFileNameFromRequest(request);
// Get include tag, and add to parameters // Get include tag, and add to parameters
IncludeTag includeTag = (IncludeTag) getParent(); IncludeTag includeTag = (IncludeTag) getParent();
includeTag.addParameter(parameterName, new Oparam(subpage.getName())); includeTag.addParameter(parameterName, new Oparam(subpage.getName()));
// if ! subpage.exist || jsp newer than subpage, write new // if ! subpage.exist || jsp newer than subpage, write new
File jsp = new File(pageContext.getServletContext().getRealPath(request.getServletPath())); File jsp = new File(pageContext.getServletContext().getRealPath(request.getServletPath()));
if (!subpage.exists() || jsp.lastModified() > subpage.lastModified()) { if (!subpage.exists() || jsp.lastModified() > subpage.lastModified()) {
return EVAL_BODY_BUFFERED; return EVAL_BODY_BUFFERED;
} }
// No need to evaluate body again! // No need to evaluate body again!
return SKIP_BODY; return SKIP_BODY;
} }
/** /**
* This is the method responsible for actually testing that the tag * This is the method responsible for actually testing that the tag
* implemented by this class is enclosed within an {@code IncludeTag}. * implemented by this class is enclosed within an {@code IncludeTag}.
* *
* @exception JspException * @exception JspException
*/ */
/* /*
protected void checkEnclosedInIncludeTag() throws JspException { protected void checkEnclosedInIncludeTag() throws JspException {
Tag parentTag = getParent(); Tag parentTag = getParent();
if ((parentTag != null) && (parentTag instanceof IncludeTag)) { if ((parentTag != null) && (parentTag instanceof IncludeTag)) {
return; return;
} }
String msg = "A class that extends EnclosedIncludeBodyReaderTag " + String msg = "A class that extends EnclosedIncludeBodyReaderTag " +
"is not enclosed within an IncludeTag."; "is not enclosed within an IncludeTag.";
log(msg); log(msg);
throw new JspException(msg); throw new JspException(msg);
} }
*/ */
/** /**
* This method cleans up the member variables for this tag in preparation * This method cleans up the member variables for this tag in preparation
* for being used again. This method is called when the tag finishes it's * for being used again. This method is called when the tag finishes it's
* current call with in the page but could be called upon again within this * current call with in the page but could be called upon again within this
* same page. This method is also called in the release stage of the tag * same page. This method is also called in the release stage of the tag
* life cycle just in case a JspException was thrown during the tag * life cycle just in case a JspException was thrown during the tag
* execution. * execution.
*/ */
protected void clearServiceState() { protected void clearServiceState() {
parameterName = null; parameterName = null;
} }
/** /**
* This is the method responsible for taking the result of the JSP code * This is the method responsible for taking the result of the JSP code
* that forms the body of this tag and inserts it as a parameter into the * that forms the body of this tag and inserts it as a parameter into the
* request scope session. If any problems occur while loading the body * request scope session. If any problems occur while loading the body
* into the session scope then a {@code JspException} will be thrown. * into the session scope then a {@code JspException} will be thrown.
* *
* @param pContent The body of the tag as a String. * @param pContent The body of the tag as a String.
* @throws JspException * @throws JspException
*/ */
protected void processBody(String pContent) throws JspException { protected void processBody(String pContent) throws JspException {
// Okay, we have the content, we need to write it to disk somewhere // Okay, we have the content, we need to write it to disk somewhere
String content = pContent; String content = pContent;
if (!StringUtil.isEmpty(language)) { if (!StringUtil.isEmpty(language)) {
content = "<%@page language=\"" + language + "\" %>" + content; content = "<%@page language=\"" + language + "\" %>" + content;
} }
if (!StringUtil.isEmpty(prefix)) { if (!StringUtil.isEmpty(prefix)) {
content = "<%@taglib uri=\"/twelvemonkeys-common\" prefix=\"" + prefix + "\" %>" + content; content = "<%@taglib uri=\"/twelvemonkeys-common\" prefix=\"" + prefix + "\" %>" + content;
} }
// Write the content of the oparam to disk // Write the content of the oparam to disk
try { try {
log("Processing subpage " + subpage.getPath()); log("Processing subpage " + subpage.getPath());
FileUtil.write(subpage, content.getBytes()); FileUtil.write(subpage, content.getBytes());
} }
catch (IOException ioe) { catch (IOException ioe) {
throw new JspException(ioe); throw new JspException(ioe);
} }
} }
/** Creates a unique filename for each (nested) oparam */ /** Creates a unique filename for each (nested) oparam */
private File createFileNameFromRequest(HttpServletRequest pRequest) { private File createFileNameFromRequest(HttpServletRequest pRequest) {
//System.out.println("ServletPath" + pRequest.getServletPath()); //System.out.println("ServletPath" + pRequest.getServletPath());
String path = pRequest.getServletPath(); String path = pRequest.getServletPath();
// Find last '/' // Find last '/'
int splitIndex = path.lastIndexOf("/"); int splitIndex = path.lastIndexOf("/");
// Split -> path + name // Split -> path + name
String name = path.substring(splitIndex + 1); String name = path.substring(splitIndex + 1);
path = path.substring(0, splitIndex); path = path.substring(0, splitIndex);
// Replace special chars in name with '_' // Replace special chars in name with '_'
name = name.replace('.', '_'); name = name.replace('.', '_');
String param = parameterName.replace('.', '_'); String param = parameterName.replace('.', '_');
param = param.replace('/', '_'); param = param.replace('/', '_');
param = param.replace('\\', '_'); param = param.replace('\\', '_');
param = param.replace(':', '_'); param = param.replace(':', '_');
// tempfile = realPath(path) + name + "_oparam_" + number + ".jsp" // tempfile = realPath(path) + name + "_oparam_" + number + ".jsp"
int count = getOparamCountFromRequest(pRequest); int count = getOparamCountFromRequest(pRequest);
// Hmm.. Would be great, but seems like I can't serve pages from within the temp dir // Hmm.. Would be great, but seems like I can't serve pages from within the temp dir
//File temp = (File) getServletContext().getAttribute("javax.servlet.context.tempdir"); //File temp = (File) getServletContext().getAttribute("javax.servlet.context.tempdir");
//return new File(new File(temp, path), name + "_oparam_" + count + "_" + param + ".jsp"); //return new File(new File(temp, path), name + "_oparam_" + count + "_" + param + ".jsp");
return new File(new File(pageContext.getServletContext().getRealPath(path)), name + "_oparam_" + count + "_" + param + ".jsp"); return new File(new File(pageContext.getServletContext().getRealPath(path)), name + "_oparam_" + count + "_" + param + ".jsp");
} }
/** Gets the current oparam count for this request */ /** Gets the current oparam count for this request */
private int getOparamCountFromRequest(HttpServletRequest pRequest) { private int getOparamCountFromRequest(HttpServletRequest pRequest) {
// Use request.attribute for incrementing oparam counter // Use request.attribute for incrementing oparam counter
Integer count = (Integer) pRequest.getAttribute(COUNTER); Integer count = (Integer) pRequest.getAttribute(COUNTER);
if (count == null) { if (count == null) {
count = new Integer(0); count = new Integer(0);
} }
else { else {
count = new Integer(count.intValue() + 1); count = new Integer(count.intValue() + 1);
} }
// ... and set it back // ... and set it back
pRequest.setAttribute(COUNTER, count); pRequest.setAttribute(COUNTER, count);
return count.intValue(); return count.intValue();
} }
} }

View File

@@ -1,129 +1,129 @@
/* /*
* Copyright (c) 2002 TwelveMonkeys. * Copyright (c) 2002 TwelveMonkeys.
* All rights reserved. * All rights reserved.
* *
* $Log: ParamTag.java,v $ * $Log: ParamTag.java,v $
* Revision 1.2 2003/10/06 14:26:00 WMHAKUR * Revision 1.2 2003/10/06 14:26:00 WMHAKUR
* Code clean-up only. * Code clean-up only.
* *
* Revision 1.1 2002/10/18 14:03:09 WMHAKUR * Revision 1.1 2002/10/18 14:03:09 WMHAKUR
* Moved to com.twelvemonkeys.servlet.jsp.droplet.taglib * Moved to com.twelvemonkeys.servlet.jsp.droplet.taglib
* *
* *
*/ */
package com.twelvemonkeys.servlet.jsp.droplet.taglib; package com.twelvemonkeys.servlet.jsp.droplet.taglib;
import com.twelvemonkeys.servlet.jsp.droplet.Param; import com.twelvemonkeys.servlet.jsp.droplet.Param;
import com.twelvemonkeys.servlet.jsp.taglib.ExTagSupport; import com.twelvemonkeys.servlet.jsp.taglib.ExTagSupport;
import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspException;
/** /**
* Parameter tag that emulates ATG Dynamo JHTML behaviour for JSP. * Parameter tag that emulates ATG Dynamo JHTML behaviour for JSP.
* *
* @author Thomas Purcell (CSC Australia) * @author Thomas Purcell (CSC Australia)
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $ * @author last modified by $Author: haku $
* *
* @version $Revision: #1 $, ($Date: 2008/05/05 $) * @version $Revision: #1 $, ($Date: 2008/05/05 $)
* *
*/ */
public class ParamTag extends ExTagSupport { public class ParamTag extends ExTagSupport {
/** /**
* This is the name of the parameter to be inserted into the {@code * This is the name of the parameter to be inserted into the {@code
* PageContext.REQUEST_SCOPE} scope. * PageContext.REQUEST_SCOPE} scope.
*/ */
private String parameterName; private String parameterName;
/** /**
* This is the value for the parameter to be inserted into the {@code * This is the value for the parameter to be inserted into the {@code
* PageContext.REQUEST_SCOPE} scope. * PageContext.REQUEST_SCOPE} scope.
*/ */
private Object parameterValue; private Object parameterValue;
/** /**
* This method allows the JSP page to set the name for the parameter by * This method allows the JSP page to set the name for the parameter by
* using the {@code name} tag attribute. * using the {@code name} tag attribute.
* *
* @param pName The name for the parameter to insert into the {@code * @param pName The name for the parameter to insert into the {@code
* PageContext.REQUEST_SCOPE} scope. * PageContext.REQUEST_SCOPE} scope.
*/ */
public void setName(String pName) { public void setName(String pName) {
parameterName = pName; parameterName = pName;
} }
/** /**
* This method allows the JSP page to set the value for hte parameter by * This method allows the JSP page to set the value for hte parameter by
* using the {@code value} tag attribute. * using the {@code value} tag attribute.
* *
* @param pValue The value for the parameter to insert into the <code> * @param pValue The value for the parameter to insert into the <code>
* PageContext.REQUEST_SCOPE</page> scope. * PageContext.REQUEST_SCOPE</page> scope.
*/ */
public void setValue(String pValue) { public void setValue(String pValue) {
parameterValue = new Param(pValue); parameterValue = new Param(pValue);
} }
/** /**
* Ensure that the tag implemented by this class is enclosed by an {@code * Ensure that the tag implemented by this class is enclosed by an {@code
* IncludeTag}. If the tag is not enclosed by an * IncludeTag}. If the tag is not enclosed by an
* {@code IncludeTag} then a {@code JspException} is thrown. * {@code IncludeTag} then a {@code JspException} is thrown.
* *
* @return If this tag is enclosed within an {@code IncludeTag}, then * @return If this tag is enclosed within an {@code IncludeTag}, then
* the default return value from this method is the {@code * the default return value from this method is the {@code
* TagSupport.SKIP_BODY} value. * TagSupport.SKIP_BODY} value.
* @exception JspException * @exception JspException
*/ */
public int doStartTag() throws JspException { public int doStartTag() throws JspException {
//checkEnclosedInIncludeTag(); //checkEnclosedInIncludeTag();
addParameter(); addParameter();
return SKIP_BODY; return SKIP_BODY;
} }
/** /**
* This is the method responsible for actually testing that the tag * This is the method responsible for actually testing that the tag
* implemented by this class is enclosed within an {@code IncludeTag}. * implemented by this class is enclosed within an {@code IncludeTag}.
* *
* @exception JspException * @exception JspException
*/ */
/* /*
protected void checkEnclosedInIncludeTag() throws JspException { protected void checkEnclosedInIncludeTag() throws JspException {
Tag parentTag = getParent(); Tag parentTag = getParent();
if ((parentTag != null) && (parentTag instanceof IncludeTag)) { if ((parentTag != null) && (parentTag instanceof IncludeTag)) {
return; return;
} }
String msg = "A class that extends EnclosedIncludeBodyReaderTag " + String msg = "A class that extends EnclosedIncludeBodyReaderTag " +
"is not enclosed within an IncludeTag."; "is not enclosed within an IncludeTag.";
log(msg); log(msg);
throw new JspException(msg); throw new JspException(msg);
} }
*/ */
/** /**
* This method adds the parameter whose name and value were passed to this * This method adds the parameter whose name and value were passed to this
* object via the tag attributes to the parent {@code Include} tag. * object via the tag attributes to the parent {@code Include} tag.
*/ */
private void addParameter() { private void addParameter() {
IncludeTag includeTag = (IncludeTag) getParent(); IncludeTag includeTag = (IncludeTag) getParent();
includeTag.addParameter(parameterName, parameterValue); includeTag.addParameter(parameterName, parameterValue);
} }
/** /**
* This method cleans up the member variables for this tag in preparation * This method cleans up the member variables for this tag in preparation
* for being used again. This method is called when the tag finishes it's * for being used again. This method is called when the tag finishes it's
* current call with in the page but could be called upon again within this * current call with in the page but could be called upon again within this
* same page. This method is also called in the release stage of the tag * same page. This method is also called in the release stage of the tag
* life cycle just in case a JspException was thrown during the tag * life cycle just in case a JspException was thrown during the tag
* execution. * execution.
*/ */
protected void clearServiceState() { protected void clearServiceState() {
parameterName = null; parameterName = null;
parameterValue = null; parameterValue = null;
} }
} }

View File

@@ -1,47 +1,47 @@
/* /*
* Copyright (c) 2002 TwelveMonkeys. * Copyright (c) 2002 TwelveMonkeys.
* All rights reserved. * All rights reserved.
* *
* $Log: ValueOfTEI.java,v $ * $Log: ValueOfTEI.java,v $
* Revision 1.3 2003/10/06 14:26:07 WMHAKUR * Revision 1.3 2003/10/06 14:26:07 WMHAKUR
* Code clean-up only. * Code clean-up only.
* *
* Revision 1.2 2002/10/18 14:28:07 WMHAKUR * Revision 1.2 2002/10/18 14:28:07 WMHAKUR
* Fixed package error. * Fixed package error.
* *
* Revision 1.1 2002/10/18 14:03:52 WMHAKUR * Revision 1.1 2002/10/18 14:03:52 WMHAKUR
* Moved to com.twelvemonkeys.servlet.jsp.droplet.taglib * Moved to com.twelvemonkeys.servlet.jsp.droplet.taglib
* *
* *
*/ */
package com.twelvemonkeys.servlet.jsp.droplet.taglib; package com.twelvemonkeys.servlet.jsp.droplet.taglib;
import javax.servlet.jsp.tagext.TagData; import javax.servlet.jsp.tagext.TagData;
import javax.servlet.jsp.tagext.TagExtraInfo; import javax.servlet.jsp.tagext.TagExtraInfo;
/** /**
* TagExtraInfo for ValueOf. * TagExtraInfo for ValueOf.
* @todo More meaningful response to the user. * @todo More meaningful response to the user.
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $ * @author last modified by $Author: haku $
* *
* @version $Revision: #1 $, ($Date: 2008/05/05 $) * @version $Revision: #1 $, ($Date: 2008/05/05 $)
* *
*/ */
public class ValueOfTEI extends TagExtraInfo { public class ValueOfTEI extends TagExtraInfo {
public boolean isValid(TagData pTagData) { public boolean isValid(TagData pTagData) {
Object nameAttr = pTagData.getAttribute("name"); Object nameAttr = pTagData.getAttribute("name");
Object paramAttr = pTagData.getAttribute("param"); Object paramAttr = pTagData.getAttribute("param");
if ((nameAttr != null && paramAttr == null) || (nameAttr == null && paramAttr != null)) { if ((nameAttr != null && paramAttr == null) || (nameAttr == null && paramAttr != null)) {
return true; // Exactly one of name or param set return true; // Exactly one of name or param set
} }
// Either both or none, // Either both or none,
return false; return false;
} }
} }

View File

@@ -1,148 +1,148 @@
/* /*
* Copyright (c) 2002 TwelveMonkeys. * Copyright (c) 2002 TwelveMonkeys.
* All rights reserved. * All rights reserved.
* *
* $Log: ValueOfTag.java,v $ * $Log: ValueOfTag.java,v $
* Revision 1.2 2003/10/06 14:26:14 WMHAKUR * Revision 1.2 2003/10/06 14:26:14 WMHAKUR
* Code clean-up only. * Code clean-up only.
* *
* Revision 1.1 2002/10/18 14:03:52 WMHAKUR * Revision 1.1 2002/10/18 14:03:52 WMHAKUR
* Moved to com.twelvemonkeys.servlet.jsp.droplet.taglib * Moved to com.twelvemonkeys.servlet.jsp.droplet.taglib
* *
* *
*/ */
package com.twelvemonkeys.servlet.jsp.droplet.taglib; package com.twelvemonkeys.servlet.jsp.droplet.taglib;
import com.twelvemonkeys.servlet.jsp.droplet.JspFragment; import com.twelvemonkeys.servlet.jsp.droplet.JspFragment;
import com.twelvemonkeys.servlet.jsp.taglib.ExTagSupport; import com.twelvemonkeys.servlet.jsp.taglib.ExTagSupport;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter; import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.PageContext; import javax.servlet.jsp.PageContext;
import java.io.IOException; import java.io.IOException;
/** /**
* ValueOf tag that emulates ATG Dynamo JHTML behaviour for JSP. * ValueOf tag that emulates ATG Dynamo JHTML behaviour for JSP.
* *
* @author Thomas Purcell (CSC Australia) * @author Thomas Purcell (CSC Australia)
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $ * @author last modified by $Author: haku $
* *
* @version $Revision: #1 $, ($Date: 2008/05/05 $) * @version $Revision: #1 $, ($Date: 2008/05/05 $)
*/ */
public class ValueOfTag extends ExTagSupport { public class ValueOfTag extends ExTagSupport {
/** /**
* This is the name of the parameter whose value is to be inserted into * This is the name of the parameter whose value is to be inserted into
* the current JSP page. This value will be set via the {@code name} * the current JSP page. This value will be set via the {@code name}
* attribute. * attribute.
*/ */
private String parameterName; private String parameterName;
/** /**
* This is the value of the parameter read from the {@code * This is the value of the parameter read from the {@code
* PageContext.REQUEST_SCOPE} scope. If the parameter doesn't exist, * PageContext.REQUEST_SCOPE} scope. If the parameter doesn't exist,
* then this will be null. * then this will be null.
*/ */
private Object parameterValue; private Object parameterValue;
/** /**
* This method is called as part of the initialisation phase of the tag * This method is called as part of the initialisation phase of the tag
* life cycle. It sets the parameter name to be read from the {@code * life cycle. It sets the parameter name to be read from the {@code
* PageContext.REQUEST_SCOPE} scope. * PageContext.REQUEST_SCOPE} scope.
* *
* @param pName The name of the parameter to be read from the {@code * @param pName The name of the parameter to be read from the {@code
* PageContext.REQUEST_SCOPE} scope. * PageContext.REQUEST_SCOPE} scope.
*/ */
public void setName(String pName) { public void setName(String pName) {
parameterName = pName; parameterName = pName;
} }
/** /**
* This method is called as part of the initialisation phase of the tag * This method is called as part of the initialisation phase of the tag
* life cycle. It sets the parameter name to be read from the {@code * life cycle. It sets the parameter name to be read from the {@code
* PageContext.REQUEST_SCOPE} scope. This is just a synonym for * PageContext.REQUEST_SCOPE} scope. This is just a synonym for
* setName, to be more like ATG Dynamo. * setName, to be more like ATG Dynamo.
* *
* @param pName The name of the parameter to be read from the {@code * @param pName The name of the parameter to be read from the {@code
* PageContext.REQUEST_SCOPE} scope. * PageContext.REQUEST_SCOPE} scope.
*/ */
public void setParam(String pName) { public void setParam(String pName) {
parameterName = pName; parameterName = pName;
} }
/** /**
* This method looks in the session scope for the session-scoped attribute * This method looks in the session scope for the session-scoped attribute
* whose name matches the {@code name} tag attribute for this tag. * whose name matches the {@code name} tag attribute for this tag.
* If it finds it, then it replaces this tag with the value for the * If it finds it, then it replaces this tag with the value for the
* session-scoped attribute. If it fails to find the session-scoped * session-scoped attribute. If it fails to find the session-scoped
* attribute, it displays the body for this tag. * attribute, it displays the body for this tag.
* *
* @return If the session-scoped attribute is found, then this method will * @return If the session-scoped attribute is found, then this method will
* return {@code TagSupport.SKIP_BODY}, otherwise it will return * return {@code TagSupport.SKIP_BODY}, otherwise it will return
* {@code TagSupport.EVAL_BODY_INCLUDE}. * {@code TagSupport.EVAL_BODY_INCLUDE}.
* @exception JspException * @exception JspException
* *
*/ */
public int doStartTag() throws JspException { public int doStartTag() throws JspException {
try { try {
if (parameterExists()) { if (parameterExists()) {
if (parameterValue instanceof JspFragment) { if (parameterValue instanceof JspFragment) {
// OPARAM or PARAM // OPARAM or PARAM
((JspFragment) parameterValue).service(pageContext); ((JspFragment) parameterValue).service(pageContext);
/* /*
log("Service subpage " + pageContext.getServletContext().getRealPath(((Oparam) parameterValue).getName())); log("Service subpage " + pageContext.getServletContext().getRealPath(((Oparam) parameterValue).getName()));
pageContext.include(((Oparam) parameterValue).getName()); pageContext.include(((Oparam) parameterValue).getName());
*/ */
} }
else { else {
// Normal JSP parameter value // Normal JSP parameter value
JspWriter writer = pageContext.getOut(); JspWriter writer = pageContext.getOut();
writer.print(parameterValue); writer.print(parameterValue);
} }
return SKIP_BODY; return SKIP_BODY;
} }
else { else {
return EVAL_BODY_INCLUDE; return EVAL_BODY_INCLUDE;
} }
} }
catch (ServletException se) { catch (ServletException se) {
log(se.getMessage(), se); log(se.getMessage(), se);
throw new JspException(se); throw new JspException(se);
} }
catch (IOException ioe) { catch (IOException ioe) {
String msg = "Caught an IOException in ValueOfTag.doStartTag()\n" String msg = "Caught an IOException in ValueOfTag.doStartTag()\n"
+ ioe.toString(); + ioe.toString();
log(msg, ioe); log(msg, ioe);
throw new JspException(msg); throw new JspException(msg);
} }
} }
/** /**
* This method is used to determine whether the parameter whose name is * This method is used to determine whether the parameter whose name is
* stored in {@code mParameterName} exists within the {@code * stored in {@code mParameterName} exists within the {@code
* PageContext.REQUEST_SCOPE} scope. If the parameter does exist, * PageContext.REQUEST_SCOPE} scope. If the parameter does exist,
* then this method will return {@code true}, otherwise it returns * then this method will return {@code true}, otherwise it returns
* {@code false}. This method has the side affect of loading the * {@code false}. This method has the side affect of loading the
* parameter value into {@code mParameterValue} if the parameter * parameter value into {@code mParameterValue} if the parameter
* does exist. * does exist.
* *
* @return {@code true} if the parameter whose name is in {@code * @return {@code true} if the parameter whose name is in {@code
* mParameterName} exists in the {@code PageContext.REQUEST_SCOPE * mParameterName} exists in the {@code PageContext.REQUEST_SCOPE
* } scope, {@code false} otherwise. * } scope, {@code false} otherwise.
*/ */
private boolean parameterExists() { private boolean parameterExists() {
parameterValue = pageContext.getAttribute(parameterName, PageContext.REQUEST_SCOPE); parameterValue = pageContext.getAttribute(parameterName, PageContext.REQUEST_SCOPE);
// -- Harald K 20020726 // -- Harald K 20020726
if (parameterValue == null) { if (parameterValue == null) {
parameterValue = pageContext.getRequest().getParameter(parameterName); parameterValue = pageContext.getRequest().getParameter(parameterName);
} }
return (parameterValue != null); return (parameterValue != null);
} }
} }

View File

@@ -1,39 +1,39 @@
package com.twelvemonkeys.servlet.jsp.taglib; package com.twelvemonkeys.servlet.jsp.taglib;
import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspException;
/** /**
* *
* *
* @author Thomas Purcell (CSC Australia) * @author Thomas Purcell (CSC Australia)
* *
* @version 1.0 * @version 1.0
*/ */
public abstract class BodyReaderTag extends ExBodyTagSupport { public abstract class BodyReaderTag extends ExBodyTagSupport {
/** /**
* This is the method called by the JSP engine when the body for a tag * This is the method called by the JSP engine when the body for a tag
* has been parsed and is ready for inclusion in this current tag. This * has been parsed and is ready for inclusion in this current tag. This
* method takes the content as a string and passes it to the {@code * method takes the content as a string and passes it to the {@code
* processBody} method. * processBody} method.
* *
* @return This method returns the {@code BodyTagSupport.SKIP_BODY} * @return This method returns the {@code BodyTagSupport.SKIP_BODY}
* constant. This means that the body of the tag will only be * constant. This means that the body of the tag will only be
* processed the one time. * processed the one time.
* @exception JspException * @exception JspException
*/ */
public int doAfterBody() throws JspException { public int doAfterBody() throws JspException {
processBody(bodyContent.getString()); processBody(bodyContent.getString());
return SKIP_BODY; return SKIP_BODY;
} }
/** /**
* This is the method that child classes must implement. It takes the * This is the method that child classes must implement. It takes the
* body of the tag converted to a String as it's parameter. The body of * body of the tag converted to a String as it's parameter. The body of
* the tag will have been interpreted to a String by the JSP engine before * the tag will have been interpreted to a String by the JSP engine before
* this method is called. * this method is called.
* *
* @param pContent The body for the custom tag converted to a String. * @param pContent The body for the custom tag converted to a String.
* @exception JspException * @exception JspException
*/ */
protected abstract void processBody(String pContent) throws JspException; protected abstract void processBody(String pContent) throws JspException;
} }

View File

@@ -1,235 +1,235 @@
/* /*
* Copyright (c) 2002 TwelveMonkeys. * Copyright (c) 2002 TwelveMonkeys.
* All rights reserved. * All rights reserved.
* *
* $Log: CSVToTableTag.java,v $ * $Log: CSVToTableTag.java,v $
* Revision 1.3 2003/10/06 14:24:50 WMHAKUR * Revision 1.3 2003/10/06 14:24:50 WMHAKUR
* Code clean-up only. * Code clean-up only.
* *
* Revision 1.2 2002/11/26 17:33:49 WMHAKUR * Revision 1.2 2002/11/26 17:33:49 WMHAKUR
* Added documentation & removed System.out.println()s. * Added documentation & removed System.out.println()s.
* *
* Revision 1.1 2002/11/19 10:50:10 WMHAKUR * Revision 1.1 2002/11/19 10:50:10 WMHAKUR
* Renamed from CSVToTable, to follow naming conventions. * Renamed from CSVToTable, to follow naming conventions.
* *
* Revision 1.1 2002/11/18 22:11:16 WMHAKUR * Revision 1.1 2002/11/18 22:11:16 WMHAKUR
* Tag to convert CSV to HTML table. * Tag to convert CSV to HTML table.
* Can be further transformed, using XSLT. * Can be further transformed, using XSLT.
* *
*/ */
package com.twelvemonkeys.servlet.jsp.taglib; package com.twelvemonkeys.servlet.jsp.taglib;
import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter; import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.BodyContent; import javax.servlet.jsp.tagext.BodyContent;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.Reader; import java.io.Reader;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.StringTokenizer; import java.util.StringTokenizer;
/** /**
* Creates a table from a string of "comma-separated values" (CSV). * Creates a table from a string of "comma-separated values" (CSV).
* The delimiter character can be any character (or combination of characters). * The delimiter character can be any character (or combination of characters).
* The default delimiter is TAB ({@code \t}). * The default delimiter is TAB ({@code \t}).
* *
* <P/> * <P/>
* <HR/> * <HR/>
* <P/> * <P/>
* *
* The input may look like this: * The input may look like this:
* <PRE> * <PRE>
* &lt;c:totable firstRowIsHeader="true" delimiter=";"&gt; * &lt;c:totable firstRowIsHeader="true" delimiter=";"&gt;
* header A;header B * header A;header B
* data 1A; data 1B * data 1A; data 1B
* data 2A; data 2B * data 2A; data 2B
* &lt;/c:totable&gt; * &lt;/c:totable&gt;
* </PRE> * </PRE>
* *
* The output (source) will look like this: * The output (source) will look like this:
* <PRE> * <PRE>
* &lt;TABLE&gt; * &lt;TABLE&gt;
* &lt;TR&gt; * &lt;TR&gt;
* &lt;TH&gt;header A&lt;/TH&gt;&lt;TH&gt;header B&lt;/TH&gt; * &lt;TH&gt;header A&lt;/TH&gt;&lt;TH&gt;header B&lt;/TH&gt;
* &lt;/TR&gt; * &lt;/TR&gt;
* &lt;TR&gt; * &lt;TR&gt;
* &lt;TD&gt;data 1A&lt;/TD&gt;&lt;TD&gt;data 1B&lt;/TD&gt; * &lt;TD&gt;data 1A&lt;/TD&gt;&lt;TD&gt;data 1B&lt;/TD&gt;
* &lt;/TR&gt; * &lt;/TR&gt;
* &lt;TR&gt; * &lt;TR&gt;
* &lt;TD&gt;data 2A&lt;/TD&gt;&lt;TD&gt;data 2B&lt;/TD&gt; * &lt;TD&gt;data 2A&lt;/TD&gt;&lt;TD&gt;data 2B&lt;/TD&gt;
* &lt;/TR&gt; * &lt;/TR&gt;
* &lt;/TABLE&gt; * &lt;/TABLE&gt;
* </PRE> * </PRE>
* You wil probably want to use XSLT to make the final output look nicer. :-) * You wil probably want to use XSLT to make the final output look nicer. :-)
* *
* @see StringTokenizer * @see StringTokenizer
* @see <A href="http://www.w3.org/TR/xslt">XSLT spec</A> * @see <A href="http://www.w3.org/TR/xslt">XSLT spec</A>
* *
* @author Harald Kuhr * @author Harald Kuhr
* *
* @version $Id: jsp/taglib/CSVToTableTag.java#1 $ * @version $Id: jsp/taglib/CSVToTableTag.java#1 $
*/ */
public class CSVToTableTag extends ExBodyTagSupport { public class CSVToTableTag extends ExBodyTagSupport {
public final static String TAB = "\t"; public final static String TAB = "\t";
protected String delimiter = null; protected String delimiter = null;
protected boolean firstRowIsHeader = false; protected boolean firstRowIsHeader = false;
protected boolean firstColIsHeader = false; protected boolean firstColIsHeader = false;
public void setDelimiter(String pDelimiter) { public void setDelimiter(String pDelimiter) {
delimiter = pDelimiter; delimiter = pDelimiter;
} }
public String getDelimiter() { public String getDelimiter() {
return delimiter != null ? delimiter : TAB; return delimiter != null ? delimiter : TAB;
} }
public void setFirstRowIsHeader(String pBoolean) { public void setFirstRowIsHeader(String pBoolean) {
firstRowIsHeader = Boolean.valueOf(pBoolean); firstRowIsHeader = Boolean.valueOf(pBoolean);
} }
public void setFirstColIsHeader(String pBoolean) { public void setFirstColIsHeader(String pBoolean) {
firstColIsHeader = Boolean.valueOf(pBoolean); firstColIsHeader = Boolean.valueOf(pBoolean);
} }
public int doEndTag() throws JspException { public int doEndTag() throws JspException {
BodyContent content = getBodyContent(); BodyContent content = getBodyContent();
try { try {
Table table = Table table =
Table.parseContent(content.getReader(), getDelimiter()); Table.parseContent(content.getReader(), getDelimiter());
JspWriter out = pageContext.getOut(); JspWriter out = pageContext.getOut();
//System.out.println("CSVToTable: " + table.getRows() + " rows, " //System.out.println("CSVToTable: " + table.getRows() + " rows, "
// + table.getCols() + " cols."); // + table.getCols() + " cols.");
if (table.getRows() > 0) { if (table.getRows() > 0) {
out.println("<TABLE>"); out.println("<TABLE>");
// Loop over rows // Loop over rows
for (int row = 0; row < table.getRows(); row++) { for (int row = 0; row < table.getRows(); row++) {
out.println("<TR>"); out.println("<TR>");
// Loop over cells in each row // Loop over cells in each row
for (int col = 0; col < table.getCols(); col++) { for (int col = 0; col < table.getCols(); col++) {
// Test if we are using headers, else normal cell // Test if we are using headers, else normal cell
if (firstRowIsHeader && row == 0 || firstColIsHeader && col == 0) { if (firstRowIsHeader && row == 0 || firstColIsHeader && col == 0) {
out.println("<TH>" + table.get(row, col) + " </TH>"); out.println("<TH>" + table.get(row, col) + " </TH>");
} }
else { else {
out.println("<TD>" + table.get(row, col) + " </TD>"); out.println("<TD>" + table.get(row, col) + " </TD>");
} }
} }
out.println("</TR>"); out.println("</TR>");
} }
out.println("</TABLE>"); out.println("</TABLE>");
} }
} }
catch (IOException ioe) { catch (IOException ioe) {
throw new JspException(ioe); throw new JspException(ioe);
} }
return super.doEndTag(); return super.doEndTag();
} }
static class Table { static class Table {
List rows = null; List rows = null;
int cols = 0; int cols = 0;
private Table(List pRows, int pCols) { private Table(List pRows, int pCols) {
rows = pRows; rows = pRows;
cols = pCols; cols = pCols;
} }
int getRows() { int getRows() {
return rows != null ? rows.size() : 0; return rows != null ? rows.size() : 0;
} }
int getCols() { int getCols() {
return cols; return cols;
} }
List getTableRows() { List getTableRows() {
return rows; return rows;
} }
List getTableRow(int pRow) { List getTableRow(int pRow) {
return rows != null return rows != null
? (List) rows.get(pRow) ? (List) rows.get(pRow)
: Collections.EMPTY_LIST; : Collections.EMPTY_LIST;
} }
String get(int pRow, int pCol) { String get(int pRow, int pCol) {
List row = getTableRow(pRow); List row = getTableRow(pRow);
// Rows may contain unequal number of cols // Rows may contain unequal number of cols
return (row.size() > pCol) ? (String) row.get(pCol) : ""; return (row.size() > pCol) ? (String) row.get(pCol) : "";
} }
/** /**
* Parses a BodyContent to a table. * Parses a BodyContent to a table.
* *
*/ */
static Table parseContent(Reader pContent, String pDelim) throws IOException { static Table parseContent(Reader pContent, String pDelim) throws IOException {
List<List<String>> tableRows = new ArrayList<List<String>>(); List<List<String>> tableRows = new ArrayList<List<String>>();
int tdsPerTR = 0; int tdsPerTR = 0;
// Loop through TRs // Loop through TRs
BufferedReader reader = new BufferedReader(pContent); BufferedReader reader = new BufferedReader(pContent);
String tr; String tr;
while ((tr = reader.readLine()) != null) { while ((tr = reader.readLine()) != null) {
// Discard blank lines // Discard blank lines
if (tr.trim().length() <= 0 && tr.indexOf(pDelim) < 0) { if (tr.trim().length() <= 0 && tr.indexOf(pDelim) < 0) {
continue; continue;
} }
//System.out.println("CSVToTable: read LINE=\"" + tr + "\""); //System.out.println("CSVToTable: read LINE=\"" + tr + "\"");
List<String> tableDatas = new ArrayList<String>(); List<String> tableDatas = new ArrayList<String>();
StringTokenizer tableRow = new StringTokenizer(tr, pDelim, StringTokenizer tableRow = new StringTokenizer(tr, pDelim,
true); true);
boolean lastWasDelim = false; boolean lastWasDelim = false;
while (tableRow.hasMoreTokens()) { while (tableRow.hasMoreTokens()) {
String td = tableRow.nextToken(); String td = tableRow.nextToken();
//System.out.println("CSVToTable: read data=\"" + td + "\""); //System.out.println("CSVToTable: read data=\"" + td + "\"");
// Test if we have empty TD // Test if we have empty TD
if (td.equals(pDelim)) { if (td.equals(pDelim)) {
if (lastWasDelim) { if (lastWasDelim) {
// Add empty TD // Add empty TD
tableDatas.add(""); tableDatas.add("");
} }
// We just read a delimitter // We just read a delimitter
lastWasDelim = true; lastWasDelim = true;
} }
else { else {
// No tab, normal data // No tab, normal data
lastWasDelim = false; lastWasDelim = false;
// Add normal TD // Add normal TD
tableDatas.add(td); tableDatas.add(td);
} }
} // end while (tableRow.hasNext()) } // end while (tableRow.hasNext())
// Store max TD count // Store max TD count
if (tableDatas.size() > tdsPerTR) { if (tableDatas.size() > tdsPerTR) {
tdsPerTR = tableDatas.size(); tdsPerTR = tableDatas.size();
} }
// Add a table row // Add a table row
tableRows.add(tableDatas); tableRows.add(tableDatas);
} }
// Return TABLE // Return TABLE
return new Table(tableRows, tdsPerTR); return new Table(tableRows, tdsPerTR);
} }
} }
} }

View File

@@ -1,290 +1,290 @@
/* /*
* Copyright (c) 2002 TwelveMonkeys. * Copyright (c) 2002 TwelveMonkeys.
* All rights reserved. * All rights reserved.
* *
* $Log: ExBodyTagSupport.java,v $ * $Log: ExBodyTagSupport.java,v $
* Revision 1.3 2003/10/06 14:24:57 WMHAKUR * Revision 1.3 2003/10/06 14:24:57 WMHAKUR
* Code clean-up only. * Code clean-up only.
* *
* Revision 1.2 2002/11/18 22:10:27 WMHAKUR * Revision 1.2 2002/11/18 22:10:27 WMHAKUR
* *** empty log message *** * *** empty log message ***
* *
* *
*/ */
package com.twelvemonkeys.servlet.jsp.taglib; package com.twelvemonkeys.servlet.jsp.taglib;
import javax.servlet.ServletConfig; import javax.servlet.ServletConfig;
import javax.servlet.ServletContext; import javax.servlet.ServletContext;
import javax.servlet.ServletRequest; import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter; import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.PageContext; import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.BodyTagSupport; import javax.servlet.jsp.tagext.BodyTagSupport;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.StringTokenizer; import java.util.StringTokenizer;
/** /**
* This is the class that should be extended by all jsp pages that do use their * This is the class that should be extended by all jsp pages that do use their
* body. It contains a lot of helper methods for simplifying common tasks. * body. It contains a lot of helper methods for simplifying common tasks.
* *
* @author Thomas Purcell (CSC Australia) * @author Thomas Purcell (CSC Australia)
* @author Harald Kuhr * @author Harald Kuhr
* *
* @version $Id: jsp/taglib/ExBodyTagSupport.java#1 $ * @version $Id: jsp/taglib/ExBodyTagSupport.java#1 $
*/ */
public class ExBodyTagSupport extends BodyTagSupport implements ExTag { public class ExBodyTagSupport extends BodyTagSupport implements ExTag {
/** /**
* writeHtml ensures that the text being outputted appears as it was * writeHtml ensures that the text being outputted appears as it was
* entered. This prevents users from hacking the system by entering * entered. This prevents users from hacking the system by entering
* html or jsp code into an entry form where that value will be displayed * html or jsp code into an entry form where that value will be displayed
* later in the site. * later in the site.
* *
* @param pOut The JspWriter to write the output to. * @param pOut The JspWriter to write the output to.
* @param pHtml The original html to filter and output to the user. * @param pHtml The original html to filter and output to the user.
* @throws IOException If the user clicks Stop in the browser, or their * @throws IOException If the user clicks Stop in the browser, or their
* browser crashes, then the JspWriter will throw an IOException when * browser crashes, then the JspWriter will throw an IOException when
* the jsp tries to write to it. * the jsp tries to write to it.
*/ */
public void writeHtml(JspWriter pOut, String pHtml) throws IOException { public void writeHtml(JspWriter pOut, String pHtml) throws IOException {
StringTokenizer parser = new StringTokenizer(pHtml, "<>&", true); StringTokenizer parser = new StringTokenizer(pHtml, "<>&", true);
while (parser.hasMoreTokens()) { while (parser.hasMoreTokens()) {
String token = parser.nextToken(); String token = parser.nextToken();
if (token.equals("<")) { if (token.equals("<")) {
pOut.print("&lt;"); pOut.print("&lt;");
} }
else if (token.equals(">")) { else if (token.equals(">")) {
pOut.print("&gt;"); pOut.print("&gt;");
} }
else if (token.equals("&")) { else if (token.equals("&")) {
pOut.print("&amp;"); pOut.print("&amp;");
} }
else { else {
pOut.print(token); pOut.print(token);
} }
} }
} }
/** /**
* Log a message to the servlet context. * Log a message to the servlet context.
* *
* @param pMsg The error message to log. * @param pMsg The error message to log.
*/ */
public void log(String pMsg) { public void log(String pMsg) {
getServletContext().log(pMsg); getServletContext().log(pMsg);
} }
/** /**
* Log a message to the servlet context and include the exception that is * Log a message to the servlet context and include the exception that is
* passed in as the second parameter. * passed in as the second parameter.
* *
* @param pMsg The error message to log. * @param pMsg The error message to log.
* @param pException The exception that caused this error message to be * @param pException The exception that caused this error message to be
* logged. * logged.
*/ */
public void log(String pMsg, Throwable pException) { public void log(String pMsg, Throwable pException) {
getServletContext().log(pMsg, pException); getServletContext().log(pMsg, pException);
} }
/** /**
* Retrieves the ServletContext object associated with the current * Retrieves the ServletContext object associated with the current
* PageContext object. * PageContext object.
* *
* @return The ServletContext object associated with the current * @return The ServletContext object associated with the current
* PageContext object. * PageContext object.
*/ */
public ServletContext getServletContext() { public ServletContext getServletContext() {
return pageContext.getServletContext(); return pageContext.getServletContext();
} }
/** /**
* Called when the tag has finished running. Any clean up that needs * Called when the tag has finished running. Any clean up that needs
* to be done between calls to this tag but within the same JSP page is * to be done between calls to this tag but within the same JSP page is
* called in the {@code clearServiceState()} method call. * called in the {@code clearServiceState()} method call.
* *
* @exception JspException * @exception JspException
*/ */
public int doEndTag() throws JspException { public int doEndTag() throws JspException {
clearServiceState(); clearServiceState();
return super.doEndTag(); return super.doEndTag();
} }
/** /**
* Called when a tag's role in the current JSP page is finished. After * Called when a tag's role in the current JSP page is finished. After
* the {@code clearProperties()} method is called, the custom tag * the {@code clearProperties()} method is called, the custom tag
* should be in an identical state as when it was first created. The * should be in an identical state as when it was first created. The
* {@code clearServiceState()} method is called here just in case an * {@code clearServiceState()} method is called here just in case an
* exception was thrown in the custom tag. If an exception was thrown, * exception was thrown in the custom tag. If an exception was thrown,
* then the {@code doEndTag()} method will not have been called and * then the {@code doEndTag()} method will not have been called and
* the tag might not have been cleaned up properly. * the tag might not have been cleaned up properly.
*/ */
public void release() { public void release() {
clearServiceState(); clearServiceState();
clearProperties(); clearProperties();
super.release(); super.release();
} }
/** /**
* The default implementation for the {@code clearProperties()}. Not * The default implementation for the {@code clearProperties()}. Not
* all tags will need to overload this method call. By implementing it * all tags will need to overload this method call. By implementing it
* here, all classes that extend this object are able to call {@code * here, all classes that extend this object are able to call {@code
* super.clearProperties()}. So, if the class extends a different * super.clearProperties()}. So, if the class extends a different
* tag, or this one, the parent method should always be called. This * tag, or this one, the parent method should always be called. This
* method will be called when the tag is to be released. That is, the * method will be called when the tag is to be released. That is, the
* tag has finished for the current page and should be returned to it's * tag has finished for the current page and should be returned to it's
* initial state. * initial state.
*/ */
protected void clearProperties() { protected void clearProperties() {
} }
/** /**
* The default implementation for the {@code clearServiceState()}. * The default implementation for the {@code clearServiceState()}.
* Not all tags will need to overload this method call. By implementing it * Not all tags will need to overload this method call. By implementing it
* here, all classes that extend this object are able to call {@code * here, all classes that extend this object are able to call {@code
* super.clearServiceState()}. So, if the class extends a different * super.clearServiceState()}. So, if the class extends a different
* tag, or this one, the parent method should always be called. This * tag, or this one, the parent method should always be called. This
* method will be called when the tag has finished it's current tag * method will be called when the tag has finished it's current tag
* within the page, but may be called upon again in this same JSP page. * within the page, but may be called upon again in this same JSP page.
*/ */
protected void clearServiceState() { protected void clearServiceState() {
} }
/** /**
* Returns the initialisation parameter from the {@code * Returns the initialisation parameter from the {@code
* PageContext.APPLICATION_SCOPE} scope. These initialisation * PageContext.APPLICATION_SCOPE} scope. These initialisation
* parameters are defined in the {@code web.xml} configuration file. * parameters are defined in the {@code web.xml} configuration file.
* *
* @param pName The name of the initialisation parameter to return the * @param pName The name of the initialisation parameter to return the
* value for. * value for.
* @return The value for the parameter whose name was passed in as a * @return The value for the parameter whose name was passed in as a
* parameter. If the parameter does not exist, then {@code null} * parameter. If the parameter does not exist, then {@code null}
* will be returned. * will be returned.
*/ */
public String getInitParameter(String pName) { public String getInitParameter(String pName) {
return getInitParameter(pName, PageContext.APPLICATION_SCOPE); return getInitParameter(pName, PageContext.APPLICATION_SCOPE);
} }
/** /**
* Returns an Enumeration containing all the names for all the * Returns an Enumeration containing all the names for all the
* initialisation parametes defined in the {@code * initialisation parametes defined in the {@code
* PageContext.APPLICATION_SCOPE} scope. * PageContext.APPLICATION_SCOPE} scope.
* *
* @return An {@code Enumeration} containing all the names for all the * @return An {@code Enumeration} containing all the names for all the
* initialisation parameters. * initialisation parameters.
*/ */
public Enumeration getInitParameterNames() { public Enumeration getInitParameterNames() {
return getInitParameterNames(PageContext.APPLICATION_SCOPE); return getInitParameterNames(PageContext.APPLICATION_SCOPE);
} }
/** /**
* Returns the initialisation parameter from the scope specified with the * Returns the initialisation parameter from the scope specified with the
* name specified. * name specified.
* *
* @param pName The name of the initialisation parameter to return the * @param pName The name of the initialisation parameter to return the
* value for. * value for.
* @param pScope The scope to search for the initialisation parameter * @param pScope The scope to search for the initialisation parameter
* within. * within.
* @return The value of the parameter found. If no parameter with the * @return The value of the parameter found. If no parameter with the
* name specified is found in the scope specified, then {@code null * name specified is found in the scope specified, then {@code null
* } is returned. * } is returned.
*/ */
public String getInitParameter(String pName, int pScope) { public String getInitParameter(String pName, int pScope) {
switch (pScope) { switch (pScope) {
case PageContext.PAGE_SCOPE: case PageContext.PAGE_SCOPE:
return getServletConfig().getInitParameter(pName); return getServletConfig().getInitParameter(pName);
case PageContext.APPLICATION_SCOPE: case PageContext.APPLICATION_SCOPE:
return getServletContext().getInitParameter(pName); return getServletContext().getInitParameter(pName);
default: default:
throw new IllegalArgumentException("Illegal scope."); throw new IllegalArgumentException("Illegal scope.");
} }
} }
/** /**
* Returns an enumeration containing all the parameters defined in the * Returns an enumeration containing all the parameters defined in the
* scope specified by the parameter. * scope specified by the parameter.
* *
* @param pScope The scope to return the names of all the parameters * @param pScope The scope to return the names of all the parameters
* defined within. * defined within.
* @return An {@code Enumeration} containing all the names for all the * @return An {@code Enumeration} containing all the names for all the
* parameters defined in the scope passed in as a parameter. * parameters defined in the scope passed in as a parameter.
*/ */
public Enumeration getInitParameterNames(int pScope) { public Enumeration getInitParameterNames(int pScope) {
switch (pScope) { switch (pScope) {
case PageContext.PAGE_SCOPE: case PageContext.PAGE_SCOPE:
return getServletConfig().getInitParameterNames(); return getServletConfig().getInitParameterNames();
case PageContext.APPLICATION_SCOPE: case PageContext.APPLICATION_SCOPE:
return getServletContext().getInitParameterNames(); return getServletContext().getInitParameterNames();
default: default:
throw new IllegalArgumentException("Illegal scope"); throw new IllegalArgumentException("Illegal scope");
} }
} }
/** /**
* Returns the servlet config associated with the current JSP page request. * Returns the servlet config associated with the current JSP page request.
* *
* @return The {@code ServletConfig} associated with the current * @return The {@code ServletConfig} associated with the current
* request. * request.
*/ */
public ServletConfig getServletConfig() { public ServletConfig getServletConfig() {
return pageContext.getServletConfig(); return pageContext.getServletConfig();
} }
/** /**
* Gets the context path associated with the current JSP page request. * Gets the context path associated with the current JSP page request.
* If the request is not a HttpServletRequest, this method will * If the request is not a HttpServletRequest, this method will
* return "/". * return "/".
* *
* @return a path relative to the current context's root, or * @return a path relative to the current context's root, or
* {@code "/"} if this is not a HTTP request. * {@code "/"} if this is not a HTTP request.
*/ */
public String getContextPath() { public String getContextPath() {
ServletRequest request = pageContext.getRequest(); ServletRequest request = pageContext.getRequest();
if (request instanceof HttpServletRequest) { if (request instanceof HttpServletRequest) {
return ((HttpServletRequest) request).getContextPath(); return ((HttpServletRequest) request).getContextPath();
} }
return "/"; return "/";
} }
/** /**
* Gets the resource associated with the given relative path for the * Gets the resource associated with the given relative path for the
* current JSP page request. * current JSP page request.
* The path may be absolute, or relative to the current context root. * The path may be absolute, or relative to the current context root.
* *
* @param pPath the path * @param pPath the path
* *
* @return a path relative to the current context root * @return a path relative to the current context root
*/ */
public InputStream getResourceAsStream(String pPath) { public InputStream getResourceAsStream(String pPath) {
// throws MalformedURLException { // throws MalformedURLException {
String path = pPath; String path = pPath;
if (pPath != null && !pPath.startsWith("/")) { if (pPath != null && !pPath.startsWith("/")) {
path = getContextPath() + pPath; path = getContextPath() + pPath;
} }
return pageContext.getServletContext().getResourceAsStream(path); return pageContext.getServletContext().getResourceAsStream(path);
} }
} }

View File

@@ -1,163 +1,163 @@
/* /*
* Copyright (c) 2002 TwelveMonkeys. * Copyright (c) 2002 TwelveMonkeys.
* All rights reserved. * All rights reserved.
* *
* $Log: ExTag.java,v $ * $Log: ExTag.java,v $
* Revision 1.2 2003/10/06 14:25:05 WMHAKUR * Revision 1.2 2003/10/06 14:25:05 WMHAKUR
* Code clean-up only. * Code clean-up only.
* *
* Revision 1.1 2002/11/18 22:10:27 WMHAKUR * Revision 1.1 2002/11/18 22:10:27 WMHAKUR
* *** empty log message *** * *** empty log message ***
* *
* *
*/ */
package com.twelvemonkeys.servlet.jsp.taglib; package com.twelvemonkeys.servlet.jsp.taglib;
import javax.servlet.ServletConfig; import javax.servlet.ServletConfig;
import javax.servlet.ServletContext; import javax.servlet.ServletContext;
import javax.servlet.jsp.JspWriter; import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.Tag; import javax.servlet.jsp.tagext.Tag;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Enumeration; import java.util.Enumeration;
/** /**
* This interface contains a lot of helper methods for simplifying common * This interface contains a lot of helper methods for simplifying common
* taglib related tasks. * taglib related tasks.
* *
* @author Harald Kuhr * @author Harald Kuhr
* *
* @version $Id: jsp/taglib/ExTag.java#1 $ * @version $Id: jsp/taglib/ExTag.java#1 $
*/ */
public interface ExTag extends Tag { public interface ExTag extends Tag {
/** /**
* writeHtml ensures that the text being outputted appears as it was * writeHtml ensures that the text being outputted appears as it was
* entered. This prevents users from hacking the system by entering * entered. This prevents users from hacking the system by entering
* html or jsp code into an entry form where that value will be displayed * html or jsp code into an entry form where that value will be displayed
* later in the site. * later in the site.
* *
* @param pOut The JspWriter to write the output to. * @param pOut The JspWriter to write the output to.
* @param pHtml The original html to filter and output to the user. * @param pHtml The original html to filter and output to the user.
* @throws IOException If the user clicks Stop in the browser, or their * @throws IOException If the user clicks Stop in the browser, or their
* browser crashes, then the JspWriter will throw an IOException when * browser crashes, then the JspWriter will throw an IOException when
* the jsp tries to write to it. * the jsp tries to write to it.
*/ */
public void writeHtml(JspWriter pOut, String pHtml) throws IOException; public void writeHtml(JspWriter pOut, String pHtml) throws IOException;
/** /**
* Log a message to the servlet context. * Log a message to the servlet context.
* *
* @param pMsg The error message to log. * @param pMsg The error message to log.
*/ */
public void log(String pMsg); public void log(String pMsg);
/** /**
* Logs a message to the servlet context and include the exception that is * Logs a message to the servlet context and include the exception that is
* passed in as the second parameter. * passed in as the second parameter.
* *
* @param pMsg The error message to log. * @param pMsg The error message to log.
* @param pException The exception that caused this error message to be * @param pException The exception that caused this error message to be
* logged. * logged.
*/ */
public void log(String pMsg, Throwable pException); public void log(String pMsg, Throwable pException);
/** /**
* Retrieves the ServletContext object associated with the current * Retrieves the ServletContext object associated with the current
* PageContext object. * PageContext object.
* *
* @return The ServletContext object associated with the current * @return The ServletContext object associated with the current
* PageContext object. * PageContext object.
*/ */
public ServletContext getServletContext(); public ServletContext getServletContext();
/** /**
* Returns the initialisation parameter from the {@code * Returns the initialisation parameter from the {@code
* PageContext.APPLICATION_SCOPE} scope. These initialisation * PageContext.APPLICATION_SCOPE} scope. These initialisation
* parameters are defined in the {@code web.xml} configuration file. * parameters are defined in the {@code web.xml} configuration file.
* *
* @param pName The name of the initialisation parameter to return the * @param pName The name of the initialisation parameter to return the
* value for. * value for.
* @return The value for the parameter whose name was passed in as a * @return The value for the parameter whose name was passed in as a
* parameter. If the parameter does not exist, then {@code null} * parameter. If the parameter does not exist, then {@code null}
* will be returned. * will be returned.
*/ */
public String getInitParameter(String pName); public String getInitParameter(String pName);
/** /**
* Returns an Enumeration containing all the names for all the * Returns an Enumeration containing all the names for all the
* initialisation parametes defined in the {@code * initialisation parametes defined in the {@code
* PageContext.APPLICATION_SCOPE} scope. * PageContext.APPLICATION_SCOPE} scope.
* *
* @return An {@code Enumeration} containing all the names for all the * @return An {@code Enumeration} containing all the names for all the
* initialisation parameters. * initialisation parameters.
*/ */
public Enumeration getInitParameterNames(); public Enumeration getInitParameterNames();
/** /**
* Returns the initialisation parameter from the scope specified with the * Returns the initialisation parameter from the scope specified with the
* name specified. * name specified.
* *
* @param pName The name of the initialisation parameter to return the * @param pName The name of the initialisation parameter to return the
* value for. * value for.
* @param pScope The scope to search for the initialisation parameter * @param pScope The scope to search for the initialisation parameter
* within. * within.
* @return The value of the parameter found. If no parameter with the * @return The value of the parameter found. If no parameter with the
* name specified is found in the scope specified, then {@code null * name specified is found in the scope specified, then {@code null
* } is returned. * } is returned.
*/ */
public String getInitParameter(String pName, int pScope); public String getInitParameter(String pName, int pScope);
/** /**
* Returns an enumeration containing all the parameters defined in the * Returns an enumeration containing all the parameters defined in the
* scope specified by the parameter. * scope specified by the parameter.
* *
* @param pScope The scope to return the names of all the parameters * @param pScope The scope to return the names of all the parameters
* defined within. * defined within.
* @return An {@code Enumeration} containing all the names for all the * @return An {@code Enumeration} containing all the names for all the
* parameters defined in the scope passed in as a parameter. * parameters defined in the scope passed in as a parameter.
*/ */
public Enumeration getInitParameterNames(int pScope); public Enumeration getInitParameterNames(int pScope);
/** /**
* Returns the servlet config associated with the current JSP page request. * Returns the servlet config associated with the current JSP page request.
* *
* @return The {@code ServletConfig} associated with the current * @return The {@code ServletConfig} associated with the current
* request. * request.
*/ */
public ServletConfig getServletConfig(); public ServletConfig getServletConfig();
/** /**
* Gets the context path associated with the current JSP page request. * Gets the context path associated with the current JSP page request.
* *
* @return a path relative to the current context's root. * @return a path relative to the current context's root.
*/ */
public String getContextPath(); public String getContextPath();
/** /**
* Gets the resource associated with the given relative path for the * Gets the resource associated with the given relative path for the
* current JSP page request. * current JSP page request.
* The path may be absolute, or relative to the current context root. * The path may be absolute, or relative to the current context root.
* *
* @param pPath the path * @param pPath the path
* *
* @return a path relative to the current context root * @return a path relative to the current context root
*/ */
public InputStream getResourceAsStream(String pPath); public InputStream getResourceAsStream(String pPath);
} }

View File

@@ -1,293 +1,293 @@
/* /*
* Copyright (c) 2002 TwelveMonkeys. * Copyright (c) 2002 TwelveMonkeys.
* All rights reserved. * All rights reserved.
* *
* $Log: ExTagSupport.java,v $ * $Log: ExTagSupport.java,v $
* Revision 1.3 2003/10/06 14:25:11 WMHAKUR * Revision 1.3 2003/10/06 14:25:11 WMHAKUR
* Code clean-up only. * Code clean-up only.
* *
* Revision 1.2 2002/11/18 22:10:27 WMHAKUR * Revision 1.2 2002/11/18 22:10:27 WMHAKUR
* *** empty log message *** * *** empty log message ***
* *
* *
*/ */
package com.twelvemonkeys.servlet.jsp.taglib; package com.twelvemonkeys.servlet.jsp.taglib;
import javax.servlet.ServletConfig; import javax.servlet.ServletConfig;
import javax.servlet.ServletContext; import javax.servlet.ServletContext;
import javax.servlet.ServletRequest; import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter; import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.PageContext; import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.TagSupport; import javax.servlet.jsp.tagext.TagSupport;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.StringTokenizer; import java.util.StringTokenizer;
/** /**
* This is the class that should be extended by all jsp pages that don't use * This is the class that should be extended by all jsp pages that don't use
* their body. It contains a lot of helper methods for simplifying common * their body. It contains a lot of helper methods for simplifying common
* tasks. * tasks.
* *
* @author Thomas Purcell (CSC Australia) * @author Thomas Purcell (CSC Australia)
* @author Harald Kuhr * @author Harald Kuhr
* *
* @version $Id: jsp/taglib/ExTagSupport.java#1 $ * @version $Id: jsp/taglib/ExTagSupport.java#1 $
*/ */
public class ExTagSupport extends TagSupport implements ExTag { public class ExTagSupport extends TagSupport implements ExTag {
/** /**
* writeHtml ensures that the text being outputted appears as it was * writeHtml ensures that the text being outputted appears as it was
* entered. This prevents users from hacking the system by entering * entered. This prevents users from hacking the system by entering
* html or jsp code into an entry form where that value will be displayed * html or jsp code into an entry form where that value will be displayed
* later in the site. * later in the site.
* *
* @param pOut The JspWriter to write the output to. * @param pOut The JspWriter to write the output to.
* @param pHtml The original html to filter and output to the user. * @param pHtml The original html to filter and output to the user.
* @throws IOException If the user clicks Stop in the browser, or their * @throws IOException If the user clicks Stop in the browser, or their
* browser crashes, then the JspWriter will throw an IOException when * browser crashes, then the JspWriter will throw an IOException when
* the jsp tries to write to it. * the jsp tries to write to it.
*/ */
public void writeHtml(JspWriter pOut, String pHtml) throws IOException { public void writeHtml(JspWriter pOut, String pHtml) throws IOException {
StringTokenizer parser = new StringTokenizer(pHtml, "<>&", true); StringTokenizer parser = new StringTokenizer(pHtml, "<>&", true);
while (parser.hasMoreTokens()) { while (parser.hasMoreTokens()) {
String token = parser.nextToken(); String token = parser.nextToken();
if (token.equals("<")) { if (token.equals("<")) {
pOut.print("&lt;"); pOut.print("&lt;");
} }
else if (token.equals(">")) { else if (token.equals(">")) {
pOut.print("&gt;"); pOut.print("&gt;");
} }
else if (token.equals("&")) { else if (token.equals("&")) {
pOut.print("&amp;"); pOut.print("&amp;");
} }
else { else {
pOut.print(token); pOut.print(token);
} }
} }
} }
/** /**
* Log a message to the servlet context. * Log a message to the servlet context.
* *
* @param pMsg The error message to log. * @param pMsg The error message to log.
*/ */
public void log(String pMsg) { public void log(String pMsg) {
getServletContext().log(pMsg); getServletContext().log(pMsg);
} }
/** /**
* Log a message to the servlet context and include the exception that is * Log a message to the servlet context and include the exception that is
* passed in as the second parameter. * passed in as the second parameter.
* *
* @param pMsg The error message to log. * @param pMsg The error message to log.
* @param pException The exception that caused this error message to be * @param pException The exception that caused this error message to be
* logged. * logged.
*/ */
public void log(String pMsg, Throwable pException) { public void log(String pMsg, Throwable pException) {
getServletContext().log(pMsg, pException); getServletContext().log(pMsg, pException);
} }
/** /**
* Retrieves the ServletContext object associated with the current * Retrieves the ServletContext object associated with the current
* PageContext object. * PageContext object.
* *
* @return The ServletContext object associated with the current * @return The ServletContext object associated with the current
* PageContext object. * PageContext object.
*/ */
public ServletContext getServletContext() { public ServletContext getServletContext() {
return pageContext.getServletContext(); return pageContext.getServletContext();
} }
/** /**
* Called when the tag has finished running. Any clean up that needs * Called when the tag has finished running. Any clean up that needs
* to be done between calls to this tag but within the same JSP page is * to be done between calls to this tag but within the same JSP page is
* called in the {@code clearServiceState()} method call. * called in the {@code clearServiceState()} method call.
* *
* @exception JspException * @exception JspException
*/ */
public int doEndTag() throws JspException { public int doEndTag() throws JspException {
clearServiceState(); clearServiceState();
return super.doEndTag(); return super.doEndTag();
} }
/** /**
* Called when a tag's role in the current JSP page is finished. After * Called when a tag's role in the current JSP page is finished. After
* the {@code clearProperties()} method is called, the custom tag * the {@code clearProperties()} method is called, the custom tag
* should be in an identical state as when it was first created. The * should be in an identical state as when it was first created. The
* {@code clearServiceState()} method is called here just in case an * {@code clearServiceState()} method is called here just in case an
* exception was thrown in the custom tag. If an exception was thrown, * exception was thrown in the custom tag. If an exception was thrown,
* then the {@code doEndTag()} method will not have been called and * then the {@code doEndTag()} method will not have been called and
* the tag might not have been cleaned up properly. * the tag might not have been cleaned up properly.
*/ */
public void release() { public void release() {
clearServiceState(); clearServiceState();
clearProperties(); clearProperties();
super.release(); super.release();
} }
/** /**
* The default implementation for the {@code clearProperties()}. Not * The default implementation for the {@code clearProperties()}. Not
* all tags will need to overload this method call. By implementing it * all tags will need to overload this method call. By implementing it
* here, all classes that extend this object are able to call {@code * here, all classes that extend this object are able to call {@code
* super.clearProperties()}. So, if the class extends a different * super.clearProperties()}. So, if the class extends a different
* tag, or this one, the parent method should always be called. This * tag, or this one, the parent method should always be called. This
* method will be called when the tag is to be released. That is, the * method will be called when the tag is to be released. That is, the
* tag has finished for the current page and should be returned to it's * tag has finished for the current page and should be returned to it's
* initial state. * initial state.
*/ */
protected void clearProperties() { protected void clearProperties() {
} }
/** /**
* The default implementation for the {@code clearServiceState()}. * The default implementation for the {@code clearServiceState()}.
* Not all tags will need to overload this method call. By implementing it * Not all tags will need to overload this method call. By implementing it
* here, all classes that extend this object are able to call {@code * here, all classes that extend this object are able to call {@code
* super.clearServiceState()}. So, if the class extends a different * super.clearServiceState()}. So, if the class extends a different
* tag, or this one, the parent method should always be called. This * tag, or this one, the parent method should always be called. This
* method will be called when the tag has finished it's current tag * method will be called when the tag has finished it's current tag
* within the page, but may be called upon again in this same JSP page. * within the page, but may be called upon again in this same JSP page.
*/ */
protected void clearServiceState() { protected void clearServiceState() {
} }
/** /**
* Returns the initialisation parameter from the {@code * Returns the initialisation parameter from the {@code
* PageContext.APPLICATION_SCOPE} scope. These initialisation * PageContext.APPLICATION_SCOPE} scope. These initialisation
* parameters are defined in the {@code web.xml} configuration file. * parameters are defined in the {@code web.xml} configuration file.
* *
* @param pName The name of the initialisation parameter to return the * @param pName The name of the initialisation parameter to return the
* value for. * value for.
* @return The value for the parameter whose name was passed in as a * @return The value for the parameter whose name was passed in as a
* parameter. If the parameter does not exist, then {@code null} * parameter. If the parameter does not exist, then {@code null}
* will be returned. * will be returned.
*/ */
public String getInitParameter(String pName) { public String getInitParameter(String pName) {
return getInitParameter(pName, PageContext.APPLICATION_SCOPE); return getInitParameter(pName, PageContext.APPLICATION_SCOPE);
} }
/** /**
* Returns an Enumeration containing all the names for all the * Returns an Enumeration containing all the names for all the
* initialisation parametes defined in the {@code * initialisation parametes defined in the {@code
* PageContext.APPLICATION_SCOPE} scope. * PageContext.APPLICATION_SCOPE} scope.
* *
* @return An {@code Enumeration} containing all the names for all the * @return An {@code Enumeration} containing all the names for all the
* initialisation parameters. * initialisation parameters.
*/ */
public Enumeration getInitParameterNames() { public Enumeration getInitParameterNames() {
return getInitParameterNames(PageContext.APPLICATION_SCOPE); return getInitParameterNames(PageContext.APPLICATION_SCOPE);
} }
/** /**
* Returns the initialisation parameter from the scope specified with the * Returns the initialisation parameter from the scope specified with the
* name specified. * name specified.
* *
* @param pName The name of the initialisation parameter to return the * @param pName The name of the initialisation parameter to return the
* value for. * value for.
* @param pScope The scope to search for the initialisation parameter * @param pScope The scope to search for the initialisation parameter
* within. * within.
* @return The value of the parameter found. If no parameter with the * @return The value of the parameter found. If no parameter with the
* name specified is found in the scope specified, then {@code null * name specified is found in the scope specified, then {@code null
* } is returned. * } is returned.
*/ */
public String getInitParameter(String pName, int pScope) { public String getInitParameter(String pName, int pScope) {
switch (pScope) { switch (pScope) {
case PageContext.PAGE_SCOPE: case PageContext.PAGE_SCOPE:
return getServletConfig().getInitParameter(pName); return getServletConfig().getInitParameter(pName);
case PageContext.APPLICATION_SCOPE: case PageContext.APPLICATION_SCOPE:
return getServletContext().getInitParameter(pName); return getServletContext().getInitParameter(pName);
default: default:
throw new IllegalArgumentException("Illegal scope."); throw new IllegalArgumentException("Illegal scope.");
} }
} }
/** /**
* Returns an enumeration containing all the parameters defined in the * Returns an enumeration containing all the parameters defined in the
* scope specified by the parameter. * scope specified by the parameter.
* *
* @param pScope The scope to return the names of all the parameters * @param pScope The scope to return the names of all the parameters
* defined within. * defined within.
* @return An {@code Enumeration} containing all the names for all the * @return An {@code Enumeration} containing all the names for all the
* parameters defined in the scope passed in as a parameter. * parameters defined in the scope passed in as a parameter.
*/ */
public Enumeration getInitParameterNames(int pScope) { public Enumeration getInitParameterNames(int pScope) {
switch (pScope) { switch (pScope) {
case PageContext.PAGE_SCOPE: case PageContext.PAGE_SCOPE:
return getServletConfig().getInitParameterNames(); return getServletConfig().getInitParameterNames();
case PageContext.APPLICATION_SCOPE: case PageContext.APPLICATION_SCOPE:
return getServletContext().getInitParameterNames(); return getServletContext().getInitParameterNames();
default: default:
throw new IllegalArgumentException("Illegal scope"); throw new IllegalArgumentException("Illegal scope");
} }
} }
/** /**
* Returns the servlet config associated with the current JSP page request. * Returns the servlet config associated with the current JSP page request.
* *
* @return The {@code ServletConfig} associated with the current * @return The {@code ServletConfig} associated with the current
* request. * request.
*/ */
public ServletConfig getServletConfig() { public ServletConfig getServletConfig() {
return pageContext.getServletConfig(); return pageContext.getServletConfig();
} }
/** /**
* Gets the context path associated with the current JSP page request. * Gets the context path associated with the current JSP page request.
* If the request is not a HttpServletRequest, this method will * If the request is not a HttpServletRequest, this method will
* return "/". * return "/".
* *
* @return a path relative to the current context's root, or * @return a path relative to the current context's root, or
* {@code "/"} if this is not a HTTP request. * {@code "/"} if this is not a HTTP request.
*/ */
public String getContextPath() { public String getContextPath() {
ServletRequest request = pageContext.getRequest(); ServletRequest request = pageContext.getRequest();
if (request instanceof HttpServletRequest) { if (request instanceof HttpServletRequest) {
return ((HttpServletRequest) request).getContextPath(); return ((HttpServletRequest) request).getContextPath();
} }
return "/"; return "/";
} }
/** /**
* Gets the resource associated with the given relative path for the * Gets the resource associated with the given relative path for the
* current JSP page request. * current JSP page request.
* The path may be absolute, or relative to the current context root. * The path may be absolute, or relative to the current context root.
* *
* @param pPath the path * @param pPath the path
* *
* @return a path relative to the current context root * @return a path relative to the current context root
*/ */
public InputStream getResourceAsStream(String pPath) { public InputStream getResourceAsStream(String pPath) {
//throws MalformedURLException { //throws MalformedURLException {
String path = pPath; String path = pPath;
if (pPath != null && !pPath.startsWith("/")) { if (pPath != null && !pPath.startsWith("/")) {
path = getContextPath() + pPath; path = getContextPath() + pPath;
} }
return pageContext.getServletContext().getResourceAsStream(path); return pageContext.getServletContext().getResourceAsStream(path);
} }
} }

View File

@@ -1,20 +1,20 @@
package com.twelvemonkeys.servlet.jsp.taglib; package com.twelvemonkeys.servlet.jsp.taglib;
import javax.servlet.jsp.*; import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*; import javax.servlet.jsp.tagext.*;
/** /**
* TagExtraInfo for LastModifiedTag * TagExtraInfo for LastModifiedTag
* *
* @author Harald Kuhr * @author Harald Kuhr
* *
* @version 1.1 * @version 1.1
*/ */
public class LastModifiedTEI extends TagExtraInfo { public class LastModifiedTEI extends TagExtraInfo {
public VariableInfo[] getVariableInfo(TagData pData) { public VariableInfo[] getVariableInfo(TagData pData) {
return new VariableInfo[]{ return new VariableInfo[]{
new VariableInfo("lastModified", "java.lang.String", true, VariableInfo.NESTED), new VariableInfo("lastModified", "java.lang.String", true, VariableInfo.NESTED),
}; };
} }
} }

View File

@@ -1,87 +1,87 @@
package com.twelvemonkeys.servlet.jsp.taglib; package com.twelvemonkeys.servlet.jsp.taglib;
import java.io.IOException; import java.io.IOException;
import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspException;
/** /**
* This tag truncates all consecutive whitespace in sequence inside its body, * This tag truncates all consecutive whitespace in sequence inside its body,
* to one whitespace character. The first whitespace character in the sequence * to one whitespace character. The first whitespace character in the sequence
* will be left untouched (except for CR/LF, which will always leave a LF). * will be left untouched (except for CR/LF, which will always leave a LF).
* *
* @author Harald Kuhr * @author Harald Kuhr
* *
* @version 1.0 * @version 1.0
*/ */
public class TrimWhiteSpaceTag extends ExBodyTagSupport { public class TrimWhiteSpaceTag extends ExBodyTagSupport {
/** /**
* doStartTag implementation, simply returns * doStartTag implementation, simply returns
* {@code BodyTag.EVAL_BODY_BUFFERED}. * {@code BodyTag.EVAL_BODY_BUFFERED}.
* *
* @return {@code BodyTag.EVAL_BODY_BUFFERED} * @return {@code BodyTag.EVAL_BODY_BUFFERED}
*/ */
public int doStartTag() throws JspException { public int doStartTag() throws JspException {
return EVAL_BODY_BUFFERED; return EVAL_BODY_BUFFERED;
} }
/** /**
* doEndTag implementation, truncates all whitespace. * doEndTag implementation, truncates all whitespace.
* *
* @return {@code super.doEndTag()} * @return {@code super.doEndTag()}
*/ */
public int doEndTag() throws JspException { public int doEndTag() throws JspException {
// Trim // Trim
String trimmed = truncateWS(bodyContent.getString()); String trimmed = truncateWS(bodyContent.getString());
try { try {
// Print trimmed content // Print trimmed content
//pageContext.getOut().print("<!--TWS-->\n"); //pageContext.getOut().print("<!--TWS-->\n");
pageContext.getOut().print(trimmed); pageContext.getOut().print(trimmed);
//pageContext.getOut().print("\n<!--/TWS-->"); //pageContext.getOut().print("\n<!--/TWS-->");
} }
catch (IOException ioe) { catch (IOException ioe) {
throw new JspException(ioe); throw new JspException(ioe);
} }
return super.doEndTag(); return super.doEndTag();
} }
/** /**
* Truncates whitespace from the given string. * Truncates whitespace from the given string.
* *
* @todo Candidate for StringUtil? * @todo Candidate for StringUtil?
*/ */
private static String truncateWS(String pStr) { private static String truncateWS(String pStr) {
char[] chars = pStr.toCharArray(); char[] chars = pStr.toCharArray();
int count = 0; int count = 0;
boolean lastWasWS = true; // Avoids leading WS boolean lastWasWS = true; // Avoids leading WS
for (int i = 0; i < chars.length; i++) { for (int i = 0; i < chars.length; i++) {
if (!Character.isWhitespace(chars[i])) { if (!Character.isWhitespace(chars[i])) {
// if char is not WS, just store // if char is not WS, just store
chars[count++] = chars[i]; chars[count++] = chars[i];
lastWasWS = false; lastWasWS = false;
} }
else { else {
// else, if char is WS, store first, skip the rest // else, if char is WS, store first, skip the rest
if (!lastWasWS) { if (!lastWasWS) {
if (chars[i] == 0x0d) { if (chars[i] == 0x0d) {
chars[count++] = 0x0a; //Always new line chars[count++] = 0x0a; //Always new line
} }
else { else {
chars[count++] = chars[i]; chars[count++] = chars[i];
} }
} }
lastWasWS = true; lastWasWS = true;
} }
} }
// Return the trucated string // Return the trucated string
return new String(chars, 0, count); return new String(chars, 0, count);
} }
} }

View File

@@ -1,158 +1,158 @@
/* /*
* Copyright (c) 2002 TwelveMonkeys. * Copyright (c) 2002 TwelveMonkeys.
* All rights reserved. * All rights reserved.
* *
* $Log: XMLTransformTag.java,v $ * $Log: XMLTransformTag.java,v $
* Revision 1.2 2003/10/06 14:25:43 WMHAKUR * Revision 1.2 2003/10/06 14:25:43 WMHAKUR
* Code clean-up only. * Code clean-up only.
* *
* Revision 1.1 2002/11/19 10:50:41 WMHAKUR * Revision 1.1 2002/11/19 10:50:41 WMHAKUR
* *** empty log message *** * *** empty log message ***
* *
*/ */
package com.twelvemonkeys.servlet.jsp.taglib; package com.twelvemonkeys.servlet.jsp.taglib;
import java.io.*; import java.io.*;
import java.net.*; import java.net.*;
import javax.servlet.jsp.*; import javax.servlet.jsp.*;
import javax.xml.transform.*; import javax.xml.transform.*;
import javax.xml.transform.stream.*; import javax.xml.transform.stream.*;
/** /**
* This tag performs XSL Transformations (XSLT) on a given XML document or its * This tag performs XSL Transformations (XSLT) on a given XML document or its
* body content. * body content.
* *
* @author Harald Kuhr * @author Harald Kuhr
* *
* @version $Id: jsp/taglib/XMLTransformTag.java#1 $ * @version $Id: jsp/taglib/XMLTransformTag.java#1 $
*/ */
public class XMLTransformTag extends ExBodyTagSupport { public class XMLTransformTag extends ExBodyTagSupport {
private String mDocumentURI = null; private String mDocumentURI = null;
private String mStylesheetURI = null; private String mStylesheetURI = null;
/** /**
* Sets the document attribute for this tag. * Sets the document attribute for this tag.
*/ */
public void setDocumentURI(String pDocumentURI) { public void setDocumentURI(String pDocumentURI) {
mDocumentURI = pDocumentURI; mDocumentURI = pDocumentURI;
} }
/** /**
* Sets the stylesheet attribute for this tag. * Sets the stylesheet attribute for this tag.
*/ */
public void setStylesheetURI(String pStylesheetURI) { public void setStylesheetURI(String pStylesheetURI) {
mStylesheetURI = pStylesheetURI; mStylesheetURI = pStylesheetURI;
} }
/** /**
* doStartTag implementation, that performs XML Transformation on the * doStartTag implementation, that performs XML Transformation on the
* given document, if any. * given document, if any.
* If the documentURI attribute is set, then the transformation is * If the documentURI attribute is set, then the transformation is
* performed on the document at that location, and * performed on the document at that location, and
* {@code Tag.SKIP_BODY} is returned. * {@code Tag.SKIP_BODY} is returned.
* Otherwise, this method simply returns * Otherwise, this method simply returns
* {@code BodyTag.EVAL_BODY_BUFFERED} and leaves the transformation to * {@code BodyTag.EVAL_BODY_BUFFERED} and leaves the transformation to
* the doEndTag. * the doEndTag.
* *
* @return {@code Tag.SKIP_BODY} if {@code documentURI} is not * @return {@code Tag.SKIP_BODY} if {@code documentURI} is not
* {@code null}, otherwise * {@code null}, otherwise
* {@code BodyTag.EVAL_BODY_BUFFERED}. * {@code BodyTag.EVAL_BODY_BUFFERED}.
* *
* @todo Is it really a good idea to allow "inline" XML in a JSP? * @todo Is it really a good idea to allow "inline" XML in a JSP?
*/ */
public int doStartTag() throws JspException { public int doStartTag() throws JspException {
//log("XML: " + mDocumentURI + " XSL: " + mStylesheetURI); //log("XML: " + mDocumentURI + " XSL: " + mStylesheetURI);
if (mDocumentURI != null) { if (mDocumentURI != null) {
// If document given, transform and skip body... // If document given, transform and skip body...
try { try {
transform(getSource(mDocumentURI)); transform(getSource(mDocumentURI));
} }
catch (MalformedURLException murle) { catch (MalformedURLException murle) {
throw new JspException(murle.getMessage(), murle); throw new JspException(murle.getMessage(), murle);
} }
catch (IOException ioe) { catch (IOException ioe) {
throw new JspException(ioe.getMessage(), ioe); throw new JspException(ioe.getMessage(), ioe);
} }
return SKIP_BODY; return SKIP_BODY;
} }
// ...else process the body // ...else process the body
return EVAL_BODY_BUFFERED; return EVAL_BODY_BUFFERED;
} }
/** /**
* doEndTag implementation, that will perform XML Transformation on the * doEndTag implementation, that will perform XML Transformation on the
* body content. * body content.
* *
* @return super.doEndTag() * @return super.doEndTag()
*/ */
public int doEndTag() throws JspException { public int doEndTag() throws JspException {
// Get body content (trim is CRUCIAL, as some XML parsers are picky...) // Get body content (trim is CRUCIAL, as some XML parsers are picky...)
String body = bodyContent.getString().trim(); String body = bodyContent.getString().trim();
// Do transformation // Do transformation
transform(new StreamSource(new ByteArrayInputStream(body.getBytes()))); transform(new StreamSource(new ByteArrayInputStream(body.getBytes())));
return super.doEndTag(); return super.doEndTag();
} }
/** /**
* Performs the transformation and writes the result to the JSP writer. * Performs the transformation and writes the result to the JSP writer.
* *
* @param in the source document to transform. * @param in the source document to transform.
*/ */
public void transform(Source pIn) throws JspException { public void transform(Source pIn) throws JspException {
try { try {
// Create transformer // Create transformer
Transformer transformer = TransformerFactory.newInstance() Transformer transformer = TransformerFactory.newInstance()
.newTransformer(getSource(mStylesheetURI)); .newTransformer(getSource(mStylesheetURI));
// Store temporary output in a bytearray, as the transformer will // Store temporary output in a bytearray, as the transformer will
// usually try to flush the stream (illegal operation from a custom // usually try to flush the stream (illegal operation from a custom
// tag). // tag).
ByteArrayOutputStream os = new ByteArrayOutputStream(); ByteArrayOutputStream os = new ByteArrayOutputStream();
StreamResult out = new StreamResult(os); StreamResult out = new StreamResult(os);
// Perform the transformation // Perform the transformation
transformer.transform(pIn, out); transformer.transform(pIn, out);
// Write the result back to the JSP writer // Write the result back to the JSP writer
pageContext.getOut().print(os.toString()); pageContext.getOut().print(os.toString());
} }
catch (MalformedURLException murle) { catch (MalformedURLException murle) {
throw new JspException(murle.getMessage(), murle); throw new JspException(murle.getMessage(), murle);
} }
catch (IOException ioe) { catch (IOException ioe) {
throw new JspException(ioe.getMessage(), ioe); throw new JspException(ioe.getMessage(), ioe);
} }
catch (TransformerException te) { catch (TransformerException te) {
throw new JspException("XSLT Trandformation failed: " + te.getMessage(), te); throw new JspException("XSLT Trandformation failed: " + te.getMessage(), te);
} }
} }
/** /**
* Returns a StreamSource object, for the given URI * Returns a StreamSource object, for the given URI
*/ */
private StreamSource getSource(String pURI) private StreamSource getSource(String pURI)
throws IOException, MalformedURLException { throws IOException, MalformedURLException {
if (pURI != null && pURI.indexOf("://") < 0) { if (pURI != null && pURI.indexOf("://") < 0) {
// If local, get as stream // If local, get as stream
return new StreamSource(getResourceAsStream(pURI)); return new StreamSource(getResourceAsStream(pURI));
} }
// ...else, create from URI string // ...else, create from URI string
return new StreamSource(pURI); return new StreamSource(pURI);
} }
} }

View File

@@ -1,138 +1,138 @@
/**************************************************** /****************************************************
* * * *
* (c) 2000-2003 TwelveMonkeys * * (c) 2000-2003 TwelveMonkeys *
* All rights reserved * * All rights reserved *
* http://www.twelvemonkeys.no * * http://www.twelvemonkeys.no *
* * * *
* $RCSfile: ConditionalTagBase.java,v $ * $RCSfile: ConditionalTagBase.java,v $
* @version $Revision: #1 $ * @version $Revision: #1 $
* $Date: 2008/05/05 $ * $Date: 2008/05/05 $
* * * *
* @author Last modified by: $Author: haku $ * @author Last modified by: $Author: haku $
* * * *
****************************************************/ ****************************************************/
/* /*
* Produced (p) 2002 TwelveMonkeys * Produced (p) 2002 TwelveMonkeys
* Address : Svovelstikka 1, Box 6432 Etterstad, 0605 Oslo, Norway. * Address : Svovelstikka 1, Box 6432 Etterstad, 0605 Oslo, Norway.
* Phone : +47 22 57 70 00 * Phone : +47 22 57 70 00
* Fax : +47 22 57 70 70 * Fax : +47 22 57 70 70
*/ */
package com.twelvemonkeys.servlet.jsp.taglib.logic; package com.twelvemonkeys.servlet.jsp.taglib.logic;
import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport; import javax.servlet.jsp.tagext.TagSupport;
/** /**
* <p>An abstract base class for tags with some kind of conditional presentation of the tag body.</p> * <p>An abstract base class for tags with some kind of conditional presentation of the tag body.</p>
* *
* @version 1.0 * @version 1.0
* @author <a href="mailto:eirik.torske@twelvemonkeys.no">Eirik Torske</a> * @author <a href="mailto:eirik.torske@twelvemonkeys.no">Eirik Torske</a>
*/ */
public abstract class ConditionalTagBase extends TagSupport { public abstract class ConditionalTagBase extends TagSupport {
// Members // Members
protected String objectName; protected String objectName;
protected String objectValue; protected String objectValue;
// Properties // Properties
/** /**
* Method getName * Method getName
* *
* *
* @return * @return
* *
*/ */
public String getName() { public String getName() {
return objectName; return objectName;
} }
/** /**
* Method setName * Method setName
* *
* *
* @param pObjectName * @param pObjectName
* *
*/ */
public void setName(String pObjectName) { public void setName(String pObjectName) {
this.objectName = pObjectName; this.objectName = pObjectName;
} }
/** /**
* Method getValue * Method getValue
* *
* *
* @return * @return
* *
*/ */
public String getValue() { public String getValue() {
return objectValue; return objectValue;
} }
/** /**
* Method setValue * Method setValue
* *
* *
* @param pObjectValue * @param pObjectValue
* *
*/ */
public void setValue(String pObjectValue) { public void setValue(String pObjectValue) {
this.objectValue = pObjectValue; this.objectValue = pObjectValue;
} }
/** /**
* <p>Perform the test required for this particular tag, and either evaluate or skip the body of this tag.</p> * <p>Perform the test required for this particular tag, and either evaluate or skip the body of this tag.</p>
* *
* *
* @return * @return
* @exception JspException if a JSP exception occurs. * @exception JspException if a JSP exception occurs.
*/ */
public int doStartTag() throws JspException { public int doStartTag() throws JspException {
if (condition()) { if (condition()) {
return (EVAL_BODY_INCLUDE); return (EVAL_BODY_INCLUDE);
} else { } else {
return (SKIP_BODY); return (SKIP_BODY);
} }
} }
/** /**
* <p>Evaluate the remainder of the current page as normal.</p> * <p>Evaluate the remainder of the current page as normal.</p>
* *
* *
* @return * @return
* @exception JspException if a JSP exception occurs. * @exception JspException if a JSP exception occurs.
*/ */
public int doEndTag() throws JspException { public int doEndTag() throws JspException {
return (EVAL_PAGE); return (EVAL_PAGE);
} }
/** /**
* <p>Release all allocated resources.</p> * <p>Release all allocated resources.</p>
*/ */
public void release() { public void release() {
super.release(); super.release();
objectName = null; objectName = null;
objectValue = null; objectValue = null;
} }
/** /**
* <p>The condition that must be met in order to display the body of this tag.</p> * <p>The condition that must be met in order to display the body of this tag.</p>
* *
* @exception JspException if a JSP exception occurs. * @exception JspException if a JSP exception occurs.
* @return {@code true} if and only if all conditions are met. * @return {@code true} if and only if all conditions are met.
*/ */
protected abstract boolean condition() throws JspException; protected abstract boolean condition() throws JspException;
} }
/*--- Formatted in Sun Java Convention Style on ma, des 1, '03 ---*/ /*--- Formatted in Sun Java Convention Style on ma, des 1, '03 ---*/
/*------ Formatted by Jindent 3.23 Basic 1.0 --- http://www.jindent.de ------*/ /*------ Formatted by Jindent 3.23 Basic 1.0 --- http://www.jindent.de ------*/

View File

@@ -1,168 +1,168 @@
/* /*
* Produced (p) 2002 TwelveMonkeys * Produced (p) 2002 TwelveMonkeys
* Address : Svovelstikka 1, Box 6432 Etterstad, 0605 Oslo, Norway. * Address : Svovelstikka 1, Box 6432 Etterstad, 0605 Oslo, Norway.
* Phone : +47 22 57 70 00 * Phone : +47 22 57 70 00
* Fax : +47 22 57 70 70 * Fax : +47 22 57 70 70
*/ */
package com.twelvemonkeys.servlet.jsp.taglib.logic; package com.twelvemonkeys.servlet.jsp.taglib.logic;
import com.twelvemonkeys.lang.StringUtil; import com.twelvemonkeys.lang.StringUtil;
import javax.servlet.http.Cookie; import javax.servlet.http.Cookie;
import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspException;
/** /**
* <p> * <p>
* Custom tag for testing equality of an attribute against a given value. * Custom tag for testing equality of an attribute against a given value.
* The attribute types supported so far is: * The attribute types supported so far is:
* <ul> * <ul>
* <li>{@code java.lang.String} (ver. 1.0) * <li>{@code java.lang.String} (ver. 1.0)
* <li>{@code javax.servlet.http.Cookie} (ver. 1.0) * <li>{@code javax.servlet.http.Cookie} (ver. 1.0)
* </ul> * </ul>
* </p> * </p>
* See the implemented <a href="#condition">{@code condition}</a> method for details regarding the equality conditions. * See the implemented <a href="#condition">{@code condition}</a> method for details regarding the equality conditions.
* *
* <p><hr></p> * <p><hr></p>
* *
* <h3>Tag Reference</h3> * <h3>Tag Reference</h3>
* <table border="0" cellspacing="3" cellpadding="3" width="90%"> * <table border="0" cellspacing="3" cellpadding="3" width="90%">
* <tr bgcolor="#cccccc"> * <tr bgcolor="#cccccc">
* <td colspan="5" class="body"><b>equal</b></td> * <td colspan="5" class="body"><b>equal</b></td>
* <td width="17%" align="right" class="body">Availability:&nbsp;1.0</td> * <td width="17%" align="right" class="body">Availability:&nbsp;1.0</td>
* </tr> * </tr>
* <tr> * <tr>
* <td colspan="6" class="body"><p>Tag for testing if an attribute is equal to a given value.</p></td> * <td colspan="6" class="body"><p>Tag for testing if an attribute is equal to a given value.</p></td>
* </tr> * </tr>
* <tr> * <tr>
* <td width="15%" class="body"><b>Tag Body</b></td> * <td width="15%" class="body"><b>Tag Body</b></td>
* <td width="17%" class="body">JSP</td> * <td width="17%" class="body">JSP</td>
* <td width="17%" class="body">&nbsp;</td> * <td width="17%" class="body">&nbsp;</td>
* <td width="17%" class="body">&nbsp;</td> * <td width="17%" class="body">&nbsp;</td>
* <td width="17%" class="body">&nbsp;</td> * <td width="17%" class="body">&nbsp;</td>
* <td width="17%" class="body">&nbsp;</td> * <td width="17%" class="body">&nbsp;</td>
* </tr> * </tr>
* <tr> * <tr>
* <td class="body"><b>Restrictions</b></td> * <td class="body"><b>Restrictions</b></td>
* <td colspan="5" class="body"><p>None</p></td> * <td colspan="5" class="body"><p>None</p></td>
* </tr> * </tr>
* *
* <tr> * <tr>
* <td class="body"><b>Attributes</b></td> * <td class="body"><b>Attributes</b></td>
* <td class="body">Name</td> * <td class="body">Name</td>
* <td class="body">Required</td> * <td class="body">Required</td>
* <td colspan="2" class="body">Runtime&nbsp;Expression&nbsp;Evaluation</td> * <td colspan="2" class="body">Runtime&nbsp;Expression&nbsp;Evaluation</td>
* <td class="body">Availability</td> * <td class="body">Availability</td>
* </tr> * </tr>
* *
* <tr bgcolor="#cccccc"> * <tr bgcolor="#cccccc">
* <td bgcolor="#ffffff">&nbsp;</td> * <td bgcolor="#ffffff">&nbsp;</td>
* <td class="body_grey"><b>name</b></td> * <td class="body_grey"><b>name</b></td>
* <td class="body_grey">&nbsp;Yes</td> * <td class="body_grey">&nbsp;Yes</td>
* <td colspan="2" class="body_grey">&nbsp;Yes</td> * <td colspan="2" class="body_grey">&nbsp;Yes</td>
* <td class="body_grey">&nbsp;1.0</td> * <td class="body_grey">&nbsp;1.0</td>
* </tr> * </tr>
* <tr> * <tr>
* <td bgcolor="#ffffff">&nbsp;</td> * <td bgcolor="#ffffff">&nbsp;</td>
* <td colspan="5" class="body"><p>The attribute name</p></td> * <td colspan="5" class="body"><p>The attribute name</p></td>
* </tr> * </tr>
* *
* <tr bgcolor="#cccccc"> * <tr bgcolor="#cccccc">
* <td bgcolor="#ffffff">&nbsp;</td> * <td bgcolor="#ffffff">&nbsp;</td>
* <td class="body_grey"><b>value</b></td> * <td class="body_grey"><b>value</b></td>
* <td class="body_grey">&nbsp;No</td> * <td class="body_grey">&nbsp;No</td>
* <td colspan="2" class="body_grey">&nbsp;Yes</td> * <td colspan="2" class="body_grey">&nbsp;Yes</td>
* <td class="body_grey">&nbsp;1.0</td> * <td class="body_grey">&nbsp;1.0</td>
* </tr> * </tr>
* <tr> * <tr>
* <td bgcolor="#ffffff" class="body">&nbsp;</td> * <td bgcolor="#ffffff" class="body">&nbsp;</td>
* <td colspan="5" class="body"><p>The value for equality testing</p></td> * <td colspan="5" class="body"><p>The value for equality testing</p></td>
* </tr> * </tr>
* *
* <tr> * <tr>
* <td class="body" valign="top"><b>Variables</b></td> * <td class="body" valign="top"><b>Variables</b></td>
* <td colspan="5" class="body">None</td> * <td colspan="5" class="body">None</td>
* </tr> * </tr>
* *
* <tr> * <tr>
* <td class="body" valign="top"><b>Examples</b></td> * <td class="body" valign="top"><b>Examples</b></td>
* <td colspan="5" class="body"> * <td colspan="5" class="body">
* <pre> * <pre>
*&lt;%@ taglib prefix="twelvemonkeys" uri="twelvemonkeys-logic" %&gt; *&lt;%@ taglib prefix="twelvemonkeys" uri="twelvemonkeys-logic" %&gt;
*&lt;bean:cookie id="logonUsernameCookie" *&lt;bean:cookie id="logonUsernameCookie"
* name="&lt;%= com.strutscommand.Constants.LOGON_USERNAME_COOKIE_NAME %&gt;" * name="&lt;%= com.strutscommand.Constants.LOGON_USERNAME_COOKIE_NAME %&gt;"
* value="no_username_set" /&gt; * value="no_username_set" /&gt;
*&lt;twelvemonkeys:equal name="logonUsernameCookie" value="no_username_set"&gt; *&lt;twelvemonkeys:equal name="logonUsernameCookie" value="no_username_set"&gt;
* &lt;html:text property="username" /&gt; * &lt;html:text property="username" /&gt;
*&lt;/twelvemonkeys:equal&gt; *&lt;/twelvemonkeys:equal&gt;
* </pre> * </pre>
* </td> * </td>
* </tr> * </tr>
* </table> * </table>
* *
* <hr> * <hr>
* *
* @version 1.0 * @version 1.0
* @author <a href="mailto:eirik.torske@twelvemonkeys.no">Eirik Torske</a> * @author <a href="mailto:eirik.torske@twelvemonkeys.no">Eirik Torske</a>
* @see <a href="NotEqualTag.html">notEqual</a> * @see <a href="NotEqualTag.html">notEqual</a>
*/ */
public class EqualTag extends ConditionalTagBase { public class EqualTag extends ConditionalTagBase {
/** /**
* <a name="condition"></a> * <a name="condition"></a>
* *
* The conditions that must be met in order to display the body of this tag: * The conditions that must be met in order to display the body of this tag:
* <ol> * <ol>
* <li>The attribute name property ({@code name} -> {@code mObjectName}) must not be empty. * <li>The attribute name property ({@code name} -> {@code mObjectName}) must not be empty.
* <li>The attribute must exist. * <li>The attribute must exist.
* <li>The attribute must be an instance of one of the supported classes: * <li>The attribute must be an instance of one of the supported classes:
* <ul> * <ul>
* <li>{@code java.lang.String} * <li>{@code java.lang.String}
* <li>{@code javax.servlet.http.Cookie} * <li>{@code javax.servlet.http.Cookie}
* </ul> * </ul>
* <li>The value of the attribute must be equal to the object value property ({@code value} -> {@code mObjectValue}). * <li>The value of the attribute must be equal to the object value property ({@code value} -> {@code mObjectValue}).
* </ol> * </ol>
* <p> * <p>
* NB! If the object value property ({@code value} -> {@code mObjectValue}) is empty than {@code true} will be returned. * NB! If the object value property ({@code value} -> {@code mObjectValue}) is empty than {@code true} will be returned.
* </p> * </p>
* *
* @return {@code true} if and only if all conditions are met. * @return {@code true} if and only if all conditions are met.
*/ */
protected boolean condition() throws JspException { protected boolean condition() throws JspException {
if (StringUtil.isEmpty(objectName)) { if (StringUtil.isEmpty(objectName)) {
return false; return false;
} }
if (StringUtil.isEmpty(objectValue)) { if (StringUtil.isEmpty(objectValue)) {
return true; return true;
} }
Object pageScopedAttribute = pageContext.getAttribute(objectName); Object pageScopedAttribute = pageContext.getAttribute(objectName);
if (pageScopedAttribute == null) { if (pageScopedAttribute == null) {
return false; return false;
} }
String pageScopedStringAttribute; String pageScopedStringAttribute;
// String // String
if (pageScopedAttribute instanceof String) { if (pageScopedAttribute instanceof String) {
pageScopedStringAttribute = (String) pageScopedAttribute; pageScopedStringAttribute = (String) pageScopedAttribute;
// Cookie // Cookie
} }
else if (pageScopedAttribute instanceof Cookie) { else if (pageScopedAttribute instanceof Cookie) {
pageScopedStringAttribute = ((Cookie) pageScopedAttribute).getValue(); pageScopedStringAttribute = ((Cookie) pageScopedAttribute).getValue();
// Type not yet supported... // Type not yet supported...
} }
else { else {
return false; return false;
} }
return (pageScopedStringAttribute.equals(objectValue)); return (pageScopedStringAttribute.equals(objectValue));
} }
} }

View File

@@ -1,40 +1,40 @@
package com.twelvemonkeys.servlet.jsp.taglib.logic; package com.twelvemonkeys.servlet.jsp.taglib.logic;
import javax.servlet.jsp.tagext.*; import javax.servlet.jsp.tagext.*;
/** /**
* TagExtraInfo class for IteratorProvider tags. * TagExtraInfo class for IteratorProvider tags.
* *
* @author Harald Kuhr * @author Harald Kuhr
* @version $id: $ * @version $id: $
*/ */
public class IteratorProviderTEI extends TagExtraInfo { public class IteratorProviderTEI extends TagExtraInfo {
/** /**
* Gets the variable info for IteratorProvider tags. The attribute with the * Gets the variable info for IteratorProvider tags. The attribute with the
* name defined by the "id" attribute and type defined by the "type" * name defined by the "id" attribute and type defined by the "type"
* attribute is declared with scope {@code VariableInfo.AT_END}. * attribute is declared with scope {@code VariableInfo.AT_END}.
* *
* @param pData TagData instance provided by container * @param pData TagData instance provided by container
* @return an VariableInfo array of lenght 1, containing the attribute * @return an VariableInfo array of lenght 1, containing the attribute
* defined by the id parameter, declared, and with scope * defined by the id parameter, declared, and with scope
* {@code VariableInfo.AT_END}. * {@code VariableInfo.AT_END}.
*/ */
public VariableInfo[] getVariableInfo(TagData pData) { public VariableInfo[] getVariableInfo(TagData pData) {
// Get attribute name // Get attribute name
String attributeName = pData.getId(); String attributeName = pData.getId();
if (attributeName == null) { if (attributeName == null) {
attributeName = IteratorProviderTag.getDefaultIteratorName(); attributeName = IteratorProviderTag.getDefaultIteratorName();
} }
// Get type // Get type
String type = pData.getAttributeString(IteratorProviderTag.ATTRIBUTE_TYPE); String type = pData.getAttributeString(IteratorProviderTag.ATTRIBUTE_TYPE);
if (type == null) { if (type == null) {
type = IteratorProviderTag.getDefaultIteratorType(); type = IteratorProviderTag.getDefaultIteratorType();
} }
// Return the variable info // Return the variable info
return new VariableInfo[]{ return new VariableInfo[]{
new VariableInfo(attributeName, type, true, VariableInfo.AT_END), new VariableInfo(attributeName, type, true, VariableInfo.AT_END),
}; };
} }
} }

View File

@@ -1,86 +1,86 @@
package com.twelvemonkeys.servlet.jsp.taglib.logic; package com.twelvemonkeys.servlet.jsp.taglib.logic;
import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.Tag; import javax.servlet.jsp.tagext.Tag;
import javax.servlet.jsp.tagext.TagSupport; import javax.servlet.jsp.tagext.TagSupport;
import java.util.Iterator; import java.util.Iterator;
/** /**
* Abstract base class for adding iterators to a page. * Abstract base class for adding iterators to a page.
* *
* @todo Possible to use same strategy for all types of objects? Rename class * @todo Possible to use same strategy for all types of objects? Rename class
* to ObjectProviderTag? Hmmm... Might work. * to ObjectProviderTag? Hmmm... Might work.
* *
* @author Harald Kuhr * @author Harald Kuhr
* @version $id: $ * @version $id: $
*/ */
public abstract class IteratorProviderTag extends TagSupport { public abstract class IteratorProviderTag extends TagSupport {
/** {@code iterator} */ /** {@code iterator} */
protected final static String DEFAULT_ITERATOR_NAME = "iterator"; protected final static String DEFAULT_ITERATOR_NAME = "iterator";
/** {@code java.util.iterator} */ /** {@code java.util.iterator} */
protected final static String DEFAULT_ITERATOR_TYPE = "java.util.Iterator"; protected final static String DEFAULT_ITERATOR_TYPE = "java.util.Iterator";
/** {@code type} */ /** {@code type} */
public final static String ATTRIBUTE_TYPE = "type"; public final static String ATTRIBUTE_TYPE = "type";
/** */ /** */
private String type = null; private String type = null;
/** /**
* Gets the type. * Gets the type.
* *
* @return the type (class name) * @return the type (class name)
*/ */
public String getType() { public String getType() {
return type; return type;
} }
/** /**
* Sets the type. * Sets the type.
* *
* @param pType * @param pType
*/ */
public void setType(String pType) { public void setType(String pType) {
type = pType; type = pType;
} }
/** /**
* doEndTag implementation. * doEndTag implementation.
* *
* @return {@code Tag.EVAL_PAGE} * @return {@code Tag.EVAL_PAGE}
* @throws JspException * @throws JspException
*/ */
public int doEndTag() throws JspException { public int doEndTag() throws JspException {
// Set the iterator // Set the iterator
pageContext.setAttribute(getId(), getIterator()); pageContext.setAttribute(getId(), getIterator());
return Tag.EVAL_PAGE; return Tag.EVAL_PAGE;
} }
/** /**
* Gets the iterator for this tag. * Gets the iterator for this tag.
* *
* @return an {@link java.util.Iterator} * @return an {@link java.util.Iterator}
*/ */
protected abstract Iterator getIterator(); protected abstract Iterator getIterator();
/** /**
* Gets the default iterator name. * Gets the default iterator name.
* *
* @return {@link #DEFAULT_ITERATOR_NAME} * @return {@link #DEFAULT_ITERATOR_NAME}
*/ */
protected static String getDefaultIteratorName() { protected static String getDefaultIteratorName() {
return DEFAULT_ITERATOR_NAME; return DEFAULT_ITERATOR_NAME;
} }
/** /**
* Gets the default iterator type. * Gets the default iterator type.
* *
* @return {@link #DEFAULT_ITERATOR_TYPE} * @return {@link #DEFAULT_ITERATOR_TYPE}
*/ */
protected static String getDefaultIteratorType() { protected static String getDefaultIteratorType() {
return DEFAULT_ITERATOR_TYPE; return DEFAULT_ITERATOR_TYPE;
} }
} }

View File

@@ -1,168 +1,168 @@
/* /*
* Produced (p) 2002 TwelveMonkeys * Produced (p) 2002 TwelveMonkeys
* Address : Svovelstikka 1, Box 6432 Etterstad, 0605 Oslo, Norway. * Address : Svovelstikka 1, Box 6432 Etterstad, 0605 Oslo, Norway.
* Phone : +47 22 57 70 00 * Phone : +47 22 57 70 00
* Fax : +47 22 57 70 70 * Fax : +47 22 57 70 70
*/ */
package com.twelvemonkeys.servlet.jsp.taglib.logic; package com.twelvemonkeys.servlet.jsp.taglib.logic;
import com.twelvemonkeys.lang.StringUtil; import com.twelvemonkeys.lang.StringUtil;
import javax.servlet.http.Cookie; import javax.servlet.http.Cookie;
import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspException;
/** /**
* <p> * <p>
* Custom tag for testing non-equality of an attribute against a given value. * Custom tag for testing non-equality of an attribute against a given value.
* The attribute types supported so far is: * The attribute types supported so far is:
* <ul> * <ul>
* <li>{@code java.lang.String} (ver. 1.0) * <li>{@code java.lang.String} (ver. 1.0)
* <li>{@code javax.servlet.http.Cookie} (ver. 1.0) * <li>{@code javax.servlet.http.Cookie} (ver. 1.0)
* </ul> * </ul>
* </p> * </p>
* See the implemented <a href="#condition">{@code condition}</a> method for details regarding the non-equality conditions. * See the implemented <a href="#condition">{@code condition}</a> method for details regarding the non-equality conditions.
* *
* <p><hr></p> * <p><hr></p>
* *
* <h3>Tag Reference</h3> * <h3>Tag Reference</h3>
* <table border="0" cellspacing="3" cellpadding="3" width="90%"> * <table border="0" cellspacing="3" cellpadding="3" width="90%">
* <tr bgcolor="#cccccc"> * <tr bgcolor="#cccccc">
* <td colspan="5" class="body"><b>notEqual</b></td> * <td colspan="5" class="body"><b>notEqual</b></td>
* <td width="17%" align="right" class="body">Availability:&nbsp;1.0</td> * <td width="17%" align="right" class="body">Availability:&nbsp;1.0</td>
* </tr> * </tr>
* <tr> * <tr>
* <td colspan="6" class="body"><p>Tag for testing if an attribute is NOT equal to a given value.</p></td> * <td colspan="6" class="body"><p>Tag for testing if an attribute is NOT equal to a given value.</p></td>
* </tr> * </tr>
* <tr> * <tr>
* <td width="15%" class="body"><b>Tag Body</b></td> * <td width="15%" class="body"><b>Tag Body</b></td>
* <td width="17%" class="body">JSP</td> * <td width="17%" class="body">JSP</td>
* <td width="17%" class="body">&nbsp;</td> * <td width="17%" class="body">&nbsp;</td>
* <td width="17%" class="body">&nbsp;</td> * <td width="17%" class="body">&nbsp;</td>
* <td width="17%" class="body">&nbsp;</td> * <td width="17%" class="body">&nbsp;</td>
* <td width="17%" class="body">&nbsp;</td> * <td width="17%" class="body">&nbsp;</td>
* </tr> * </tr>
* <tr> * <tr>
* <td class="body"><b>Restrictions</b></td> * <td class="body"><b>Restrictions</b></td>
* <td colspan="5" class="body"><p>None</p></td> * <td colspan="5" class="body"><p>None</p></td>
* </tr> * </tr>
* *
* <tr> * <tr>
* <td class="body"><b>Attributes</b></td> * <td class="body"><b>Attributes</b></td>
* <td class="body">Name</td> * <td class="body">Name</td>
* <td class="body">Required</td> * <td class="body">Required</td>
* <td colspan="2" class="body">Runtime&nbsp;Expression&nbsp;Evaluation</td> * <td colspan="2" class="body">Runtime&nbsp;Expression&nbsp;Evaluation</td>
* <td class="body">Availability</td> * <td class="body">Availability</td>
* </tr> * </tr>
* *
* <tr bgcolor="#cccccc"> * <tr bgcolor="#cccccc">
* <td bgcolor="#ffffff">&nbsp;</td> * <td bgcolor="#ffffff">&nbsp;</td>
* <td class="body_grey"><b>name</b></td> * <td class="body_grey"><b>name</b></td>
* <td class="body_grey">&nbsp;Yes</td> * <td class="body_grey">&nbsp;Yes</td>
* <td colspan="2" class="body_grey">&nbsp;Yes</td> * <td colspan="2" class="body_grey">&nbsp;Yes</td>
* <td class="body_grey">&nbsp;1.0</td> * <td class="body_grey">&nbsp;1.0</td>
* </tr> * </tr>
* <tr> * <tr>
* <td bgcolor="#ffffff">&nbsp;</td> * <td bgcolor="#ffffff">&nbsp;</td>
* <td colspan="5" class="body"><p>The attribute name</p></td> * <td colspan="5" class="body"><p>The attribute name</p></td>
* </tr> * </tr>
* *
* <tr bgcolor="#cccccc"> * <tr bgcolor="#cccccc">
* <td bgcolor="#ffffff">&nbsp;</td> * <td bgcolor="#ffffff">&nbsp;</td>
* <td class="body_grey"><b>value</b></td> * <td class="body_grey"><b>value</b></td>
* <td class="body_grey">&nbsp;No</td> * <td class="body_grey">&nbsp;No</td>
* <td colspan="2" class="body_grey">&nbsp;Yes</td> * <td colspan="2" class="body_grey">&nbsp;Yes</td>
* <td class="body_grey">&nbsp;1.0</td> * <td class="body_grey">&nbsp;1.0</td>
* </tr> * </tr>
* <tr> * <tr>
* <td bgcolor="#ffffff" class="body">&nbsp;</td> * <td bgcolor="#ffffff" class="body">&nbsp;</td>
* <td colspan="5" class="body"><p>The value for equality testing</p></td> * <td colspan="5" class="body"><p>The value for equality testing</p></td>
* </tr> * </tr>
* *
* <tr> * <tr>
* <td class="body" valign="top"><b>Variables</b></td> * <td class="body" valign="top"><b>Variables</b></td>
* <td colspan="5" class="body">None</td> * <td colspan="5" class="body">None</td>
* </tr> * </tr>
* *
* <tr> * <tr>
* <td class="body" valign="top"><b>Examples</b></td> * <td class="body" valign="top"><b>Examples</b></td>
* <td colspan="5" class="body"> * <td colspan="5" class="body">
* <pre> * <pre>
*&lt;%@ taglib prefix="twelvemonkeys" uri="twelvemonkeys-logic" %&gt; *&lt;%@ taglib prefix="twelvemonkeys" uri="twelvemonkeys-logic" %&gt;
*&lt;bean:cookie id="logonUsernameCookie" *&lt;bean:cookie id="logonUsernameCookie"
* name="&lt;%= com.strutscommand.Constants.LOGON_USERNAME_COOKIE_NAME %&gt;" * name="&lt;%= com.strutscommand.Constants.LOGON_USERNAME_COOKIE_NAME %&gt;"
* value="no_username_set" /&gt; * value="no_username_set" /&gt;
*&lt;twelvemonkeys:notEqual name="logonUsernameCookie" value="no_username_set"&gt; *&lt;twelvemonkeys:notEqual name="logonUsernameCookie" value="no_username_set"&gt;
* &lt;html:text property="username" value="&lt;%= logonUsernameCookie.getValue() %&gt;" /&gt; * &lt;html:text property="username" value="&lt;%= logonUsernameCookie.getValue() %&gt;" /&gt;
*&lt;/twelvemonkeys:notEqual&gt; *&lt;/twelvemonkeys:notEqual&gt;
* </pre> * </pre>
* </td> * </td>
* </tr> * </tr>
* </table> * </table>
* *
* <hr> * <hr>
* *
* @version 1.0 * @version 1.0
* @author <a href="mailto:eirik.torske@twelvemonkeys.no">Eirik Torske</a> * @author <a href="mailto:eirik.torske@twelvemonkeys.no">Eirik Torske</a>
* @see <a href="EqualTag.html">equal</a> * @see <a href="EqualTag.html">equal</a>
*/ */
public class NotEqualTag extends ConditionalTagBase { public class NotEqualTag extends ConditionalTagBase {
/** /**
* <a name="condition"></a> * <a name="condition"></a>
* *
* The condition that must be met in order to display the body of this tag: * The condition that must be met in order to display the body of this tag:
* <ol> * <ol>
* <li>The attribute name property ({@code name} -> {@code mObjectName}) must not be empty. * <li>The attribute name property ({@code name} -> {@code mObjectName}) must not be empty.
* <li>The attribute must exist. * <li>The attribute must exist.
* <li>The attribute must be an instance of one of the supported classes: * <li>The attribute must be an instance of one of the supported classes:
* <ul> * <ul>
* <li>{@code java.lang.String} * <li>{@code java.lang.String}
* <li>{@code javax.servlet.http.Cookie} * <li>{@code javax.servlet.http.Cookie}
* </ul> * </ul>
* <li>The value of the attribute must NOT be equal to the object value property ({@code value} -> {@code mObjectValue}). * <li>The value of the attribute must NOT be equal to the object value property ({@code value} -> {@code mObjectValue}).
* </ol> * </ol>
* <p> * <p>
* NB! If the object value property ({@code value} -> {@code mObjectValue}) is empty than {@code true} will be returned. * NB! If the object value property ({@code value} -> {@code mObjectValue}) is empty than {@code true} will be returned.
* </p> * </p>
* *
* @return {@code true} if and only if all conditions are met. * @return {@code true} if and only if all conditions are met.
*/ */
protected boolean condition() throws JspException { protected boolean condition() throws JspException {
if (StringUtil.isEmpty(objectName)) { if (StringUtil.isEmpty(objectName)) {
return false; return false;
} }
if (StringUtil.isEmpty(objectValue)) { if (StringUtil.isEmpty(objectValue)) {
return true; return true;
} }
Object pageScopedAttribute = pageContext.getAttribute(objectName); Object pageScopedAttribute = pageContext.getAttribute(objectName);
if (pageScopedAttribute == null) { if (pageScopedAttribute == null) {
return false; return false;
} }
String pageScopedStringAttribute; String pageScopedStringAttribute;
// String // String
if (pageScopedAttribute instanceof String) { if (pageScopedAttribute instanceof String) {
pageScopedStringAttribute = (String) pageScopedAttribute; pageScopedStringAttribute = (String) pageScopedAttribute;
// Cookie // Cookie
} }
else if (pageScopedAttribute instanceof Cookie) { else if (pageScopedAttribute instanceof Cookie) {
pageScopedStringAttribute = ((Cookie) pageScopedAttribute).getValue(); pageScopedStringAttribute = ((Cookie) pageScopedAttribute).getValue();
// Type not yet supported... // Type not yet supported...
} }
else { else {
return false; return false;
} }
return (!(pageScopedStringAttribute.equals(objectValue))); return (!(pageScopedStringAttribute.equals(objectValue)));
} }
} }

View File

@@ -1,183 +1,183 @@
package com.twelvemonkeys.servlet.log4j; package com.twelvemonkeys.servlet.log4j;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import javax.servlet.RequestDispatcher; import javax.servlet.RequestDispatcher;
import javax.servlet.Servlet; import javax.servlet.Servlet;
import javax.servlet.ServletContext; import javax.servlet.ServletContext;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import java.io.InputStream; import java.io.InputStream;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Proxy; import java.lang.reflect.Proxy;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.Set; import java.util.Set;
/** /**
* Log4JContextWrapper * Log4JContextWrapper
* <p/> * <p/>
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: log4j/Log4JContextWrapper.java#1 $ * @version $Id: log4j/Log4JContextWrapper.java#1 $
*/ */
final class Log4JContextWrapper implements ServletContext { final class Log4JContextWrapper implements ServletContext {
// TODO: Move to sandbox // TODO: Move to sandbox
// TODO: This solution sucks... // TODO: This solution sucks...
// How about starting to create some kind of pluggable decorator system, // How about starting to create some kind of pluggable decorator system,
// something along the lines of AOP mixins/interceptor pattern.. // something along the lines of AOP mixins/interceptor pattern..
// Probably using a dynamic Proxy, delegating to the mixins and or the // Probably using a dynamic Proxy, delegating to the mixins and or the
// wrapped object based on configuration. // wrapped object based on configuration.
// This way we could simply call ServletUtil.decorate(ServletContext):ServletContext // This way we could simply call ServletUtil.decorate(ServletContext):ServletContext
// And the context would be decorated with all configured mixins at once, // And the context would be decorated with all configured mixins at once,
// requiring less boilerplate delegation code, and less layers of wrapping // requiring less boilerplate delegation code, and less layers of wrapping
// (alternatively we could decorate the Servlet/FilterConfig objects). // (alternatively we could decorate the Servlet/FilterConfig objects).
// See the ServletUtil.createWrapper methods for some hints.. // See the ServletUtil.createWrapper methods for some hints..
// Something like this: // Something like this:
public static ServletContext wrap(final ServletContext pContext, final Object[] pDelegates, final ClassLoader pLoader) { public static ServletContext wrap(final ServletContext pContext, final Object[] pDelegates, final ClassLoader pLoader) {
ClassLoader cl = pLoader != null ? pLoader : Thread.currentThread().getContextClassLoader(); ClassLoader cl = pLoader != null ? pLoader : Thread.currentThread().getContextClassLoader();
// TODO: Create a "static" mapping between methods in the ServletContext // TODO: Create a "static" mapping between methods in the ServletContext
// and the corresponding delegate // and the corresponding delegate
// TODO: Resolve super-invokations, to delegate to next delegate in // TODO: Resolve super-invokations, to delegate to next delegate in
// chain, and finally invoke pContext // chain, and finally invoke pContext
return (ServletContext) Proxy.newProxyInstance(cl, new Class[] {ServletContext.class}, new InvocationHandler() { return (ServletContext) Proxy.newProxyInstance(cl, new Class[] {ServletContext.class}, new InvocationHandler() {
public Object invoke(Object pProxy, Method pMethod, Object[] pArgs) throws Throwable { public Object invoke(Object pProxy, Method pMethod, Object[] pArgs) throws Throwable {
// TODO: Test if any of the delegates should receive, if so invoke // TODO: Test if any of the delegates should receive, if so invoke
// Else, invoke on original object // Else, invoke on original object
return pMethod.invoke(pContext, pArgs); return pMethod.invoke(pContext, pArgs);
} }
}); });
} }
private final ServletContext context; private final ServletContext context;
private final Logger logger; private final Logger logger;
Log4JContextWrapper(ServletContext pContext) { Log4JContextWrapper(ServletContext pContext) {
context = pContext; context = pContext;
// TODO: We want a logger per servlet, not per servlet context, right? // TODO: We want a logger per servlet, not per servlet context, right?
logger = Logger.getLogger(pContext.getServletContextName()); logger = Logger.getLogger(pContext.getServletContextName());
// TODO: Automatic init/config of Log4J using context parameter for log4j.xml? // TODO: Automatic init/config of Log4J using context parameter for log4j.xml?
// See Log4JInit.java // See Log4JInit.java
// TODO: Automatic config of properties in the context wrapper? // TODO: Automatic config of properties in the context wrapper?
} }
public final void log(final Exception pException, final String pMessage) { public final void log(final Exception pException, final String pMessage) {
log(pMessage, pException); log(pMessage, pException);
} }
// TODO: Add more logging methods to interface info/warn/error? // TODO: Add more logging methods to interface info/warn/error?
// TODO: Implement these mehtods in GenericFilter/GenericServlet? // TODO: Implement these mehtods in GenericFilter/GenericServlet?
public void log(String pMessage) { public void log(String pMessage) {
// TODO: Get logger for caller.. // TODO: Get logger for caller..
// Should be possible using some stack peek hack, but that's slow... // Should be possible using some stack peek hack, but that's slow...
// Find a good way... // Find a good way...
// Maybe just pass it into the constuctor, and have one wrapper per servlet // Maybe just pass it into the constuctor, and have one wrapper per servlet
logger.info(pMessage); logger.info(pMessage);
} }
public void log(String pMessage, Throwable pCause) { public void log(String pMessage, Throwable pCause) {
// TODO: Get logger for caller.. // TODO: Get logger for caller..
logger.error(pMessage, pCause); logger.error(pMessage, pCause);
} }
public Object getAttribute(String pMessage) { public Object getAttribute(String pMessage) {
return context.getAttribute(pMessage); return context.getAttribute(pMessage);
} }
public Enumeration getAttributeNames() { public Enumeration getAttributeNames() {
return context.getAttributeNames(); return context.getAttributeNames();
} }
public ServletContext getContext(String pMessage) { public ServletContext getContext(String pMessage) {
return context.getContext(pMessage); return context.getContext(pMessage);
} }
public String getInitParameter(String pMessage) { public String getInitParameter(String pMessage) {
return context.getInitParameter(pMessage); return context.getInitParameter(pMessage);
} }
public Enumeration getInitParameterNames() { public Enumeration getInitParameterNames() {
return context.getInitParameterNames(); return context.getInitParameterNames();
} }
public int getMajorVersion() { public int getMajorVersion() {
return context.getMajorVersion(); return context.getMajorVersion();
} }
public String getMimeType(String pMessage) { public String getMimeType(String pMessage) {
return context.getMimeType(pMessage); return context.getMimeType(pMessage);
} }
public int getMinorVersion() { public int getMinorVersion() {
return context.getMinorVersion(); return context.getMinorVersion();
} }
public RequestDispatcher getNamedDispatcher(String pMessage) { public RequestDispatcher getNamedDispatcher(String pMessage) {
return context.getNamedDispatcher(pMessage); return context.getNamedDispatcher(pMessage);
} }
public String getRealPath(String pMessage) { public String getRealPath(String pMessage) {
return context.getRealPath(pMessage); return context.getRealPath(pMessage);
} }
public RequestDispatcher getRequestDispatcher(String pMessage) { public RequestDispatcher getRequestDispatcher(String pMessage) {
return context.getRequestDispatcher(pMessage); return context.getRequestDispatcher(pMessage);
} }
public URL getResource(String pMessage) throws MalformedURLException { public URL getResource(String pMessage) throws MalformedURLException {
return context.getResource(pMessage); return context.getResource(pMessage);
} }
public InputStream getResourceAsStream(String pMessage) { public InputStream getResourceAsStream(String pMessage) {
return context.getResourceAsStream(pMessage); return context.getResourceAsStream(pMessage);
} }
public Set getResourcePaths(String pMessage) { public Set getResourcePaths(String pMessage) {
return context.getResourcePaths(pMessage); return context.getResourcePaths(pMessage);
} }
public String getServerInfo() { public String getServerInfo() {
return context.getServerInfo(); return context.getServerInfo();
} }
public Servlet getServlet(String pMessage) throws ServletException { public Servlet getServlet(String pMessage) throws ServletException {
//noinspection deprecation //noinspection deprecation
return context.getServlet(pMessage); return context.getServlet(pMessage);
} }
public String getServletContextName() { public String getServletContextName() {
return context.getServletContextName(); return context.getServletContextName();
} }
public Enumeration getServletNames() { public Enumeration getServletNames() {
//noinspection deprecation //noinspection deprecation
return context.getServletNames(); return context.getServletNames();
} }
public Enumeration getServlets() { public Enumeration getServlets() {
//noinspection deprecation //noinspection deprecation
return context.getServlets(); return context.getServlets();
} }
public void removeAttribute(String pMessage) { public void removeAttribute(String pMessage) {
context.removeAttribute(pMessage); context.removeAttribute(pMessage);
} }
public void setAttribute(String pMessage, Object pExtension) { public void setAttribute(String pMessage, Object pExtension) {
context.setAttribute(pMessage, pExtension); context.setAttribute(pMessage, pExtension);
} }
} }

View File

@@ -1,118 +1,118 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.servlet; package com.twelvemonkeys.servlet;
import javax.servlet.ServletRequest; import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; import javax.servlet.ServletResponse;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream; import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
import java.util.Enumeration; import java.util.Enumeration;
/** /**
* DebugServlet class description. * DebugServlet class description.
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $ * @author last modified by $Author: haku $
* @version $Id: DebugServlet.java#1 $ * @version $Id: DebugServlet.java#1 $
*/ */
public class DebugServlet extends GenericServlet { public class DebugServlet extends GenericServlet {
private long dateModified; private long dateModified;
public final void service(ServletRequest pRequest, ServletResponse pResponse) throws ServletException, IOException { public final void service(ServletRequest pRequest, ServletResponse pResponse) throws ServletException, IOException {
service((HttpServletRequest) pRequest, (HttpServletResponse) pResponse); service((HttpServletRequest) pRequest, (HttpServletResponse) pResponse);
} }
public void init() throws ServletException { public void init() throws ServletException {
super.init(); super.init();
dateModified = System.currentTimeMillis(); dateModified = System.currentTimeMillis();
} }
public void service(HttpServletRequest pRequest, HttpServletResponse pResponse) throws ServletException, IOException { public void service(HttpServletRequest pRequest, HttpServletResponse pResponse) throws ServletException, IOException {
pResponse.setContentType("text/plain"); pResponse.setContentType("text/plain");
// Include these to allow browser caching // Include these to allow browser caching
pResponse.setDateHeader("Last-Modified", dateModified); pResponse.setDateHeader("Last-Modified", dateModified);
pResponse.setHeader("ETag", getServletName()); pResponse.setHeader("ETag", getServletName());
ServletOutputStream out = pResponse.getOutputStream(); ServletOutputStream out = pResponse.getOutputStream();
out.println("Remote address: " + pRequest.getRemoteAddr()); out.println("Remote address: " + pRequest.getRemoteAddr());
out.println("Remote host name: " + pRequest.getRemoteHost()); out.println("Remote host name: " + pRequest.getRemoteHost());
out.println("Remote user: " + pRequest.getRemoteUser()); out.println("Remote user: " + pRequest.getRemoteUser());
out.println(); out.println();
out.println("Request Method: " + pRequest.getMethod()); out.println("Request Method: " + pRequest.getMethod());
out.println("Request Scheme: " + pRequest.getScheme()); out.println("Request Scheme: " + pRequest.getScheme());
out.println("Request URI: " + pRequest.getRequestURI()); out.println("Request URI: " + pRequest.getRequestURI());
out.println("Request URL: " + pRequest.getRequestURL().toString()); out.println("Request URL: " + pRequest.getRequestURL().toString());
out.println("Request PathInfo: " + pRequest.getPathInfo()); out.println("Request PathInfo: " + pRequest.getPathInfo());
out.println("Request ContentLength: " + pRequest.getContentLength()); out.println("Request ContentLength: " + pRequest.getContentLength());
out.println(); out.println();
out.println("Request Headers:"); out.println("Request Headers:");
Enumeration headerNames = pRequest.getHeaderNames(); Enumeration headerNames = pRequest.getHeaderNames();
while (headerNames.hasMoreElements()) { while (headerNames.hasMoreElements()) {
String headerName = (String) headerNames.nextElement(); String headerName = (String) headerNames.nextElement();
Enumeration headerValues = pRequest.getHeaders(headerName); Enumeration headerValues = pRequest.getHeaders(headerName);
if (headerName != null) { if (headerName != null) {
while (headerValues.hasMoreElements()) { while (headerValues.hasMoreElements()) {
String value = (String) headerValues.nextElement(); String value = (String) headerValues.nextElement();
out.println(" " + headerName + ": " + value); out.println(" " + headerName + ": " + value);
} }
} }
} }
out.println(); out.println();
out.println("Request parameters:"); out.println("Request parameters:");
Enumeration paramNames = pRequest.getParameterNames(); Enumeration paramNames = pRequest.getParameterNames();
while (paramNames.hasMoreElements()) { while (paramNames.hasMoreElements()) {
String name = (String) paramNames.nextElement(); String name = (String) paramNames.nextElement();
String[] values = pRequest.getParameterValues(name); String[] values = pRequest.getParameterValues(name);
for (String value : values) { for (String value : values) {
out.println(" " + name + ": " + value); out.println(" " + name + ": " + value);
} }
} }
out.println(); out.println();
out.println("Request attributes:"); out.println("Request attributes:");
Enumeration attribNames = pRequest.getAttributeNames(); Enumeration attribNames = pRequest.getAttributeNames();
while (attribNames.hasMoreElements()) { while (attribNames.hasMoreElements()) {
String name = (String) attribNames.nextElement(); String name = (String) attribNames.nextElement();
Object value = pRequest.getAttribute(name); Object value = pRequest.getAttribute(name);
out.println(" " + name + ": " + value); out.println(" " + name + ": " + value);
} }
out.flush(); out.flush();
} }
} }

View File

@@ -1,382 +1,382 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.servlet; package com.twelvemonkeys.servlet;
import com.twelvemonkeys.lang.BeanUtil; import com.twelvemonkeys.lang.BeanUtil;
import javax.servlet.*; import javax.servlet.*;
import java.io.IOException; import java.io.IOException;
import java.io.Serializable; import java.io.Serializable;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.util.Enumeration; import java.util.Enumeration;
/** /**
* Defines a generic, protocol-independent filter. * Defines a generic, protocol-independent filter.
* <P/> * <P/>
* {@code GenericFilter} is inspired by {@link GenericServlet}, and * {@code GenericFilter} is inspired by {@link GenericServlet}, and
* implements the {@code Filter} and {@code FilterConfig} interfaces. * implements the {@code Filter} and {@code FilterConfig} interfaces.
* <P/> * <P/>
* {@code GenericFilter} makes writing filters easier. It provides simple * {@code GenericFilter} makes writing filters easier. It provides simple
* versions of the lifecycle methods {@code init} and {@code destroy} * versions of the lifecycle methods {@code init} and {@code destroy}
* and of the methods in the {@code FilterConfig} interface. * and of the methods in the {@code FilterConfig} interface.
* {@code GenericFilter} also implements the {@code log} methods, * {@code GenericFilter} also implements the {@code log} methods,
* declared in the {@code ServletContext} interface. * declared in the {@code ServletContext} interface.
* <p/ * <p/
* {@code GenericFilter} has an auto-init system, that automatically invokes * {@code GenericFilter} has an auto-init system, that automatically invokes
* the method matching the signature {@code void setX(&lt;Type&gt;)}, * the method matching the signature {@code void setX(&lt;Type&gt;)},
* for every init-parameter {@code x}. Both camelCase and lisp-style parameter * for every init-parameter {@code x}. Both camelCase and lisp-style parameter
* naming is supported, lisp-style names will be converted to camelCase. * naming is supported, lisp-style names will be converted to camelCase.
* Parameter values are automatically converted from string representation to * Parameter values are automatically converted from string representation to
* most basic types, if necessary. * most basic types, if necessary.
* <p/> * <p/>
* To write a generic filter, you need only override the abstract * To write a generic filter, you need only override the abstract
* {@link #doFilterImpl(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)} doFilterImpl} method. * {@link #doFilterImpl(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)} doFilterImpl} method.
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $ * @author last modified by $Author: haku $
* *
* @version $Id: GenericFilter.java#1 $ * @version $Id: GenericFilter.java#1 $
* *
* @see Filter * @see Filter
* @see FilterConfig * @see FilterConfig
*/ */
public abstract class GenericFilter implements Filter, FilterConfig, Serializable { public abstract class GenericFilter implements Filter, FilterConfig, Serializable {
// TODO: Rewrite to use ServletConfigurator instead of BeanUtil // TODO: Rewrite to use ServletConfigurator instead of BeanUtil
/** /**
* The filter config. * The filter config.
*/ */
private transient FilterConfig filterConfig = null; private transient FilterConfig filterConfig = null;
/** /**
* Makes sure the filter runs once per request * Makes sure the filter runs once per request
* <p/> * <p/>
* @see #isRunOnce * @see #isRunOnce
* @see #ATTRIB_RUN_ONCE_VALUE * @see #ATTRIB_RUN_ONCE_VALUE
* @see #oncePerRequest * @see #oncePerRequest
*/ */
private final static String ATTRIB_RUN_ONCE_EXT = ".REQUEST_HANDLED"; private final static String ATTRIB_RUN_ONCE_EXT = ".REQUEST_HANDLED";
/** /**
* Makes sure the filter runs once per request. * Makes sure the filter runs once per request.
* Must be configured through init method, as the filter name is not * Must be configured through init method, as the filter name is not
* available before we have a {@code FilterConfig} object. * available before we have a {@code FilterConfig} object.
* <p/> * <p/>
* @see #isRunOnce * @see #isRunOnce
* @see #ATTRIB_RUN_ONCE_VALUE * @see #ATTRIB_RUN_ONCE_VALUE
* @see #oncePerRequest * @see #oncePerRequest
*/ */
private String attribRunOnce = null; private String attribRunOnce = null;
/** /**
* Makes sure the filter runs once per request * Makes sure the filter runs once per request
* <p/> * <p/>
* @see #isRunOnce * @see #isRunOnce
* @see #ATTRIB_RUN_ONCE_EXT * @see #ATTRIB_RUN_ONCE_EXT
* @see #oncePerRequest * @see #oncePerRequest
*/ */
private static final Object ATTRIB_RUN_ONCE_VALUE = new Object(); private static final Object ATTRIB_RUN_ONCE_VALUE = new Object();
/** /**
* Indicates if this filter should run once per request ({@code true}), * Indicates if this filter should run once per request ({@code true}),
* or for each forward/include resource ({@code false}). * or for each forward/include resource ({@code false}).
* <p/> * <p/>
* Set this variable to true, to make sure the filter runs once per request. * Set this variable to true, to make sure the filter runs once per request.
* *
* <em>NOTE: As of Servlet 2.4, this field * <em>NOTE: As of Servlet 2.4, this field
* should always be left to it's default value ({@code false}). * should always be left to it's default value ({@code false}).
* <br/> * <br/>
* To run the filter once per request, the {@code filter-mapping} element * To run the filter once per request, the {@code filter-mapping} element
* of the web-descriptor should include a {@code dispatcher} element: * of the web-descriptor should include a {@code dispatcher} element:
* <pre>&lt;dispatcher&gt;REQUEST&lt;/dispatcher&gt;</pre> * <pre>&lt;dispatcher&gt;REQUEST&lt;/dispatcher&gt;</pre>
* </em> * </em>
*/ */
protected boolean oncePerRequest = false; protected boolean oncePerRequest = false;
/** /**
* Does nothing. * Does nothing.
*/ */
public GenericFilter() {} public GenericFilter() {}
/** /**
* Called by the web container to indicate to a filter that it is being * Called by the web container to indicate to a filter that it is being
* placed into service. * placed into service.
* <p/> * <p/>
* This implementation stores the {@code FilterConfig} object it * This implementation stores the {@code FilterConfig} object it
* receives from the servlet container for later use. * receives from the servlet container for later use.
* Generally, there's no reason to override this method, override the * Generally, there's no reason to override this method, override the
* no-argument {@code init} instead. However, <em>if</em> you are * no-argument {@code init} instead. However, <em>if</em> you are
* overriding this form of the method, * overriding this form of the method,
* always call {@code super.init(config)}. * always call {@code super.init(config)}.
* <p/> * <p/>
* This implementation will also set all configured key/value pairs, that * This implementation will also set all configured key/value pairs, that
* have a matching setter method annotated with {@link InitParam}. * have a matching setter method annotated with {@link InitParam}.
* *
* @param pConfig the filter config * @param pConfig the filter config
* @throws ServletException if an error occurs during init * @throws ServletException if an error occurs during init
* *
* @see Filter#init(javax.servlet.FilterConfig) * @see Filter#init(javax.servlet.FilterConfig)
* @see #init() init * @see #init() init
* @see BeanUtil#configure(Object, java.util.Map, boolean) * @see BeanUtil#configure(Object, java.util.Map, boolean)
*/ */
public void init(final FilterConfig pConfig) throws ServletException { public void init(final FilterConfig pConfig) throws ServletException {
if (pConfig == null) { if (pConfig == null) {
throw new ServletConfigException("filter config == null"); throw new ServletConfigException("filter config == null");
} }
// Store filter config // Store filter config
filterConfig = pConfig; filterConfig = pConfig;
// Configure this // Configure this
try { try {
BeanUtil.configure(this, ServletUtil.asMap(pConfig), true); BeanUtil.configure(this, ServletUtil.asMap(pConfig), true);
} }
catch (InvocationTargetException e) { catch (InvocationTargetException e) {
throw new ServletConfigException("Could not configure " + getFilterName(), e.getCause()); throw new ServletConfigException("Could not configure " + getFilterName(), e.getCause());
} }
// Create run-once attribute name // Create run-once attribute name
attribRunOnce = pConfig.getFilterName() + ATTRIB_RUN_ONCE_EXT; attribRunOnce = pConfig.getFilterName() + ATTRIB_RUN_ONCE_EXT;
log("init (oncePerRequest=" + oncePerRequest + ", attribRunOnce=" + attribRunOnce + ")"); log("init (oncePerRequest=" + oncePerRequest + ", attribRunOnce=" + attribRunOnce + ")");
init(); init();
} }
/** /**
* A convenience method which can be overridden so that there's no need to * A convenience method which can be overridden so that there's no need to
* call {@code super.init(config)}. * call {@code super.init(config)}.
* *
* @see #init(FilterConfig) * @see #init(FilterConfig)
* *
* @throws ServletException if an error occurs during init * @throws ServletException if an error occurs during init
*/ */
public void init() throws ServletException {} public void init() throws ServletException {}
/** /**
* The {@code doFilter} method of the Filter is called by the container * 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 * 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. * client request for a resource at the end of the chain.
* <p/> * <p/>
* Subclasses <em>should not override this method</em>, but rather the * 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. * abstract {@link #doFilterImpl(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)} doFilterImpl} method.
* *
* @param pRequest the servlet request * @param pRequest the servlet request
* @param pResponse the servlet response * @param pResponse the servlet response
* @param pFilterChain the filter chain * @param pFilterChain the filter chain
* *
* @throws IOException * @throws IOException
* @throws ServletException * @throws ServletException
* *
* @see Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) Filter.doFilter * @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 * @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 { 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 request filter and already run, continue chain and return fast
if (oncePerRequest && isRunOnce(pRequest)) { if (oncePerRequest && isRunOnce(pRequest)) {
pFilterChain.doFilter(pRequest, pResponse); pFilterChain.doFilter(pRequest, pResponse);
return; return;
} }
// Do real filter // Do real filter
doFilterImpl(pRequest, pResponse, pFilterChain); doFilterImpl(pRequest, pResponse, pFilterChain);
} }
/** /**
* If request is filtered, returns true, otherwise marks request as filtered * If request is filtered, returns true, otherwise marks request as filtered
* and returns false. * and returns false.
* A return value of false, indicates that the filter has not yet run. * 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 * A return value of true, indicates that the filter has run for this
* request, and processing should not continue. * request, and processing should not continue.
* <P/> * <P/>
* Note that the method will mark the request as filtered on first * Note that the method will mark the request as filtered on first
* invocation. * invocation.
* <p/> * <p/>
* @see #ATTRIB_RUN_ONCE_EXT * @see #ATTRIB_RUN_ONCE_EXT
* @see #ATTRIB_RUN_ONCE_VALUE * @see #ATTRIB_RUN_ONCE_VALUE
* *
* @param pRequest the servlet request * @param pRequest the servlet request
* @return {@code true} if the request is already filtered, otherwise * @return {@code true} if the request is already filtered, otherwise
* {@code false}. * {@code false}.
*/ */
private boolean isRunOnce(final ServletRequest pRequest) { private boolean isRunOnce(final ServletRequest pRequest) {
// If request already filtered, return true (skip) // If request already filtered, return true (skip)
if (pRequest.getAttribute(attribRunOnce) == ATTRIB_RUN_ONCE_VALUE) { if (pRequest.getAttribute(attribRunOnce) == ATTRIB_RUN_ONCE_VALUE) {
return true; return true;
} }
// Set attribute and return false (continue) // Set attribute and return false (continue)
pRequest.setAttribute(attribRunOnce, ATTRIB_RUN_ONCE_VALUE); pRequest.setAttribute(attribRunOnce, ATTRIB_RUN_ONCE_VALUE);
return false; return false;
} }
/** /**
* Invoked once, or each time a request/response pair is passed through the * Invoked once, or each time a request/response pair is passed through the
* chain, depending on the {@link #oncePerRequest} member variable. * chain, depending on the {@link #oncePerRequest} member variable.
* *
* @param pRequest the servlet request * @param pRequest the servlet request
* @param pResponse the servlet response * @param pResponse the servlet response
* @param pChain the filter chain * @param pChain the filter chain
* *
* @throws IOException if an I/O error occurs * @throws IOException if an I/O error occurs
* @throws ServletException if an exception occurs during the filter process * @throws ServletException if an exception occurs during the filter process
* *
* @see #oncePerRequest * @see #oncePerRequest
* @see #doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) doFilter * @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 * @see Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) Filter.doFilter
*/ */
protected abstract void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain) protected abstract void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain)
throws IOException, ServletException; throws IOException, ServletException;
/** /**
* Called by the web container to indicate to a filter that it is being * Called by the web container to indicate to a filter that it is being
* taken out of service. * taken out of service.
* *
* @see Filter#destroy * @see Filter#destroy
*/ */
public void destroy() { public void destroy() {
log("destroy"); log("destroy");
filterConfig = null; filterConfig = null;
} }
/** /**
* Returns the filter-name of this filter as defined in the deployment * Returns the filter-name of this filter as defined in the deployment
* descriptor. * descriptor.
* *
* @return the filter-name * @return the filter-name
* @see FilterConfig#getFilterName * @see FilterConfig#getFilterName
*/ */
public String getFilterName() { public String getFilterName() {
return filterConfig.getFilterName(); return filterConfig.getFilterName();
} }
/** /**
* Returns a reference to the {@link ServletContext} in which the caller is * Returns a reference to the {@link ServletContext} in which the caller is
* executing. * executing.
* *
* @return the {@code ServletContext} object, used by the caller to * @return the {@code ServletContext} object, used by the caller to
* interact with its servlet container * interact with its servlet container
* @see FilterConfig#getServletContext * @see FilterConfig#getServletContext
* @see ServletContext * @see ServletContext
*/ */
public ServletContext getServletContext() { public ServletContext getServletContext() {
return filterConfig.getServletContext(); return filterConfig.getServletContext();
} }
/** /**
* Returns a {@code String} containing the value of the named * Returns a {@code String} containing the value of the named
* initialization parameter, or null if the parameter does not exist. * initialization parameter, or null if the parameter does not exist.
* *
* @param pKey a {@code String} specifying the name of the * @param pKey a {@code String} specifying the name of the
* initialization parameter * initialization parameter
* @return a {@code String} containing the value of the initialization * @return a {@code String} containing the value of the initialization
* parameter * parameter
*/ */
public String getInitParameter(final String pKey) { public String getInitParameter(final String pKey) {
return filterConfig.getInitParameter(pKey); return filterConfig.getInitParameter(pKey);
} }
/** /**
* Returns the names of the servlet's initialization parameters as an * Returns the names of the servlet's initialization parameters as an
* {@code Enumeration} of {@code String} objects, or an empty * {@code Enumeration} of {@code String} objects, or an empty
* {@code Enumeration} if the servlet has no initialization parameters. * {@code Enumeration} if the servlet has no initialization parameters.
* *
* @return an {@code Enumeration} of {@code String} objects * @return an {@code Enumeration} of {@code String} objects
* containing the mNames of the servlet's initialization parameters * containing the mNames of the servlet's initialization parameters
*/ */
public Enumeration getInitParameterNames() { public Enumeration getInitParameterNames() {
return filterConfig.getInitParameterNames(); return filterConfig.getInitParameterNames();
} }
/** /**
* Writes the specified message to a servlet log file, prepended by the * Writes the specified message to a servlet log file, prepended by the
* filter's name. * filter's name.
* *
* @param pMessage the log message * @param pMessage the log message
* @see ServletContext#log(String) * @see ServletContext#log(String)
*/ */
protected void log(final String pMessage) { protected void log(final String pMessage) {
getServletContext().log(getFilterName() + ": " + pMessage); getServletContext().log(getFilterName() + ": " + pMessage);
} }
/** /**
* Writes an explanatory message and a stack trace for a given * Writes an explanatory message and a stack trace for a given
* {@code Throwable} to the servlet log file, prepended by the * {@code Throwable} to the servlet log file, prepended by the
* filter's name. * filter's name.
* *
* @param pMessage the log message * @param pMessage the log message
* @param pThrowable the exception * @param pThrowable the exception
* @see ServletContext#log(String,Throwable) * @see ServletContext#log(String,Throwable)
*/ */
protected void log(final String pMessage, final Throwable pThrowable) { protected void log(final String pMessage, final Throwable pThrowable) {
getServletContext().log(getFilterName() + ": " + pMessage, pThrowable); getServletContext().log(getFilterName() + ": " + pMessage, pThrowable);
} }
/** /**
* Initializes the filter. * Initializes the filter.
* *
* @param pFilterConfig the filter config * @param pFilterConfig the filter config
* @see #init init * @see #init init
* *
* @deprecated For compatibility only, use {@link #init init} instead. * @deprecated For compatibility only, use {@link #init init} instead.
*/ */
@SuppressWarnings("UnusedDeclaration") @SuppressWarnings("UnusedDeclaration")
public void setFilterConfig(final FilterConfig pFilterConfig) { public void setFilterConfig(final FilterConfig pFilterConfig) {
try { try {
init(pFilterConfig); init(pFilterConfig);
} }
catch (ServletException e) { catch (ServletException e) {
log("Error in init(), see stack trace for details.", e); log("Error in init(), see stack trace for details.", e);
} }
} }
/** /**
* Gets the {@code FilterConfig} for this filter. * Gets the {@code FilterConfig} for this filter.
* *
* @return the {@code FilterConfig} for this filter * @return the {@code FilterConfig} for this filter
* @see FilterConfig * @see FilterConfig
*/ */
public FilterConfig getFilterConfig() { public FilterConfig getFilterConfig() {
return filterConfig; return filterConfig;
} }
/** /**
* Specifies if this filter should run once per request ({@code true}), * Specifies if this filter should run once per request ({@code true}),
* or for each forward/include resource ({@code false}). * or for each forward/include resource ({@code false}).
* Called automatically from the {@code init}-method, with settings * Called automatically from the {@code init}-method, with settings
* from web.xml. * from web.xml.
* *
* @param pOncePerRequest {@code true} if the filter should run only * @param pOncePerRequest {@code true} if the filter should run only
* once per request * once per request
* @see #oncePerRequest * @see #oncePerRequest
*/ */
@InitParam(name = "once-per-request") @InitParam(name = "once-per-request")
public void setOncePerRequest(final boolean pOncePerRequest) { public void setOncePerRequest(final boolean pOncePerRequest) {
oncePerRequest = pOncePerRequest; oncePerRequest = pOncePerRequest;
} }
} }

View File

@@ -1,88 +1,88 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.servlet; package com.twelvemonkeys.servlet;
import com.twelvemonkeys.lang.BeanUtil; import com.twelvemonkeys.lang.BeanUtil;
import javax.servlet.ServletConfig; import javax.servlet.ServletConfig;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
/** /**
* Defines a generic, protocol-independent servlet. * Defines a generic, protocol-independent servlet.
* <p/> * <p/>
* {@code GenericServlet} has an auto-init system, that automatically invokes * {@code GenericServlet} has an auto-init system, that automatically invokes
* the method matching the signature {@code void setX(&lt;Type&gt;)}, * the method matching the signature {@code void setX(&lt;Type&gt;)},
* for every init-parameter {@code x}. Both camelCase and lisp-style parameter * for every init-parameter {@code x}. Both camelCase and lisp-style parameter
* naming is supported, lisp-style names will be converted to camelCase. * naming is supported, lisp-style names will be converted to camelCase.
* Parameter values are automatically converted from string representation to * Parameter values are automatically converted from string representation to
* most basic types, if necessary. * most basic types, if necessary.
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $ * @author last modified by $Author: haku $
* *
* @version $Id: GenericServlet.java#1 $ * @version $Id: GenericServlet.java#1 $
*/ */
public abstract class GenericServlet extends javax.servlet.GenericServlet { public abstract class GenericServlet extends javax.servlet.GenericServlet {
// TODO: Rewrite to use ServletConfigurator instead of BeanUtil // TODO: Rewrite to use ServletConfigurator instead of BeanUtil
/** /**
* Called by the web container to indicate to a servlet that it is being * Called by the web container to indicate to a servlet that it is being
* placed into service. * placed into service.
* <p/> * <p/>
* This implementation stores the {@code ServletConfig} object it * This implementation stores the {@code ServletConfig} object it
* receives from the servlet container for later use. When overriding this * receives from the servlet container for later use. When overriding this
* form of the method, call {@code super.init(config)}. * form of the method, call {@code super.init(config)}.
* <p/> * <p/>
* This implementation will also set all configured key/value pairs, that * This implementation will also set all configured key/value pairs, that
* have a matching setter method annotated with {@link InitParam}. * have a matching setter method annotated with {@link InitParam}.
* *
* @param pConfig the servlet config * @param pConfig the servlet config
* @throws ServletException * @throws ServletException
* *
* @see javax.servlet.GenericServlet#init * @see javax.servlet.GenericServlet#init
* @see #init() init * @see #init() init
* @see BeanUtil#configure(Object, java.util.Map, boolean) * @see BeanUtil#configure(Object, java.util.Map, boolean)
*/ */
@Override @Override
public void init(final ServletConfig pConfig) throws ServletException { public void init(final ServletConfig pConfig) throws ServletException {
if (pConfig == null) { if (pConfig == null) {
throw new ServletConfigException("servlet config == null"); throw new ServletConfigException("servlet config == null");
} }
try { try {
BeanUtil.configure(this, ServletUtil.asMap(pConfig), true); BeanUtil.configure(this, ServletUtil.asMap(pConfig), true);
} }
catch (InvocationTargetException e) { catch (InvocationTargetException e) {
throw new ServletConfigException("Could not configure " + getServletName(), e.getCause()); throw new ServletConfigException("Could not configure " + getServletName(), e.getCause());
} }
super.init(pConfig); super.init(pConfig);
} }
} }

View File

@@ -1,88 +1,88 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.servlet; package com.twelvemonkeys.servlet;
import com.twelvemonkeys.lang.BeanUtil; import com.twelvemonkeys.lang.BeanUtil;
import javax.servlet.ServletConfig; import javax.servlet.ServletConfig;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
/** /**
* Defines a generic, HTTP specific servlet. * Defines a generic, HTTP specific servlet.
* <p/> * <p/>
* {@code HttpServlet} has an auto-init system, that automatically invokes * {@code HttpServlet} has an auto-init system, that automatically invokes
* the method matching the signature {@code void setX(&lt;Type&gt;)}, * the method matching the signature {@code void setX(&lt;Type&gt;)},
* for every init-parameter {@code x}. Both camelCase and lisp-style parameter * for every init-parameter {@code x}. Both camelCase and lisp-style parameter
* naming is supported, lisp-style names will be converted to camelCase. * naming is supported, lisp-style names will be converted to camelCase.
* Parameter values are automatically converted from string representation to * Parameter values are automatically converted from string representation to
* most basic types, if necessary. * most basic types, if necessary.
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $ * @author last modified by $Author: haku $
* *
* @version $Id: HttpServlet.java#1 $ * @version $Id: HttpServlet.java#1 $
*/ */
public abstract class HttpServlet extends javax.servlet.http.HttpServlet { public abstract class HttpServlet extends javax.servlet.http.HttpServlet {
// TODO: Rewrite to use ServletConfigurator instead of BeanUtil // TODO: Rewrite to use ServletConfigurator instead of BeanUtil
/** /**
* Called by the web container to indicate to a servlet that it is being * Called by the web container to indicate to a servlet that it is being
* placed into service. * placed into service.
* <p/> * <p/>
* This implementation stores the {@code ServletConfig} object it * This implementation stores the {@code ServletConfig} object it
* receives from the servlet container for later use. When overriding this * receives from the servlet container for later use. When overriding this
* form of the method, call {@code super.init(config)}. * form of the method, call {@code super.init(config)}.
* <p/> * <p/>
* This implementation will also set all configured key/value pairs, that * This implementation will also set all configured key/value pairs, that
* have a matching setter method annotated with {@link InitParam}. * have a matching setter method annotated with {@link InitParam}.
* *
* @param pConfig the servlet config * @param pConfig the servlet config
* @throws ServletException if an error occurred during init * @throws ServletException if an error occurred during init
* *
* @see javax.servlet.GenericServlet#init * @see javax.servlet.GenericServlet#init
* @see #init() init * @see #init() init
* @see BeanUtil#configure(Object, java.util.Map, boolean) * @see BeanUtil#configure(Object, java.util.Map, boolean)
*/ */
@Override @Override
public void init(ServletConfig pConfig) throws ServletException { public void init(ServletConfig pConfig) throws ServletException {
if (pConfig == null) { if (pConfig == null) {
throw new ServletConfigException("servlet config == null"); throw new ServletConfigException("servlet config == null");
} }
try { try {
BeanUtil.configure(this, ServletUtil.asMap(pConfig), true); BeanUtil.configure(this, ServletUtil.asMap(pConfig), true);
} }
catch (InvocationTargetException e) { catch (InvocationTargetException e) {
throw new ServletConfigException("Could not configure " + getServletName(), e.getCause()); throw new ServletConfigException("Could not configure " + getServletName(), e.getCause());
} }
super.init(pConfig); super.init(pConfig);
} }
} }

View File

@@ -1,120 +1,120 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.servlet; package com.twelvemonkeys.servlet;
import com.twelvemonkeys.lang.Validate; import com.twelvemonkeys.lang.Validate;
import javax.servlet.ServletOutputStream; import javax.servlet.ServletOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
/** /**
* A {@code ServletOutputStream} implementation backed by a * A {@code ServletOutputStream} implementation backed by a
* {@link java.io.OutputStream}. For filters that need to buffer the * {@link java.io.OutputStream}. For filters that need to buffer the
* response and do post filtering, it may be used like this:<pre> * response and do post filtering, it may be used like this:<pre>
* ByteArrayOutputStream buffer = new ByteArraOutputStream(); * ByteArrayOutputStream buffer = new ByteArraOutputStream();
* ServletOutputStream adapter = new OutputStreamAdapter(buffer); * ServletOutputStream adapter = new OutputStreamAdapter(buffer);
* </pre> * </pre>
* <p/> * <p/>
* As a {@code ServletOutputStream} is itself an {@code OutputStream}, this * As a {@code ServletOutputStream} is itself an {@code OutputStream}, this
* class may also be used as a superclass for wrappers of other * class may also be used as a superclass for wrappers of other
* {@code ServletOutputStream}s, like this:<pre> * {@code ServletOutputStream}s, like this:<pre>
* class FilterServletOutputStream extends OutputStreamAdapter { * class FilterServletOutputStream extends OutputStreamAdapter {
* public FilterServletOutputStream(ServletOutputStream out) { * public FilterServletOutputStream(ServletOutputStream out) {
* super(out); * super(out);
* } * }
* *
* public void write(int abyte) { * public void write(int abyte) {
* // do filtering... * // do filtering...
* super.write(...); * super.write(...);
* } * }
* } * }
* *
* ... * ...
* *
* ServletOutputStream original = response.getOutputStream(); * ServletOutputStream original = response.getOutputStream();
* ServletOutputStream wrapper = new FilterServletOutputStream(original); * ServletOutputStream wrapper = new FilterServletOutputStream(original);
* </pre> * </pre>
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author $Author: haku $ * @author $Author: haku $
* @version $Id: OutputStreamAdapter.java#1 $ * @version $Id: OutputStreamAdapter.java#1 $
* *
*/ */
public class OutputStreamAdapter extends ServletOutputStream { public class OutputStreamAdapter extends ServletOutputStream {
/** The wrapped {@code OutputStream}. */ /** The wrapped {@code OutputStream}. */
protected final OutputStream out; protected final OutputStream out;
/** /**
* Creates an {@code OutputStreamAdapter}. * Creates an {@code OutputStreamAdapter}.
* *
* @param pOut the wrapped {@code OutputStream} * @param pOut the wrapped {@code OutputStream}
* *
* @throws IllegalArgumentException if {@code pOut} is {@code null}. * @throws IllegalArgumentException if {@code pOut} is {@code null}.
*/ */
public OutputStreamAdapter(final OutputStream pOut) { public OutputStreamAdapter(final OutputStream pOut) {
Validate.notNull(pOut, "out"); Validate.notNull(pOut, "out");
out = pOut; out = pOut;
} }
/** /**
* Returns the wrapped {@code OutputStream}. * Returns the wrapped {@code OutputStream}.
* *
* @return the wrapped {@code OutputStream}. * @return the wrapped {@code OutputStream}.
*/ */
public OutputStream getOutputStream() { public OutputStream getOutputStream() {
return out; return out;
} }
@Override @Override
public String toString() { public String toString() {
return "ServletOutputStream adapted from " + out.toString(); return "ServletOutputStream adapted from " + out.toString();
} }
/** /**
* Writes a byte to the underlying stream. * Writes a byte to the underlying stream.
* *
* @param pByte the byte to write. * @param pByte the byte to write.
* *
* @throws IOException if an error occurs during writing * @throws IOException if an error occurs during writing
*/ */
public void write(final int pByte) throws IOException { public void write(final int pByte) throws IOException {
out.write(pByte); out.write(pByte);
} }
// Overide for efficiency // Overide for efficiency
public void write(final byte pBytes[]) throws IOException { public void write(final byte pBytes[]) throws IOException {
out.write(pBytes); out.write(pBytes);
} }
// Overide for efficiency // Overide for efficiency
public void write(final byte pBytes[], final int pOff, final int pLen) throws IOException { public void write(final byte pBytes[], final int pOff, final int pLen) throws IOException {
out.write(pBytes, pOff, pLen); out.write(pBytes, pOff, pLen);
} }
} }

View File

@@ -1,435 +1,435 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.servlet; package com.twelvemonkeys.servlet;
import com.twelvemonkeys.io.FileUtil; import com.twelvemonkeys.io.FileUtil;
import com.twelvemonkeys.lang.StringUtil; import com.twelvemonkeys.lang.StringUtil;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.ServletRequest; import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.ConnectException; import java.net.ConnectException;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URL; import java.net.URL;
import java.util.Enumeration; import java.util.Enumeration;
/** /**
* A simple proxy servlet implementation. Supports HTTP and HTTPS. * A simple proxy servlet implementation. Supports HTTP and HTTPS.
* <p/> * <p/>
* Note: The servlet is not a true HTTP proxy as described in * Note: The servlet is not a true HTTP proxy as described in
* <a href="http://www.w3.org/Protocols/rfc2616/rfc2616.html">RFC 2616</a>, * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616.html">RFC 2616</a>,
* instead it passes on all incoming HTTP requests to the configured remote * instead it passes on all incoming HTTP requests to the configured remote
* server. * server.
* Useful for bypassing firewalls or to avoid exposing internal network * Useful for bypassing firewalls or to avoid exposing internal network
* infrastructure to external clients. * infrastructure to external clients.
* <p/> * <p/>
* At the moment, no caching of content is implemented. * At the moment, no caching of content is implemented.
* <p/> * <p/>
* If the {@code remoteServer} init parameter is not set, the servlet will * If the {@code remoteServer} init parameter is not set, the servlet will
* respond by sending a {@code 500 Internal Server Error} response to the client. * respond by sending a {@code 500 Internal Server Error} response to the client.
* If the configured remote server is down, or unreachable, the servlet will * If the configured remote server is down, or unreachable, the servlet will
* respond by sending a {@code 502 Bad Gateway} response to the client. * respond by sending a {@code 502 Bad Gateway} response to the client.
* Otherwise, the response from the remote server will be tunneled unmodified * Otherwise, the response from the remote server will be tunneled unmodified
* to the client. * to the client.
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $ * @author last modified by $Author: haku $
* *
* @version $Id: ProxyServlet.java#1 $ * @version $Id: ProxyServlet.java#1 $
*/ */
public class ProxyServlet extends GenericServlet { public class ProxyServlet extends GenericServlet {
/** Remote server host name or IP address */ /** Remote server host name or IP address */
protected String remoteServer = null; protected String remoteServer = null;
/** Remote server port */ /** Remote server port */
protected int remotePort = 80; protected int remotePort = 80;
/** Remote server "mount" path */ /** Remote server "mount" path */
protected String remotePath = ""; protected String remotePath = "";
private static final String HTTP_REQUEST_HEADER_HOST = "host"; private static final String HTTP_REQUEST_HEADER_HOST = "host";
private static final String HTTP_RESPONSE_HEADER_SERVER = "server"; private static final String HTTP_RESPONSE_HEADER_SERVER = "server";
private static final String MESSAGE_REMOTE_SERVER_NOT_CONFIGURED = "Remote server not configured."; private static final String MESSAGE_REMOTE_SERVER_NOT_CONFIGURED = "Remote server not configured.";
/** /**
* Called by {@code init} to set the remote server. Must be a valid host * Called by {@code init} to set the remote server. Must be a valid host
* name or IP address. No default. * name or IP address. No default.
* *
* @param pRemoteServer * @param pRemoteServer
*/ */
public void setRemoteServer(String pRemoteServer) { public void setRemoteServer(String pRemoteServer) {
remoteServer = pRemoteServer; remoteServer = pRemoteServer;
} }
/** /**
* Called by {@code init} to set the remote port. Must be a number. * Called by {@code init} to set the remote port. Must be a number.
* Default is {@code 80}. * Default is {@code 80}.
* *
* @param pRemotePort * @param pRemotePort
*/ */
public void setRemotePort(String pRemotePort) { public void setRemotePort(String pRemotePort) {
try { try {
remotePort = Integer.parseInt(pRemotePort); remotePort = Integer.parseInt(pRemotePort);
} }
catch (NumberFormatException e) { catch (NumberFormatException e) {
log("RemotePort must be a number!", e); log("RemotePort must be a number!", e);
} }
} }
/** /**
* Called by {@code init} to set the remote path. May be an empty string * Called by {@code init} to set the remote path. May be an empty string
* for the root path, or any other valid path on the remote server. * for the root path, or any other valid path on the remote server.
* Default is {@code ""}. * Default is {@code ""}.
* *
* @param pRemotePath * @param pRemotePath
*/ */
public void setRemotePath(String pRemotePath) { public void setRemotePath(String pRemotePath) {
if (StringUtil.isEmpty(pRemotePath)) { if (StringUtil.isEmpty(pRemotePath)) {
pRemotePath = ""; pRemotePath = "";
} }
else if (pRemotePath.charAt(0) != '/') { else if (pRemotePath.charAt(0) != '/') {
pRemotePath = "/" + pRemotePath; pRemotePath = "/" + pRemotePath;
} }
remotePath = pRemotePath; remotePath = pRemotePath;
} }
/** /**
* Override {@code service} to use HTTP specifics. * Override {@code service} to use HTTP specifics.
* *
* @param pRequest * @param pRequest
* @param pResponse * @param pResponse
* *
* @throws ServletException * @throws ServletException
* @throws IOException * @throws IOException
* *
* @see #service(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) * @see #service(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/ */
public final void service(ServletRequest pRequest, ServletResponse pResponse) throws ServletException, IOException { public final void service(ServletRequest pRequest, ServletResponse pResponse) throws ServletException, IOException {
service((HttpServletRequest) pRequest, (HttpServletResponse) pResponse); service((HttpServletRequest) pRequest, (HttpServletResponse) pResponse);
} }
/** /**
* Services a single request. * Services a single request.
* Supports HTTP and HTTPS. * Supports HTTP and HTTPS.
* *
* @param pRequest * @param pRequest
* @param pResponse * @param pResponse
* *
* @throws ServletException * @throws ServletException
* @throws IOException * @throws IOException
* *
* @see ProxyServlet Class descrition * @see ProxyServlet Class descrition
*/ */
protected void service(HttpServletRequest pRequest, HttpServletResponse pResponse) throws ServletException, IOException { protected void service(HttpServletRequest pRequest, HttpServletResponse pResponse) throws ServletException, IOException {
// Sanity check configuration // Sanity check configuration
if (remoteServer == null) { if (remoteServer == null) {
log(MESSAGE_REMOTE_SERVER_NOT_CONFIGURED); log(MESSAGE_REMOTE_SERVER_NOT_CONFIGURED);
pResponse.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, pResponse.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
MESSAGE_REMOTE_SERVER_NOT_CONFIGURED); MESSAGE_REMOTE_SERVER_NOT_CONFIGURED);
return; return;
} }
HttpURLConnection remoteConnection = null; HttpURLConnection remoteConnection = null;
try { try {
// Recreate request URI for remote request // Recreate request URI for remote request
String requestURI = createRemoteRequestURI(pRequest); String requestURI = createRemoteRequestURI(pRequest);
URL remoteURL = new URL(pRequest.getScheme(), remoteServer, remotePort, requestURI); URL remoteURL = new URL(pRequest.getScheme(), remoteServer, remotePort, requestURI);
// Get connection, with method from original request // Get connection, with method from original request
// NOTE: The actual connection is not done before we ask for streams... // NOTE: The actual connection is not done before we ask for streams...
// NOTE: The HttpURLConnection is supposed to handle multiple // NOTE: The HttpURLConnection is supposed to handle multiple
// requests to the same server internally // requests to the same server internally
String method = pRequest.getMethod(); String method = pRequest.getMethod();
remoteConnection = (HttpURLConnection) remoteURL.openConnection(); remoteConnection = (HttpURLConnection) remoteURL.openConnection();
remoteConnection.setRequestMethod(method); remoteConnection.setRequestMethod(method);
// Copy header fields // Copy header fields
copyHeadersFromClient(pRequest, remoteConnection); copyHeadersFromClient(pRequest, remoteConnection);
// Do proxy specifc stuff? // Do proxy specifc stuff?
// TODO: Read up the specs from RFC 2616 (HTTP) on proxy behaviour // TODO: Read up the specs from RFC 2616 (HTTP) on proxy behaviour
// TODO: RFC 2616 says "[a] proxy server MUST NOT establish an HTTP/1.1 // TODO: RFC 2616 says "[a] proxy server MUST NOT establish an HTTP/1.1
// persistent connection with an HTTP/1.0 client" // persistent connection with an HTTP/1.0 client"
// Copy message body from client to remote server // Copy message body from client to remote server
copyBodyFromClient(pRequest, remoteConnection); copyBodyFromClient(pRequest, remoteConnection);
// Set response status code from remote server to client // Set response status code from remote server to client
int responseCode = remoteConnection.getResponseCode(); int responseCode = remoteConnection.getResponseCode();
pResponse.setStatus(responseCode); pResponse.setStatus(responseCode);
//System.out.println("Response is: " + responseCode + " " + remoteConnection.getResponseMessage()); //System.out.println("Response is: " + responseCode + " " + remoteConnection.getResponseMessage());
// Copy header fields back // Copy header fields back
copyHeadersToClient(remoteConnection, pResponse); copyHeadersToClient(remoteConnection, pResponse);
// More proxy specific stuff? // More proxy specific stuff?
// Copy message body from remote server to client // Copy message body from remote server to client
copyBodyToClient(remoteConnection, pResponse); copyBodyToClient(remoteConnection, pResponse);
} }
catch (ConnectException e) { catch (ConnectException e) {
// In case we could not connecto to the remote server // In case we could not connecto to the remote server
log("Could not connect to remote server.", e); log("Could not connect to remote server.", e);
pResponse.sendError(HttpServletResponse.SC_BAD_GATEWAY, e.getMessage()); pResponse.sendError(HttpServletResponse.SC_BAD_GATEWAY, e.getMessage());
} }
finally { finally {
// Disconnect from server // Disconnect from server
// TODO: Should we actually do this? // TODO: Should we actually do this?
if (remoteConnection != null) { if (remoteConnection != null) {
remoteConnection.disconnect(); remoteConnection.disconnect();
} }
} }
} }
/** /**
* Copies the message body from the remote server to the client (outgoing * Copies the message body from the remote server to the client (outgoing
* {@code HttpServletResponse}). * {@code HttpServletResponse}).
* *
* @param pRemoteConnection * @param pRemoteConnection
* @param pResponse * @param pResponse
* *
* @throws IOException * @throws IOException
*/ */
private void copyBodyToClient(HttpURLConnection pRemoteConnection, HttpServletResponse pResponse) throws IOException { private void copyBodyToClient(HttpURLConnection pRemoteConnection, HttpServletResponse pResponse) throws IOException {
InputStream fromRemote = null; InputStream fromRemote = null;
OutputStream toClient = null; OutputStream toClient = null;
try { try {
// Get either input or error stream // Get either input or error stream
try { try {
fromRemote = pRemoteConnection.getInputStream(); fromRemote = pRemoteConnection.getInputStream();
} }
catch (IOException e) { catch (IOException e) {
// If exception, use errorStream instead // If exception, use errorStream instead
fromRemote = pRemoteConnection.getErrorStream(); fromRemote = pRemoteConnection.getErrorStream();
} }
// I guess the stream might be null if there is no response other // I guess the stream might be null if there is no response other
// than headers (Continue, No Content, etc). // than headers (Continue, No Content, etc).
if (fromRemote != null) { if (fromRemote != null) {
toClient = pResponse.getOutputStream(); toClient = pResponse.getOutputStream();
FileUtil.copy(fromRemote, toClient); FileUtil.copy(fromRemote, toClient);
} }
} }
finally { finally {
if (fromRemote != null) { if (fromRemote != null) {
try { try {
fromRemote.close(); fromRemote.close();
} }
catch (IOException e) { catch (IOException e) {
log("Stream from remote could not be closed.", e); log("Stream from remote could not be closed.", e);
} }
} }
if (toClient != null) { if (toClient != null) {
try { try {
toClient.close(); toClient.close();
} }
catch (IOException e) { catch (IOException e) {
log("Stream to client could not be closed.", e); log("Stream to client could not be closed.", e);
} }
} }
} }
} }
/** /**
* Copies the message body from the client (incomming * Copies the message body from the client (incomming
* {@code HttpServletRequest}) to the remote server if the request method * {@code HttpServletRequest}) to the remote server if the request method
* is {@code POST} or <tt>PUT<tt>. * is {@code POST} or <tt>PUT<tt>.
* Otherwise this method does nothing. * Otherwise this method does nothing.
* *
* @param pRequest * @param pRequest
* @param pRemoteConnection * @param pRemoteConnection
* *
* @throws java.io.IOException * @throws java.io.IOException
*/ */
private void copyBodyFromClient(HttpServletRequest pRequest, HttpURLConnection pRemoteConnection) throws IOException { private void copyBodyFromClient(HttpServletRequest pRequest, HttpURLConnection pRemoteConnection) throws IOException {
// If this is a POST or PUT, copy message body from client remote server // If this is a POST or PUT, copy message body from client remote server
if (!("POST".equals(pRequest.getMethod()) || "PUT".equals(pRequest.getMethod()))) { if (!("POST".equals(pRequest.getMethod()) || "PUT".equals(pRequest.getMethod()))) {
return; return;
} }
// NOTE: Setting doOutput to true, will make it a POST request (why?)... // NOTE: Setting doOutput to true, will make it a POST request (why?)...
pRemoteConnection.setDoOutput(true); pRemoteConnection.setDoOutput(true);
// Get streams and do the copying // Get streams and do the copying
InputStream fromClient = null; InputStream fromClient = null;
OutputStream toRemote = null; OutputStream toRemote = null;
try { try {
fromClient = pRequest.getInputStream(); fromClient = pRequest.getInputStream();
toRemote = pRemoteConnection.getOutputStream(); toRemote = pRemoteConnection.getOutputStream();
FileUtil.copy(fromClient, toRemote); FileUtil.copy(fromClient, toRemote);
} }
finally { finally {
if (fromClient != null) { if (fromClient != null) {
try { try {
fromClient.close(); fromClient.close();
} }
catch (IOException e) { catch (IOException e) {
log("Stream from client could not be closed.", e); log("Stream from client could not be closed.", e);
} }
} }
if (toRemote != null) { if (toRemote != null) {
try { try {
toRemote.close(); toRemote.close();
} }
catch (IOException e) { catch (IOException e) {
log("Stream to remote could not be closed.", e); log("Stream to remote could not be closed.", e);
} }
} }
} }
} }
/** /**
* Creates the remote request URI based on the incoming request. * Creates the remote request URI based on the incoming request.
* The URI will include any query strings etc. * The URI will include any query strings etc.
* *
* @param pRequest * @param pRequest
* *
* @return a {@code String} representing the remote request URI * @return a {@code String} representing the remote request URI
*/ */
private String createRemoteRequestURI(HttpServletRequest pRequest) { private String createRemoteRequestURI(HttpServletRequest pRequest) {
StringBuilder requestURI = new StringBuilder(remotePath); StringBuilder requestURI = new StringBuilder(remotePath);
requestURI.append(pRequest.getPathInfo()); requestURI.append(pRequest.getPathInfo());
if (!StringUtil.isEmpty(pRequest.getQueryString())) { if (!StringUtil.isEmpty(pRequest.getQueryString())) {
requestURI.append("?"); requestURI.append("?");
requestURI.append(pRequest.getQueryString()); requestURI.append(pRequest.getQueryString());
} }
return requestURI.toString(); return requestURI.toString();
} }
/** /**
* Copies headers from the remote connection back to the client * Copies headers from the remote connection back to the client
* (the outgoing HttpServletResponse). All headers except the "Server" * (the outgoing HttpServletResponse). All headers except the "Server"
* header are copied. * header are copied.
* *
* @param pRemoteConnection * @param pRemoteConnection
* @param pResponse * @param pResponse
*/ */
private void copyHeadersToClient(HttpURLConnection pRemoteConnection, HttpServletResponse pResponse) { private void copyHeadersToClient(HttpURLConnection pRemoteConnection, HttpServletResponse pResponse) {
// NOTE: There is no getHeaderFieldCount method or similar... // NOTE: There is no getHeaderFieldCount method or similar...
// Also, the getHeaderFields() method was introduced in J2SE 1.4, and // Also, the getHeaderFields() method was introduced in J2SE 1.4, and
// we want to be 1.2 compatible. // we want to be 1.2 compatible.
// So, just try to loop until there are no more headers. // So, just try to loop until there are no more headers.
int i = 0; int i = 0;
while (true) { while (true) {
String key = pRemoteConnection.getHeaderFieldKey(i); String key = pRemoteConnection.getHeaderFieldKey(i);
// NOTE: getHeaderField(String) returns only the last value // NOTE: getHeaderField(String) returns only the last value
String value = pRemoteConnection.getHeaderField(i); String value = pRemoteConnection.getHeaderField(i);
// If the key is not null, life is simple, and Sun is shining // If the key is not null, life is simple, and Sun is shining
// However, the default implementations includes the HTTP response // However, the default implementations includes the HTTP response
// code ("HTTP/1.1 200 Ok" or similar) as a header field with // code ("HTTP/1.1 200 Ok" or similar) as a header field with
// key "null" (why..?)... // key "null" (why..?)...
// In addition, we want to skip the original "Server" header // In addition, we want to skip the original "Server" header
if (key != null && !HTTP_RESPONSE_HEADER_SERVER.equalsIgnoreCase(key)) { if (key != null && !HTTP_RESPONSE_HEADER_SERVER.equalsIgnoreCase(key)) {
//System.out.println("client <<<-- remote: " + key + ": " + value); //System.out.println("client <<<-- remote: " + key + ": " + value);
pResponse.setHeader(key, value); pResponse.setHeader(key, value);
} }
else if (value == null) { else if (value == null) {
// If BOTH key and value is null, there are no more header fields // If BOTH key and value is null, there are no more header fields
break; break;
} }
i++; i++;
} }
/* 1.4+ version below.... /* 1.4+ version below....
Map headers = pRemoteConnection.getHeaderFields(); Map headers = pRemoteConnection.getHeaderFields();
for (Iterator iterator = headers.entrySet().iterator(); iterator.hasNext();) { for (Iterator iterator = headers.entrySet().iterator(); iterator.hasNext();) {
Map.Entry header = (Map.Entry) iterator.next(); Map.Entry header = (Map.Entry) iterator.next();
List values = (List) header.getValue(); List values = (List) header.getValue();
for (Iterator valueIter = values.iterator(); valueIter.hasNext();) { for (Iterator valueIter = values.iterator(); valueIter.hasNext();) {
String value = (String) valueIter.next(); String value = (String) valueIter.next();
String key = (String) header.getKey(); String key = (String) header.getKey();
// Skip the server header // Skip the server header
if (HTTP_RESPONSE_HEADER_SERVER.equalsIgnoreCase(key)) { if (HTTP_RESPONSE_HEADER_SERVER.equalsIgnoreCase(key)) {
key = null; key = null;
} }
// The default implementations includes the HTTP response code // The default implementations includes the HTTP response code
// ("HTTP/1.1 200 Ok" or similar) as a header field with // ("HTTP/1.1 200 Ok" or similar) as a header field with
// key "null" (why..?)... // key "null" (why..?)...
if (key != null) { if (key != null) {
//System.out.println("client <<<-- remote: " + key + ": " + value); //System.out.println("client <<<-- remote: " + key + ": " + value);
pResponse.setHeader(key, value); pResponse.setHeader(key, value);
} }
} }
} }
*/ */
} }
/** /**
* Copies headers from the client (the incoming {@code HttpServletRequest}) * Copies headers from the client (the incoming {@code HttpServletRequest})
* to the outgoing connection. * to the outgoing connection.
* All headers except the "Host" header are copied. * All headers except the "Host" header are copied.
* *
* @param pRequest * @param pRequest
* @param pRemoteConnection * @param pRemoteConnection
*/ */
private void copyHeadersFromClient(HttpServletRequest pRequest, HttpURLConnection pRemoteConnection) { private void copyHeadersFromClient(HttpServletRequest pRequest, HttpURLConnection pRemoteConnection) {
Enumeration headerNames = pRequest.getHeaderNames(); Enumeration headerNames = pRequest.getHeaderNames();
while (headerNames.hasMoreElements()) { while (headerNames.hasMoreElements()) {
String headerName = (String) headerNames.nextElement(); String headerName = (String) headerNames.nextElement();
Enumeration headerValues = pRequest.getHeaders(headerName); Enumeration headerValues = pRequest.getHeaders(headerName);
// Skip the "host" header, as we want something else // Skip the "host" header, as we want something else
if (HTTP_REQUEST_HEADER_HOST.equalsIgnoreCase(headerName)) { if (HTTP_REQUEST_HEADER_HOST.equalsIgnoreCase(headerName)) {
// Skip this header // Skip this header
headerName = null; headerName = null;
} }
// Set the the header to the remoteConnection // Set the the header to the remoteConnection
if (headerName != null) { if (headerName != null) {
// Convert from multiple line to single line, comma separated, as // Convert from multiple line to single line, comma separated, as
// there seems to be a shortcoming in the URLConneciton API... // there seems to be a shortcoming in the URLConneciton API...
StringBuilder headerValue = new StringBuilder(); StringBuilder headerValue = new StringBuilder();
while (headerValues.hasMoreElements()) { while (headerValues.hasMoreElements()) {
String value = (String) headerValues.nextElement(); String value = (String) headerValues.nextElement();
headerValue.append(value); headerValue.append(value);
if (headerValues.hasMoreElements()) { if (headerValues.hasMoreElements()) {
headerValue.append(", "); headerValue.append(", ");
} }
} }
//System.out.println("client -->>> remote: " + headerName + ": " + headerValue); //System.out.println("client -->>> remote: " + headerName + ": " + headerValue);
pRemoteConnection.setRequestProperty(headerName, headerValue.toString()); pRemoteConnection.setRequestProperty(headerName, headerValue.toString());
} }
} }
} }
} }

View File

@@ -1,81 +1,81 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.servlet; package com.twelvemonkeys.servlet;
import javax.servlet.ServletException; import javax.servlet.ServletException;
/** /**
* ServletConfigException. * ServletConfigException.
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: ServletConfigException.java#2 $ * @version $Id: ServletConfigException.java#2 $
*/ */
public class ServletConfigException extends ServletException { public class ServletConfigException extends ServletException {
// TODO: Parameters for init-param at fault, and possibly servlet name? // TODO: Parameters for init-param at fault, and possibly servlet name?
/** /**
* Creates a {@code ServletConfigException} with the given message. * Creates a {@code ServletConfigException} with the given message.
* *
* @param pMessage the exception message * @param pMessage the exception message
*/ */
public ServletConfigException(String pMessage) { public ServletConfigException(String pMessage) {
super(pMessage); super(pMessage);
} }
/** /**
* Creates a {@code ServletConfigException} with the given message and cause. * Creates a {@code ServletConfigException} with the given message and cause.
* *
* @param pMessage the exception message * @param pMessage the exception message
* @param pCause the exception cause * @param pCause the exception cause
*/ */
public ServletConfigException(final String pMessage, final Throwable pCause) { public ServletConfigException(final String pMessage, final Throwable pCause) {
super(pMessage, pCause); super(pMessage, pCause);
maybeInitCause(pCause); maybeInitCause(pCause);
} }
/** /**
* Creates a {@code ServletConfigException} with the cause. * Creates a {@code ServletConfigException} with the cause.
* *
* @param pCause the exception cause * @param pCause the exception cause
*/ */
public ServletConfigException(final Throwable pCause) { public ServletConfigException(final Throwable pCause) {
super(String.format("Error in Servlet configuration: %s", pCause.getMessage()), pCause); super(String.format("Error in Servlet configuration: %s", pCause.getMessage()), pCause);
maybeInitCause(pCause); maybeInitCause(pCause);
} }
private void maybeInitCause(Throwable pCause) { private void maybeInitCause(Throwable pCause) {
// Workaround for ServletExceptions that does not do proper exception chaining // Workaround for ServletExceptions that does not do proper exception chaining
if (getCause() == null) { if (getCause() == null) {
initCause(pCause); initCause(pCause);
} }
} }
} }

View File

@@ -1,282 +1,282 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.servlet; package com.twelvemonkeys.servlet;
import com.twelvemonkeys.lang.StringUtil; import com.twelvemonkeys.lang.StringUtil;
import com.twelvemonkeys.lang.Validate; import com.twelvemonkeys.lang.Validate;
import javax.servlet.FilterConfig; import javax.servlet.FilterConfig;
import javax.servlet.ServletConfig; import javax.servlet.ServletConfig;
import javax.servlet.ServletContext; import javax.servlet.ServletContext;
import java.io.Serializable; import java.io.Serializable;
import java.util.*; import java.util.*;
/** /**
* {@code ServletConfig} or {@code FilterConfig} adapter, that implements * {@code ServletConfig} or {@code FilterConfig} adapter, that implements
* the {@code Map} interface for interoperability with collection-based API's. * the {@code Map} interface for interoperability with collection-based API's.
* <p/> * <p/>
* This {@code Map} is not synchronized. * This {@code Map} is not synchronized.
* <p/> * <p/>
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: ServletConfigMapAdapter.java#2 $ * @version $Id: ServletConfigMapAdapter.java#2 $
*/ */
class ServletConfigMapAdapter extends AbstractMap<String, String> implements Map<String, String>, Serializable, Cloneable { class ServletConfigMapAdapter extends AbstractMap<String, String> implements Map<String, String>, Serializable, Cloneable {
enum ConfigType { enum ConfigType {
ServletConfig, FilterConfig, ServletContext ServletConfig, FilterConfig, ServletContext
} }
private final ConfigType type; private final ConfigType type;
private final ServletConfig servletConfig; private final ServletConfig servletConfig;
private final FilterConfig filterConfig; private final FilterConfig filterConfig;
private final ServletContext servletContext; private final ServletContext servletContext;
// Cache the entry set // Cache the entry set
private transient Set<Entry<String, String>> entrySet; private transient Set<Entry<String, String>> entrySet;
public ServletConfigMapAdapter(final ServletConfig pConfig) { public ServletConfigMapAdapter(final ServletConfig pConfig) {
this(pConfig, ConfigType.ServletConfig); this(pConfig, ConfigType.ServletConfig);
} }
public ServletConfigMapAdapter(final FilterConfig pConfig) { public ServletConfigMapAdapter(final FilterConfig pConfig) {
this(pConfig, ConfigType.FilterConfig); this(pConfig, ConfigType.FilterConfig);
} }
public ServletConfigMapAdapter(final ServletContext pContext) { public ServletConfigMapAdapter(final ServletContext pContext) {
this(pContext, ConfigType.ServletContext); this(pContext, ConfigType.ServletContext);
} }
private ServletConfigMapAdapter(final Object pConfig, final ConfigType pType) { private ServletConfigMapAdapter(final Object pConfig, final ConfigType pType) {
// Could happen if client code invokes with null reference // Could happen if client code invokes with null reference
Validate.notNull(pConfig, "config"); Validate.notNull(pConfig, "config");
type = pType; type = pType;
switch (type) { switch (type) {
case ServletConfig: case ServletConfig:
servletConfig = (ServletConfig) pConfig; servletConfig = (ServletConfig) pConfig;
filterConfig = null; filterConfig = null;
servletContext = null; servletContext = null;
break; break;
case FilterConfig: case FilterConfig:
servletConfig = null; servletConfig = null;
filterConfig = (FilterConfig) pConfig; filterConfig = (FilterConfig) pConfig;
servletContext = null; servletContext = null;
break; break;
case ServletContext: case ServletContext:
servletConfig = null; servletConfig = null;
filterConfig = null; filterConfig = null;
servletContext = (ServletContext) pConfig; servletContext = (ServletContext) pConfig;
break; break;
default: default:
throw new IllegalArgumentException("Wrong type: " + pType); throw new IllegalArgumentException("Wrong type: " + pType);
} }
} }
/** /**
* Gets the servlet or filter name from the config. * Gets the servlet or filter name from the config.
* *
* @return the servlet or filter name * @return the servlet or filter name
*/ */
public final String getName() { public final String getName() {
switch (type) { switch (type) {
case ServletConfig: case ServletConfig:
return servletConfig.getServletName(); return servletConfig.getServletName();
case FilterConfig: case FilterConfig:
return filterConfig.getFilterName(); return filterConfig.getFilterName();
case ServletContext: case ServletContext:
return servletContext.getServletContextName(); return servletContext.getServletContextName();
default: default:
throw new IllegalStateException(); throw new IllegalStateException();
} }
} }
/** /**
* Gets the servlet context from the config. * Gets the servlet context from the config.
* *
* @return the servlet context * @return the servlet context
*/ */
public final ServletContext getServletContext() { public final ServletContext getServletContext() {
switch (type) { switch (type) {
case ServletConfig: case ServletConfig:
return servletConfig.getServletContext(); return servletConfig.getServletContext();
case FilterConfig: case FilterConfig:
return filterConfig.getServletContext(); return filterConfig.getServletContext();
case ServletContext: case ServletContext:
return servletContext; return servletContext;
default: default:
throw new IllegalStateException(); throw new IllegalStateException();
} }
} }
public final Enumeration getInitParameterNames() { public final Enumeration getInitParameterNames() {
switch (type) { switch (type) {
case ServletConfig: case ServletConfig:
return servletConfig.getInitParameterNames(); return servletConfig.getInitParameterNames();
case FilterConfig: case FilterConfig:
return filterConfig.getInitParameterNames(); return filterConfig.getInitParameterNames();
case ServletContext: case ServletContext:
return servletContext.getInitParameterNames(); return servletContext.getInitParameterNames();
default: default:
throw new IllegalStateException(); throw new IllegalStateException();
} }
} }
public final String getInitParameter(final String pName) { public final String getInitParameter(final String pName) {
switch (type) { switch (type) {
case ServletConfig: case ServletConfig:
return servletConfig.getInitParameter(pName); return servletConfig.getInitParameter(pName);
case FilterConfig: case FilterConfig:
return filterConfig.getInitParameter(pName); return filterConfig.getInitParameter(pName);
case ServletContext: case ServletContext:
return servletContext.getInitParameter(pName); return servletContext.getInitParameter(pName);
default: default:
throw new IllegalStateException(); throw new IllegalStateException();
} }
} }
public Set<Entry<String, String>> entrySet() { public Set<Entry<String, String>> entrySet() {
if (entrySet == null) { if (entrySet == null) {
entrySet = createEntrySet(); entrySet = createEntrySet();
} }
return entrySet; return entrySet;
} }
private Set<Entry<String, String>> createEntrySet() { private Set<Entry<String, String>> createEntrySet() {
return new AbstractSet<Entry<String, String>>() { return new AbstractSet<Entry<String, String>>() {
// Cache size, if requested, -1 means not calculated // Cache size, if requested, -1 means not calculated
private int size = -1; private int size = -1;
public Iterator<Entry<String, String>> iterator() { public Iterator<Entry<String, String>> iterator() {
return new Iterator<Entry<String, String>>() { return new Iterator<Entry<String, String>>() {
// Iterator is backed by initParameterNames enumeration // Iterator is backed by initParameterNames enumeration
final Enumeration names = getInitParameterNames(); final Enumeration names = getInitParameterNames();
public boolean hasNext() { public boolean hasNext() {
return names.hasMoreElements(); return names.hasMoreElements();
} }
public Entry<String, String> next() { public Entry<String, String> next() {
final String key = (String) names.nextElement(); final String key = (String) names.nextElement();
return new Entry<String, String>() { return new Entry<String, String>() {
public String getKey() { public String getKey() {
return key; return key;
} }
public String getValue() { public String getValue() {
return get(key); return get(key);
} }
public String setValue(String pValue) { public String setValue(String pValue) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
// NOTE: Override equals // NOTE: Override equals
public boolean equals(Object pOther) { public boolean equals(Object pOther) {
if (!(pOther instanceof Map.Entry)) { if (!(pOther instanceof Map.Entry)) {
return false; return false;
} }
Map.Entry e = (Map.Entry) pOther; Map.Entry e = (Map.Entry) pOther;
Object value = get(key); Object value = get(key);
Object rKey = e.getKey(); Object rKey = e.getKey();
Object rValue = e.getValue(); Object rValue = e.getValue();
return (key == null ? rKey == null : key.equals(rKey)) return (key == null ? rKey == null : key.equals(rKey))
&& (value == null ? rValue == null : value.equals(rValue)); && (value == null ? rValue == null : value.equals(rValue));
} }
// NOTE: Override hashCode to keep the map's // NOTE: Override hashCode to keep the map's
// hashCode constant and compatible // hashCode constant and compatible
public int hashCode() { public int hashCode() {
Object value = get(key); Object value = get(key);
return ((key == null) ? 0 : key.hashCode()) ^ return ((key == null) ? 0 : key.hashCode()) ^
((value == null) ? 0 : value.hashCode()); ((value == null) ? 0 : value.hashCode());
} }
public String toString() { public String toString() {
return key + "=" + get(key); return key + "=" + get(key);
} }
}; };
} }
public void remove() { public void remove() {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
}; };
} }
public int size() { public int size() {
if (size < 0) { if (size < 0) {
size = calculateSize(); size = calculateSize();
} }
return size; return size;
} }
private int calculateSize() { private int calculateSize() {
final Enumeration names = getInitParameterNames(); final Enumeration names = getInitParameterNames();
int size = 0; int size = 0;
while (names.hasMoreElements()) { while (names.hasMoreElements()) {
size++; size++;
names.nextElement(); names.nextElement();
} }
return size; return size;
} }
}; };
} }
public String get(Object pKey) { public String get(Object pKey) {
return getInitParameter(StringUtil.valueOf(pKey)); return getInitParameter(StringUtil.valueOf(pKey));
} }
/// Unsupported Map methods /// Unsupported Map methods
@Override @Override
public String put(String pKey, String pValue) { public String put(String pKey, String pValue) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override @Override
public String remove(Object pKey) { public String remove(Object pKey) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override @Override
public void putAll(Map pMap) { public void putAll(Map pMap) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override @Override
public void clear() { public void clear() {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
} }

View File

@@ -1,114 +1,114 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.servlet; package com.twelvemonkeys.servlet;
import javax.servlet.ServletOutputStream; import javax.servlet.ServletOutputStream;
import javax.servlet.ServletResponse; import javax.servlet.ServletResponse;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.OutputStreamWriter; import java.io.OutputStreamWriter;
import java.io.PrintWriter; import java.io.PrintWriter;
import static com.twelvemonkeys.lang.Validate.notNull; import static com.twelvemonkeys.lang.Validate.notNull;
/** /**
* A delegate for handling stream support in wrapped servlet responses. * A delegate for handling stream support in wrapped servlet responses.
* <p/> * <p/>
* Client code should delegate {@code getOutputStream}, {@code getWriter}, * Client code should delegate {@code getOutputStream}, {@code getWriter},
* {@code flushBuffer} and {@code resetBuffer} methods from the servlet response. * {@code flushBuffer} and {@code resetBuffer} methods from the servlet response.
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $ * @author last modified by $Author: haku $
* @version $Id: ServletResponseStreamDelegate.java#2 $ * @version $Id: ServletResponseStreamDelegate.java#2 $
*/ */
public class ServletResponseStreamDelegate { public class ServletResponseStreamDelegate {
private Object out = null; private Object out = null;
protected final ServletResponse response; protected final ServletResponse response;
public ServletResponseStreamDelegate(final ServletResponse pResponse) { public ServletResponseStreamDelegate(final ServletResponse pResponse) {
response = notNull(pResponse, "response"); response = notNull(pResponse, "response");
} }
// NOTE: Intentionally NOT thread safe, as one request/response should be handled by one thread ONLY. // NOTE: Intentionally NOT thread safe, as one request/response should be handled by one thread ONLY.
public final ServletOutputStream getOutputStream() throws IOException { public final ServletOutputStream getOutputStream() throws IOException {
if (out == null) { if (out == null) {
OutputStream out = createOutputStream(); OutputStream out = createOutputStream();
this.out = out instanceof ServletOutputStream ? out : new OutputStreamAdapter(out); this.out = out instanceof ServletOutputStream ? out : new OutputStreamAdapter(out);
} }
else if (out instanceof PrintWriter) { else if (out instanceof PrintWriter) {
throw new IllegalStateException("getWriter() already called."); throw new IllegalStateException("getWriter() already called.");
} }
return (ServletOutputStream) out; return (ServletOutputStream) out;
} }
// NOTE: Intentionally NOT thread safe, as one request/response should be handled by one thread ONLY. // NOTE: Intentionally NOT thread safe, as one request/response should be handled by one thread ONLY.
public final PrintWriter getWriter() throws IOException { public final PrintWriter getWriter() throws IOException {
if (out == null) { if (out == null) {
// NOTE: getCharacterEncoding may/should not return null // NOTE: getCharacterEncoding may/should not return null
OutputStream out = createOutputStream(); OutputStream out = createOutputStream();
String charEncoding = response.getCharacterEncoding(); String charEncoding = response.getCharacterEncoding();
this.out = new PrintWriter(charEncoding != null ? new OutputStreamWriter(out, charEncoding) : new OutputStreamWriter(out)); this.out = new PrintWriter(charEncoding != null ? new OutputStreamWriter(out, charEncoding) : new OutputStreamWriter(out));
} }
else if (out instanceof ServletOutputStream) { else if (out instanceof ServletOutputStream) {
throw new IllegalStateException("getOutputStream() already called."); throw new IllegalStateException("getOutputStream() already called.");
} }
return (PrintWriter) out; return (PrintWriter) out;
} }
/** /**
* Returns the {@code OutputStream}. * Returns the {@code OutputStream}.
* Subclasses should override this method to provide a decorated output stream. * Subclasses should override this method to provide a decorated output stream.
* This method is guaranteed to be invoked only once for a request/response * This method is guaranteed to be invoked only once for a request/response
* (unless {@code resetBuffer} is invoked). * (unless {@code resetBuffer} is invoked).
* <P/> * <P/>
* This implementation simply returns the output stream from the wrapped * This implementation simply returns the output stream from the wrapped
* response. * response.
* *
* @return the {@code OutputStream} to use for the response * @return the {@code OutputStream} to use for the response
* @throws IOException if an I/O exception occurs * @throws IOException if an I/O exception occurs
*/ */
protected OutputStream createOutputStream() throws IOException { protected OutputStream createOutputStream() throws IOException {
return response.getOutputStream(); return response.getOutputStream();
} }
public void flushBuffer() throws IOException { public void flushBuffer() throws IOException {
if (out instanceof ServletOutputStream) { if (out instanceof ServletOutputStream) {
((ServletOutputStream) out).flush(); ((ServletOutputStream) out).flush();
} }
else if (out != null) { else if (out != null) {
((PrintWriter) out).flush(); ((PrintWriter) out).flush();
} }
} }
public void resetBuffer() { public void resetBuffer() {
out = null; out = null;
} }
} }

View File

@@ -1,306 +1,306 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.servlet; package com.twelvemonkeys.servlet;
import com.twelvemonkeys.io.FileUtil; import com.twelvemonkeys.io.FileUtil;
import com.twelvemonkeys.lang.StringUtil; import com.twelvemonkeys.lang.StringUtil;
import javax.servlet.FilterChain; import javax.servlet.FilterChain;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.ServletRequest; import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
* ThrottleFilter, a filter for easing server during heavy load. * ThrottleFilter, a filter for easing server during heavy load.
* <p/> * <p/>
* Intercepts requests, and returns HTTP response code {@code 503 (Service Unavailable)}, * Intercepts requests, and returns HTTP response code {@code 503 (Service Unavailable)},
* if there are more than a given number of concurrent * if there are more than a given number of concurrent
* requests, to avoid large backlogs. The number of concurrent requests and the * requests, to avoid large backlogs. The number of concurrent requests and the
* response messages sent to the user agent, is configurable from the web * response messages sent to the user agent, is configurable from the web
* descriptor. * descriptor.
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $ * @author last modified by $Author: haku $
* @version $Id: ThrottleFilter.java#1 $ * @version $Id: ThrottleFilter.java#1 $
* @see #setMaxConcurrentThreadCount * @see #setMaxConcurrentThreadCount
* @see #setResponseMessages * @see #setResponseMessages
*/ */
public class ThrottleFilter extends GenericFilter { public class ThrottleFilter extends GenericFilter {
/** /**
* Minimum free thread count, defaults to {@code 10} * Minimum free thread count, defaults to {@code 10}
*/ */
protected int maxConcurrentThreadCount = 10; protected int maxConcurrentThreadCount = 10;
/** /**
* The number of running request threads * The number of running request threads
*/ */
private int runningThreads = 0; private int runningThreads = 0;
private final Object runningThreadsLock = new Object(); private final Object runningThreadsLock = new Object();
/** /**
* Default response message sent to user agents, if the request is rejected * Default response message sent to user agents, if the request is rejected
*/ */
protected final static String DEFUALT_RESPONSE_MESSAGE = protected final static String DEFUALT_RESPONSE_MESSAGE =
"Service temporarily unavailable, please try again later."; "Service temporarily unavailable, please try again later.";
/** /**
* Default response content type * Default response content type
*/ */
protected static final String DEFAULT_TYPE = "text/html"; protected static final String DEFAULT_TYPE = "text/html";
/** /**
* The reposne message sent to user agenta, if the request is rejected * The reposne message sent to user agenta, if the request is rejected
*/ */
private Map<String, String> responseMessageNames = new HashMap<String, String>(10); private Map<String, String> responseMessageNames = new HashMap<String, String>(10);
/** /**
* The reposne message sent to user agents, if the request is rejected * The reposne message sent to user agents, if the request is rejected
*/ */
private String[] responseMessageTypes = null; private String[] responseMessageTypes = null;
/** /**
* Cache for response messages * Cache for response messages
*/ */
private Map<String, CacheEntry> responseCache = new HashMap<String, CacheEntry>(10); private Map<String, CacheEntry> responseCache = new HashMap<String, CacheEntry>(10);
/** /**
* Sets the minimum free thread count. * Sets the minimum free thread count.
* *
* @param pMaxConcurrentThreadCount * @param pMaxConcurrentThreadCount
*/ */
public void setMaxConcurrentThreadCount(String pMaxConcurrentThreadCount) { public void setMaxConcurrentThreadCount(String pMaxConcurrentThreadCount) {
if (!StringUtil.isEmpty(pMaxConcurrentThreadCount)) { if (!StringUtil.isEmpty(pMaxConcurrentThreadCount)) {
try { try {
maxConcurrentThreadCount = Integer.parseInt(pMaxConcurrentThreadCount); maxConcurrentThreadCount = Integer.parseInt(pMaxConcurrentThreadCount);
} }
catch (NumberFormatException nfe) { catch (NumberFormatException nfe) {
// Use default // Use default
} }
} }
} }
/** /**
* Sets the response message sent to the user agent, if the request is * Sets the response message sent to the user agent, if the request is
* rejected. * rejected.
* <BR/> * <BR/>
* The format is {@code &lt;mime-type&gt;=&lt;filename&gt;, * The format is {@code &lt;mime-type&gt;=&lt;filename&gt;,
* &lt;mime-type&gt;=&lt;filename&gt;}. * &lt;mime-type&gt;=&lt;filename&gt;}.
* <BR/> * <BR/>
* Example: {@code &lt;text/vnd.wap.wmlgt;=&lt;/errors/503.wml&gt;, * Example: {@code &lt;text/vnd.wap.wmlgt;=&lt;/errors/503.wml&gt;,
* &lt;text/html&gt;=&lt;/errors/503.html&gt;} * &lt;text/html&gt;=&lt;/errors/503.html&gt;}
* *
* @param pResponseMessages * @param pResponseMessages
*/ */
public void setResponseMessages(String pResponseMessages) { public void setResponseMessages(String pResponseMessages) {
// Split string in type=filename pairs // Split string in type=filename pairs
String[] mappings = StringUtil.toStringArray(pResponseMessages, ", \r\n\t"); String[] mappings = StringUtil.toStringArray(pResponseMessages, ", \r\n\t");
List<String> types = new ArrayList<String>(); List<String> types = new ArrayList<String>();
for (String pair : mappings) { for (String pair : mappings) {
// Split pairs on '=' // Split pairs on '='
String[] mapping = StringUtil.toStringArray(pair, "= "); String[] mapping = StringUtil.toStringArray(pair, "= ");
// Test for wrong mapping // Test for wrong mapping
if ((mapping == null) || (mapping.length < 2)) { if ((mapping == null) || (mapping.length < 2)) {
log("Error in init param \"responseMessages\": " + pResponseMessages); log("Error in init param \"responseMessages\": " + pResponseMessages);
continue; continue;
} }
types.add(mapping[0]); types.add(mapping[0]);
responseMessageNames.put(mapping[0], mapping[1]); responseMessageNames.put(mapping[0], mapping[1]);
} }
// Create arrays // Create arrays
responseMessageTypes = types.toArray(new String[types.size()]); responseMessageTypes = types.toArray(new String[types.size()]);
} }
/** /**
* @param pRequest * @param pRequest
* @param pResponse * @param pResponse
* @param pChain * @param pChain
* @throws IOException * @throws IOException
* @throws ServletException * @throws ServletException
*/ */
protected void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain) throws IOException, ServletException { protected void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain) throws IOException, ServletException {
try { try {
if (beginRequest()) { if (beginRequest()) {
// Continue request // Continue request
pChain.doFilter(pRequest, pResponse); pChain.doFilter(pRequest, pResponse);
} }
else { else {
// Send error and end request // Send error and end request
// Get HTTP specific versions // Get HTTP specific versions
HttpServletRequest request = (HttpServletRequest) pRequest; HttpServletRequest request = (HttpServletRequest) pRequest;
HttpServletResponse response = (HttpServletResponse) pResponse; HttpServletResponse response = (HttpServletResponse) pResponse;
// Get content type // Get content type
String contentType = getContentType(request); String contentType = getContentType(request);
// Note: This is not the way the spec says you should do it. // Note: This is not the way the spec says you should do it.
// However, we handle error response this way for preformace reasons. // However, we handle error response this way for preformace reasons.
// The "correct" way would be to use sendError() and register a servlet // The "correct" way would be to use sendError() and register a servlet
// that does the content negotiation as errorpage in the web descriptor. // that does the content negotiation as errorpage in the web descriptor.
response.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE); response.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
response.setContentType(contentType); response.setContentType(contentType);
response.getWriter().println(getMessage(contentType)); response.getWriter().println(getMessage(contentType));
// Log warning, as this shouldn't happen too often // Log warning, as this shouldn't happen too often
log("Request denied, no more available threads for requestURI=" + request.getRequestURI()); log("Request denied, no more available threads for requestURI=" + request.getRequestURI());
} }
} }
finally { finally {
doneRequest(); doneRequest();
} }
} }
/** /**
* Marks the beginning of a request * Marks the beginning of a request
* *
* @return {@code true} if the request should be handled. * @return {@code true} if the request should be handled.
*/ */
private boolean beginRequest() { private boolean beginRequest() {
synchronized (runningThreadsLock) { synchronized (runningThreadsLock) {
runningThreads++; runningThreads++;
} }
return (runningThreads <= maxConcurrentThreadCount); return (runningThreads <= maxConcurrentThreadCount);
} }
/** /**
* Marks the end of the request * Marks the end of the request
*/ */
private void doneRequest() { private void doneRequest() {
synchronized (runningThreadsLock) { synchronized (runningThreadsLock) {
runningThreads--; runningThreads--;
} }
} }
/** /**
* Gets the content type for the response, suitable for the requesting user agent. * Gets the content type for the response, suitable for the requesting user agent.
* *
* @param pRequest * @param pRequest
* @return the content type * @return the content type
*/ */
private String getContentType(HttpServletRequest pRequest) { private String getContentType(HttpServletRequest pRequest) {
if (responseMessageTypes != null) { if (responseMessageTypes != null) {
String accept = pRequest.getHeader("Accept"); String accept = pRequest.getHeader("Accept");
for (String type : responseMessageTypes) { for (String type : responseMessageTypes) {
// Note: This is not 100% correct way of doing content negotiation // Note: This is not 100% correct way of doing content negotiation
// But we just want a compatible result, quick, so this is okay // But we just want a compatible result, quick, so this is okay
if (StringUtil.contains(accept, type)) { if (StringUtil.contains(accept, type)) {
return type; return type;
} }
} }
} }
// If none found, return default // If none found, return default
return DEFAULT_TYPE; return DEFAULT_TYPE;
} }
/** /**
* Gets the response message for the given content type. * Gets the response message for the given content type.
* *
* @param pContentType * @param pContentType
* @return the message * @return the message
*/ */
private String getMessage(String pContentType) { private String getMessage(String pContentType) {
String fileName = responseMessageNames.get(pContentType); String fileName = responseMessageNames.get(pContentType);
// Get cached value // Get cached value
CacheEntry entry = responseCache.get(fileName); CacheEntry entry = responseCache.get(fileName);
if ((entry == null) || entry.isExpired()) { if ((entry == null) || entry.isExpired()) {
// Create and add or replace cached value // Create and add or replace cached value
entry = new CacheEntry(readMessage(fileName)); entry = new CacheEntry(readMessage(fileName));
responseCache.put(fileName, entry); responseCache.put(fileName, entry);
} }
// Return value // Return value
return (entry.getValue() != null) return (entry.getValue() != null)
? (String) entry.getValue() ? (String) entry.getValue()
: DEFUALT_RESPONSE_MESSAGE; : DEFUALT_RESPONSE_MESSAGE;
} }
/** /**
* Reads the response message from a file in the current web app. * Reads the response message from a file in the current web app.
* *
* @param pFileName * @param pFileName
* @return the message * @return the message
*/ */
private String readMessage(String pFileName) { private String readMessage(String pFileName) {
try { try {
// Read resource from web app // Read resource from web app
InputStream is = getServletContext().getResourceAsStream(pFileName); InputStream is = getServletContext().getResourceAsStream(pFileName);
if (is != null) { if (is != null) {
return new String(FileUtil.read(is)); return new String(FileUtil.read(is));
} }
else { else {
log("File not found: " + pFileName); log("File not found: " + pFileName);
} }
} }
catch (IOException ioe) { catch (IOException ioe) {
log("Error reading file: " + pFileName + " (" + ioe.getMessage() + ")"); log("Error reading file: " + pFileName + " (" + ioe.getMessage() + ")");
} }
return null; return null;
} }
/** /**
* Keeps track of Cached objects * Keeps track of Cached objects
*/ */
private static class CacheEntry { private static class CacheEntry {
private Object value; private Object value;
private long timestamp = -1; private long timestamp = -1;
CacheEntry(Object pValue) { CacheEntry(Object pValue) {
value = pValue; value = pValue;
timestamp = System.currentTimeMillis(); timestamp = System.currentTimeMillis();
} }
Object getValue() { Object getValue() {
return value; return value;
} }
boolean isExpired() { boolean isExpired() {
return (System.currentTimeMillis() - timestamp) > 60000; // Cache 1 minute return (System.currentTimeMillis() - timestamp) > 60000; // Cache 1 minute
} }
} }
} }

View File

@@ -1,112 +1,112 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.servlet; package com.twelvemonkeys.servlet;
import javax.servlet.FilterChain; import javax.servlet.FilterChain;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.ServletRequest; import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import java.io.IOException; import java.io.IOException;
/** /**
* TimingFilter class description. * TimingFilter class description.
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $ * @author last modified by $Author: haku $
* @version $Id: TimingFilter.java#1 $ * @version $Id: TimingFilter.java#1 $
*/ */
public class TimingFilter extends GenericFilter { public class TimingFilter extends GenericFilter {
private String attribUsage = null; private String attribUsage = null;
/** /**
* Method init * Method init
* *
* @throws ServletException * @throws ServletException
*/ */
public void init() throws ServletException { public void init() throws ServletException {
attribUsage = getFilterName() + ".timerDelta"; attribUsage = getFilterName() + ".timerDelta";
} }
/** /**
* *
* @param pRequest * @param pRequest
* @param pResponse * @param pResponse
* @param pChain * @param pChain
* @throws IOException * @throws IOException
* @throws ServletException * @throws ServletException
*/ */
protected void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain) protected void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain)
throws IOException, ServletException { throws IOException, ServletException {
// Get total usage of earlier filters on same level // Get total usage of earlier filters on same level
Object usageAttrib = pRequest.getAttribute(attribUsage); Object usageAttrib = pRequest.getAttribute(attribUsage);
long total = 0; long total = 0;
if (usageAttrib instanceof Long) { if (usageAttrib instanceof Long) {
// If set, get value, and remove attribute for nested resources // If set, get value, and remove attribute for nested resources
total = (Long) usageAttrib; total = (Long) usageAttrib;
pRequest.removeAttribute(attribUsage); pRequest.removeAttribute(attribUsage);
} }
// Start timing // Start timing
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
try { try {
// Continue chain // Continue chain
pChain.doFilter(pRequest, pResponse); pChain.doFilter(pRequest, pResponse);
} }
finally { finally {
// Stop timing // Stop timing
long end = System.currentTimeMillis(); long end = System.currentTimeMillis();
// Get time usage of included resources, add to total usage // Get time usage of included resources, add to total usage
usageAttrib = pRequest.getAttribute(attribUsage); usageAttrib = pRequest.getAttribute(attribUsage);
long usage = 0; long usage = 0;
if (usageAttrib instanceof Long) { if (usageAttrib instanceof Long) {
usage = (Long) usageAttrib; usage = (Long) usageAttrib;
} }
// Get the name of the included resource // Get the name of the included resource
String resourceURI = ServletUtil.getIncludeRequestURI(pRequest); String resourceURI = ServletUtil.getIncludeRequestURI(pRequest);
// If none, this is probably the parent page itself // If none, this is probably the parent page itself
if (resourceURI == null) { if (resourceURI == null) {
resourceURI = ((HttpServletRequest) pRequest).getRequestURI(); resourceURI = ((HttpServletRequest) pRequest).getRequestURI();
} }
long delta = end - start; long delta = end - start;
log(String.format("Request processing time for resource \"%s\": %d ms (accumulated: %d ms).", resourceURI, (delta - usage), delta)); log(String.format("Request processing time for resource \"%s\": %d ms (accumulated: %d ms).", resourceURI, (delta - usage), delta));
// Store total usage // Store total usage
total += delta; total += delta;
pRequest.setAttribute(attribUsage, total); pRequest.setAttribute(attribUsage, total);
} }
} }
} }

View File

@@ -1,238 +1,238 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.servlet; package com.twelvemonkeys.servlet;
import javax.servlet.FilterChain; import javax.servlet.FilterChain;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream; import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest; import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; import javax.servlet.ServletResponse;
import javax.servlet.ServletResponseWrapper; import javax.servlet.ServletResponseWrapper;
import java.io.IOException; import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.FilterOutputStream; import java.io.FilterOutputStream;
/** /**
* Removes extra unneccessary white space from a servlet response. * Removes extra unneccessary white space from a servlet response.
* White space is defined as per {@link Character#isWhitespace(char)}. * White space is defined as per {@link Character#isWhitespace(char)}.
* <p/> * <p/>
* This filter has no understanding of the content in the reponse, and will * 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 * 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 * removing white space from HTML or XML streams, but this limitation makes it
* less suited for filtering HTML/XHTML with embedded CSS or JavaScript, * less suited for filtering HTML/XHTML with embedded CSS or JavaScript,
* in case white space should be significant here. It is strongly reccommended * 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 * you keep CSS and JavaScript in separate files (this will have the added
* benefit of further reducing the ammount of data communicated between * benefit of further reducing the ammount of data communicated between
* server and client). * server and client).
* <p/> * <p/>
* <em>At the moment this filter has no concept of encoding</em>. * <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 * 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 * bytes that <em>individually</em> is treated as a white space, these bytes
* may be skipped. * may be skipped.
* As <a href="http://en.wikipedia.org/wiki/UTF-8" title="UTF-8">UTF-8</a> * As <a href="http://en.wikipedia.org/wiki/UTF-8" title="UTF-8">UTF-8</a>
* guarantees that no bytes are repeated in this way, this filter can safely * guarantees that no bytes are repeated in this way, this filter can safely
* filter UTF-8. * filter UTF-8.
* Simple 8 bit character encodings, like the * Simple 8 bit character encodings, like the
* <a href="http://en.wikipedia.org/wiki/ISO/IEC_8859" * <a href="http://en.wikipedia.org/wiki/ISO/IEC_8859"
* title="ISO/IEC 8859">ISO/IEC 8859</a> standard, or * title="ISO/IEC 8859">ISO/IEC 8859</a> standard, or
* <a href="http://en.wikipedia.org/wiki/Windows-1252" title="Windows-1252"> * <a href="http://en.wikipedia.org/wiki/Windows-1252" title="Windows-1252">
* are always safe. * are always safe.
* <p/> * <p/>
* <b>Configuration</b><br/> * <b>Configuration</b><br/>
* To use {@code TrimWhiteSpaceFilter} in your web-application, you simply need * To use {@code TrimWhiteSpaceFilter} in your web-application, you simply need
* to add it to your web descriptor ({@code web.xml}). * to add it to your web descriptor ({@code web.xml}).
* If using a servlet container that supports the Servlet 2.4 spec, the new * If using a servlet container that supports the Servlet 2.4 spec, the new
* {@code dispatcher} element should be used, and set to * {@code dispatcher} element should be used, and set to
* {@code REQUEST/FORWARD}, to make sure the filter is invoked only once for * {@code REQUEST/FORWARD}, to make sure the filter is invoked only once for
* requests. * requests.
* If using an older web descriptor, set the {@code init-param} * If using an older web descriptor, set the {@code init-param}
* {@code "once-per-request"} to {@code "true"} (this will have the same effect, * {@code "once-per-request"} to {@code "true"} (this will have the same effect,
* but might perform slightly worse than the 2.4 version). * but might perform slightly worse than the 2.4 version).
* Please see the examples below. * Please see the examples below.
* <p/> * <p/>
* <b>Servlet 2.4 version, filter section:</b><br/> * <b>Servlet 2.4 version, filter section:</b><br/>
* <pre> * <pre>
* &lt;!-- TrimWS Filter Configuration --&gt; * &lt;!-- TrimWS Filter Configuration --&gt;
* &lt;filter&gt; * &lt;filter&gt;
* &lt;filter-name&gt;trimws&lt;/filter-name&gt; * &lt;filter-name&gt;trimws&lt;/filter-name&gt;
* &lt;filter-class&gt;com.twelvemonkeys.servlet.TrimWhiteSpaceFilter&lt;/filter-class&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;!-- auto-flush=true is the default, may be omitted --&gt;
* &lt;init-param&gt; * &lt;init-param&gt;
* &lt;param-name&gt;auto-flush&lt;/param-name&gt; * &lt;param-name&gt;auto-flush&lt;/param-name&gt;
* &lt;param-value&gt;true&lt;/param-value&gt; * &lt;param-value&gt;true&lt;/param-value&gt;
* &lt;/init-param&gt; * &lt;/init-param&gt;
* &lt;/filter&gt; * &lt;/filter&gt;
* </pre> * </pre>
* <b>Filter-mapping section:</b><br/> * <b>Filter-mapping section:</b><br/>
* <pre> * <pre>
* &lt;!-- TimWS Filter Mapping --&gt; * &lt;!-- TimWS Filter Mapping --&gt;
* &lt;filter-mapping&gt; * &lt;filter-mapping&gt;
* &lt;filter-name&gt;trimws&lt;/filter-name&gt; * &lt;filter-name&gt;trimws&lt;/filter-name&gt;
* &lt;url-pattern&gt;*.html&lt;/url-pattern&gt; * &lt;url-pattern&gt;*.html&lt;/url-pattern&gt;
* &lt;dispatcher&gt;REQUEST&lt;/dispatcher&gt; * &lt;dispatcher&gt;REQUEST&lt;/dispatcher&gt;
* &lt;dispatcher&gt;FORWARD&lt;/dispatcher&gt; * &lt;dispatcher&gt;FORWARD&lt;/dispatcher&gt;
* &lt;/filter-mapping&gt; * &lt;/filter-mapping&gt;
* &lt;filter-mapping&gt; * &lt;filter-mapping&gt;
* &lt;filter-name&gt;trimws&lt;/filter-name&gt; * &lt;filter-name&gt;trimws&lt;/filter-name&gt;
* &lt;url-pattern&gt;*.jsp&lt;/url-pattern&gt; * &lt;url-pattern&gt;*.jsp&lt;/url-pattern&gt;
* &lt;dispatcher&gt;REQUEST&lt;/dispatcher&gt; * &lt;dispatcher&gt;REQUEST&lt;/dispatcher&gt;
* &lt;dispatcher&gt;FORWARD&lt;/dispatcher&gt; * &lt;dispatcher&gt;FORWARD&lt;/dispatcher&gt;
* &lt;/filter-mapping&gt; * &lt;/filter-mapping&gt;
* </pre> * </pre>
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $ * @author last modified by $Author: haku $
* @version $Id: TrimWhiteSpaceFilter.java#2 $ * @version $Id: TrimWhiteSpaceFilter.java#2 $
*/ */
public class TrimWhiteSpaceFilter extends GenericFilter { public class TrimWhiteSpaceFilter extends GenericFilter {
private boolean autoFlush = true; private boolean autoFlush = true;
@InitParam @InitParam
public void setAutoFlush(final boolean pAutoFlush) { public void setAutoFlush(final boolean pAutoFlush) {
autoFlush = pAutoFlush; autoFlush = pAutoFlush;
} }
public void init() throws ServletException { public void init() throws ServletException {
super.init(); super.init();
log("Automatic flushing is " + (autoFlush ? "enabled" : "disabled")); log("Automatic flushing is " + (autoFlush ? "enabled" : "disabled"));
} }
protected void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain) throws IOException, ServletException { protected void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain) throws IOException, ServletException {
ServletResponseWrapper wrapped = new TrimWSServletResponseWrapper(pResponse); ServletResponseWrapper wrapped = new TrimWSServletResponseWrapper(pResponse);
pChain.doFilter(pRequest, ServletUtil.createWrapper(wrapped)); pChain.doFilter(pRequest, ServletUtil.createWrapper(wrapped));
if (autoFlush) { if (autoFlush) {
wrapped.flushBuffer(); wrapped.flushBuffer();
} }
} }
static final class TrimWSFilterOutputStream extends FilterOutputStream { static final class TrimWSFilterOutputStream extends FilterOutputStream {
boolean lastWasWS = true; // Avoids leading WS by init to true boolean lastWasWS = true; // Avoids leading WS by init to true
public TrimWSFilterOutputStream(OutputStream pOut) { public TrimWSFilterOutputStream(OutputStream pOut) {
super(pOut); super(pOut);
} }
// Override this, in case the wrapped outputstream overrides... // Override this, in case the wrapped outputstream overrides...
public final void write(byte pBytes[]) throws IOException { public final void write(byte pBytes[]) throws IOException {
write(pBytes, 0, pBytes.length); write(pBytes, 0, pBytes.length);
} }
// Override this, in case the wrapped outputstream overrides... // Override this, in case the wrapped outputstream overrides...
public final void write(byte pBytes[], int pOff, int pLen) throws IOException { public final void write(byte pBytes[], int pOff, int pLen) throws IOException {
if (pBytes == null) { if (pBytes == null) {
throw new NullPointerException("bytes == null"); throw new NullPointerException("bytes == null");
} }
else if (pOff < 0 || pLen < 0 || (pOff + pLen > pBytes.length)) { else if (pOff < 0 || pLen < 0 || (pOff + pLen > pBytes.length)) {
throw new IndexOutOfBoundsException("Bytes: " + pBytes.length + " Offset: " + pOff + " Length: " + pLen); throw new IndexOutOfBoundsException("Bytes: " + pBytes.length + " Offset: " + pOff + " Length: " + pLen);
} }
for (int i = 0; i < pLen ; i++) { for (int i = 0; i < pLen ; i++) {
write(pBytes[pOff + i]); write(pBytes[pOff + i]);
} }
} }
public void write(int pByte) throws IOException { public void write(int pByte) throws IOException {
// TODO: Is this good enough for multi-byte encodings like UTF-16? // TODO: Is this good enough for multi-byte encodings like UTF-16?
// Consider writing through a Writer that does that for us, and // Consider writing through a Writer that does that for us, and
// also buffer whitespace, so we write a linefeed every time there's // also buffer whitespace, so we write a linefeed every time there's
// one in the original... // one in the original...
// According to http://en.wikipedia.org/wiki/UTF-8: // According to http://en.wikipedia.org/wiki/UTF-8:
// "[...] US-ASCII octet values do not appear otherwise in a UTF-8 // "[...] US-ASCII octet values do not appear otherwise in a UTF-8
// encoded character stream. This provides compatibility with file // encoded character stream. This provides compatibility with file
// systems or other software (e.g., the printf() function in // systems or other software (e.g., the printf() function in
// C libraries) that parse based on US-ASCII values but are // C libraries) that parse based on US-ASCII values but are
// transparent to other values." // transparent to other values."
if (!Character.isWhitespace((char) pByte)) { if (!Character.isWhitespace((char) pByte)) {
// If char is not WS, just store // If char is not WS, just store
super.write(pByte); super.write(pByte);
lastWasWS = false; lastWasWS = false;
} }
else { else {
// TODO: Consider writing only 0x0a (LF) and 0x20 (space) // TODO: Consider writing only 0x0a (LF) and 0x20 (space)
// Else, if char is WS, store first, skip the rest // Else, if char is WS, store first, skip the rest
if (!lastWasWS) { if (!lastWasWS) {
if (pByte == 0x0d) { // Convert all CR/LF's to 0x0a if (pByte == 0x0d) { // Convert all CR/LF's to 0x0a
super.write(0x0a); super.write(0x0a);
} }
else { else {
super.write(pByte); super.write(pByte);
} }
} }
lastWasWS = true; lastWasWS = true;
} }
} }
} }
private static class TrimWSStreamDelegate extends ServletResponseStreamDelegate { private static class TrimWSStreamDelegate extends ServletResponseStreamDelegate {
public TrimWSStreamDelegate(ServletResponse pResponse) { public TrimWSStreamDelegate(ServletResponse pResponse) {
super(pResponse); super(pResponse);
} }
protected OutputStream createOutputStream() throws IOException { protected OutputStream createOutputStream() throws IOException {
return new TrimWSFilterOutputStream(response.getOutputStream()); return new TrimWSFilterOutputStream(response.getOutputStream());
} }
} }
static class TrimWSServletResponseWrapper extends ServletResponseWrapper { static class TrimWSServletResponseWrapper extends ServletResponseWrapper {
private final ServletResponseStreamDelegate streamDelegate = new TrimWSStreamDelegate(getResponse()); private final ServletResponseStreamDelegate streamDelegate = new TrimWSStreamDelegate(getResponse());
public TrimWSServletResponseWrapper(ServletResponse pResponse) { public TrimWSServletResponseWrapper(ServletResponse pResponse) {
super(pResponse); super(pResponse);
} }
public ServletOutputStream getOutputStream() throws IOException { public ServletOutputStream getOutputStream() throws IOException {
return streamDelegate.getOutputStream(); return streamDelegate.getOutputStream();
} }
public PrintWriter getWriter() throws IOException { public PrintWriter getWriter() throws IOException {
return streamDelegate.getWriter(); return streamDelegate.getWriter();
} }
public void setContentLength(int pLength) { public void setContentLength(int pLength) {
// Will be changed by filter, so don't set. // Will be changed by filter, so don't set.
} }
@Override @Override
public void flushBuffer() throws IOException { public void flushBuffer() throws IOException {
streamDelegate.flushBuffer(); streamDelegate.flushBuffer();
} }
@Override @Override
public void resetBuffer() { public void resetBuffer() {
streamDelegate.resetBuffer(); streamDelegate.resetBuffer();
} }
// TODO: Consider picking up content-type/encoding, as we can only // TODO: Consider picking up content-type/encoding, as we can only
// filter US-ASCII, UTF-8 and other compatible encodings? // filter US-ASCII, UTF-8 and other compatible encodings?
} }
} }

View File

@@ -1,200 +1,200 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.servlet.cache; package com.twelvemonkeys.servlet.cache;
import com.twelvemonkeys.lang.StringUtil; import com.twelvemonkeys.lang.StringUtil;
import com.twelvemonkeys.servlet.GenericFilter; import com.twelvemonkeys.servlet.GenericFilter;
import com.twelvemonkeys.servlet.ServletConfigException; import com.twelvemonkeys.servlet.ServletConfigException;
import com.twelvemonkeys.servlet.ServletUtil; import com.twelvemonkeys.servlet.ServletUtil;
import javax.servlet.*; import javax.servlet.*;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
/** /**
* A Filter that provides response caching, for HTTP {@code GET} requests. * A Filter that provides response caching, for HTTP {@code GET} requests.
* <p/> * <p/>
* Originally based on ideas and code found in the ONJava article * 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 * <a href="http://www.onjava.com/pub/a/onjava/2003/11/19/filters.html">Two
* Servlet Filters Every Web Application Should Have</a> * Servlet Filters Every Web Application Should Have</a>
* by Jayson Falkner. * by Jayson Falkner.
* *
* @author Jayson Falkner * @author Jayson Falkner
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $ * @author last modified by $Author: haku $
* @version $Id: CacheFilter.java#4 $ * @version $Id: CacheFilter.java#4 $
* *
*/ */
public class CacheFilter extends GenericFilter { public class CacheFilter extends GenericFilter {
HTTPCache cache; HTTPCache cache;
/** /**
* Initializes the filter * Initializes the filter
* *
* @throws javax.servlet.ServletException * @throws javax.servlet.ServletException
*/ */
public void init() throws ServletException { public void init() throws ServletException {
FilterConfig config = getFilterConfig(); FilterConfig config = getFilterConfig();
// Default don't delete cache files on exit (persistent cache) // Default don't delete cache files on exit (persistent cache)
boolean deleteCacheOnExit = "TRUE".equalsIgnoreCase(config.getInitParameter("deleteCacheOnExit")); boolean deleteCacheOnExit = "TRUE".equalsIgnoreCase(config.getInitParameter("deleteCacheOnExit"));
// Default expiry time 10 minutes // Default expiry time 10 minutes
int expiryTime = 10 * 60 * 1000; int expiryTime = 10 * 60 * 1000;
String expiryTimeStr = config.getInitParameter("expiryTime"); String expiryTimeStr = config.getInitParameter("expiryTime");
if (!StringUtil.isEmpty(expiryTimeStr)) { if (!StringUtil.isEmpty(expiryTimeStr)) {
try { try {
// TODO: This is insane.. :-P Let the expiry time be in minutes or seconds.. // TODO: This is insane.. :-P Let the expiry time be in minutes or seconds..
expiryTime = Integer.parseInt(expiryTimeStr); expiryTime = Integer.parseInt(expiryTimeStr);
} }
catch (NumberFormatException e) { catch (NumberFormatException e) {
throw new ServletConfigException("Could not parse expiryTime: " + e.toString(), e); throw new ServletConfigException("Could not parse expiryTime: " + e.toString(), e);
} }
} }
// Default max mem cache size 10 MB // Default max mem cache size 10 MB
int memCacheSize = 10; int memCacheSize = 10;
String memCacheSizeStr = config.getInitParameter("memCacheSize"); String memCacheSizeStr = config.getInitParameter("memCacheSize");
if (!StringUtil.isEmpty(memCacheSizeStr)) { if (!StringUtil.isEmpty(memCacheSizeStr)) {
try { try {
memCacheSize = Integer.parseInt(memCacheSizeStr); memCacheSize = Integer.parseInt(memCacheSizeStr);
} }
catch (NumberFormatException e) { catch (NumberFormatException e) {
throw new ServletConfigException("Could not parse memCacheSize: " + e.toString(), e); throw new ServletConfigException("Could not parse memCacheSize: " + e.toString(), e);
} }
} }
int maxCachedEntites = 10000; int maxCachedEntites = 10000;
try { try {
cache = new HTTPCache( cache = new HTTPCache(
getTempFolder(), getTempFolder(),
expiryTime, expiryTime,
memCacheSize * 1024 * 1024, memCacheSize * 1024 * 1024,
maxCachedEntites, maxCachedEntites,
deleteCacheOnExit, deleteCacheOnExit,
new ServletContextLoggerAdapter(getFilterName(), getServletContext()) new ServletContextLoggerAdapter(getFilterName(), getServletContext())
) { ) {
@Override @Override
protected File getRealFile(CacheRequest pRequest) { protected File getRealFile(CacheRequest pRequest) {
String contextRelativeURI = ServletUtil.getContextRelativeURI(((ServletCacheRequest) pRequest).getRequest()); String contextRelativeURI = ServletUtil.getContextRelativeURI(((ServletCacheRequest) pRequest).getRequest());
String path = getServletContext().getRealPath(contextRelativeURI); String path = getServletContext().getRealPath(contextRelativeURI);
if (path != null) { if (path != null) {
return new File(path); return new File(path);
} }
return null; return null;
} }
}; };
log("Created cache: " + cache); log("Created cache: " + cache);
} }
catch (IllegalArgumentException e) { catch (IllegalArgumentException e) {
throw new ServletConfigException("Could not create cache: " + e.toString(), e); throw new ServletConfigException("Could not create cache: " + e.toString(), e);
} }
} }
private File getTempFolder() { private File getTempFolder() {
File tempRoot = (File) getServletContext().getAttribute("javax.servlet.context.tempdir"); File tempRoot = (File) getServletContext().getAttribute("javax.servlet.context.tempdir");
if (tempRoot == null) { if (tempRoot == null) {
throw new IllegalStateException("Missing context attribute \"javax.servlet.context.tempdir\""); throw new IllegalStateException("Missing context attribute \"javax.servlet.context.tempdir\"");
} }
return new File(tempRoot, getFilterName()); return new File(tempRoot, getFilterName());
} }
public void destroy() { public void destroy() {
log("Destroying cache: " + cache); log("Destroying cache: " + cache);
cache = null; cache = null;
super.destroy(); super.destroy();
} }
protected void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain) throws IOException, ServletException { protected void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain) throws IOException, ServletException {
// We can only cache HTTP GET/HEAD requests // We can only cache HTTP GET/HEAD requests
if (!(pRequest instanceof HttpServletRequest if (!(pRequest instanceof HttpServletRequest
&& pResponse instanceof HttpServletResponse && pResponse instanceof HttpServletResponse
&& isCachable((HttpServletRequest) pRequest))) { && isCachable((HttpServletRequest) pRequest))) {
pChain.doFilter(pRequest, pResponse); // Continue chain pChain.doFilter(pRequest, pResponse); // Continue chain
} }
else { else {
ServletCacheRequest cacheRequest = new ServletCacheRequest((HttpServletRequest) pRequest); ServletCacheRequest cacheRequest = new ServletCacheRequest((HttpServletRequest) pRequest);
ServletCacheResponse cacheResponse = new ServletCacheResponse((HttpServletResponse) pResponse); ServletCacheResponse cacheResponse = new ServletCacheResponse((HttpServletResponse) pResponse);
ServletResponseResolver resolver = new ServletResponseResolver(cacheRequest, cacheResponse, pChain); ServletResponseResolver resolver = new ServletResponseResolver(cacheRequest, cacheResponse, pChain);
// Render fast // Render fast
try { try {
cache.doCached(cacheRequest, cacheResponse, resolver); cache.doCached(cacheRequest, cacheResponse, resolver);
} }
catch (CacheException e) { catch (CacheException e) {
if (e.getCause() instanceof ServletException) { if (e.getCause() instanceof ServletException) {
throw (ServletException) e.getCause(); throw (ServletException) e.getCause();
} }
else { else {
throw new ServletException(e); throw new ServletException(e);
} }
} }
finally { finally {
pResponse.flushBuffer(); pResponse.flushBuffer();
} }
} }
} }
private boolean isCachable(HttpServletRequest pRequest) { private boolean isCachable(HttpServletRequest pRequest) {
// TODO: Get Cache-Control: no-cache/max-age=0 and Pragma: no-cache from REQUEST too? // 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()); return "GET".equals(pRequest.getMethod()) || "HEAD".equals(pRequest.getMethod());
} }
// TODO: Extract, complete and document this class, might be useful in other cases // TODO: Extract, complete and document this class, might be useful in other cases
// Maybe add it to the ServletUtil class // Maybe add it to the ServletUtil class
static class ServletContextLoggerAdapter extends Logger { static class ServletContextLoggerAdapter extends Logger {
private final ServletContext context; private final ServletContext context;
public ServletContextLoggerAdapter(String pName, ServletContext pContext) { public ServletContextLoggerAdapter(String pName, ServletContext pContext) {
super(pName, null); super(pName, null);
context = pContext; context = pContext;
} }
@Override @Override
public void log(Level pLevel, String pMessage) { public void log(Level pLevel, String pMessage) {
context.log(pMessage); context.log(pMessage);
} }
@Override @Override
public void log(Level pLevel, String pMessage, Throwable pThrowable) { public void log(Level pLevel, String pMessage, Throwable pThrowable) {
context.log(pMessage, pThrowable); context.log(pMessage, pThrowable);
} }
} }
} }

View File

@@ -1,261 +1,261 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.servlet.cache; package com.twelvemonkeys.servlet.cache;
import com.twelvemonkeys.lang.StringUtil; import com.twelvemonkeys.lang.StringUtil;
import com.twelvemonkeys.net.HTTPUtil; import com.twelvemonkeys.net.HTTPUtil;
import com.twelvemonkeys.servlet.ServletResponseStreamDelegate; import com.twelvemonkeys.servlet.ServletResponseStreamDelegate;
import javax.servlet.ServletOutputStream; import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponseWrapper; import javax.servlet.http.HttpServletResponseWrapper;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.PrintWriter; import java.io.PrintWriter;
/** /**
* CacheResponseWrapper class description. * CacheResponseWrapper class description.
* <p/> * <p/>
* Based on ideas and code found in the ONJava article * 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 * <a href="http://www.onjava.com/pub/a/onjava/2003/11/19/filters.html">Two
* Servlet Filters Every Web Application Should Have</a> * Servlet Filters Every Web Application Should Have</a>
* by Jayson Falkner. * by Jayson Falkner.
* *
* @author Jayson Falkner * @author Jayson Falkner
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $ * @author last modified by $Author: haku $
* @version $Id: CacheResponseWrapper.java#3 $ * @version $Id: CacheResponseWrapper.java#3 $
*/ */
class CacheResponseWrapper extends HttpServletResponseWrapper { class CacheResponseWrapper extends HttpServletResponseWrapper {
private ServletResponseStreamDelegate streamDelegate; private ServletResponseStreamDelegate streamDelegate;
private CacheResponse response; private CacheResponse response;
private CachedEntity cached; private CachedEntity cached;
private WritableCachedResponse cachedResponse; private WritableCachedResponse cachedResponse;
private Boolean cacheable; private Boolean cacheable;
private int status; private int status;
public CacheResponseWrapper(final ServletCacheResponse pResponse, final CachedEntity pCached) { public CacheResponseWrapper(final ServletCacheResponse pResponse, final CachedEntity pCached) {
super(pResponse.getResponse()); super(pResponse.getResponse());
response = pResponse; response = pResponse;
cached = pCached; cached = pCached;
init(); init();
} }
/* /*
NOTE: This class defers determining if a response is cacheable until the NOTE: This class defers determining if a response is cacheable until the
output stream is needed. output stream is needed.
This it the reason for the somewhat complicated logic in the add/setHeader This it the reason for the somewhat complicated logic in the add/setHeader
methods below. methods below.
*/ */
private void init() { private void init() {
cacheable = null; cacheable = null;
status = SC_OK; status = SC_OK;
cachedResponse = cached.createCachedResponse(); cachedResponse = cached.createCachedResponse();
streamDelegate = new ServletResponseStreamDelegate(this) { streamDelegate = new ServletResponseStreamDelegate(this) {
protected OutputStream createOutputStream() throws IOException { protected OutputStream createOutputStream() throws IOException {
// Test if this request is really cacheable, otherwise, // Test if this request is really cacheable, otherwise,
// just write through to underlying response, and don't cache // just write through to underlying response, and don't cache
if (isCacheable()) { if (isCacheable()) {
return cachedResponse.getOutputStream(); return cachedResponse.getOutputStream();
} }
else { else {
cachedResponse.setStatus(status); cachedResponse.setStatus(status);
cachedResponse.writeHeadersTo(CacheResponseWrapper.this.response); cachedResponse.writeHeadersTo(CacheResponseWrapper.this.response);
return super.getOutputStream(); return super.getOutputStream();
} }
} }
}; };
} }
CachedResponse getCachedResponse() { CachedResponse getCachedResponse() {
return cachedResponse.getCachedResponse(); return cachedResponse.getCachedResponse();
} }
public boolean isCacheable() { public boolean isCacheable() {
// NOTE: Intentionally not synchronized // NOTE: Intentionally not synchronized
if (cacheable == null) { if (cacheable == null) {
cacheable = isCacheableImpl(); cacheable = isCacheableImpl();
} }
return cacheable; return cacheable;
} }
private boolean isCacheableImpl() { private boolean isCacheableImpl() {
if (status != SC_OK) { if (status != SC_OK) {
return false; return false;
} }
// Vary: * // Vary: *
String[] values = cachedResponse.getHeaderValues(HTTPCache.HEADER_VARY); String[] values = cachedResponse.getHeaderValues(HTTPCache.HEADER_VARY);
if (values != null) { if (values != null) {
for (String value : values) { for (String value : values) {
if ("*".equals(value)) { if ("*".equals(value)) {
return false; return false;
} }
} }
} }
// Cache-Control: no-cache, no-store, must-revalidate // Cache-Control: no-cache, no-store, must-revalidate
values = cachedResponse.getHeaderValues(HTTPCache.HEADER_CACHE_CONTROL); values = cachedResponse.getHeaderValues(HTTPCache.HEADER_CACHE_CONTROL);
if (values != null) { if (values != null) {
for (String value : values) { for (String value : values) {
if (StringUtil.contains(value, "no-cache") if (StringUtil.contains(value, "no-cache")
|| StringUtil.contains(value, "no-store") || StringUtil.contains(value, "no-store")
|| StringUtil.contains(value, "must-revalidate")) { || StringUtil.contains(value, "must-revalidate")) {
return false; return false;
} }
} }
} }
// Pragma: no-cache // Pragma: no-cache
values = cachedResponse.getHeaderValues(HTTPCache.HEADER_PRAGMA); values = cachedResponse.getHeaderValues(HTTPCache.HEADER_PRAGMA);
if (values != null) { if (values != null) {
for (String value : values) { for (String value : values) {
if (StringUtil.contains(value, "no-cache")) { if (StringUtil.contains(value, "no-cache")) {
return false; return false;
} }
} }
} }
return true; return true;
} }
public void flushBuffer() throws IOException { public void flushBuffer() throws IOException {
streamDelegate.flushBuffer(); streamDelegate.flushBuffer();
} }
public void resetBuffer() { public void resetBuffer() {
// Servlet 2.3 // Servlet 2.3
streamDelegate.resetBuffer(); streamDelegate.resetBuffer();
} }
public void reset() { public void reset() {
if (Boolean.FALSE.equals(cacheable)) { if (Boolean.FALSE.equals(cacheable)) {
super.reset(); super.reset();
} }
// No else, might be cacheable after all.. // No else, might be cacheable after all..
init(); init();
} }
public ServletOutputStream getOutputStream() throws IOException { public ServletOutputStream getOutputStream() throws IOException {
return streamDelegate.getOutputStream(); return streamDelegate.getOutputStream();
} }
public PrintWriter getWriter() throws IOException { public PrintWriter getWriter() throws IOException {
return streamDelegate.getWriter(); return streamDelegate.getWriter();
} }
public boolean containsHeader(String name) { public boolean containsHeader(String name) {
return cachedResponse.getHeaderValues(name) != null; return cachedResponse.getHeaderValues(name) != null;
} }
public void sendError(int pStatusCode, String msg) throws IOException { public void sendError(int pStatusCode, String msg) throws IOException {
// NOT cacheable // NOT cacheable
status = pStatusCode; status = pStatusCode;
super.sendError(pStatusCode, msg); super.sendError(pStatusCode, msg);
} }
public void sendError(int pStatusCode) throws IOException { public void sendError(int pStatusCode) throws IOException {
// NOT cacheable // NOT cacheable
status = pStatusCode; status = pStatusCode;
super.sendError(pStatusCode); super.sendError(pStatusCode);
} }
public void setStatus(int pStatusCode, String sm) { public void setStatus(int pStatusCode, String sm) {
// NOTE: This method is deprecated // NOTE: This method is deprecated
setStatus(pStatusCode); setStatus(pStatusCode);
} }
public void setStatus(int pStatusCode) { public void setStatus(int pStatusCode) {
// NOT cacheable unless pStatusCode == 200 (or a FEW others?) // NOT cacheable unless pStatusCode == 200 (or a FEW others?)
if (pStatusCode != SC_OK) { if (pStatusCode != SC_OK) {
status = pStatusCode; status = pStatusCode;
super.setStatus(pStatusCode); super.setStatus(pStatusCode);
} }
} }
public void sendRedirect(String pLocation) throws IOException { public void sendRedirect(String pLocation) throws IOException {
// NOT cacheable // NOT cacheable
status = SC_MOVED_TEMPORARILY; status = SC_MOVED_TEMPORARILY;
super.sendRedirect(pLocation); super.sendRedirect(pLocation);
} }
public void setDateHeader(String pName, long pValue) { public void setDateHeader(String pName, long pValue) {
// If in write-trough-mode, set headers directly // If in write-trough-mode, set headers directly
if (Boolean.FALSE.equals(cacheable)) { if (Boolean.FALSE.equals(cacheable)) {
super.setDateHeader(pName, pValue); super.setDateHeader(pName, pValue);
} }
cachedResponse.setHeader(pName, HTTPUtil.formatHTTPDate(pValue)); cachedResponse.setHeader(pName, HTTPUtil.formatHTTPDate(pValue));
} }
public void addDateHeader(String pName, long pValue) { public void addDateHeader(String pName, long pValue) {
// If in write-trough-mode, set headers directly // If in write-trough-mode, set headers directly
if (Boolean.FALSE.equals(cacheable)) { if (Boolean.FALSE.equals(cacheable)) {
super.addDateHeader(pName, pValue); super.addDateHeader(pName, pValue);
} }
cachedResponse.addHeader(pName, HTTPUtil.formatHTTPDate(pValue)); cachedResponse.addHeader(pName, HTTPUtil.formatHTTPDate(pValue));
} }
public void setHeader(String pName, String pValue) { public void setHeader(String pName, String pValue) {
// If in write-trough-mode, set headers directly // If in write-trough-mode, set headers directly
if (Boolean.FALSE.equals(cacheable)) { if (Boolean.FALSE.equals(cacheable)) {
super.setHeader(pName, pValue); super.setHeader(pName, pValue);
} }
cachedResponse.setHeader(pName, pValue); cachedResponse.setHeader(pName, pValue);
} }
public void addHeader(String pName, String pValue) { public void addHeader(String pName, String pValue) {
// If in write-trough-mode, set headers directly // If in write-trough-mode, set headers directly
if (Boolean.FALSE.equals(cacheable)) { if (Boolean.FALSE.equals(cacheable)) {
super.addHeader(pName, pValue); super.addHeader(pName, pValue);
} }
cachedResponse.addHeader(pName, pValue); cachedResponse.addHeader(pName, pValue);
} }
public void setIntHeader(String pName, int pValue) { public void setIntHeader(String pName, int pValue) {
// If in write-trough-mode, set headers directly // If in write-trough-mode, set headers directly
if (Boolean.FALSE.equals(cacheable)) { if (Boolean.FALSE.equals(cacheable)) {
super.setIntHeader(pName, pValue); super.setIntHeader(pName, pValue);
} }
cachedResponse.setHeader(pName, String.valueOf(pValue)); cachedResponse.setHeader(pName, String.valueOf(pValue));
} }
public void addIntHeader(String pName, int pValue) { public void addIntHeader(String pName, int pValue) {
// If in write-trough-mode, set headers directly // If in write-trough-mode, set headers directly
if (Boolean.FALSE.equals(cacheable)) { if (Boolean.FALSE.equals(cacheable)) {
super.addIntHeader(pName, pValue); super.addIntHeader(pName, pValue);
} }
cachedResponse.addHeader(pName, String.valueOf(pValue)); cachedResponse.addHeader(pName, String.valueOf(pValue));
} }
public final void setContentType(String type) { public final void setContentType(String type) {
setHeader(HTTPCache.HEADER_CONTENT_TYPE, type); setHeader(HTTPCache.HEADER_CONTENT_TYPE, type);
} }
} }

View File

@@ -1,75 +1,75 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.servlet.cache; package com.twelvemonkeys.servlet.cache;
import java.io.IOException; import java.io.IOException;
/** /**
* CachedEntity * CachedEntity
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: CachedEntity.java#3 $ * @version $Id: CachedEntity.java#3 $
*/ */
interface CachedEntity { interface CachedEntity {
/** /**
* Renders the cached entity to the response. * Renders the cached entity to the response.
* *
* @param pRequest the request * @param pRequest the request
* @param pResponse the response * @param pResponse the response
* @throws java.io.IOException if an I/O exception occurs * @throws java.io.IOException if an I/O exception occurs
*/ */
void render(CacheRequest pRequest, CacheResponse pResponse) throws IOException; void render(CacheRequest pRequest, CacheResponse pResponse) throws IOException;
/** /**
* Captures (caches) the response for the given request. * Captures (caches) the response for the given request.
* *
* @param pRequest the request * @param pRequest the request
* @param pResponse the response * @param pResponse the response
* @throws java.io.IOException if an I/O exception occurs * @throws java.io.IOException if an I/O exception occurs
* *
* @see #createCachedResponse() * @see #createCachedResponse()
*/ */
void capture(CacheRequest pRequest, CachedResponse pResponse) throws IOException; void capture(CacheRequest pRequest, CachedResponse pResponse) throws IOException;
/** /**
* Tests if the content of this entity is stale for the given request. * Tests if the content of this entity is stale for the given request.
* *
* @param pRequest the request * @param pRequest the request
* @return {@code true} if content is stale * @return {@code true} if content is stale
*/ */
boolean isStale(CacheRequest pRequest); boolean isStale(CacheRequest pRequest);
/** /**
* Creates a {@code WritableCachedResponse} to use to capture the response. * Creates a {@code WritableCachedResponse} to use to capture the response.
* *
* @return a {@code WritableCachedResponse} * @return a {@code WritableCachedResponse}
*/ */
WritableCachedResponse createCachedResponse(); WritableCachedResponse createCachedResponse();
} }

View File

@@ -1,170 +1,170 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.servlet.cache; package com.twelvemonkeys.servlet.cache;
import com.twelvemonkeys.lang.Validate; import com.twelvemonkeys.lang.Validate;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
/** /**
* CachedEntity * CachedEntity
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: CachedEntityImpl.java#3 $ * @version $Id: CachedEntityImpl.java#3 $
*/ */
class CachedEntityImpl implements CachedEntity { class CachedEntityImpl implements CachedEntity {
private String cacheURI; private String cacheURI;
private HTTPCache cache; private HTTPCache cache;
CachedEntityImpl(String pCacheURI, HTTPCache pCache) { CachedEntityImpl(String pCacheURI, HTTPCache pCache) {
cacheURI = Validate.notNull(pCacheURI, "cacheURI"); cacheURI = Validate.notNull(pCacheURI, "cacheURI");
cache = pCache; cache = pCache;
} }
public void render(CacheRequest pRequest, CacheResponse pResponse) throws IOException { public void render(CacheRequest pRequest, CacheResponse pResponse) throws IOException {
// Get cached content // Get cached content
CachedResponse cached = cache.getContent(cacheURI, pRequest); CachedResponse cached = cache.getContent(cacheURI, pRequest);
// Sanity check // Sanity check
if (cached == null) { if (cached == null) {
throw new IllegalStateException("Tried to render non-cached response (cache == 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 // If the cached entity is not modified since the date of the browsers
// version, then simply send a "304 Not Modified" response // version, then simply send a "304 Not Modified" response
// Otherwise send the full response. // Otherwise send the full response.
// TODO: WHY DID I COMMENT OUT THIS LINE AND REPLACE IT WITH THE ONE BELOW?? // 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_LAST_MODIFIED));
long lastModified = HTTPCache.getDateHeader(cached.getHeaderValue(HTTPCache.HEADER_CACHED_TIME)); long lastModified = HTTPCache.getDateHeader(cached.getHeaderValue(HTTPCache.HEADER_CACHED_TIME));
// TODO: Consider handling time skews between server "now" and client "now"? // TODO: Consider handling time skews between server "now" and client "now"?
// NOTE: The If-Modified-Since is probably right according to the server // 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 // even in a time skew situation, as the client should use either the
// Date or Last-Modifed dates from the response headers (server generated) // Date or Last-Modifed dates from the response headers (server generated)
long ifModifiedSince = -1L; long ifModifiedSince = -1L;
try { try {
List<String> ifmh = pRequest.getHeaders().get(HTTPCache.HEADER_IF_MODIFIED_SINCE); List<String> ifmh = pRequest.getHeaders().get(HTTPCache.HEADER_IF_MODIFIED_SINCE);
ifModifiedSince = ifmh != null ? HTTPCache.getDateHeader(ifmh.get(0)) : -1L; ifModifiedSince = ifmh != null ? HTTPCache.getDateHeader(ifmh.get(0)) : -1L;
if (ifModifiedSince != -1L) { if (ifModifiedSince != -1L) {
/* /*
long serverTime = DateUtil.currentTimeMinute(); long serverTime = DateUtil.currentTimeMinute();
long clientTime = DateUtil.roundToMinute(pRequest.getDateHeader(HTTPCache.HEADER_DATE)); long clientTime = DateUtil.roundToMinute(pRequest.getDateHeader(HTTPCache.HEADER_DATE));
// Test if time skew is greater than time skew threshold (currently 1 minute) // Test if time skew is greater than time skew threshold (currently 1 minute)
if (Math.abs(serverTime - clientTime) > 1) { if (Math.abs(serverTime - clientTime) > 1) {
// TODO: Correct error in ifModifiedSince? // 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 >> 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)); // System.out.println(" << CachedEntity >> Last-Modified for entity: " + lastModified + " --> " + NetUtil.formatHTTPDate(lastModified));
} }
} }
catch (IllegalArgumentException e) { catch (IllegalArgumentException e) {
// Seems to be a bug in FireFox 1.0.2..?! // 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); cache.log("Error in date header from user-agent. User-Agent: " + pRequest.getHeaders().get("User-Agent"), e);
} }
if (lastModified == -1L || (ifModifiedSince < (lastModified / 1000L) * 1000L)) { if (lastModified == -1L || (ifModifiedSince < (lastModified / 1000L) * 1000L)) {
pResponse.setStatus(cached.getStatus()); pResponse.setStatus(cached.getStatus());
cached.writeHeadersTo(pResponse); cached.writeHeadersTo(pResponse);
if (isStale(pRequest)) { if (isStale(pRequest)) {
// Add warning header // Add warning header
// Warning: 110 <server>:<port> Content is stale // Warning: 110 <server>:<port> Content is stale
pResponse.addHeader(HTTPCache.HEADER_WARNING, "110 " + getHost(pRequest) + " 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 // NOTE: At the moment we only ever try to cache HEAD and GET requests
if (!"HEAD".equals(pRequest.getMethod())) { if (!"HEAD".equals(pRequest.getMethod())) {
cached.writeContentsTo(pResponse.getOutputStream()); cached.writeContentsTo(pResponse.getOutputStream());
} }
} }
else { else {
pResponse.setStatus(HttpServletResponse.SC_NOT_MODIFIED); pResponse.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
// System.out.println(" << CachedEntity >> Not modified: " + toString()); // System.out.println(" << CachedEntity >> Not modified: " + toString());
if (isStale(pRequest)) { if (isStale(pRequest)) {
// Add warning header // Add warning header
// Warning: 110 <server>:<port> Content is stale // Warning: 110 <server>:<port> Content is stale
pResponse.addHeader(HTTPCache.HEADER_WARNING, "110 " + getHost(pRequest) + " Content is stale."); pResponse.addHeader(HTTPCache.HEADER_WARNING, "110 " + getHost(pRequest) + " Content is stale.");
} }
} }
} }
/* Utility method to get Host header */ /* Utility method to get Host header */
private static String getHost(CacheRequest pRequest) { private static String getHost(CacheRequest pRequest) {
return pRequest.getServerName() + ":" + pRequest.getServerPort(); return pRequest.getServerName() + ":" + pRequest.getServerPort();
} }
public void capture(CacheRequest pRequest, CachedResponse pResponse) throws IOException { public void capture(CacheRequest pRequest, CachedResponse pResponse) throws IOException {
// if (!(pResponse instanceof CacheResponseWrapper)) { // if (!(pResponse instanceof CacheResponseWrapper)) {
// throw new IllegalArgumentException("Response must be created by CachedEntity.createResponseWrapper()"); // throw new IllegalArgumentException("Response must be created by CachedEntity.createResponseWrapper()");
// } // }
// //
// CacheResponseWrapper response = (CacheResponseWrapper) pResponse; // CacheResponseWrapper response = (CacheResponseWrapper) pResponse;
// if (response.isCacheable()) { // if (response.isCacheable()) {
cache.registerContent( cache.registerContent(
cacheURI, cacheURI,
pRequest, pRequest,
pResponse instanceof WritableCachedResponse ? ((WritableCachedResponse) pResponse).getCachedResponse() : pResponse pResponse instanceof WritableCachedResponse ? ((WritableCachedResponse) pResponse).getCachedResponse() : pResponse
); );
// } // }
// else { // else {
// Else store that the response for this request is not cachable // Else store that the response for this request is not cachable
// pRequest.setAttribute(ATTRIB_NOT_CACHEABLE, Boolean.TRUE); // pRequest.setAttribute(ATTRIB_NOT_CACHEABLE, Boolean.TRUE);
// TODO: Store this in HTTPCache, for subsequent requests to same resource? // TODO: Store this in HTTPCache, for subsequent requests to same resource?
// } // }
} }
public boolean isStale(CacheRequest pRequest) { public boolean isStale(CacheRequest pRequest) {
return cache.isContentStale(cacheURI, pRequest); return cache.isContentStale(cacheURI, pRequest);
} }
public WritableCachedResponse createCachedResponse() { public WritableCachedResponse createCachedResponse() {
return new WritableCachedResponseImpl(); return new WritableCachedResponseImpl();
} }
public int hashCode() { public int hashCode() {
return (cacheURI != null ? cacheURI.hashCode() : 0) + 1397; return (cacheURI != null ? cacheURI.hashCode() : 0) + 1397;
} }
public boolean equals(Object pOther) { public boolean equals(Object pOther) {
return pOther instanceof CachedEntityImpl && return pOther instanceof CachedEntityImpl &&
((cacheURI == null && ((CachedEntityImpl) pOther).cacheURI == null) || ((cacheURI == null && ((CachedEntityImpl) pOther).cacheURI == null) ||
cacheURI != null && cacheURI.equals(((CachedEntityImpl) pOther).cacheURI)); cacheURI != null && cacheURI.equals(((CachedEntityImpl) pOther).cacheURI));
} }
public String toString() { public String toString() {
return "CachedEntity[URI=" + cacheURI + "]"; return "CachedEntity[URI=" + cacheURI + "]";
} }
} }

View File

@@ -1,95 +1,95 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.servlet.cache; package com.twelvemonkeys.servlet.cache;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
/** /**
* CachedResponse * CachedResponse
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: CachedResponse.java#3 $ * @version $Id: CachedResponse.java#3 $
*/ */
interface CachedResponse { interface CachedResponse {
/** /**
* Writes the cached headers to the response * Writes the cached headers to the response
* *
* @param pResponse the servlet response * @param pResponse the servlet response
*/ */
void writeHeadersTo(CacheResponse pResponse); void writeHeadersTo(CacheResponse pResponse);
/** /**
* Writes the cahced content to the response * Writes the cahced content to the response
* *
* @param pStream the response output stream * @param pStream the response output stream
* @throws IOException if an I/O exception occurs during write * @throws IOException if an I/O exception occurs during write
*/ */
void writeContentsTo(OutputStream pStream) throws IOException; void writeContentsTo(OutputStream pStream) throws IOException;
int getStatus(); int getStatus();
// TODO: Map<String, List<String>> getHeaders() // TODO: Map<String, List<String>> getHeaders()
/** /**
* Gets the header names of all headers set in this response. * Gets the header names of all headers set in this response.
* *
* @return an array of {@code String}s * @return an array of {@code String}s
*/ */
String[] getHeaderNames(); String[] getHeaderNames();
/** /**
* Gets all header values set for the given header in this response. If the * Gets all header values set for the given header in this response. If the
* header is not set, {@code null} is returned. * header is not set, {@code null} is returned.
* *
* @param pHeaderName the header name * @param pHeaderName the header name
* @return an array of {@code String}s, or {@code null} if there is no * @return an array of {@code String}s, or {@code null} if there is no
* such header in this response. * such header in this response.
*/ */
String[] getHeaderValues(String pHeaderName); String[] getHeaderValues(String pHeaderName);
/** /**
* Gets the first header value set for the given header in this response. * Gets the first header value set for the given header in this response.
* If the header is not set, {@code null} is returned. * If the header is not set, {@code null} is returned.
* Useful for headers that don't have multiple values, like * Useful for headers that don't have multiple values, like
* {@code "Content-Type"} or {@code "Content-Length"}. * {@code "Content-Type"} or {@code "Content-Length"}.
* *
* @param pHeaderName the header name * @param pHeaderName the header name
* @return a {@code String}, or {@code null} if there is no * @return a {@code String}, or {@code null} if there is no
* such header in this response. * such header in this response.
*/ */
String getHeaderValue(String pHeaderName); String getHeaderValue(String pHeaderName);
/** /**
* Returns the size of this cached response in bytes. * Returns the size of this cached response in bytes.
* *
* @return the size * @return the size
*/ */
int size(); int size();
} }

View File

@@ -1,213 +1,213 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.servlet.cache; package com.twelvemonkeys.servlet.cache;
import com.twelvemonkeys.io.FastByteArrayOutputStream; import com.twelvemonkeys.io.FastByteArrayOutputStream;
import com.twelvemonkeys.lang.Validate; import com.twelvemonkeys.lang.Validate;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.*; import java.util.*;
/** /**
* CachedResponseImpl * CachedResponseImpl
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: CachedResponseImpl.java#4 $ * @version $Id: CachedResponseImpl.java#4 $
*/ */
class CachedResponseImpl implements CachedResponse { class CachedResponseImpl implements CachedResponse {
final protected Map<String, List<String>> headers; final protected Map<String, List<String>> headers;
protected int headersSize; protected int headersSize;
protected ByteArrayOutputStream content = null; protected ByteArrayOutputStream content = null;
int status; int status;
protected CachedResponseImpl() { protected CachedResponseImpl() {
headers = new LinkedHashMap<String, List<String>>(); // Keep headers in insertion order headers = new LinkedHashMap<String, List<String>>(); // Keep headers in insertion order
} }
// For use by HTTPCache, when recreating CachedResponses from disk cache // 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) { CachedResponseImpl(final int pStatus, final LinkedHashMap<String, List<String>> pHeaders, final int pHeaderSize, final byte[] pContent) {
status = pStatus; status = pStatus;
headers = Validate.notNull(pHeaders, "headers"); headers = Validate.notNull(pHeaders, "headers");
headersSize = pHeaderSize; headersSize = pHeaderSize;
content = new FastByteArrayOutputStream(pContent); content = new FastByteArrayOutputStream(pContent);
} }
public int getStatus() { public int getStatus() {
return status; return status;
} }
/** /**
* Writes the cached headers to the response * Writes the cached headers to the response
* *
* @param pResponse the response * @param pResponse the response
*/ */
public void writeHeadersTo(final CacheResponse pResponse) { public void writeHeadersTo(final CacheResponse pResponse) {
String[] headers = getHeaderNames(); String[] headers = getHeaderNames();
for (String header : headers) { for (String header : headers) {
// HACK... // HACK...
// Strip away internal headers // Strip away internal headers
if (HTTPCache.HEADER_CACHED_TIME.equals(header)) { if (HTTPCache.HEADER_CACHED_TIME.equals(header)) {
continue; continue;
} }
// TODO: Replace Last-Modified with X-Cached-At? See CachedEntityImpl // TODO: Replace Last-Modified with X-Cached-At? See CachedEntityImpl
String[] headerValues = getHeaderValues(header); String[] headerValues = getHeaderValues(header);
for (int i = 0; i < headerValues.length; i++) { for (int i = 0; i < headerValues.length; i++) {
String headerValue = headerValues[i]; String headerValue = headerValues[i];
if (i == 0) { if (i == 0) {
pResponse.setHeader(header, headerValue); pResponse.setHeader(header, headerValue);
} }
else { else {
pResponse.addHeader(header, headerValue); pResponse.addHeader(header, headerValue);
} }
} }
} }
} }
/** /**
* Writes the cached content to the response * Writes the cached content to the response
* *
* @param pStream the response stream * @param pStream the response stream
* @throws java.io.IOException * @throws java.io.IOException
*/ */
public void writeContentsTo(final OutputStream pStream) throws IOException { public void writeContentsTo(final OutputStream pStream) throws IOException {
if (content == null) { if (content == null) {
throw new IOException("Cache is null, no content to write."); throw new IOException("Cache is null, no content to write.");
} }
content.writeTo(pStream); content.writeTo(pStream);
} }
/** /**
* Gets the header names of all headers set in this response. * Gets the header names of all headers set in this response.
* *
* @return an array of {@code String}s * @return an array of {@code String}s
*/ */
public String[] getHeaderNames() { public String[] getHeaderNames() {
Set<String> headers = this.headers.keySet(); Set<String> headers = this.headers.keySet();
return headers.toArray(new String[headers.size()]); return headers.toArray(new String[headers.size()]);
} }
/** /**
* Gets all header values set for the given header in this response. If the * Gets all header values set for the given header in this response. If the
* header is not set, {@code null} is returned. * header is not set, {@code null} is returned.
* *
* @param pHeaderName the header name * @param pHeaderName the header name
* @return an array of {@code String}s, or {@code null} if there is no * @return an array of {@code String}s, or {@code null} if there is no
* such header in this response. * such header in this response.
*/ */
public String[] getHeaderValues(final String pHeaderName) { public String[] getHeaderValues(final String pHeaderName) {
List<String> values = headers.get(pHeaderName); List<String> values = headers.get(pHeaderName);
return values == null ? null : values.toArray(new String[values.size()]); return values == null ? null : values.toArray(new String[values.size()]);
} }
/** /**
* Gets the first header value set for the given header in this response. * Gets the first header value set for the given header in this response.
* If the header is not set, {@code null} is returned. * If the header is not set, {@code null} is returned.
* Useful for headers that don't have multiple values, like * Useful for headers that don't have multiple values, like
* {@code "Content-Type"} or {@code "Content-Length"}. * {@code "Content-Type"} or {@code "Content-Length"}.
* *
* @param pHeaderName the header name * @param pHeaderName the header name
* @return a {@code String}, or {@code null} if there is no * @return a {@code String}, or {@code null} if there is no
* such header in this response. * such header in this response.
*/ */
public String getHeaderValue(final String pHeaderName) { public String getHeaderValue(final String pHeaderName) {
List<String> values = headers.get(pHeaderName); List<String> values = headers.get(pHeaderName);
return (values != null && values.size() > 0) ? values.get(0) : null; return (values != null && values.size() > 0) ? values.get(0) : null;
} }
public int size() { public int size() {
// content.size() is exact size in bytes, headersSize is an estimate // content.size() is exact size in bytes, headersSize is an estimate
return (content != null ? content.size() : 0) + headersSize; return (content != null ? content.size() : 0) + headersSize;
} }
public boolean equals(final Object pOther) { public boolean equals(final Object pOther) {
if (this == pOther) { if (this == pOther) {
return true; return true;
} }
if (pOther instanceof CachedResponseImpl) { if (pOther instanceof CachedResponseImpl) {
// "Fast" // "Fast"
return equalsImpl((CachedResponseImpl) pOther); return equalsImpl((CachedResponseImpl) pOther);
} }
else if (pOther instanceof CachedResponse) { else if (pOther instanceof CachedResponse) {
// Slow // Slow
return equalsGeneric((CachedResponse) pOther); return equalsGeneric((CachedResponse) pOther);
} }
return false; return false;
} }
private boolean equalsImpl(final CachedResponseImpl pOther) { private boolean equalsImpl(final CachedResponseImpl pOther) {
return headersSize == pOther.headersSize && return headersSize == pOther.headersSize &&
(content == null ? pOther.content == null : content.equals(pOther.content)) && (content == null ? pOther.content == null : content.equals(pOther.content)) &&
headers.equals(pOther.headers); headers.equals(pOther.headers);
} }
private boolean equalsGeneric(final CachedResponse pOther) { private boolean equalsGeneric(final CachedResponse pOther) {
if (size() != pOther.size()) { if (size() != pOther.size()) {
return false; return false;
} }
String[] headers = getHeaderNames(); String[] headers = getHeaderNames();
String[] otherHeaders = pOther.getHeaderNames(); String[] otherHeaders = pOther.getHeaderNames();
if (!Arrays.equals(headers, otherHeaders)) { if (!Arrays.equals(headers, otherHeaders)) {
return false; return false;
} }
if (headers != null) { if (headers != null) {
for (String header : headers) { for (String header : headers) {
String[] values = getHeaderValues(header); String[] values = getHeaderValues(header);
String[] otherValues = pOther.getHeaderValues(header); String[] otherValues = pOther.getHeaderValues(header);
if (!Arrays.equals(values, otherValues)) { if (!Arrays.equals(values, otherValues)) {
return false; return false;
} }
} }
} }
return true; return true;
} }
public int hashCode() { public int hashCode() {
int result; int result;
result = headers.hashCode(); result = headers.hashCode();
result = 29 * result + headersSize; result = 29 * result + headersSize;
result = 37 * result + (content != null ? content.hashCode() : 0); result = 37 * result + (content != null ? content.hashCode() : 0);
return result; return result;
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,273 +1,273 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.servlet.cache; package com.twelvemonkeys.servlet.cache;
import com.twelvemonkeys.lang.StringUtil; import com.twelvemonkeys.lang.StringUtil;
import com.twelvemonkeys.net.HTTPUtil; import com.twelvemonkeys.net.HTTPUtil;
import com.twelvemonkeys.servlet.ServletResponseStreamDelegate; import com.twelvemonkeys.servlet.ServletResponseStreamDelegate;
import javax.servlet.ServletOutputStream; import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponseWrapper; import javax.servlet.http.HttpServletResponseWrapper;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
* CacheResponseWrapper class description. * CacheResponseWrapper class description.
* <p/> * <p/>
* Based on ideas and code found in the ONJava article * 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 * <a href="http://www.onjava.com/pub/a/onjava/2003/11/19/filters.html">Two
* Servlet Filters Every Web Application Should Have</a> * Servlet Filters Every Web Application Should Have</a>
* by Jayson Falkner. * by Jayson Falkner.
* *
* @author Jayson Falkner * @author Jayson Falkner
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $ * @author last modified by $Author: haku $
* @version $Id: SerlvetCacheResponseWrapper.java#2 $ * @version $Id: SerlvetCacheResponseWrapper.java#2 $
*/ */
class SerlvetCacheResponseWrapper extends HttpServletResponseWrapper { class SerlvetCacheResponseWrapper extends HttpServletResponseWrapper {
private ServletResponseStreamDelegate streamDelegate; private ServletResponseStreamDelegate streamDelegate;
private CacheResponse cacheResponse; private CacheResponse cacheResponse;
private Boolean cacheable; private Boolean cacheable;
private int status; private int status;
public SerlvetCacheResponseWrapper(final HttpServletResponse pServletResponse, final CacheResponse pResponse) { public SerlvetCacheResponseWrapper(final HttpServletResponse pServletResponse, final CacheResponse pResponse) {
super(pServletResponse); super(pServletResponse);
cacheResponse = pResponse; cacheResponse = pResponse;
init(); init();
} }
/* /*
NOTE: This class defers determining if a response is cacheable until the NOTE: This class defers determining if a response is cacheable until the
output stream is needed. output stream is needed.
This it the reason for the somewhat complicated logic in the add/setHeader This it the reason for the somewhat complicated logic in the add/setHeader
methods below. methods below.
*/ */
private void init() { private void init() {
cacheable = null; cacheable = null;
status = SC_OK; status = SC_OK;
streamDelegate = new ServletResponseStreamDelegate(this) { streamDelegate = new ServletResponseStreamDelegate(this) {
protected OutputStream createOutputStream() throws IOException { protected OutputStream createOutputStream() throws IOException {
// Test if this request is really cacheable, otherwise, // Test if this request is really cacheable, otherwise,
// just write through to underlying response, and don't cache // just write through to underlying response, and don't cache
if (isCacheable()) { if (isCacheable()) {
return cacheResponse.getOutputStream(); return cacheResponse.getOutputStream();
} }
else { else {
// TODO: We need to tell the cache about this, somehow... // TODO: We need to tell the cache about this, somehow...
writeHeaders(cacheResponse, (HttpServletResponse) getResponse()); writeHeaders(cacheResponse, (HttpServletResponse) getResponse());
return super.getOutputStream(); return super.getOutputStream();
} }
} }
}; };
} }
private void writeHeaders(final CacheResponse pResponse, final HttpServletResponse pServletResponse) { private void writeHeaders(final CacheResponse pResponse, final HttpServletResponse pServletResponse) {
Map<String,List<String>> headers = pResponse.getHeaders(); Map<String,List<String>> headers = pResponse.getHeaders();
for (Map.Entry<String, List<String>> header : headers.entrySet()) { for (Map.Entry<String, List<String>> header : headers.entrySet()) {
for (int i = 0; i < header.getValue().size(); i++) { for (int i = 0; i < header.getValue().size(); i++) {
String value = header.getValue().get(i); String value = header.getValue().get(i);
if (i == 0) { if (i == 0) {
pServletResponse.setHeader(header.getKey(), value); pServletResponse.setHeader(header.getKey(), value);
} }
else { else {
pServletResponse.addHeader(header.getKey(), value); pServletResponse.addHeader(header.getKey(), value);
} }
} }
} }
} }
public boolean isCacheable() { public boolean isCacheable() {
// NOTE: Intentionally not synchronized // NOTE: Intentionally not synchronized
if (cacheable == null) { if (cacheable == null) {
cacheable = isCacheableImpl(); cacheable = isCacheableImpl();
} }
return cacheable; return cacheable;
} }
private boolean isCacheableImpl() { private boolean isCacheableImpl() {
// TODO: This code is duped in the cache... // TODO: This code is duped in the cache...
if (status != SC_OK) { if (status != SC_OK) {
return false; return false;
} }
// Vary: * // Vary: *
List<String> values = cacheResponse.getHeaders().get(HTTPCache.HEADER_VARY); List<String> values = cacheResponse.getHeaders().get(HTTPCache.HEADER_VARY);
if (values != null) { if (values != null) {
for (String value : values) { for (String value : values) {
if ("*".equals(value)) { if ("*".equals(value)) {
return false; return false;
} }
} }
} }
// Cache-Control: no-cache, no-store, must-revalidate // Cache-Control: no-cache, no-store, must-revalidate
values = cacheResponse.getHeaders().get(HTTPCache.HEADER_CACHE_CONTROL); values = cacheResponse.getHeaders().get(HTTPCache.HEADER_CACHE_CONTROL);
if (values != null) { if (values != null) {
for (String value : values) { for (String value : values) {
if (StringUtil.contains(value, "no-cache") if (StringUtil.contains(value, "no-cache")
|| StringUtil.contains(value, "no-store") || StringUtil.contains(value, "no-store")
|| StringUtil.contains(value, "must-revalidate")) { || StringUtil.contains(value, "must-revalidate")) {
return false; return false;
} }
} }
} }
// Pragma: no-cache // Pragma: no-cache
values = cacheResponse.getHeaders().get(HTTPCache.HEADER_PRAGMA); values = cacheResponse.getHeaders().get(HTTPCache.HEADER_PRAGMA);
if (values != null) { if (values != null) {
for (String value : values) { for (String value : values) {
if (StringUtil.contains(value, "no-cache")) { if (StringUtil.contains(value, "no-cache")) {
return false; return false;
} }
} }
} }
return true; return true;
} }
public void flushBuffer() throws IOException { public void flushBuffer() throws IOException {
streamDelegate.flushBuffer(); streamDelegate.flushBuffer();
} }
public void resetBuffer() { public void resetBuffer() {
// Servlet 2.3 // Servlet 2.3
streamDelegate.resetBuffer(); streamDelegate.resetBuffer();
} }
public void reset() { public void reset() {
if (Boolean.FALSE.equals(cacheable)) { if (Boolean.FALSE.equals(cacheable)) {
super.reset(); super.reset();
} }
// No else, might be cacheable after all.. // No else, might be cacheable after all..
init(); init();
} }
public ServletOutputStream getOutputStream() throws IOException { public ServletOutputStream getOutputStream() throws IOException {
return streamDelegate.getOutputStream(); return streamDelegate.getOutputStream();
} }
public PrintWriter getWriter() throws IOException { public PrintWriter getWriter() throws IOException {
return streamDelegate.getWriter(); return streamDelegate.getWriter();
} }
public boolean containsHeader(String name) { public boolean containsHeader(String name) {
return cacheResponse.getHeaders().get(name) != null; return cacheResponse.getHeaders().get(name) != null;
} }
public void sendError(int pStatusCode, String msg) throws IOException { public void sendError(int pStatusCode, String msg) throws IOException {
// NOT cacheable // NOT cacheable
status = pStatusCode; status = pStatusCode;
super.sendError(pStatusCode, msg); super.sendError(pStatusCode, msg);
} }
public void sendError(int pStatusCode) throws IOException { public void sendError(int pStatusCode) throws IOException {
// NOT cacheable // NOT cacheable
status = pStatusCode; status = pStatusCode;
super.sendError(pStatusCode); super.sendError(pStatusCode);
} }
public void setStatus(int pStatusCode, String sm) { public void setStatus(int pStatusCode, String sm) {
// NOTE: This method is deprecated // NOTE: This method is deprecated
setStatus(pStatusCode); setStatus(pStatusCode);
} }
public void setStatus(int pStatusCode) { public void setStatus(int pStatusCode) {
// NOT cacheable unless pStatusCode == 200 (or a FEW others?) // NOT cacheable unless pStatusCode == 200 (or a FEW others?)
if (pStatusCode != SC_OK) { if (pStatusCode != SC_OK) {
status = pStatusCode; status = pStatusCode;
super.setStatus(pStatusCode); super.setStatus(pStatusCode);
} }
} }
public void sendRedirect(String pLocation) throws IOException { public void sendRedirect(String pLocation) throws IOException {
// NOT cacheable // NOT cacheable
status = SC_MOVED_TEMPORARILY; status = SC_MOVED_TEMPORARILY;
super.sendRedirect(pLocation); super.sendRedirect(pLocation);
} }
public void setDateHeader(String pName, long pValue) { public void setDateHeader(String pName, long pValue) {
// If in write-trough-mode, set headers directly // If in write-trough-mode, set headers directly
if (Boolean.FALSE.equals(cacheable)) { if (Boolean.FALSE.equals(cacheable)) {
super.setDateHeader(pName, pValue); super.setDateHeader(pName, pValue);
} }
cacheResponse.setHeader(pName, HTTPUtil.formatHTTPDate(pValue)); cacheResponse.setHeader(pName, HTTPUtil.formatHTTPDate(pValue));
} }
public void addDateHeader(String pName, long pValue) { public void addDateHeader(String pName, long pValue) {
// If in write-trough-mode, set headers directly // If in write-trough-mode, set headers directly
if (Boolean.FALSE.equals(cacheable)) { if (Boolean.FALSE.equals(cacheable)) {
super.addDateHeader(pName, pValue); super.addDateHeader(pName, pValue);
} }
cacheResponse.addHeader(pName, HTTPUtil.formatHTTPDate(pValue)); cacheResponse.addHeader(pName, HTTPUtil.formatHTTPDate(pValue));
} }
public void setHeader(String pName, String pValue) { public void setHeader(String pName, String pValue) {
// If in write-trough-mode, set headers directly // If in write-trough-mode, set headers directly
if (Boolean.FALSE.equals(cacheable)) { if (Boolean.FALSE.equals(cacheable)) {
super.setHeader(pName, pValue); super.setHeader(pName, pValue);
} }
cacheResponse.setHeader(pName, pValue); cacheResponse.setHeader(pName, pValue);
} }
public void addHeader(String pName, String pValue) { public void addHeader(String pName, String pValue) {
// If in write-trough-mode, set headers directly // If in write-trough-mode, set headers directly
if (Boolean.FALSE.equals(cacheable)) { if (Boolean.FALSE.equals(cacheable)) {
super.addHeader(pName, pValue); super.addHeader(pName, pValue);
} }
cacheResponse.addHeader(pName, pValue); cacheResponse.addHeader(pName, pValue);
} }
public void setIntHeader(String pName, int pValue) { public void setIntHeader(String pName, int pValue) {
// If in write-trough-mode, set headers directly // If in write-trough-mode, set headers directly
if (Boolean.FALSE.equals(cacheable)) { if (Boolean.FALSE.equals(cacheable)) {
super.setIntHeader(pName, pValue); super.setIntHeader(pName, pValue);
} }
cacheResponse.setHeader(pName, String.valueOf(pValue)); cacheResponse.setHeader(pName, String.valueOf(pValue));
} }
public void addIntHeader(String pName, int pValue) { public void addIntHeader(String pName, int pValue) {
// If in write-trough-mode, set headers directly // If in write-trough-mode, set headers directly
if (Boolean.FALSE.equals(cacheable)) { if (Boolean.FALSE.equals(cacheable)) {
super.addIntHeader(pName, pValue); super.addIntHeader(pName, pValue);
} }
cacheResponse.addHeader(pName, String.valueOf(pValue)); cacheResponse.addHeader(pName, String.valueOf(pValue));
} }
public final void setContentType(String type) { public final void setContentType(String type) {
setHeader(HTTPCache.HEADER_CONTENT_TYPE, type); setHeader(HTTPCache.HEADER_CONTENT_TYPE, type);
} }
} }

View File

@@ -1,77 +1,77 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.servlet.cache; package com.twelvemonkeys.servlet.cache;
import java.io.OutputStream; import java.io.OutputStream;
/** /**
* WritableCachedResponse * WritableCachedResponse
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: WritableCachedResponse.java#2 $ * @version $Id: WritableCachedResponse.java#2 $
*/ */
public interface WritableCachedResponse extends CachedResponse, CacheResponse { public interface WritableCachedResponse extends CachedResponse, CacheResponse {
/** /**
* Gets the {@code OutputStream} for this cached response. * Gets the {@code OutputStream} for this cached response.
* This allows a client to write to the cached response. * This allows a client to write to the cached response.
* *
* @return the {@code OutputStream} for this response. * @return the {@code OutputStream} for this response.
*/ */
OutputStream getOutputStream(); OutputStream getOutputStream();
/** /**
* Sets a header key/value pair for this response. * Sets a header key/value pair for this response.
* Any prior header value for the given header key will be overwritten. * Any prior header value for the given header key will be overwritten.
* *
* @see #addHeader(String, String) * @see #addHeader(String, String)
* *
* @param pName the header name * @param pName the header name
* @param pValue the header value * @param pValue the header value
*/ */
void setHeader(String pName, String pValue); void setHeader(String pName, String pValue);
/** /**
* Adds a header key/value pair for this response. * Adds a header key/value pair for this response.
* If a value allready exists for the given key, the value will be appended. * If a value allready exists for the given key, the value will be appended.
* *
* @see #setHeader(String, String) * @see #setHeader(String, String)
* *
* @param pName the header name * @param pName the header name
* @param pValue the header value * @param pValue the header value
*/ */
void addHeader(String pName, String pValue); void addHeader(String pName, String pValue);
/** /**
* Returns the final (immutable) {@code CachedResponse} created by this * Returns the final (immutable) {@code CachedResponse} created by this
* {@code WritableCachedResponse}. * {@code WritableCachedResponse}.
* *
* @return the {@code CachedResponse} * @return the {@code CachedResponse}
*/ */
CachedResponse getCachedResponse(); CachedResponse getCachedResponse();
} }

View File

@@ -1,186 +1,186 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.servlet.cache; package com.twelvemonkeys.servlet.cache;
import com.twelvemonkeys.io.FastByteArrayOutputStream; import com.twelvemonkeys.io.FastByteArrayOutputStream;
import com.twelvemonkeys.net.HTTPUtil; import com.twelvemonkeys.net.HTTPUtil;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
* WritableCachedResponseImpl * WritableCachedResponseImpl
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: WritableCachedResponseImpl.java#3 $ * @version $Id: WritableCachedResponseImpl.java#3 $
*/ */
class WritableCachedResponseImpl implements WritableCachedResponse { class WritableCachedResponseImpl implements WritableCachedResponse {
private final CachedResponseImpl cachedResponse; private final CachedResponseImpl cachedResponse;
/** /**
* Creates a {@code WritableCachedResponseImpl}. * Creates a {@code WritableCachedResponseImpl}.
*/ */
protected WritableCachedResponseImpl() { protected WritableCachedResponseImpl() {
cachedResponse = new CachedResponseImpl(); cachedResponse = new CachedResponseImpl();
// Hmmm.. // Hmmm..
setHeader(HTTPCache.HEADER_CACHED_TIME, HTTPUtil.formatHTTPDate(System.currentTimeMillis())); setHeader(HTTPCache.HEADER_CACHED_TIME, HTTPUtil.formatHTTPDate(System.currentTimeMillis()));
} }
public CachedResponse getCachedResponse() { public CachedResponse getCachedResponse() {
return cachedResponse; return cachedResponse;
} }
public void setHeader(String pName, String pValue) { public void setHeader(String pName, String pValue) {
setHeader(pName, pValue, false); setHeader(pName, pValue, false);
} }
public void addHeader(String pName, String pValue) { public void addHeader(String pName, String pValue) {
setHeader(pName, pValue, true); setHeader(pName, pValue, true);
} }
public Map<String, List<String>> getHeaders() { public Map<String, List<String>> getHeaders() {
return cachedResponse.headers; return cachedResponse.headers;
} }
/** /**
* *
* @param pName the header name * @param pName the header name
* @param pValue the new header value * @param pValue the new header value
* @param pAdd {@code true} if the value should add to the list of values, not replace existing 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) { private void setHeader(String pName, String pValue, boolean pAdd) {
// System.out.println(" ++ CachedResponse ++ " + (pAdd ? "addHeader(" : "setHeader(") + pName + ", " + pValue + ")"); // System.out.println(" ++ CachedResponse ++ " + (pAdd ? "addHeader(" : "setHeader(") + pName + ", " + pValue + ")");
// If adding, get list and append, otherwise replace list // If adding, get list and append, otherwise replace list
List<String> values = pAdd ? cachedResponse.headers.get(pName) : null; List<String> values = pAdd ? cachedResponse.headers.get(pName) : null;
if (values == null) { if (values == null) {
values = new ArrayList<String>(); values = new ArrayList<String>();
if (pAdd) { if (pAdd) {
// Add length of pName // Add length of pName
cachedResponse.headersSize += (pName != null ? pName.length() : 0); cachedResponse.headersSize += (pName != null ? pName.length() : 0);
} }
else { else {
// Remove length of potential replaced old values + pName // Remove length of potential replaced old values + pName
String[] oldValues = getHeaderValues(pName); String[] oldValues = getHeaderValues(pName);
if (oldValues != null) { if (oldValues != null) {
for (String oldValue : oldValues) { for (String oldValue : oldValues) {
cachedResponse.headersSize -= oldValue.length(); cachedResponse.headersSize -= oldValue.length();
} }
} }
else { else {
cachedResponse.headersSize += (pName != null ? pName.length() : 0); cachedResponse.headersSize += (pName != null ? pName.length() : 0);
} }
} }
} }
// Add value, if not null // Add value, if not null
if (pValue != null) { if (pValue != null) {
values.add(pValue); values.add(pValue);
// Add length of pValue // Add length of pValue
cachedResponse.headersSize += pValue.length(); cachedResponse.headersSize += pValue.length();
} }
// Always add to headers // Always add to headers
cachedResponse.headers.put(pName, values); cachedResponse.headers.put(pName, values);
} }
public OutputStream getOutputStream() { public OutputStream getOutputStream() {
// TODO: Hmm.. Smells like DCL..? // TODO: Hmm.. Smells like DCL..?
if (cachedResponse.content == null) { if (cachedResponse.content == null) {
createOutputStream(); createOutputStream();
} }
return cachedResponse.content; return cachedResponse.content;
} }
public void setStatus(int pStatusCode) { public void setStatus(int pStatusCode) {
cachedResponse.status = pStatusCode; cachedResponse.status = pStatusCode;
} }
public int getStatus() { public int getStatus() {
return cachedResponse.getStatus(); return cachedResponse.getStatus();
} }
private synchronized void createOutputStream() { private synchronized void createOutputStream() {
ByteArrayOutputStream cache = cachedResponse.content; ByteArrayOutputStream cache = cachedResponse.content;
if (cache == null) { if (cache == null) {
String contentLengthStr = getHeaderValue("Content-Length"); String contentLengthStr = getHeaderValue("Content-Length");
if (contentLengthStr != null) { if (contentLengthStr != null) {
int contentLength = Integer.parseInt(contentLengthStr); int contentLength = Integer.parseInt(contentLengthStr);
cache = new FastByteArrayOutputStream(contentLength); cache = new FastByteArrayOutputStream(contentLength);
} }
else { else {
cache = new FastByteArrayOutputStream(1024); cache = new FastByteArrayOutputStream(1024);
} }
cachedResponse.content = cache; cachedResponse.content = cache;
} }
} }
public void writeHeadersTo(CacheResponse pResponse) { public void writeHeadersTo(CacheResponse pResponse) {
cachedResponse.writeHeadersTo(pResponse); cachedResponse.writeHeadersTo(pResponse);
} }
public void writeContentsTo(OutputStream pStream) throws IOException { public void writeContentsTo(OutputStream pStream) throws IOException {
cachedResponse.writeContentsTo(pStream); cachedResponse.writeContentsTo(pStream);
} }
public String[] getHeaderNames() { public String[] getHeaderNames() {
return cachedResponse.getHeaderNames(); return cachedResponse.getHeaderNames();
} }
public String[] getHeaderValues(String pHeaderName) { public String[] getHeaderValues(String pHeaderName) {
return cachedResponse.getHeaderValues(pHeaderName); return cachedResponse.getHeaderValues(pHeaderName);
} }
public String getHeaderValue(String pHeaderName) { public String getHeaderValue(String pHeaderName) {
return cachedResponse.getHeaderValue(pHeaderName); return cachedResponse.getHeaderValue(pHeaderName);
} }
public int size() { public int size() {
return cachedResponse.size(); return cachedResponse.size();
} }
public boolean equals(Object pOther) { public boolean equals(Object pOther) {
if (pOther instanceof WritableCachedResponse) { if (pOther instanceof WritableCachedResponse) {
// Take advantage of faster implementation // Take advantage of faster implementation
return cachedResponse.equals(((WritableCachedResponse) pOther).getCachedResponse()); return cachedResponse.equals(((WritableCachedResponse) pOther).getCachedResponse());
} }
return cachedResponse.equals(pOther); return cachedResponse.equals(pOther);
} }
public int hashCode() { public int hashCode() {
return cachedResponse.hashCode(); return cachedResponse.hashCode();
} }
} }

View File

@@ -1,42 +1,42 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.servlet.fileupload; package com.twelvemonkeys.servlet.fileupload;
/** /**
* FileSizeExceededException * FileSizeExceededException
* <p/> * <p/>
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: FileSizeExceededException.java#1 $ * @version $Id: FileSizeExceededException.java#1 $
*/ */
public class FileSizeExceededException extends FileUploadException { public class FileSizeExceededException extends FileUploadException {
public FileSizeExceededException(Throwable pCause) { public FileSizeExceededException(Throwable pCause) {
super(pCause.getMessage(), pCause); super(pCause.getMessage(), pCause);
} }
} }

View File

@@ -1,52 +1,52 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.servlet.fileupload; package com.twelvemonkeys.servlet.fileupload;
import javax.servlet.ServletException; import javax.servlet.ServletException;
/** /**
* FileUploadException * FileUploadException
* <p/> * <p/>
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: FileUploadException.java#1 $ * @version $Id: FileUploadException.java#1 $
*/ */
public class FileUploadException extends ServletException { public class FileUploadException extends ServletException {
public FileUploadException(String pMessage) { public FileUploadException(String pMessage) {
super(pMessage); super(pMessage);
} }
public FileUploadException(String pMessage, Throwable pCause) { public FileUploadException(String pMessage, Throwable pCause) {
super(pMessage, pCause); super(pMessage, pCause);
} }
public FileUploadException(Throwable pCause) { public FileUploadException(Throwable pCause) {
super(pCause.getMessage(), pCause); super(pCause.getMessage(), pCause);
} }
} }

View File

@@ -1,125 +1,125 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.servlet.fileupload; package com.twelvemonkeys.servlet.fileupload;
import com.twelvemonkeys.servlet.GenericFilter; import com.twelvemonkeys.servlet.GenericFilter;
import com.twelvemonkeys.servlet.ServletUtil; import com.twelvemonkeys.servlet.ServletUtil;
import com.twelvemonkeys.io.FileUtil; import com.twelvemonkeys.io.FileUtil;
import com.twelvemonkeys.lang.StringUtil; import com.twelvemonkeys.lang.StringUtil;
import javax.servlet.FilterChain; import javax.servlet.FilterChain;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.ServletRequest; import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import java.io.IOException; import java.io.IOException;
import java.io.File; import java.io.File;
import java.net.URL; import java.net.URL;
import java.net.MalformedURLException; import java.net.MalformedURLException;
/** /**
* A servlet {@code Filter} for processing HTTP file upload requests, as * A servlet {@code Filter} for processing HTTP file upload requests, as
* specified by * specified by
* <a href="http://www.ietf.org/rfc/rfc1867.txt">Form-based File Upload in HTML (RFC1867)</a>. * <a href="http://www.ietf.org/rfc/rfc1867.txt">Form-based File Upload in HTML (RFC1867)</a>.
* *
* @see HttpFileUploadRequest * @see HttpFileUploadRequest
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: FileUploadFilter.java#1 $ * @version $Id: FileUploadFilter.java#1 $
*/ */
public class FileUploadFilter extends GenericFilter { public class FileUploadFilter extends GenericFilter {
private File uploadDir; private File uploadDir;
private long maxFileSize = 1024 * 1024; // 1 MByte private long maxFileSize = 1024 * 1024; // 1 MByte
/** /**
* This method is called by the server before the filter goes into service, * This method is called by the server before the filter goes into service,
* and here it determines the file upload directory. * and here it determines the file upload directory.
* *
* @throws ServletException * @throws ServletException
*/ */
public void init() throws ServletException { public void init() throws ServletException {
// Get the name of the upload directory. // Get the name of the upload directory.
String uploadDirParam = getInitParameter("uploadDir"); String uploadDirParam = getInitParameter("uploadDir");
if (!StringUtil.isEmpty(uploadDirParam)) { if (!StringUtil.isEmpty(uploadDirParam)) {
try { try {
URL uploadDirURL = getServletContext().getResource(uploadDirParam); URL uploadDirURL = getServletContext().getResource(uploadDirParam);
uploadDir = FileUtil.toFile(uploadDirURL); uploadDir = FileUtil.toFile(uploadDirURL);
} }
catch (MalformedURLException e) { catch (MalformedURLException e) {
throw new ServletException(e.getMessage(), e); throw new ServletException(e.getMessage(), e);
} }
} }
if (uploadDir == null) { if (uploadDir == null) {
uploadDir = ServletUtil.getTempDir(getServletContext()); uploadDir = ServletUtil.getTempDir(getServletContext());
} }
} }
/** /**
* Sets max filesize allowed for upload. * Sets max filesize allowed for upload.
* <!-- used by automagic init --> * <!-- used by automagic init -->
* *
* @param pMaxSize * @param pMaxSize
*/ */
public void setMaxFileSize(long pMaxSize) { public void setMaxFileSize(long pMaxSize) {
log("maxFileSize=" + pMaxSize); log("maxFileSize=" + pMaxSize);
maxFileSize = pMaxSize; maxFileSize = pMaxSize;
} }
/** /**
* Examines the request content type, and if it is a * Examines the request content type, and if it is a
* {@code multipart/*} request, wraps the request with a * {@code multipart/*} request, wraps the request with a
* {@code HttpFileUploadRequest}. * {@code HttpFileUploadRequest}.
* *
* @param pRequest The servlet request * @param pRequest The servlet request
* @param pResponse The servlet response * @param pResponse The servlet response
* @param pChain The filter chain * @param pChain The filter chain
* *
* @throws ServletException * @throws ServletException
* @throws IOException * @throws IOException
*/ */
public void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain) throws IOException, ServletException { public void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) pRequest; HttpServletRequest request = (HttpServletRequest) pRequest;
// Get the content type from the request // Get the content type from the request
String contentType = request.getContentType(); String contentType = request.getContentType();
// If the content type is multipart, wrap // If the content type is multipart, wrap
if (isMultipartFileUpload(contentType)) { if (isMultipartFileUpload(contentType)) {
pRequest = new HttpFileUploadRequestWrapper(request, uploadDir, maxFileSize); pRequest = new HttpFileUploadRequestWrapper(request, uploadDir, maxFileSize);
} }
pChain.doFilter(pRequest, pResponse); pChain.doFilter(pRequest, pResponse);
} }
private boolean isMultipartFileUpload(String pContentType) { private boolean isMultipartFileUpload(String pContentType) {
return pContentType != null && pContentType.startsWith("multipart/"); return pContentType != null && pContentType.startsWith("multipart/");
} }
} }

View File

@@ -1,63 +1,63 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.servlet.fileupload; package com.twelvemonkeys.servlet.fileupload;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
/** /**
* This interface represents an HTTP file upload request, as specified by * 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>. * <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> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: HttpFileUploadRequest.java#1 $ * @version $Id: HttpFileUploadRequest.java#1 $
*/ */
public interface HttpFileUploadRequest extends HttpServletRequest { public interface HttpFileUploadRequest extends HttpServletRequest {
/** /**
* Returns the value of a request parameter as an {@code UploadedFile}, * Returns the value of a request parameter as an {@code UploadedFile},
* or {@code null} if the parameter does not exist. * or {@code null} if the parameter does not exist.
* You should only use this method when you are sure the parameter has only * You should only use this method when you are sure the parameter has only
* one value. * one value.
* *
* @param pName the name of the requested parameter * @param pName the name of the requested parameter
* @return a {@code UoploadedFile} or {@code null} * @return a {@code UoploadedFile} or {@code null}
* *
* @see #getUploadedFiles(String) * @see #getUploadedFiles(String)
*/ */
UploadedFile getUploadedFile(String pName); UploadedFile getUploadedFile(String pName);
/** /**
* Returns an array of {@code UploadedFile} objects containing all the * Returns an array of {@code UploadedFile} objects containing all the
* values for the given request parameter, * values for the given request parameter,
* or {@code null} if the parameter does not exist. * or {@code null} if the parameter does not exist.
* *
* @param pName the name of the requested parameter * @param pName the name of the requested parameter
* @return an array of {@code UoploadedFile}s or {@code null} * @return an array of {@code UoploadedFile}s or {@code null}
*/ */
UploadedFile[] getUploadedFiles(String pName); UploadedFile[] getUploadedFiles(String pName);
} }

View File

@@ -1,154 +1,154 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.servlet.fileupload; package com.twelvemonkeys.servlet.fileupload;
import org.apache.commons.fileupload.*; import org.apache.commons.fileupload.*;
import org.apache.commons.fileupload.servlet.ServletRequestContext; import org.apache.commons.fileupload.servlet.ServletRequestContext;
import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import java.io.File; import java.io.File;
import java.util.*; import java.util.*;
/** /**
* An {@code HttpFileUploadRequest} implementation, based on * An {@code HttpFileUploadRequest} implementation, based on
* <a href="http://jakarta.apache.org/commons/fileupload/">Jakarta Commons FileUpload</a>. * <a href="http://jakarta.apache.org/commons/fileupload/">Jakarta Commons FileUpload</a>.
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: HttpFileUploadRequestWrapper.java#1 $ * @version $Id: HttpFileUploadRequestWrapper.java#1 $
*/ */
class HttpFileUploadRequestWrapper extends HttpServletRequestWrapper implements HttpFileUploadRequest { class HttpFileUploadRequestWrapper extends HttpServletRequestWrapper implements HttpFileUploadRequest {
private final Map<String, String[]> parameters = new HashMap<String, String[]>(); private final Map<String, String[]> parameters = new HashMap<String, String[]>();
private final Map<String, UploadedFile[]> files = new HashMap<String, UploadedFile[]>(); private final Map<String, UploadedFile[]> files = new HashMap<String, UploadedFile[]>();
public HttpFileUploadRequestWrapper(HttpServletRequest pRequest, File pUploadDir, long pMaxSize) throws ServletException { public HttpFileUploadRequestWrapper(HttpServletRequest pRequest, File pUploadDir, long pMaxSize) throws ServletException {
super(pRequest); super(pRequest);
DiskFileItemFactory factory = new DiskFileItemFactory( DiskFileItemFactory factory = new DiskFileItemFactory(
128 * 1024, // 128 KByte 128 * 1024, // 128 KByte
new File(pUploadDir.getAbsolutePath()) new File(pUploadDir.getAbsolutePath())
); );
FileUpload upload = new FileUpload(factory); FileUpload upload = new FileUpload(factory);
upload.setSizeMax(pMaxSize); upload.setSizeMax(pMaxSize);
// TODO: Defer request parsing?? // TODO: Defer request parsing??
try { try {
//noinspection unchecked //noinspection unchecked
List<FileItem> items = upload.parseRequest(new ServletRequestContext(pRequest)); List<FileItem> items = upload.parseRequest(new ServletRequestContext(pRequest));
for (FileItem item : items) { for (FileItem item : items) {
if (item.isFormField()) { if (item.isFormField()) {
processFormField(item.getFieldName(), item.getString()); processFormField(item.getFieldName(), item.getString());
} }
else { else {
processeFile(item); processeFile(item);
} }
} }
} }
catch (FileUploadBase.SizeLimitExceededException e) { catch (FileUploadBase.SizeLimitExceededException e) {
throw new FileSizeExceededException(e); throw new FileSizeExceededException(e);
} }
catch (org.apache.commons.fileupload.FileUploadException e) { catch (org.apache.commons.fileupload.FileUploadException e) {
throw new FileUploadException(e); throw new FileUploadException(e);
} }
} }
private void processeFile(final FileItem pItem) { private void processeFile(final FileItem pItem) {
UploadedFile value = new UploadedFileImpl(pItem); UploadedFile value = new UploadedFileImpl(pItem);
String name = pItem.getFieldName(); String name = pItem.getFieldName();
UploadedFile[] values; UploadedFile[] values;
UploadedFile[] oldValues = files.get(name); UploadedFile[] oldValues = files.get(name);
if (oldValues != null) { if (oldValues != null) {
values = new UploadedFile[oldValues.length + 1]; values = new UploadedFile[oldValues.length + 1];
System.arraycopy(oldValues, 0, values, 0, oldValues.length); System.arraycopy(oldValues, 0, values, 0, oldValues.length);
values[oldValues.length] = value; values[oldValues.length] = value;
} }
else { else {
values = new UploadedFile[] {value}; values = new UploadedFile[] {value};
} }
files.put(name, values); files.put(name, values);
// Also add to normal fields // Also add to normal fields
processFormField(name, value.getName()); processFormField(name, value.getName());
} }
private void processFormField(String pName, String pValue) { private void processFormField(String pName, String pValue) {
// Multiple parameter values are not that common, so it's // Multiple parameter values are not that common, so it's
// probably faster to just use arrays... // probably faster to just use arrays...
// TODO: Research and document... // TODO: Research and document...
String[] values; String[] values;
String[] oldValues = parameters.get(pName); String[] oldValues = parameters.get(pName);
if (oldValues != null) { if (oldValues != null) {
values = new String[oldValues.length + 1]; values = new String[oldValues.length + 1];
System.arraycopy(oldValues, 0, values, 0, oldValues.length); System.arraycopy(oldValues, 0, values, 0, oldValues.length);
values[oldValues.length] = pValue; values[oldValues.length] = pValue;
} }
else { else {
values = new String[] {pValue}; values = new String[] {pValue};
} }
parameters.put(pName, values); parameters.put(pName, values);
} }
public Map getParameterMap() { public Map getParameterMap() {
// TODO: The spec dicates immutable map, but what about the value arrays?! // TODO: The spec dicates immutable map, but what about the value arrays?!
// Probably just leave as-is, for performance // Probably just leave as-is, for performance
return Collections.unmodifiableMap(parameters); return Collections.unmodifiableMap(parameters);
} }
public Enumeration getParameterNames() { public Enumeration getParameterNames() {
return Collections.enumeration(parameters.keySet()); return Collections.enumeration(parameters.keySet());
} }
public String getParameter(String pString) { public String getParameter(String pString) {
String[] values = getParameterValues(pString); String[] values = getParameterValues(pString);
return values != null ? values[0] : null; return values != null ? values[0] : null;
} }
public String[] getParameterValues(String pString) { public String[] getParameterValues(String pString) {
// TODO: Optimize? // TODO: Optimize?
return parameters.get(pString).clone(); return parameters.get(pString).clone();
} }
public UploadedFile getUploadedFile(String pName) { public UploadedFile getUploadedFile(String pName) {
UploadedFile[] files = getUploadedFiles(pName); UploadedFile[] files = getUploadedFiles(pName);
return files != null ? files[0] : null; return files != null ? files[0] : null;
} }
public UploadedFile[] getUploadedFiles(String pName) { public UploadedFile[] getUploadedFiles(String pName) {
// TODO: Optimize? // TODO: Optimize?
return files.get(pName).clone(); return files.get(pName).clone();
} }
} }

View File

@@ -1,86 +1,86 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.servlet.fileupload; package com.twelvemonkeys.servlet.fileupload;
import java.io.File; import java.io.File;
import java.io.InputStream; import java.io.InputStream;
import java.io.IOException; import java.io.IOException;
/** /**
* This class represents an uploaded file. * This class represents an uploaded file.
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: UploadedFile.java#1 $ * @version $Id: UploadedFile.java#1 $
*/ */
public interface UploadedFile { public interface UploadedFile {
/** /**
* Returns the length of file, in bytes. * Returns the length of file, in bytes.
* *
* @return length of file * @return length of file
*/ */
long length(); long length();
/** /**
* Returns the original file name (from client). * Returns the original file name (from client).
* *
* @return original name * @return original name
*/ */
String getName(); String getName();
/** /**
* Returns the content type of the file. * Returns the content type of the file.
* *
* @return the content type * @return the content type
*/ */
String getContentType(); String getContentType();
/** /**
* Returns the file data, as an {@code InputStream}. * Returns the file data, as an {@code InputStream}.
* The file data may be read from disk, or from an in-memory source, * The file data may be read from disk, or from an in-memory source,
* depending on implementation. * depending on implementation.
* *
* @return an {@code InputStream} containing the file data * @return an {@code InputStream} containing the file data
* @throws IOException * @throws IOException
* @throws RuntimeException * @throws RuntimeException
*/ */
InputStream getInputStream() throws IOException; InputStream getInputStream() throws IOException;
/** /**
* Writes the file data to the given {@code File}. * Writes the file data to the given {@code File}.
* Note that implementations are free to optimize this to a rename * Note that implementations are free to optimize this to a rename
* operation, if the file is allready cached to disk. * operation, if the file is allready cached to disk.
* *
* @param pFile the {@code File} (file name) to write to. * @param pFile the {@code File} (file name) to write to.
* @throws IOException * @throws IOException
* @throws RuntimeException * @throws RuntimeException
*/ */
void writeTo(File pFile) throws IOException; void writeTo(File pFile) throws IOException;
// TODO: void delete()? // TODO: void delete()?
} }

View File

@@ -1,91 +1,91 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.servlet.fileupload; package com.twelvemonkeys.servlet.fileupload;
import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.FileUploadException;
import java.io.InputStream; import java.io.InputStream;
import java.io.IOException; import java.io.IOException;
import java.io.File; import java.io.File;
/** /**
* An {@code UploadedFile} implementation, based on * An {@code UploadedFile} implementation, based on
* <a href="http://jakarta.apache.org/commons/fileupload/">Jakarta Commons FileUpload</a>. * <a href="http://jakarta.apache.org/commons/fileupload/">Jakarta Commons FileUpload</a>.
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: UploadedFileImpl.java#1 $ * @version $Id: UploadedFileImpl.java#1 $
*/ */
class UploadedFileImpl implements UploadedFile { class UploadedFileImpl implements UploadedFile {
private final FileItem item; private final FileItem item;
public UploadedFileImpl(FileItem pItem) { public UploadedFileImpl(FileItem pItem) {
if (pItem == null) { if (pItem == null) {
throw new IllegalArgumentException("fileitem == null"); throw new IllegalArgumentException("fileitem == null");
} }
item = pItem; item = pItem;
} }
public String getContentType() { public String getContentType() {
return item.getContentType(); return item.getContentType();
} }
public InputStream getInputStream() throws IOException { public InputStream getInputStream() throws IOException {
return item.getInputStream(); return item.getInputStream();
} }
public String getName() { public String getName() {
return item.getName(); return item.getName();
} }
public long length() { public long length() {
return item.getSize(); return item.getSize();
} }
public void writeTo(File pFile) throws IOException { public void writeTo(File pFile) throws IOException {
try { try {
item.write(pFile); item.write(pFile);
} }
catch(RuntimeException e) { catch(RuntimeException e) {
throw e; throw e;
} }
catch (IOException e) { catch (IOException e) {
throw e; throw e;
} }
catch (FileUploadException e) { catch (FileUploadException e) {
// We deliberately change this exception to an IOException, as it really is // We deliberately change this exception to an IOException, as it really is
throw (IOException) new IOException(e.getMessage()).initCause(e); throw (IOException) new IOException(e.getMessage()).initCause(e);
} }
catch (Exception e) { catch (Exception e) {
// Should not really happen, ever // Should not really happen, ever
throw new RuntimeException(e.getMessage(), e); throw new RuntimeException(e.getMessage(), e);
} }
} }
} }

View File

@@ -1,141 +1,141 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.servlet.gzip; package com.twelvemonkeys.servlet.gzip;
import com.twelvemonkeys.servlet.GenericFilter; import com.twelvemonkeys.servlet.GenericFilter;
import javax.servlet.FilterChain; import javax.servlet.FilterChain;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.ServletRequest; import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
/** /**
* A filter to reduce the output size of web resources. * A filter to reduce the output size of web resources.
* <p/> * <p/>
* The HTTP protocol supports compression of the content to reduce network * The HTTP protocol supports compression of the content to reduce network
* bandwidth. The important headers involved, are the {@code Accept-Encoding} * bandwidth. The important headers involved, are the {@code Accept-Encoding}
* request header, and the {@code Content-Encoding} response header. * request header, and the {@code Content-Encoding} response header.
* This feature can be used to further reduce the number of bytes transferred * 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. * 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 * Most modern browsers supports compression in GZIP format, which is fairly
* efficient in cost/compression ratio. * efficient in cost/compression ratio.
* <p/> * <p/>
* The filter tests for the presence of an {@code Accept-Encoding} header with a * The filter tests for the presence of an {@code Accept-Encoding} header with a
* value of {@code "gzip"} (several different encoding header values are * value of {@code "gzip"} (several different encoding header values are
* possible in one header). If not present, the filter simply passes the * possible in one header). If not present, the filter simply passes the
* request/response pair through, leaving it untouched. If present, the * request/response pair through, leaving it untouched. If present, the
* {@code Content-Encoding} header is set, with the value {@code "gzip"}, * {@code Content-Encoding} header is set, with the value {@code "gzip"},
* and the response is wrapped. * and the response is wrapped.
* The response output stream is wrapped in a * The response output stream is wrapped in a
* {@link java.util.zip.GZIPOutputStream} which performs the GZIP encoding. * {@link java.util.zip.GZIPOutputStream} which performs the GZIP encoding.
* For efficiency, the filter does not buffer the response, but writes through * For efficiency, the filter does not buffer the response, but writes through
* the gzipped output stream. * the gzipped output stream.
* <p/> * <p/>
* <b>Configuration</b><br/> * <b>Configuration</b><br/>
* To use {@code GZIPFilter} in your web-application, you simply need to add it * 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 * 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 * 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 * used, and set to {@code REQUEST/FORWARD}, to make sure the filter is invoked
* only once for requests. * only once for requests.
* If using an older web descriptor, set the {@code init-param} * If using an older web descriptor, set the {@code init-param}
* {@code "once-per-request"} to {@code "true"} (this will have the same effect, * {@code "once-per-request"} to {@code "true"} (this will have the same effect,
* but might perform slightly worse than the 2.4 version). * but might perform slightly worse than the 2.4 version).
* Please see the examples below. * Please see the examples below.
* <b>Servlet 2.4 version, filter section:</b><br/> * <b>Servlet 2.4 version, filter section:</b><br/>
* <pre> * <pre>
* &lt;!-- GZIP Filter Configuration --&gt; * &lt;!-- GZIP Filter Configuration --&gt;
* &lt;filter&gt; * &lt;filter&gt;
* &lt;filter-name&gt;gzip&lt;/filter-name&gt; * &lt;filter-name&gt;gzip&lt;/filter-name&gt;
* &lt;filter-class&gt;com.twelvemonkeys.servlet.GZIPFilter&lt;/filter-class&gt; * &lt;filter-class&gt;com.twelvemonkeys.servlet.GZIPFilter&lt;/filter-class&gt;
* &lt;/filter&gt; * &lt;/filter&gt;
* </pre> * </pre>
* <b>Filter-mapping section:</b><br/> * <b>Filter-mapping section:</b><br/>
* <pre> * <pre>
* &lt;!-- GZIP Filter Mapping --&gt; * &lt;!-- GZIP Filter Mapping --&gt;
* &lt;filter-mapping&gt; * &lt;filter-mapping&gt;
* &lt;filter-name&gt;gzip&lt;/filter-name&gt; * &lt;filter-name&gt;gzip&lt;/filter-name&gt;
* &lt;url-pattern&gt;*.html&lt;/url-pattern&gt; * &lt;url-pattern&gt;*.html&lt;/url-pattern&gt;
* &lt;dispatcher&gt;REQUEST&lt;/dispatcher&gt; * &lt;dispatcher&gt;REQUEST&lt;/dispatcher&gt;
* &lt;dispatcher&gt;FORWARD&lt;/dispatcher&gt; * &lt;dispatcher&gt;FORWARD&lt;/dispatcher&gt;
* &lt;/filter-mapping&gt; * &lt;/filter-mapping&gt;
* &lt;filter-mapping&gt; * &lt;filter-mapping&gt;
* &lt;filter-name&gt;gzip&lt;/filter-name&gt; * &lt;filter-name&gt;gzip&lt;/filter-name&gt;
* &lt;url-pattern&gt;*.jsp&lt; /url-pattern&gt; * &lt;url-pattern&gt;*.jsp&lt; /url-pattern&gt;
* &lt;dispatcher&gt;REQUEST&lt;/dispatcher&gt; * &lt;dispatcher&gt;REQUEST&lt;/dispatcher&gt;
* &lt;dispatcher&gt;FORWARD&lt;/dispatcher&gt; * &lt;dispatcher&gt;FORWARD&lt;/dispatcher&gt;
* &lt;/filter-mapping&gt; * &lt;/filter-mapping&gt;
* </pre> * </pre>
* <p/> * <p/>
* Based on ideas and code found in the ONJava article * 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 * <a href="http://www.onjava.com/pub/a/onjava/2003/11/19/filters.html">Two
* Servlet Filters Every Web Application Should Have</a> * Servlet Filters Every Web Application Should Have</a>
* by Jayson Falkner. * by Jayson Falkner.
* <p/> * <p/>
* *
* @author Jayson Falkner * @author Jayson Falkner
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $ * @author last modified by $Author: haku $
* @version $Id: GZIPFilter.java#1 $ * @version $Id: GZIPFilter.java#1 $
*/ */
public class GZIPFilter extends GenericFilter { public class GZIPFilter extends GenericFilter {
{ {
oncePerRequest = true; oncePerRequest = true;
} }
protected void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain) throws IOException, ServletException { protected void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain) throws IOException, ServletException {
// Can only filter HTTP responses // Can only filter HTTP responses
if (pRequest instanceof HttpServletRequest) { if (pRequest instanceof HttpServletRequest) {
HttpServletRequest request = (HttpServletRequest) pRequest; HttpServletRequest request = (HttpServletRequest) pRequest;
HttpServletResponse response = (HttpServletResponse) pResponse; HttpServletResponse response = (HttpServletResponse) pResponse;
// If GZIP is supported, use compression // If GZIP is supported, use compression
String accept = request.getHeader("Accept-Encoding"); String accept = request.getHeader("Accept-Encoding");
if (accept != null && accept.contains("gzip")) { if (accept != null && accept.contains("gzip")) {
//System.out.println("GZIP supported, compressing."); //System.out.println("GZIP supported, compressing.");
GZIPResponseWrapper wrapped = new GZIPResponseWrapper(response); GZIPResponseWrapper wrapped = new GZIPResponseWrapper(response);
try { try {
pChain.doFilter(pRequest, wrapped); pChain.doFilter(pRequest, wrapped);
} }
finally { finally {
wrapped.flushResponse(); wrapped.flushResponse();
} }
return; return;
} }
} }
// Else, continue chain // Else, continue chain
pChain.doFilter(pRequest, pResponse); pChain.doFilter(pRequest, pResponse);
} }
} }

View File

@@ -1,147 +1,147 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.servlet.gzip; package com.twelvemonkeys.servlet.gzip;
import com.twelvemonkeys.servlet.OutputStreamAdapter; import com.twelvemonkeys.servlet.OutputStreamAdapter;
import javax.servlet.ServletOutputStream; import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper; import javax.servlet.http.HttpServletResponseWrapper;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStreamWriter; import java.io.OutputStreamWriter;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.zip.GZIPOutputStream; import java.util.zip.GZIPOutputStream;
/** /**
* GZIPResponseWrapper class description. * GZIPResponseWrapper class description.
* <p/> * <p/>
* Based on ideas and code found in the ONJava article * 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> * <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. * by Jayson Falkner.
* *
* @author Jayson Falkner * @author Jayson Falkner
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $ * @author last modified by $Author: haku $
* @version $Id: GZIPResponseWrapper.java#1 $ * @version $Id: GZIPResponseWrapper.java#1 $
*/ */
public class GZIPResponseWrapper extends HttpServletResponseWrapper { public class GZIPResponseWrapper extends HttpServletResponseWrapper {
// TODO: Remove/update ETags if needed? Read the spec (RFC 2616) on Vary/ETag for caching // TODO: Remove/update ETags if needed? Read the spec (RFC 2616) on Vary/ETag for caching
protected ServletOutputStream out; protected ServletOutputStream out;
protected PrintWriter writer; protected PrintWriter writer;
protected GZIPOutputStream gzipOut; protected GZIPOutputStream gzipOut;
protected int contentLength = -1; protected int contentLength = -1;
public GZIPResponseWrapper(final HttpServletResponse response) { public GZIPResponseWrapper(final HttpServletResponse response) {
super(response); super(response);
response.addHeader("Content-Encoding", "gzip"); response.addHeader("Content-Encoding", "gzip");
response.addHeader("Vary", "Accept"); response.addHeader("Vary", "Accept");
} }
public ServletOutputStream createOutputStream() throws IOException { public ServletOutputStream createOutputStream() throws IOException {
// FIX: Write directly to servlet output stream, for faster responses. // FIX: Write directly to servlet output stream, for faster responses.
// Relies on chunked streams, or buffering in the servlet engine. // Relies on chunked streams, or buffering in the servlet engine.
if (contentLength >= 0) { if (contentLength >= 0) {
gzipOut = new GZIPOutputStream(getResponse().getOutputStream(), contentLength); gzipOut = new GZIPOutputStream(getResponse().getOutputStream(), contentLength);
} }
else { else {
gzipOut = new GZIPOutputStream(getResponse().getOutputStream()); gzipOut = new GZIPOutputStream(getResponse().getOutputStream());
} }
// Wrap in ServletOutputStream and return // Wrap in ServletOutputStream and return
return new OutputStreamAdapter(gzipOut); return new OutputStreamAdapter(gzipOut);
} }
// TODO: Move this to flushbuffer or something? Hmmm.. // TODO: Move this to flushbuffer or something? Hmmm..
public void flushResponse() throws IOException { public void flushResponse() throws IOException {
try { try {
// Finish GZIP encodig // Finish GZIP encodig
if (gzipOut != null) { if (gzipOut != null) {
gzipOut.finish(); gzipOut.finish();
} }
flushBuffer(); flushBuffer();
} }
finally { finally {
// Close stream // Close stream
if (writer != null) { if (writer != null) {
writer.close(); writer.close();
} }
else { else {
if (out != null) { if (out != null) {
out.close(); out.close();
} }
} }
} }
} }
public void flushBuffer() throws IOException { public void flushBuffer() throws IOException {
if (writer != null) { if (writer != null) {
writer.flush(); writer.flush();
} }
else if (out != null) { else if (out != null) {
out.flush(); out.flush();
} }
} }
public ServletOutputStream getOutputStream() throws IOException { public ServletOutputStream getOutputStream() throws IOException {
if (writer != null) { if (writer != null) {
throw new IllegalStateException("getWriter() has already been called!"); throw new IllegalStateException("getWriter() has already been called!");
} }
if (out == null) { if (out == null) {
out = createOutputStream(); out = createOutputStream();
} }
return out; return out;
} }
public PrintWriter getWriter() throws IOException { public PrintWriter getWriter() throws IOException {
if (writer != null) { if (writer != null) {
return (writer); return (writer);
} }
if (out != null) { if (out != null) {
throw new IllegalStateException("getOutputStream() has already been called!"); throw new IllegalStateException("getOutputStream() has already been called!");
} }
out = createOutputStream(); out = createOutputStream();
// TODO: This is wrong. Should use getCharacterEncoding() or "ISO-8859-1" if getCE returns null. // TODO: This is wrong. Should use getCharacterEncoding() or "ISO-8859-1" if getCE returns null.
writer = new PrintWriter(new OutputStreamWriter(out, "UTF-8")); writer = new PrintWriter(new OutputStreamWriter(out, "UTF-8"));
return writer; return writer;
} }
public void setContentLength(int pLength) { public void setContentLength(int pLength) {
// NOTE: Do not call super, as we will shrink the size. // NOTE: Do not call super, as we will shrink the size.
contentLength = pLength; contentLength = pLength;
} }
} }

View File

@@ -1,72 +1,72 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.servlet.image; package com.twelvemonkeys.servlet.image;
import com.twelvemonkeys.image.ImageUtil; import com.twelvemonkeys.image.ImageUtil;
import javax.servlet.ServletRequest; import javax.servlet.ServletRequest;
import java.awt.*; import java.awt.*;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage; import java.awt.image.RenderedImage;
/** /**
* AWTImageFilterAdapter * AWTImageFilterAdapter
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: AWTImageFilterAdapter.java#1 $ * @version $Id: AWTImageFilterAdapter.java#1 $
* *
*/ */
public class AWTImageFilterAdapter extends ImageFilter { public class AWTImageFilterAdapter extends ImageFilter {
private java.awt.image.ImageFilter imageFilter = null; private java.awt.image.ImageFilter imageFilter = null;
public void setImageFilter(String pFilterClass) { public void setImageFilter(String pFilterClass) {
try { try {
Class filterClass = Class.forName(pFilterClass); Class filterClass = Class.forName(pFilterClass);
imageFilter = (java.awt.image.ImageFilter) filterClass.newInstance(); imageFilter = (java.awt.image.ImageFilter) filterClass.newInstance();
} }
catch (ClassNotFoundException e) { catch (ClassNotFoundException e) {
log("Could not load filter class.", e); log("Could not load filter class.", e);
} }
catch (InstantiationException e) { catch (InstantiationException e) {
log("Could not instantiate filter.", e); log("Could not instantiate filter.", e);
} }
catch (IllegalAccessException e) { catch (IllegalAccessException e) {
log("Could not access filter class.", e); log("Could not access filter class.", e);
} }
} }
protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) { protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) {
// Filter // Filter
Image img = ImageUtil.filter(pImage, imageFilter); Image img = ImageUtil.filter(pImage, imageFilter);
// Create BufferedImage & return // Create BufferedImage & return
return ImageUtil.toBuffered(img, BufferedImage.TYPE_INT_RGB); // TODO: This is ok for JPEG only... return ImageUtil.toBuffered(img, BufferedImage.TYPE_INT_RGB); // TODO: This is ok for JPEG only...
} }
} }

View File

@@ -1,67 +1,67 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.servlet.image; package com.twelvemonkeys.servlet.image;
import javax.servlet.ServletRequest; import javax.servlet.ServletRequest;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp; import java.awt.image.BufferedImageOp;
import java.awt.image.RenderedImage; import java.awt.image.RenderedImage;
/** /**
* BufferedImageOpAdapter * BufferedImageOpAdapter
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: BufferedImageOpAdapter.java#1 $ * @version $Id: BufferedImageOpAdapter.java#1 $
* *
*/ */
public class BufferedImageOpAdapter extends ImageFilter { public class BufferedImageOpAdapter extends ImageFilter {
private BufferedImageOp filter = null; private BufferedImageOp filter = null;
public void setImageFilter(String pFilterClass) { public void setImageFilter(String pFilterClass) {
try { try {
Class filterClass = Class.forName(pFilterClass); Class filterClass = Class.forName(pFilterClass);
filter = (BufferedImageOp) filterClass.newInstance(); filter = (BufferedImageOp) filterClass.newInstance();
} }
catch (ClassNotFoundException e) { catch (ClassNotFoundException e) {
log("Could not instantiate filter class.", e); log("Could not instantiate filter class.", e);
} }
catch (InstantiationException e) { catch (InstantiationException e) {
log("Could not instantiate filter.", e); log("Could not instantiate filter.", e);
} }
catch (IllegalAccessException e) { catch (IllegalAccessException e) {
log("Could not access filter class.", e); log("Could not access filter class.", e);
} }
} }
protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) { protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) {
// Filter & return // Filter & return
return filter.filter(pImage, null); return filter.filter(pImage, null);
} }
} }

View File

@@ -1,211 +1,211 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.servlet.image; package com.twelvemonkeys.servlet.image;
import com.twelvemonkeys.servlet.GenericServlet; import com.twelvemonkeys.servlet.GenericServlet;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream; import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest; import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; import javax.servlet.ServletResponse;
import java.io.IOException; import java.io.IOException;
import java.util.zip.CRC32; import java.util.zip.CRC32;
/** /**
* Creates a minimal 1 x 1 pixel PNG image, in a color specified by the * 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 * {@code "color"} parameter. The color is HTML-style #RRGGBB, with two
* digits hex number for red, green and blue (the hash, '#', is optional). * digits hex number for red, green and blue (the hash, '#', is optional).
* <p/> * <p/>
* The class does only byte manipulation, there is no server-side image * The class does only byte manipulation, there is no server-side image
* processing involving AWT ({@code Toolkit} class) of any kind. * processing involving AWT ({@code Toolkit} class) of any kind.
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $ * @author last modified by $Author: haku $
* @version $Id: ColorServlet.java#2 $ * @version $Id: ColorServlet.java#2 $
*/ */
public class ColorServlet extends GenericServlet { public class ColorServlet extends GenericServlet {
private final static String RGB_PARAME = "color"; private final static String RGB_PARAME = "color";
// A minimal, one color indexed PNG // A minimal, one color indexed PNG
private final static byte[] PNG_IMG = new byte[]{ private final static byte[] PNG_IMG = new byte[]{
(byte) 0x89, (byte) 'P', (byte) 'N', (byte) 'G', // PNG signature (8 bytes) (byte) 0x89, (byte) 'P', (byte) 'N', (byte) 'G', // PNG signature (8 bytes)
0x0d, 0x0a, 0x1a, 0x0a, 0x0d, 0x0a, 0x1a, 0x0a,
0x00, 0x00, 0x00, 0x0d, // IHDR length (13) 0x00, 0x00, 0x00, 0x0d, // IHDR length (13)
(byte) 'I', (byte) 'H', (byte) 'D', (byte) 'R', // Image header (byte) 'I', (byte) 'H', (byte) 'D', (byte) 'R', // Image header
0x00, 0x00, 0x00, 0x01, // width 0x00, 0x00, 0x00, 0x01, // width
0x00, 0x00, 0x00, 0x01, // height 0x00, 0x00, 0x00, 0x01, // height
0x01, 0x03, 0x00, 0x00, 0x00, // bits, color type, compression, filter, interlace 0x01, 0x03, 0x00, 0x00, 0x00, // bits, color type, compression, filter, interlace
0x25, (byte) 0xdb, 0x56, (byte) 0xca, // IHDR CRC 0x25, (byte) 0xdb, 0x56, (byte) 0xca, // IHDR CRC
0x00, 0x00, 0x00, 0x03, // PLTE length (3) 0x00, 0x00, 0x00, 0x03, // PLTE length (3)
(byte) 'P', (byte) 'L', (byte) 'T', (byte) 'E', // Palette (byte) 'P', (byte) 'L', (byte) 'T', (byte) 'E', // Palette
0x00, 0x00, (byte) 0xff, // red, green, blue (updated by this servlet) 0x00, 0x00, (byte) 0xff, // red, green, blue (updated by this servlet)
(byte) 0x8a, (byte) 0x78, (byte) 0xd2, 0x57, // PLTE CRC (byte) 0x8a, (byte) 0x78, (byte) 0xd2, 0x57, // PLTE CRC
0x00, 0x00, 0x00, 0x0a, // IDAT length (10) 0x00, 0x00, 0x00, 0x0a, // IDAT length (10)
(byte) 'I', (byte) 'D', (byte) 'A', (byte) 'T', // Image data (byte) 'I', (byte) 'D', (byte) 'A', (byte) 'T', // Image data
0x78, (byte) 0xda, 0x63, 0x60, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x78, (byte) 0xda, 0x63, 0x60, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01,
(byte) 0xe5, 0x27, (byte) 0xde, (byte) 0xfc, // IDAT CRC (byte) 0xe5, 0x27, (byte) 0xde, (byte) 0xfc, // IDAT CRC
0x00, 0x00, 0x00, 0x00, // IEND length (0) 0x00, 0x00, 0x00, 0x00, // IEND length (0)
(byte) 'I', (byte) 'E', (byte) 'N', (byte) 'D', // Image end (byte) 'I', (byte) 'E', (byte) 'N', (byte) 'D', // Image end
(byte) 0xae, (byte) 0x42, (byte) 0x60, (byte) 0x82 // IEND CRC (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_START = 37; // after chunk length
private final static int PLTE_CHUNK_LENGTH = 7; // chunk name & data private final static int PLTE_CHUNK_LENGTH = 7; // chunk name & data
private final static int RED_IDX = 4; private final static int RED_IDX = 4;
private final static int GREEN_IDX = RED_IDX + 1; private final static int GREEN_IDX = RED_IDX + 1;
private final static int BLUE_IDX = GREEN_IDX + 1; private final static int BLUE_IDX = GREEN_IDX + 1;
private final CRC32 crc = new CRC32(); private final CRC32 crc = new CRC32();
/** /**
* Creates a ColorDroplet. * Creates a ColorDroplet.
*/ */
public ColorServlet() { public ColorServlet() {
super(); super();
} }
/** /**
* Renders the 1 x 1 single color PNG to the response. * Renders the 1 x 1 single color PNG to the response.
* *
* @see ColorServlet class description * @see ColorServlet class description
* *
* @param pRequest the request * @param pRequest the request
* @param pResponse the response * @param pResponse the response
* *
* @throws IOException * @throws IOException
* @throws ServletException * @throws ServletException
*/ */
public void service(ServletRequest pRequest, ServletResponse pResponse) throws IOException, ServletException { public void service(ServletRequest pRequest, ServletResponse pResponse) throws IOException, ServletException {
int red = 0; int red = 0;
int green = 0; int green = 0;
int blue = 0; int blue = 0;
// Get color parameter and parse color // Get color parameter and parse color
String rgb = pRequest.getParameter(RGB_PARAME); String rgb = pRequest.getParameter(RGB_PARAME);
if (rgb != null && rgb.length() >= 6 && rgb.length() <= 7) { if (rgb != null && rgb.length() >= 6 && rgb.length() <= 7) {
int index = 0; int index = 0;
// If the hash ('#') character is included, skip it. // If the hash ('#') character is included, skip it.
if (rgb.length() == 7) { if (rgb.length() == 7) {
index++; index++;
} }
try { try {
// Two digit hex for each color // Two digit hex for each color
String r = rgb.substring(index, index += 2); String r = rgb.substring(index, index += 2);
red = Integer.parseInt(r, 0x10); red = Integer.parseInt(r, 0x10);
String g = rgb.substring(index, index += 2); String g = rgb.substring(index, index += 2);
green = Integer.parseInt(g, 0x10); green = Integer.parseInt(g, 0x10);
String b = rgb.substring(index, index += 2); String b = rgb.substring(index, index += 2);
blue = Integer.parseInt(b, 0x10); blue = Integer.parseInt(b, 0x10);
} }
catch (NumberFormatException nfe) { catch (NumberFormatException nfe) {
log("Wrong color format for ColorDroplet: " + rgb + ". Must be RRGGBB."); log("Wrong color format for ColorDroplet: " + rgb + ". Must be RRGGBB.");
} }
} }
// Set MIME type for PNG // Set MIME type for PNG
pResponse.setContentType("image/png"); pResponse.setContentType("image/png");
ServletOutputStream out = pResponse.getOutputStream(); ServletOutputStream out = pResponse.getOutputStream();
try { try {
// Write header (and palette chunk length) // Write header (and palette chunk length)
out.write(PNG_IMG, 0, PLTE_CHUNK_START); out.write(PNG_IMG, 0, PLTE_CHUNK_START);
// Create palette chunk, excl lenght, and write // Create palette chunk, excl lenght, and write
byte[] palette = makePalette(red, green, blue); byte[] palette = makePalette(red, green, blue);
out.write(palette); out.write(palette);
// Write image data until end // Write image data until end
int pos = PLTE_CHUNK_START + PLTE_CHUNK_LENGTH + 4; int pos = PLTE_CHUNK_START + PLTE_CHUNK_LENGTH + 4;
out.write(PNG_IMG, pos, PNG_IMG.length - pos); out.write(PNG_IMG, pos, PNG_IMG.length - pos);
} }
finally { finally {
out.flush(); out.flush();
} }
} }
/** /**
* Updates the CRC for a byte array. Note that the byte array must be at * 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 * least {@code pOff + pLen + 4} bytes long, as the CRC is stored in the
* 4 last bytes. * 4 last bytes.
* *
* @param pBytes the bytes to create CRC for * @param pBytes the bytes to create CRC for
* @param pOff the offset into the byte array 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 * @param pLen the length of the byte array to create CRC for
*/ */
private void updateCRC(byte[] pBytes, int pOff, int pLen) { private void updateCRC(byte[] pBytes, int pOff, int pLen) {
int value; int value;
synchronized (crc) { synchronized (crc) {
crc.reset(); crc.reset();
crc.update(pBytes, pOff, pLen); crc.update(pBytes, pOff, pLen);
value = (int) crc.getValue(); value = (int) crc.getValue();
} }
pBytes[pOff + pLen ] = (byte) ((value >> 24) & 0xff); pBytes[pOff + pLen ] = (byte) ((value >> 24) & 0xff);
pBytes[pOff + pLen + 1] = (byte) ((value >> 16) & 0xff); pBytes[pOff + pLen + 1] = (byte) ((value >> 16) & 0xff);
pBytes[pOff + pLen + 2] = (byte) ((value >> 8) & 0xff); pBytes[pOff + pLen + 2] = (byte) ((value >> 8) & 0xff);
pBytes[pOff + pLen + 3] = (byte) ( value & 0xff); pBytes[pOff + pLen + 3] = (byte) ( value & 0xff);
} }
/** /**
* Creates a PNG palette (PLTE) chunk with one color. * Creates a PNG palette (PLTE) chunk with one color.
* The palette chunk data is always 3 bytes in length (one byte per color * The palette chunk data is always 3 bytes in length (one byte per color
* component). * component).
* The returned byte array is then {@code 4 + 3 + 4 = 11} bytes, * The returned byte array is then {@code 4 + 3 + 4 = 11} bytes,
* including chunk header, data and CRC. * including chunk header, data and CRC.
* *
* @param pRed the red component * @param pRed the red component
* @param pGreen the reen component * @param pGreen the reen component
* @param pBlue the blue component * @param pBlue the blue component
* *
* @return the bytes for the PLTE chunk, including CRC (but not length) * @return the bytes for the PLTE chunk, including CRC (but not length)
*/ */
private byte[] makePalette(int pRed, int pGreen, int pBlue) { private byte[] makePalette(int pRed, int pGreen, int pBlue) {
byte[] palette = new byte[PLTE_CHUNK_LENGTH + 4]; byte[] palette = new byte[PLTE_CHUNK_LENGTH + 4];
System.arraycopy(PNG_IMG, PLTE_CHUNK_START, palette, 0, PLTE_CHUNK_LENGTH); System.arraycopy(PNG_IMG, PLTE_CHUNK_START, palette, 0, PLTE_CHUNK_LENGTH);
palette[RED_IDX] = (byte) pRed; palette[RED_IDX] = (byte) pRed;
palette[GREEN_IDX] = (byte) pGreen; palette[GREEN_IDX] = (byte) pGreen;
palette[BLUE_IDX] = (byte) pBlue; palette[BLUE_IDX] = (byte) pBlue;
updateCRC(palette, 0, PLTE_CHUNK_LENGTH); updateCRC(palette, 0, PLTE_CHUNK_LENGTH);
return palette; return palette;
} }
} }

View File

@@ -1,59 +1,59 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.servlet.image; package com.twelvemonkeys.servlet.image;
import javax.servlet.ServletRequest; import javax.servlet.ServletRequest;
import java.awt.image.RenderedImage; import java.awt.image.RenderedImage;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.IOException; import java.io.IOException;
/** /**
* ComposeFilter * ComposeFilter
* <p/> * <p/>
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: ComposeFilter.java#1 $ * @version $Id: ComposeFilter.java#1 $
*/ */
public class ComposeFilter extends ImageFilter { public class ComposeFilter extends ImageFilter {
protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) throws IOException { protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) throws IOException {
// 1. Load different image, locally (using ServletContext.getResource) // 1. Load different image, locally (using ServletContext.getResource)
// - Allow loading other filtered sources, or servlets? For example to // - Allow loading other filtered sources, or servlets? For example to
// apply filename or timestamps? // apply filename or timestamps?
// - Allow applying text directly? Variables? // - Allow applying text directly? Variables?
// 2. Apply transformations from config // 2. Apply transformations from config
// - Relative positioning // - Relative positioning
// - Relative scaling // - Relative scaling
// - Repeat (fill-pattern)? // - Repeat (fill-pattern)?
// - Rotation? // - Rotation?
// - Transparency? // - Transparency?
// - Background or foreground (layers)? // - Background or foreground (layers)?
// 3. Apply loaded image to original image (or vice versa?). // 3. Apply loaded image to original image (or vice versa?).
return pImage; return pImage;
} }
} }

View File

@@ -1,439 +1,439 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.servlet.image; package com.twelvemonkeys.servlet.image;
import com.twelvemonkeys.image.ImageUtil; import com.twelvemonkeys.image.ImageUtil;
import com.twelvemonkeys.lang.StringUtil; import com.twelvemonkeys.lang.StringUtil;
import javax.servlet.ServletRequest; import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; import javax.servlet.ServletResponse;
import javax.servlet.FilterChain; import javax.servlet.FilterChain;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import java.awt.*; import java.awt.*;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.awt.image.IndexColorModel; import java.awt.image.IndexColorModel;
import java.awt.image.RenderedImage; import java.awt.image.RenderedImage;
import java.io.IOException; import java.io.IOException;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.util.*; import java.util.*;
/** /**
* This filter implements server side content negotiation and transcoding for * This filter implements server side content negotiation and transcoding for
* images. * images.
* *
* @todo Add support for automatic recognition of known browsers, to avoid * @todo Add support for automatic recognition of known browsers, to avoid
* unneccessary conversion (as IE supports PNG, the latests FireFox supports * unneccessary conversion (as IE supports PNG, the latests FireFox supports
* JPEG and GIF, etc. even though they both don't explicitly list these formats * JPEG and GIF, etc. even though they both don't explicitly list these formats
* in their Accept headers). * in their Accept headers).
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: ContentNegotiationFilter.java#1 $ * @version $Id: ContentNegotiationFilter.java#1 $
*/ */
public class ContentNegotiationFilter extends ImageFilter { public class ContentNegotiationFilter extends ImageFilter {
private final static String MIME_TYPE_IMAGE_PREFIX = "image/"; 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_IMAGE_ANY = MIME_TYPE_IMAGE_PREFIX + "*";
private static final String MIME_TYPE_ANY = "*/*"; private static final String MIME_TYPE_ANY = "*/*";
private static final String HTTP_HEADER_ACCEPT = "Accept"; private static final String HTTP_HEADER_ACCEPT = "Accept";
private static final String HTTP_HEADER_VARY = "Vary"; private static final String HTTP_HEADER_VARY = "Vary";
protected static final String HTTP_HEADER_USER_AGENT = "User-Agent"; protected static final String HTTP_HEADER_USER_AGENT = "User-Agent";
private static final String FORMAT_JPEG = "image/jpeg"; private static final String FORMAT_JPEG = "image/jpeg";
private static final String FORMAT_WBMP = "image/wbmp"; private static final String FORMAT_WBMP = "image/wbmp";
private static final String FORMAT_GIF = "image/gif"; private static final String FORMAT_GIF = "image/gif";
private static final String FORMAT_PNG = "image/png"; private static final String FORMAT_PNG = "image/png";
private final static String[] sKnownFormats = new String[] { private final static String[] sKnownFormats = new String[] {
FORMAT_JPEG, FORMAT_PNG, FORMAT_GIF, FORMAT_WBMP FORMAT_JPEG, FORMAT_PNG, FORMAT_GIF, FORMAT_WBMP
}; };
private float[] knownFormatQuality = new float[] { private float[] knownFormatQuality = new float[] {
1f, 1f, 0.99f, 0.5f 1f, 1f, 0.99f, 0.5f
}; };
private HashMap<String, Float> formatQuality; // HashMap, as I need to clone this for each request private HashMap<String, Float> formatQuality; // HashMap, as I need to clone this for each request
private final Object lock = new Object(); private final Object lock = new Object();
/* /*
private Pattern[] mKnownAgentPatterns; private Pattern[] mKnownAgentPatterns;
private String[] mKnownAgentAccpets; private String[] mKnownAgentAccpets;
*/ */
{ {
// Hack: Make sure the filter don't trigger all the time // Hack: Make sure the filter don't trigger all the time
// See: super.trigger(ServletRequest) // See: super.trigger(ServletRequest)
triggerParams = new String[] {}; triggerParams = new String[] {};
} }
/* /*
public void setAcceptMappings(String pPropertiesFile) { public void setAcceptMappings(String pPropertiesFile) {
// NOTE: Supposed to be: // NOTE: Supposed to be:
// <agent-name>=<reg-exp> // <agent-name>=<reg-exp>
// <agent-name>.accept=<http-accept-header> // <agent-name>.accept=<http-accept-header>
Properties mappings = new Properties(); Properties mappings = new Properties();
try { try {
mappings.load(getServletContext().getResourceAsStream(pPropertiesFile)); mappings.load(getServletContext().getResourceAsStream(pPropertiesFile));
List patterns = new ArrayList(); List patterns = new ArrayList();
List accepts = new ArrayList(); List accepts = new ArrayList();
for (Iterator iterator = mappings.keySet().iterator(); iterator.hasNext();) { for (Iterator iterator = mappings.keySet().iterator(); iterator.hasNext();) {
String agent = (String) iterator.next(); String agent = (String) iterator.next();
if (agent.endsWith(".accept")) { if (agent.endsWith(".accept")) {
continue; continue;
} }
try { try {
patterns.add(Pattern.compile((String) mappings.get(agent))); patterns.add(Pattern.compile((String) mappings.get(agent)));
// TODO: Consider preparsing ACCEPT header?? // TODO: Consider preparsing ACCEPT header??
accepts.add(mappings.get(agent + ".accept")); accepts.add(mappings.get(agent + ".accept"));
} }
catch (PatternSyntaxException e) { catch (PatternSyntaxException e) {
log("Could not parse User-Agent identification for " + agent, e); log("Could not parse User-Agent identification for " + agent, e);
} }
mKnownAgentPatterns = (Pattern[]) patterns.toArray(new Pattern[patterns.size()]); mKnownAgentPatterns = (Pattern[]) patterns.toArray(new Pattern[patterns.size()]);
mKnownAgentAccpets = (String[]) accepts.toArray(new String[accepts.size()]); mKnownAgentAccpets = (String[]) accepts.toArray(new String[accepts.size()]);
} }
} }
catch (IOException e) { catch (IOException e) {
log("Could not read accetp-mappings properties file: " + pPropertiesFile, e); log("Could not read accetp-mappings properties file: " + pPropertiesFile, e);
} }
} }
*/ */
protected void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain) throws IOException, ServletException { protected void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain) throws IOException, ServletException {
// NOTE: super invokes trigger() and image specific doFilter() if needed // NOTE: super invokes trigger() and image specific doFilter() if needed
super.doFilterImpl(pRequest, pResponse, pChain); super.doFilterImpl(pRequest, pResponse, pChain);
if (pResponse instanceof HttpServletResponse) { if (pResponse instanceof HttpServletResponse) {
// Update the Vary HTTP header field // Update the Vary HTTP header field
((HttpServletResponse) pResponse).addHeader(HTTP_HEADER_VARY, HTTP_HEADER_ACCEPT); ((HttpServletResponse) pResponse).addHeader(HTTP_HEADER_VARY, HTTP_HEADER_ACCEPT);
//((HttpServletResponse) pResponse).addHeader(HTTP_HEADER_VARY, HTTP_HEADER_USER_AGENT); //((HttpServletResponse) pResponse).addHeader(HTTP_HEADER_VARY, HTTP_HEADER_USER_AGENT);
} }
} }
/** /**
* Makes sure the filter triggers for unknown file formats. * Makes sure the filter triggers for unknown file formats.
* *
* @param pRequest the request * @param pRequest the request
* @return {@code true} if the filter should execute, {@code false} * @return {@code true} if the filter should execute, {@code false}
* otherwise * otherwise
*/ */
protected boolean trigger(ServletRequest pRequest) { protected boolean trigger(ServletRequest pRequest) {
boolean trigger = false; boolean trigger = false;
if (pRequest instanceof HttpServletRequest) { if (pRequest instanceof HttpServletRequest) {
HttpServletRequest request = (HttpServletRequest) pRequest; HttpServletRequest request = (HttpServletRequest) pRequest;
String accept = getAcceptedFormats(request); String accept = getAcceptedFormats(request);
String originalFormat = getServletContext().getMimeType(request.getRequestURI()); String originalFormat = getServletContext().getMimeType(request.getRequestURI());
//System.out.println("Accept: " + accept); //System.out.println("Accept: " + accept);
//System.out.println("Original format: " + originalFormat); //System.out.println("Original format: " + originalFormat);
// Only override original format if it is not accpeted by the client // Only override original format if it is not accpeted by the client
// Note: Only explicit matches are okay, */* or image/* is not. // Note: Only explicit matches are okay, */* or image/* is not.
if (!StringUtil.contains(accept, originalFormat)) { if (!StringUtil.contains(accept, originalFormat)) {
trigger = true; trigger = true;
} }
} }
// Call super, to allow content negotiation even though format is supported // Call super, to allow content negotiation even though format is supported
return trigger || super.trigger(pRequest); return trigger || super.trigger(pRequest);
} }
private String getAcceptedFormats(HttpServletRequest pRequest) { private String getAcceptedFormats(HttpServletRequest pRequest) {
return pRequest.getHeader(HTTP_HEADER_ACCEPT); return pRequest.getHeader(HTTP_HEADER_ACCEPT);
} }
/* /*
private String getAcceptedFormats(HttpServletRequest pRequest) { private String getAcceptedFormats(HttpServletRequest pRequest) {
String accept = pRequest.getHeader(HTTP_HEADER_ACCEPT); String accept = pRequest.getHeader(HTTP_HEADER_ACCEPT);
// Check if User-Agent is in list of known agents // Check if User-Agent is in list of known agents
if (mKnownAgentPatterns != null) { if (mKnownAgentPatterns != null) {
String agent = pRequest.getHeader(HTTP_HEADER_USER_AGENT); String agent = pRequest.getHeader(HTTP_HEADER_USER_AGENT);
for (int i = 0; i < mKnownAgentPatterns.length; i++) { for (int i = 0; i < mKnownAgentPatterns.length; i++) {
Pattern pattern = mKnownAgentPatterns[i]; Pattern pattern = mKnownAgentPatterns[i];
if (pattern.matcher(agent).matches()) { if (pattern.matcher(agent).matches()) {
// Merge known with real accpet, in case plugins add extra capabilities // Merge known with real accpet, in case plugins add extra capabilities
accept = mergeAccept(mKnownAgentAccpets[i], accept); accept = mergeAccept(mKnownAgentAccpets[i], accept);
System.out.println("--> User-Agent: " + agent + " accepts: " + accept); System.out.println("--> User-Agent: " + agent + " accepts: " + accept);
return accept; return accept;
} }
} }
} }
System.out.println("No agent match, defaulting to Accept header: " + accept); System.out.println("No agent match, defaulting to Accept header: " + accept);
return accept; return accept;
} }
private String mergeAccept(String pKnown, String pAccept) { private String mergeAccept(String pKnown, String pAccept) {
// TODO: Make sure there are no duplicates... // TODO: Make sure there are no duplicates...
return pKnown + ", " + pAccept; return pKnown + ", " + pAccept;
} }
*/ */
protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) throws IOException { protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) throws IOException {
if (pRequest instanceof HttpServletRequest) { if (pRequest instanceof HttpServletRequest) {
HttpServletRequest request = (HttpServletRequest) pRequest; HttpServletRequest request = (HttpServletRequest) pRequest;
Map<String, Float> formatQuality = getFormatQualityMapping(); Map<String, Float> formatQuality = getFormatQualityMapping();
// TODO: Consider adding original format, and use as fallback in worst case? // TODO: Consider adding original format, and use as fallback in worst case?
// TODO: Original format should have some boost, to avoid unneccesary convertsion? // TODO: Original format should have some boost, to avoid unneccesary convertsion?
// Update source quality settings from image properties // Update source quality settings from image properties
adjustQualityFromImage(formatQuality, pImage); adjustQualityFromImage(formatQuality, pImage);
//System.out.println("Source quality mapping: " + formatQuality); //System.out.println("Source quality mapping: " + formatQuality);
adjustQualityFromAccept(formatQuality, request); adjustQualityFromAccept(formatQuality, request);
//System.out.println("Final media scores: " + formatQuality); //System.out.println("Final media scores: " + formatQuality);
// Find the formats with the highest quality factor, and use the first (predictable) // Find the formats with the highest quality factor, and use the first (predictable)
String acceptable = findBestFormat(formatQuality); String acceptable = findBestFormat(formatQuality);
//System.out.println("Acceptable: " + acceptable); //System.out.println("Acceptable: " + acceptable);
// Send HTTP 406 Not Acceptable // Send HTTP 406 Not Acceptable
if (acceptable == null) { if (acceptable == null) {
if (pResponse instanceof HttpServletResponse) { if (pResponse instanceof HttpServletResponse) {
((HttpServletResponse) pResponse).sendError(HttpURLConnection.HTTP_NOT_ACCEPTABLE); ((HttpServletResponse) pResponse).sendError(HttpURLConnection.HTTP_NOT_ACCEPTABLE);
} }
return null; return null;
} }
else { else {
// TODO: Only if the format was changed! // TODO: Only if the format was changed!
// Let other filters/caches/proxies know we changed the image // Let other filters/caches/proxies know we changed the image
} }
// Set format // Set format
pResponse.setOutputContentType(acceptable); pResponse.setOutputContentType(acceptable);
//System.out.println("Set format: " + acceptable); //System.out.println("Set format: " + acceptable);
} }
return pImage; return pImage;
} }
private Map<String, Float> getFormatQualityMapping() { private Map<String, Float> getFormatQualityMapping() {
synchronized(lock) { synchronized(lock) {
if (formatQuality == null) { if (formatQuality == null) {
formatQuality = new HashMap<String, Float>(); formatQuality = new HashMap<String, Float>();
// Use ImageIO to find formats we can actually write // Use ImageIO to find formats we can actually write
String[] formats = ImageIO.getWriterMIMETypes(); String[] formats = ImageIO.getWriterMIMETypes();
// All known formats qs are initially 1.0 // All known formats qs are initially 1.0
// Others should be 0.1 or something like that... // Others should be 0.1 or something like that...
for (String format : formats) { for (String format : formats) {
formatQuality.put(format, getKnownFormatQuality(format)); formatQuality.put(format, getKnownFormatQuality(format));
} }
} }
} }
//noinspection unchecked //noinspection unchecked
return (Map<String, Float>) formatQuality.clone(); return (Map<String, Float>) formatQuality.clone();
} }
/** /**
* Finds the best available format. * Finds the best available format.
* *
* @param pFormatQuality the format to quality mapping * @param pFormatQuality the format to quality mapping
* @return the mime type of the best available format * @return the mime type of the best available format
*/ */
private static String findBestFormat(Map<String, Float> pFormatQuality) { private static String findBestFormat(Map<String, Float> pFormatQuality) {
String acceptable = null; String acceptable = null;
float acceptQuality = 0.0f; float acceptQuality = 0.0f;
for (Map.Entry<String, Float> entry : pFormatQuality.entrySet()) { for (Map.Entry<String, Float> entry : pFormatQuality.entrySet()) {
float qValue = entry.getValue(); float qValue = entry.getValue();
if (qValue > acceptQuality) { if (qValue > acceptQuality) {
acceptQuality = qValue; acceptQuality = qValue;
acceptable = entry.getKey(); acceptable = entry.getKey();
} }
} }
//System.out.println("Accepted format: " + acceptable); //System.out.println("Accepted format: " + acceptable);
//System.out.println("Accepted quality: " + acceptQuality); //System.out.println("Accepted quality: " + acceptQuality);
return acceptable; return acceptable;
} }
/** /**
* Adjust quality from HTTP Accept header * Adjust quality from HTTP Accept header
* *
* @param pFormatQuality the format to quality mapping * @param pFormatQuality the format to quality mapping
* @param pRequest the request * @param pRequest the request
*/ */
private void adjustQualityFromAccept(Map<String, Float> pFormatQuality, HttpServletRequest pRequest) { private void adjustQualityFromAccept(Map<String, Float> pFormatQuality, HttpServletRequest pRequest) {
// Multiply all q factors with qs factors // Multiply all q factors with qs factors
// No q=.. should be interpreted as q=1.0 // No q=.. should be interpreted as q=1.0
// Apache does some extras; if both explicit types and wildcards // Apache does some extras; if both explicit types and wildcards
// (without qaulity factor) are present, */* is interpreted as // (without qaulity factor) are present, */* is interpreted as
// */*;q=0.01 and image/* is interpreted as image/*;q=0.02 // */*;q=0.01 and image/* is interpreted as image/*;q=0.02
// See: http://httpd.apache.org/docs-2.0/content-negotiation.html // See: http://httpd.apache.org/docs-2.0/content-negotiation.html
String accept = getAcceptedFormats(pRequest); String accept = getAcceptedFormats(pRequest);
//System.out.println("Accept: " + accept); //System.out.println("Accept: " + accept);
float anyImageFactor = getQualityFactor(accept, MIME_TYPE_IMAGE_ANY); float anyImageFactor = getQualityFactor(accept, MIME_TYPE_IMAGE_ANY);
anyImageFactor = (anyImageFactor == 1) ? 0.02f : anyImageFactor; anyImageFactor = (anyImageFactor == 1) ? 0.02f : anyImageFactor;
float anyFactor = getQualityFactor(accept, MIME_TYPE_ANY); float anyFactor = getQualityFactor(accept, MIME_TYPE_ANY);
anyFactor = (anyFactor == 1) ? 0.01f : anyFactor; anyFactor = (anyFactor == 1) ? 0.01f : anyFactor;
for (String format : pFormatQuality.keySet()) { for (String format : pFormatQuality.keySet()) {
//System.out.println("Trying format: " + format); //System.out.println("Trying format: " + format);
String formatMIME = MIME_TYPE_IMAGE_PREFIX + format; String formatMIME = MIME_TYPE_IMAGE_PREFIX + format;
float qFactor = getQualityFactor(accept, formatMIME); float qFactor = getQualityFactor(accept, formatMIME);
qFactor = (qFactor == 0f) ? Math.max(anyFactor, anyImageFactor) : qFactor; qFactor = (qFactor == 0f) ? Math.max(anyFactor, anyImageFactor) : qFactor;
adjustQuality(pFormatQuality, format, qFactor); adjustQuality(pFormatQuality, format, qFactor);
} }
} }
/** /**
* *
* @param pAccept the accpet header value * @param pAccept the accpet header value
* @param pContentType the content type to get the quality factor for * @param pContentType the content type to get the quality factor for
* @return the q factor of the given format, according to the accept header * @return the q factor of the given format, according to the accept header
*/ */
private static float getQualityFactor(String pAccept, String pContentType) { private static float getQualityFactor(String pAccept, String pContentType) {
float qFactor = 0; float qFactor = 0;
int foundIndex = pAccept.indexOf(pContentType); int foundIndex = pAccept.indexOf(pContentType);
if (foundIndex >= 0) { if (foundIndex >= 0) {
int startQIndex = foundIndex + pContentType.length(); int startQIndex = foundIndex + pContentType.length();
if (startQIndex < pAccept.length() && pAccept.charAt(startQIndex) == ';') { if (startQIndex < pAccept.length() && pAccept.charAt(startQIndex) == ';') {
while (startQIndex < pAccept.length() && pAccept.charAt(startQIndex++) == ' ') { while (startQIndex < pAccept.length() && pAccept.charAt(startQIndex++) == ' ') {
// Skip over whitespace // Skip over whitespace
} }
if (pAccept.charAt(startQIndex++) == 'q' && pAccept.charAt(startQIndex++) == '=') { if (pAccept.charAt(startQIndex++) == 'q' && pAccept.charAt(startQIndex++) == '=') {
int endQIndex = pAccept.indexOf(',', startQIndex); int endQIndex = pAccept.indexOf(',', startQIndex);
if (endQIndex < 0) { if (endQIndex < 0) {
endQIndex = pAccept.length(); endQIndex = pAccept.length();
} }
try { try {
qFactor = Float.parseFloat(pAccept.substring(startQIndex, endQIndex)); qFactor = Float.parseFloat(pAccept.substring(startQIndex, endQIndex));
//System.out.println("Found qFactor " + qFactor); //System.out.println("Found qFactor " + qFactor);
} }
catch (NumberFormatException e) { catch (NumberFormatException e) {
// TODO: Determine what to do here.. Maybe use a very low value? // 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.. // Ahem.. The specs don't say anything about how to interpret a wrong q factor..
//System.out.println("Unparseable q setting; " + e.getMessage()); //System.out.println("Unparseable q setting; " + e.getMessage());
} }
} }
// TODO: Determine what to do here.. Maybe use a very low value? // TODO: Determine what to do here.. Maybe use a very low value?
// Unparseable q value, use 0 // Unparseable q value, use 0
} }
else { else {
// Else, assume quality is 1.0 // Else, assume quality is 1.0
qFactor = 1; qFactor = 1;
} }
} }
return qFactor; return qFactor;
} }
/** /**
* Adjusts source quality settings from image properties. * Adjusts source quality settings from image properties.
* *
* @param pFormatQuality the format to quality mapping * @param pFormatQuality the format to quality mapping
* @param pImage the image * @param pImage the image
*/ */
private static void adjustQualityFromImage(Map<String, Float> pFormatQuality, BufferedImage pImage) { private static void adjustQualityFromImage(Map<String, Float> pFormatQuality, BufferedImage pImage) {
// NOTE: The values are all made-up. May need tuning. // NOTE: The values are all made-up. May need tuning.
// If pImage.getColorModel() instanceof IndexColorModel // If pImage.getColorModel() instanceof IndexColorModel
// JPEG qs*=0.6 // JPEG qs*=0.6
// If NOT binary or 2 color index // If NOT binary or 2 color index
// WBMP qs*=0.5 // WBMP qs*=0.5
// Else // Else
// GIF qs*=0.02 // GIF qs*=0.02
// PNG qs*=0.9 // JPEG is smaller/faster // PNG qs*=0.9 // JPEG is smaller/faster
if (pImage.getColorModel() instanceof IndexColorModel) { if (pImage.getColorModel() instanceof IndexColorModel) {
adjustQuality(pFormatQuality, FORMAT_JPEG, 0.6f); adjustQuality(pFormatQuality, FORMAT_JPEG, 0.6f);
if (pImage.getType() != BufferedImage.TYPE_BYTE_BINARY || ((IndexColorModel) pImage.getColorModel()).getMapSize() != 2) { if (pImage.getType() != BufferedImage.TYPE_BYTE_BINARY || ((IndexColorModel) pImage.getColorModel()).getMapSize() != 2) {
adjustQuality(pFormatQuality, FORMAT_WBMP, 0.5f); adjustQuality(pFormatQuality, FORMAT_WBMP, 0.5f);
} }
} }
else { else {
adjustQuality(pFormatQuality, FORMAT_GIF, 0.01f); adjustQuality(pFormatQuality, FORMAT_GIF, 0.01f);
adjustQuality(pFormatQuality, FORMAT_PNG, 0.99f); // JPEG is smaller/faster adjustQuality(pFormatQuality, FORMAT_PNG, 0.99f); // JPEG is smaller/faster
} }
// If pImage.getColorModel().hasTransparentPixels() // If pImage.getColorModel().hasTransparentPixels()
// JPEG qs*=0.05 // JPEG qs*=0.05
// WBMP qs*=0.05 // WBMP qs*=0.05
// If NOT transparency == BITMASK // If NOT transparency == BITMASK
// GIF qs*=0.8 // GIF qs*=0.8
if (ImageUtil.hasTransparentPixels(pImage, true)) { if (ImageUtil.hasTransparentPixels(pImage, true)) {
adjustQuality(pFormatQuality, FORMAT_JPEG, 0.009f); adjustQuality(pFormatQuality, FORMAT_JPEG, 0.009f);
adjustQuality(pFormatQuality, FORMAT_WBMP, 0.009f); adjustQuality(pFormatQuality, FORMAT_WBMP, 0.009f);
if (pImage.getColorModel().getTransparency() != Transparency.BITMASK) { if (pImage.getColorModel().getTransparency() != Transparency.BITMASK) {
adjustQuality(pFormatQuality, FORMAT_GIF, 0.8f); adjustQuality(pFormatQuality, FORMAT_GIF, 0.8f);
} }
} }
} }
/** /**
* Updates the quality in the map. * Updates the quality in the map.
* *
* @param pFormatQuality Map<String,Float> * @param pFormatQuality Map<String,Float>
* @param pFormat the format * @param pFormat the format
* @param pFactor the quality factor * @param pFactor the quality factor
*/ */
private static void adjustQuality(Map<String, Float> pFormatQuality, String pFormat, float pFactor) { private static void adjustQuality(Map<String, Float> pFormatQuality, String pFormat, float pFactor) {
Float oldValue = pFormatQuality.get(pFormat); Float oldValue = pFormatQuality.get(pFormat);
if (oldValue != null) { if (oldValue != null) {
pFormatQuality.put(pFormat, oldValue * pFactor); pFormatQuality.put(pFormat, oldValue * pFactor);
//System.out.println("New vallue after multiplying with " + pFactor + " is " + pFormatQuality.get(pFormat)); //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 * Gets the initial quality if this is a known format, otherwise 0.1
* *
* @param pFormat the format name * @param pFormat the format name
* @return the q factor of the given format * @return the q factor of the given format
*/ */
private float getKnownFormatQuality(String pFormat) { private float getKnownFormatQuality(String pFormat) {
for (int i = 0; i < sKnownFormats.length; i++) { for (int i = 0; i < sKnownFormats.length; i++) {
if (pFormat.equals(sKnownFormats[i])) { if (pFormat.equals(sKnownFormats[i])) {
return knownFormatQuality[i]; return knownFormatQuality[i];
} }
} }
return 0.1f; return 0.1f;
} }
} }

View File

@@ -1,232 +1,232 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.servlet.image; package com.twelvemonkeys.servlet.image;
import com.twelvemonkeys.servlet.ServletUtil; import com.twelvemonkeys.servlet.ServletUtil;
import javax.servlet.ServletRequest; import javax.servlet.ServletRequest;
import java.awt.*; import java.awt.*;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage; import java.awt.image.RenderedImage;
/** /**
* This Servlet is able to render a cropped part of an image. * This Servlet is able to render a cropped part of an image.
* *
* <P><HR><P> * <P><HR><P>
* *
* <A name="parameters"></A><STRONG>Parameters:</STRONG><BR> * <A name="parameters"></A><STRONG>Parameters:</STRONG><BR>
* <DL> * <DL>
* <DT>{@code cropX}</DT> * <DT>{@code cropX}</DT>
* <DD>integer, the new left edge of the image. * <DD>integer, the new left edge of the image.
* <DT>{@code cropY}</DT> * <DT>{@code cropY}</DT>
* <DD>integer, the new top of the image. * <DD>integer, the new top of the image.
* <DT>{@code cropWidth}</DT> * <DT>{@code cropWidth}</DT>
* <DD>integer, the new width of the image. * <DD>integer, the new width of the image.
* <DT>{@code cropHeight}</DT> * <DT>{@code cropHeight}</DT>
* <DD>integer, the new height of the image. * <DD>integer, the new height of the image.
* <DT>{@code cropUniform}</DT> * <DT>{@code cropUniform}</DT>
* <DD>boolean, wether or not uniform scalnig should be used. Default is * <DD>boolean, wether or not uniform scalnig should be used. Default is
* {@code true}. * {@code true}.
* <DT>{@code cropUnits}</DT> * <DT>{@code cropUnits}</DT>
* <DD>string, one of {@code PIXELS}, {@code PERCENT}. * <DD>string, one of {@code PIXELS}, {@code PERCENT}.
* {@code PIXELS} is default. * {@code PIXELS} is default.
* *
* <!-- inherited from ScaleImage below: --> * <!-- inherited from ScaleImage below: -->
* *
* <DT>{@code image}</DT> * <DT>{@code image}</DT>
* <DD>string, the URL of the image to scale. * <DD>string, the URL of the image to scale.
* *
* <DT>{@code scaleX}</DT> * <DT>{@code scaleX}</DT>
* <DD>integer, the new width of the image. * <DD>integer, the new width of the image.
* *
* <DT>{@code scaleY}</DT> * <DT>{@code scaleY}</DT>
* <DD>integer, the new height of the image. * <DD>integer, the new height of the image.
* *
* <DT>{@code scaleUniform}</DT> * <DT>{@code scaleUniform}</DT>
* <DD>boolean, wether or not uniform scalnig should be used. Default is * <DD>boolean, wether or not uniform scalnig should be used. Default is
* {@code true}. * {@code true}.
* *
* <DT>{@code scaleUnits}</DT> * <DT>{@code scaleUnits}</DT>
* <DD>string, one of {@code PIXELS}, {@code PERCENT}. * <DD>string, one of {@code PIXELS}, {@code PERCENT}.
* {@code PIXELS} is default. * {@code PIXELS} is default.
* *
* <DT>{@code scaleQuality}</DT> * <DT>{@code scaleQuality}</DT>
* <DD>string, one of {@code SCALE_SMOOTH}, {@code SCALE_FAST}, * <DD>string, one of {@code SCALE_SMOOTH}, {@code SCALE_FAST},
* {@code SCALE_REPLICATE}, {@code SCALE_AREA_AVERAGING}. * {@code SCALE_REPLICATE}, {@code SCALE_AREA_AVERAGING}.
* {@code SCALE_DEFAULT} is default. * {@code SCALE_DEFAULT} is default.
* *
* </DL> * </DL>
* *
* @example * @example
* &lt;IMG src="/crop/test.jpg?image=http://www.iconmedialab.com/images/random/home_image_12.jpg&cropWidth=500&cropUniform=true"&gt; * &lt;IMG src="/crop/test.jpg?image=http://www.iconmedialab.com/images/random/home_image_12.jpg&cropWidth=500&cropUniform=true"&gt;
* *
* @example * @example
* &lt;IMG src="/crop/test.png?cache=false&image=http://www.iconmedialab.com/images/random/home_image_12.jpg&cropWidth=50&cropUnits=PERCENT"&gt; * &lt;IMG src="/crop/test.png?cache=false&image=http://www.iconmedialab.com/images/random/home_image_12.jpg&cropWidth=50&cropUnits=PERCENT"&gt;
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $ * @author last modified by $Author: haku $
* @version $Id: CropFilter.java#1 $ * @version $Id: CropFilter.java#1 $
*/ */
public class CropFilter extends ScaleFilter { public class CropFilter extends ScaleFilter {
/** {@code cropX}*/ /** {@code cropX}*/
protected final static String PARAM_CROP_X = "cropX"; protected final static String PARAM_CROP_X = "cropX";
/** {@code cropY}*/ /** {@code cropY}*/
protected final static String PARAM_CROP_Y = "cropY"; protected final static String PARAM_CROP_Y = "cropY";
/** {@code cropWidth}*/ /** {@code cropWidth}*/
protected final static String PARAM_CROP_WIDTH = "cropWidth"; protected final static String PARAM_CROP_WIDTH = "cropWidth";
/** {@code cropHeight}*/ /** {@code cropHeight}*/
protected final static String PARAM_CROP_HEIGHT = "cropHeight"; protected final static String PARAM_CROP_HEIGHT = "cropHeight";
/** {@code cropUniform}*/ /** {@code cropUniform}*/
protected final static String PARAM_CROP_UNIFORM = "cropUniform"; protected final static String PARAM_CROP_UNIFORM = "cropUniform";
/** {@code cropUnits}*/ /** {@code cropUnits}*/
protected final static String PARAM_CROP_UNITS = "cropUnits"; protected final static String PARAM_CROP_UNITS = "cropUnits";
/** /**
* Reads the image from the requested URL, scales it, crops it, and returns * Reads the image from the requested URL, scales it, crops it, and returns
* it in the * it in the
* Servlet stream. See above for details on parameters. * Servlet stream. See above for details on parameters.
*/ */
protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) { protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) {
// Get crop coordinates // Get crop coordinates
int x = ServletUtil.getIntParameter(pRequest, PARAM_CROP_X, -1); int x = ServletUtil.getIntParameter(pRequest, PARAM_CROP_X, -1);
int y = ServletUtil.getIntParameter(pRequest, PARAM_CROP_Y, -1); int y = ServletUtil.getIntParameter(pRequest, PARAM_CROP_Y, -1);
int width = ServletUtil.getIntParameter(pRequest, PARAM_CROP_WIDTH, -1); int width = ServletUtil.getIntParameter(pRequest, PARAM_CROP_WIDTH, -1);
int height = ServletUtil.getIntParameter(pRequest, PARAM_CROP_HEIGHT, -1); int height = ServletUtil.getIntParameter(pRequest, PARAM_CROP_HEIGHT, -1);
boolean uniform = boolean uniform =
ServletUtil.getBooleanParameter(pRequest, PARAM_CROP_UNIFORM, false); ServletUtil.getBooleanParameter(pRequest, PARAM_CROP_UNIFORM, false);
int units = getUnits(ServletUtil.getParameter(pRequest, PARAM_CROP_UNITS, null)); int units = getUnits(ServletUtil.getParameter(pRequest, PARAM_CROP_UNITS, null));
// Get crop bounds // Get crop bounds
Rectangle bounds = Rectangle bounds =
getBounds(x, y, width, height, units, uniform, pImage); getBounds(x, y, width, height, units, uniform, pImage);
// Return cropped version // Return cropped version
return pImage.getSubimage((int) bounds.getX(), (int) bounds.getY(), return pImage.getSubimage((int) bounds.getX(), (int) bounds.getY(),
(int) bounds.getWidth(), (int) bounds.getWidth(),
(int) bounds.getHeight()); (int) bounds.getHeight());
//return scaled.getSubimage(x, y, width, height); //return scaled.getSubimage(x, y, width, height);
} }
protected Rectangle getBounds(int pX, int pY, int pWidth, int pHeight, protected Rectangle getBounds(int pX, int pY, int pWidth, int pHeight,
int pUnits, boolean pUniform, int pUnits, boolean pUniform,
BufferedImage pImg) { BufferedImage pImg) {
// Algoritm: // Algoritm:
// Try to get x and y (default 0,0). // Try to get x and y (default 0,0).
// Try to get width and height (default width-x, height-y) // Try to get width and height (default width-x, height-y)
// //
// If percent, get ratio // If percent, get ratio
// //
// If uniform // If uniform
// //
int oldWidth = pImg.getWidth(); int oldWidth = pImg.getWidth();
int oldHeight = pImg.getHeight(); int oldHeight = pImg.getHeight();
float ratio; float ratio;
if (pUnits == UNITS_PERCENT) { if (pUnits == UNITS_PERCENT) {
if (pWidth >= 0 && pHeight >= 0) { if (pWidth >= 0 && pHeight >= 0) {
// Non-uniform // Non-uniform
pWidth = (int) ((float) oldWidth * (float) pWidth / 100f); pWidth = (int) ((float) oldWidth * (float) pWidth / 100f);
pHeight = (int) ((float) oldHeight * (float) pHeight / 100f); pHeight = (int) ((float) oldHeight * (float) pHeight / 100f);
} }
else if (pWidth >= 0) { else if (pWidth >= 0) {
// Find ratio from pWidth // Find ratio from pWidth
ratio = (float) pWidth / 100f; ratio = (float) pWidth / 100f;
pWidth = (int) ((float) oldWidth * ratio); pWidth = (int) ((float) oldWidth * ratio);
pHeight = (int) ((float) oldHeight * ratio); pHeight = (int) ((float) oldHeight * ratio);
} }
else if (pHeight >= 0) { else if (pHeight >= 0) {
// Find ratio from pHeight // Find ratio from pHeight
ratio = (float) pHeight / 100f; ratio = (float) pHeight / 100f;
pWidth = (int) ((float) oldWidth * ratio); pWidth = (int) ((float) oldWidth * ratio);
pHeight = (int) ((float) oldHeight * ratio); pHeight = (int) ((float) oldHeight * ratio);
} }
// Else: No crop // Else: No crop
} }
//else if (UNITS_PIXELS.equalsIgnoreCase(pUnits)) { //else if (UNITS_PIXELS.equalsIgnoreCase(pUnits)) {
else if (pUnits == UNITS_PIXELS) { else if (pUnits == UNITS_PIXELS) {
// Uniform // Uniform
if (pUniform) { if (pUniform) {
if (pWidth >= 0 && pHeight >= 0) { if (pWidth >= 0 && pHeight >= 0) {
// Compute both ratios // Compute both ratios
ratio = (float) pWidth / (float) oldWidth; ratio = (float) pWidth / (float) oldWidth;
float heightRatio = (float) pHeight / (float) oldHeight; float heightRatio = (float) pHeight / (float) oldHeight;
// Find the largest ratio, and use that for both // Find the largest ratio, and use that for both
if (heightRatio < ratio) { if (heightRatio < ratio) {
ratio = heightRatio; ratio = heightRatio;
pWidth = (int) ((float) oldWidth * ratio); pWidth = (int) ((float) oldWidth * ratio);
} }
else { else {
pHeight = (int) ((float) oldHeight * ratio); pHeight = (int) ((float) oldHeight * ratio);
} }
} }
else if (pWidth >= 0) { else if (pWidth >= 0) {
// Find ratio from pWidth // Find ratio from pWidth
ratio = (float) pWidth / (float) oldWidth; ratio = (float) pWidth / (float) oldWidth;
pHeight = (int) ((float) oldHeight * ratio); pHeight = (int) ((float) oldHeight * ratio);
} }
else if (pHeight >= 0) { else if (pHeight >= 0) {
// Find ratio from pHeight // Find ratio from pHeight
ratio = (float) pHeight / (float) oldHeight; ratio = (float) pHeight / (float) oldHeight;
pWidth = (int) ((float) oldWidth * ratio); pWidth = (int) ((float) oldWidth * ratio);
} }
// Else: No crop // Else: No crop
} }
} }
// Else: No crop // Else: No crop
// Not specified, or outside bounds: Use original dimensions // Not specified, or outside bounds: Use original dimensions
if (pWidth < 0 || (pX < 0 && pWidth > oldWidth) if (pWidth < 0 || (pX < 0 && pWidth > oldWidth)
|| (pX >= 0 && (pX + pWidth) > oldWidth)) { || (pX >= 0 && (pX + pWidth) > oldWidth)) {
pWidth = (pX >= 0 ? oldWidth - pX : oldWidth); pWidth = (pX >= 0 ? oldWidth - pX : oldWidth);
} }
if (pHeight < 0 || (pY < 0 && pHeight > oldHeight) if (pHeight < 0 || (pY < 0 && pHeight > oldHeight)
|| (pY >= 0 && (pY + pHeight) > oldHeight)) { || (pY >= 0 && (pY + pHeight) > oldHeight)) {
pHeight = (pY >= 0 ? oldHeight - pY : oldHeight); pHeight = (pY >= 0 ? oldHeight - pY : oldHeight);
} }
// Center // Center
if (pX < 0) { if (pX < 0) {
pX = (pImg.getWidth() - pWidth) / 2; pX = (pImg.getWidth() - pWidth) / 2;
} }
if (pY < 0) { if (pY < 0) {
pY = (pImg.getHeight() - pHeight) / 2; pY = (pImg.getHeight() - pHeight) / 2;
} }
//System.out.println("x: " + pX + " y: " + pY //System.out.println("x: " + pX + " y: " + pY
// + " w: " + pWidth + " h " + pHeight); // + " w: " + pWidth + " h " + pHeight);
return new Rectangle(pX, pY, pWidth, pHeight); return new Rectangle(pX, pY, pWidth, pHeight);
} }
} }

View File

@@ -1,217 +1,217 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.servlet.image; package com.twelvemonkeys.servlet.image;
import com.twelvemonkeys.image.ImageUtil; import com.twelvemonkeys.image.ImageUtil;
import com.twelvemonkeys.lang.StringUtil; import com.twelvemonkeys.lang.StringUtil;
import com.twelvemonkeys.servlet.GenericFilter; import com.twelvemonkeys.servlet.GenericFilter;
import javax.servlet.*; import javax.servlet.*;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage; import java.awt.image.RenderedImage;
import java.io.IOException; import java.io.IOException;
/** /**
* Abstract base class for image filters. Automatically decoding and encoding of * Abstract base class for image filters. Automatically decoding and encoding of
* the image is handled in the {@code doFilterImpl} method. * the image is handled in the {@code doFilterImpl} method.
* *
* @see #doFilter(java.awt.image.BufferedImage,javax.servlet.ServletRequest,ImageServletResponse) * @see #doFilter(java.awt.image.BufferedImage,javax.servlet.ServletRequest,ImageServletResponse)
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: ImageFilter.java#2 $ * @version $Id: ImageFilter.java#2 $
* *
*/ */
public abstract class ImageFilter extends GenericFilter { public abstract class ImageFilter extends GenericFilter {
// TODO: Take the design back to the drawing board (see ImageServletResponseImpl) // TODO: Take the design back to the drawing board (see ImageServletResponseImpl)
// - Allow multiple filters to set size attribute // - Allow multiple filters to set size attribute
// - Allow a later filter to reset, to get pass-through given certain criteria... // - 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) // - 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; protected String[] triggerParams = null;
/** /**
* The {@code doFilterImpl} method is called once, or each time a * The {@code doFilterImpl} method is called once, or each time a
* request/response pair is passed through the chain, depending on the * request/response pair is passed through the chain, depending on the
* {@link #oncePerRequest} member variable. * {@link #oncePerRequest} member variable.
* *
* @see #oncePerRequest * @see #oncePerRequest
* @see com.twelvemonkeys.servlet.GenericFilter#doFilterImpl(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) doFilter * @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 * @see Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) Filter.doFilter
* *
* @param pRequest the servlet request * @param pRequest the servlet request
* @param pResponse the servlet response * @param pResponse the servlet response
* @param pChain the filter chain * @param pChain the filter chain
* *
* @throws IOException * @throws IOException
* @throws ServletException * @throws ServletException
*/ */
protected void doFilterImpl(final ServletRequest pRequest, final ServletResponse pResponse, final FilterChain pChain) protected void doFilterImpl(final ServletRequest pRequest, final ServletResponse pResponse, final FilterChain pChain)
throws IOException, ServletException { throws IOException, ServletException {
//System.out.println("Starting filtering..."); //System.out.println("Starting filtering...");
// Test for trigger params // Test for trigger params
if (!trigger(pRequest)) { if (!trigger(pRequest)) {
//System.out.println("Passing request on to next in chain (skipping " + getFilterName() + ")..."); //System.out.println("Passing request on to next in chain (skipping " + getFilterName() + ")...");
// Pass the request on // Pass the request on
pChain.doFilter(pRequest, pResponse); pChain.doFilter(pRequest, pResponse);
} }
else { else {
// If already wrapped, the image will be encoded later in the chain // 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 // Or, if this is first filter in chain, we must encode when done
boolean encode = !(pResponse instanceof ImageServletResponse); boolean encode = !(pResponse instanceof ImageServletResponse);
// For images, we do post filtering only and need to wrap the response // For images, we do post filtering only and need to wrap the response
ImageServletResponse imageResponse = createImageServletResponse(pRequest, pResponse); ImageServletResponse imageResponse = createImageServletResponse(pRequest, pResponse);
//System.out.println("Passing request on to next in chain..."); //System.out.println("Passing request on to next in chain...");
// Pass the request on // Pass the request on
pChain.doFilter(pRequest, imageResponse); pChain.doFilter(pRequest, imageResponse);
//System.out.println("Post filtering..."); //System.out.println("Post filtering...");
// Get image // Get image
//System.out.println("Getting image from ImageServletResponse..."); //System.out.println("Getting image from ImageServletResponse...");
// Get the image from the wrapped response // Get the image from the wrapped response
RenderedImage image = imageResponse.getImage(); RenderedImage image = imageResponse.getImage();
//System.out.println("Got image: " + image); //System.out.println("Got image: " + image);
// Note: Image will be null if this is a HEAD request, the // Note: Image will be null if this is a HEAD request, the
// If-Modified-Since header is present, or similar. // If-Modified-Since header is present, or similar.
if (image != null) { if (image != null) {
// Do the image filtering // Do the image filtering
//System.out.println("Filtering image (" + getFilterName() + ")..."); //System.out.println("Filtering image (" + getFilterName() + ")...");
image = doFilter(ImageUtil.toBuffered(image), pRequest, imageResponse); image = doFilter(ImageUtil.toBuffered(image), pRequest, imageResponse);
//System.out.println("Done filtering."); //System.out.println("Done filtering.");
//System.out.println("Making image available..."); //System.out.println("Making image available...");
// Make image available to other filters (avoid unnecessary serializing/deserializing) // Make image available to other filters (avoid unnecessary serializing/deserializing)
imageResponse.setImage(image); imageResponse.setImage(image);
//System.out.println("Done."); //System.out.println("Done.");
} }
if (encode) { if (encode) {
//System.out.println("Encoding image..."); //System.out.println("Encoding image...");
// Encode image to original response // Encode image to original response
if (image != null) { if (image != null) {
// TODO: Be smarter than this... // TODO: Be smarter than this...
// TODO: Make sure ETag is same, if image content is the same... // TODO: Make sure ETag is same, if image content is the same...
// Use ETag of original response (or derived from) // Use ETag of original response (or derived from)
// Use last modified of original response? Or keep original resource's, don't set at all? // Use last modified of original response? Or keep original resource's, don't set at all?
// TODO: Why weak ETag? // TODO: Why weak ETag?
String etag = "W/\"" + Integer.toHexString(hashCode()) + "-" + Integer.toHexString(image.hashCode()) + "\""; String etag = "W/\"" + Integer.toHexString(hashCode()) + "-" + Integer.toHexString(image.hashCode()) + "\"";
// TODO: This breaks for wrapped instances, need to either unwrap or test for HttpSR... // TODO: This breaks for wrapped instances, need to either unwrap or test for HttpSR...
((HttpServletResponse) pResponse).setHeader("ETag", etag); ((HttpServletResponse) pResponse).setHeader("ETag", etag);
((HttpServletResponse) pResponse).setDateHeader("Last-Modified", (System.currentTimeMillis() / 1000) * 1000); ((HttpServletResponse) pResponse).setDateHeader("Last-Modified", (System.currentTimeMillis() / 1000) * 1000);
} }
imageResponse.flush(); imageResponse.flush();
//System.out.println("Done encoding."); //System.out.println("Done encoding.");
} }
} }
//System.out.println("Filtering done."); //System.out.println("Filtering done.");
} }
/** /**
* Creates the image servlet response for this response. * Creates the image servlet response for this response.
* *
* @param pResponse the original response * @param pResponse the original response
* @param pRequest the original request * @param pRequest the original request
* @return the new response, or {@code pResponse} if the response is already wrapped * @return the new response, or {@code pResponse} if the response is already wrapped
* *
* @see com.twelvemonkeys.servlet.image.ImageServletResponseWrapper * @see com.twelvemonkeys.servlet.image.ImageServletResponseWrapper
*/ */
private ImageServletResponse createImageServletResponse(final ServletRequest pRequest, final ServletResponse pResponse) { private ImageServletResponse createImageServletResponse(final ServletRequest pRequest, final ServletResponse pResponse) {
if (pResponse instanceof ImageServletResponseImpl) { if (pResponse instanceof ImageServletResponseImpl) {
ImageServletResponseImpl response = (ImageServletResponseImpl) pResponse; ImageServletResponseImpl response = (ImageServletResponseImpl) pResponse;
// response.setRequest(pRequest); // response.setRequest(pRequest);
return response; return response;
} }
return new ImageServletResponseImpl(pRequest, pResponse, getServletContext()); return new ImageServletResponseImpl(pRequest, pResponse, getServletContext());
} }
/** /**
* Tests if the filter should do image filtering/processing. * Tests if the filter should do image filtering/processing.
* <P/> * <P/>
* This default implementation uses {@link #triggerParams} to test if: * This default implementation uses {@link #triggerParams} to test if:
* <dl> * <dl>
* <dt>{@code mTriggerParams == null}</dt> * <dt>{@code mTriggerParams == null}</dt>
* <dd>{@code return true}</dd> * <dd>{@code return true}</dd>
* <dt>{@code mTriggerParams != null}, loop through parameters, and test * <dt>{@code mTriggerParams != null}, loop through parameters, and test
* if {@code pRequest} contains the parameter. If match</dt> * if {@code pRequest} contains the parameter. If match</dt>
* <dd>{@code return true}</dd> * <dd>{@code return true}</dd>
* <dt>Otherwise</dt> * <dt>Otherwise</dt>
* <dd>{@code return false}</dd> * <dd>{@code return false}</dd>
* </dl> * </dl>
* *
* *
* @param pRequest the servlet request * @param pRequest the servlet request
* @return {@code true} if the filter should do image filtering * @return {@code true} if the filter should do image filtering
*/ */
protected boolean trigger(final ServletRequest pRequest) { protected boolean trigger(final ServletRequest pRequest) {
// If triggerParams not set, assume always trigger // If triggerParams not set, assume always trigger
if (triggerParams == null) { if (triggerParams == null) {
return true; return true;
} }
// Trigger only for certain request parameters // Trigger only for certain request parameters
for (String triggerParam : triggerParams) { for (String triggerParam : triggerParams) {
if (pRequest.getParameter(triggerParam) != null) { if (pRequest.getParameter(triggerParam) != null) {
return true; return true;
} }
} }
// Didn't trigger // Didn't trigger
return false; return false;
} }
/** /**
* Sets the trigger parameters. * Sets the trigger parameters.
* The parameter is supposed to be a comma-separated string of parameter * The parameter is supposed to be a comma-separated string of parameter
* names. * names.
* *
* @param pTriggerParams 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? // TODO: Make it an @InitParam, and make sure we may set String[]/Collection<String> as parameter?
public void setTriggerParams(final String pTriggerParams) { public void setTriggerParams(final String pTriggerParams) {
triggerParams = StringUtil.toStringArray(pTriggerParams); triggerParams = StringUtil.toStringArray(pTriggerParams);
} }
/** /**
* Filters the image for this request. * Filters the image for this request.
* *
* @param pImage the image to filter * @param pImage the image to filter
* @param pRequest the servlet request * @param pRequest the servlet request
* @param pResponse the servlet response * @param pResponse the servlet response
* *
* @return the filtered image * @return the filtered image
* @throws java.io.IOException if an I/O error occurs during filtering * @throws java.io.IOException if an I/O error occurs during filtering
*/ */
protected abstract RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) throws IOException; protected abstract RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) throws IOException;
} }

View File

@@ -1,55 +1,55 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.servlet.image; package com.twelvemonkeys.servlet.image;
import javax.servlet.*; import javax.servlet.*;
/** /**
* This exception is a subclass of ServletException, and acts just as a marker * This exception is a subclass of ServletException, and acts just as a marker
* for exceptions thrown by the ImageServlet API. * for exceptions thrown by the ImageServlet API.
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $ * @author last modified by $Author: haku $
* *
* @version $Id: ImageServletException.java#2 $ * @version $Id: ImageServletException.java#2 $
*/ */
public class ImageServletException extends ServletException { public class ImageServletException extends ServletException {
public ImageServletException(String pMessage) { public ImageServletException(String pMessage) {
super(pMessage); super(pMessage);
} }
public ImageServletException(Throwable pThrowable) { public ImageServletException(Throwable pThrowable) {
super(pThrowable); super(pThrowable);
} }
public ImageServletException(String pMessage, Throwable pThrowable) { public ImageServletException(String pMessage, Throwable pThrowable) {
super(pMessage, pThrowable); super(pMessage, pThrowable);
} }
} }

View File

@@ -1,193 +1,193 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.servlet.image; package com.twelvemonkeys.servlet.image;
import javax.servlet.ServletResponse; import javax.servlet.ServletResponse;
import java.io.IOException; import java.io.IOException;
import java.awt.image.RenderedImage; import java.awt.image.RenderedImage;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
/** /**
* ImageServletResponse. * ImageServletResponse.
* <p/> * <p/>
* The request attributes regarding image size and source region (AOI) are used * 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 * in the decoding process, and must be set before the first invocation of
* {@link #getImage()} to have any effect. * {@link #getImage()} to have any effect.
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: ImageServletResponse.java#4 $ * @version $Id: ImageServletResponse.java#4 $
*/ */
public interface ImageServletResponse extends ServletResponse { public interface ImageServletResponse extends ServletResponse {
/** /**
* Request attribute of type {@link java.awt.Dimension} controlling image * Request attribute of type {@link java.awt.Dimension} controlling image
* size. * size.
* If either {@code width} or {@code height} is negative, the size is * If either {@code width} or {@code height} is negative, the size is
* computed, using uniform scaling. * computed, using uniform scaling.
* Else, if {@code SIZE_UNIFORM} is {@code true}, the size will be * Else, if {@code SIZE_UNIFORM} is {@code true}, the size will be
* computed to the largest possible area (with correct aspect ratio) * computed to the largest possible area (with correct aspect ratio)
* fitting inside the target area. * fitting inside the target area.
* Otherwise, the image is scaled to the given size, with no regard to * Otherwise, the image is scaled to the given size, with no regard to
* aspect ratio. * aspect ratio.
* <p/> * <p/>
* Defaults to {@code null} (original image size). * Defaults to {@code null} (original image size).
*/ */
String ATTRIB_SIZE = "com.twelvemonkeys.servlet.image.ImageServletResponse.SIZE"; String ATTRIB_SIZE = "com.twelvemonkeys.servlet.image.ImageServletResponse.SIZE";
/** /**
* Request attribute of type {@link Boolean} controlling image sizing. * Request attribute of type {@link Boolean} controlling image sizing.
* <p/> * <p/>
* Defaults to {@code Boolean.TRUE}. * Defaults to {@code Boolean.TRUE}.
*/ */
String ATTRIB_SIZE_UNIFORM = "com.twelvemonkeys.servlet.image.ImageServletResponse.SIZE_UNIFORM"; String ATTRIB_SIZE_UNIFORM = "com.twelvemonkeys.servlet.image.ImageServletResponse.SIZE_UNIFORM";
/** /**
* Request attribute of type {@link Boolean} controlling image sizing. * Request attribute of type {@link Boolean} controlling image sizing.
* <p/> * <p/>
* Defaults to {@code Boolean.FALSE}. * Defaults to {@code Boolean.FALSE}.
*/ */
String ATTRIB_SIZE_PERCENT = "com.twelvemonkeys.servlet.image.ImageServletResponse.SIZE_PERCENT"; String ATTRIB_SIZE_PERCENT = "com.twelvemonkeys.servlet.image.ImageServletResponse.SIZE_PERCENT";
/** /**
* Request attribute of type {@link java.awt.Rectangle} controlling image * Request attribute of type {@link java.awt.Rectangle} controlling image
* source region (area of interest). * source region (area of interest).
* <p/> * <p/>
* Defaults to {@code null} (the entire image). * Defaults to {@code null} (the entire image).
*/ */
String ATTRIB_AOI = "com.twelvemonkeys.servlet.image.ImageServletResponse.AOI"; String ATTRIB_AOI = "com.twelvemonkeys.servlet.image.ImageServletResponse.AOI";
/** /**
* Request attribute of type {@link Boolean} controlling image AOI. * Request attribute of type {@link Boolean} controlling image AOI.
* <p/> * <p/>
* Defaults to {@code Boolean.FALSE}. * Defaults to {@code Boolean.FALSE}.
*/ */
String ATTRIB_AOI_UNIFORM = "com.twelvemonkeys.servlet.image.ImageServletResponse.AOI_UNIFORM"; String ATTRIB_AOI_UNIFORM = "com.twelvemonkeys.servlet.image.ImageServletResponse.AOI_UNIFORM";
/** /**
* Request attribute of type {@link Boolean} controlling image AOI. * Request attribute of type {@link Boolean} controlling image AOI.
* <p/> * <p/>
* Defaults to {@code Boolean.FALSE}. * Defaults to {@code Boolean.FALSE}.
*/ */
String ATTRIB_AOI_PERCENT = "com.twelvemonkeys.servlet.image.ImageServletResponse.AOI_PERCENT"; String ATTRIB_AOI_PERCENT = "com.twelvemonkeys.servlet.image.ImageServletResponse.AOI_PERCENT";
/** /**
* Request attribute of type {@link java.awt.Color} controlling background * Request attribute of type {@link java.awt.Color} controlling background
* color for any transparent/translucent areas of the image. * color for any transparent/translucent areas of the image.
* <p/> * <p/>
* Defaults to {@code null} (keeps the transparent areas transparent). * Defaults to {@code null} (keeps the transparent areas transparent).
*/ */
String ATTRIB_BG_COLOR = "com.twelvemonkeys.servlet.image.ImageServletResponse.BG_COLOR"; String ATTRIB_BG_COLOR = "com.twelvemonkeys.servlet.image.ImageServletResponse.BG_COLOR";
/** /**
* Request attribute of type {@link Float} controlling image output compression/quality. * Request attribute of type {@link Float} controlling image output compression/quality.
* Used for formats that accepts compression or quality settings, * Used for formats that accepts compression or quality settings,
* like JPEG (quality), PNG (compression only) etc. * like JPEG (quality), PNG (compression only) etc.
* <p/> * <p/>
* Defaults to {@code 0.8f} for JPEG. * Defaults to {@code 0.8f} for JPEG.
*/ */
String ATTRIB_OUTPUT_QUALITY = "com.twelvemonkeys.servlet.image.ImageServletResponse.OUTPUT_QUALITY"; String ATTRIB_OUTPUT_QUALITY = "com.twelvemonkeys.servlet.image.ImageServletResponse.OUTPUT_QUALITY";
/** /**
* Request attribute of type {@link Double} controlling image read * Request attribute of type {@link Double} controlling image read
* subsampling factor. Controls the maximum sample pixels in each direction, * subsampling factor. Controls the maximum sample pixels in each direction,
* that is read per pixel in the output image, if the result will be * that is read per pixel in the output image, if the result will be
* downscaled. * downscaled.
* Larger values will result in better quality, at the expense of higher * Larger values will result in better quality, at the expense of higher
* memory consumption and CPU usage. * memory consumption and CPU usage.
* However, using values above {@code 3.0} will usually not improve image * However, using values above {@code 3.0} will usually not improve image
* quality. * quality.
* Legal values are in the range {@code [1.0 .. positive infinity&gt;}. * Legal values are in the range {@code [1.0 .. positive infinity&gt;}.
* <p/> * <p/>
* Defaults to {@code 2.0}. * Defaults to {@code 2.0}.
*/ */
String ATTRIB_READ_SUBSAMPLING_FACTOR = "com.twelvemonkeys.servlet.image.ImageServletResponse.READ_SUBSAMPLING_FACTOR"; String ATTRIB_READ_SUBSAMPLING_FACTOR = "com.twelvemonkeys.servlet.image.ImageServletResponse.READ_SUBSAMPLING_FACTOR";
/** /**
* Request attribute of type {@link Integer} controlling image resample * Request attribute of type {@link Integer} controlling image resample
* algorithm. * algorithm.
* Legal values are {@link java.awt.Image#SCALE_DEFAULT SCALE_DEFAULT}, * 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_FAST SCALE_FAST} or
* {@link java.awt.Image#SCALE_SMOOTH SCALE_SMOOTH}. * {@link java.awt.Image#SCALE_SMOOTH SCALE_SMOOTH}.
* <p/> * <p/>
* Note: When using a value of {@code SCALE_FAST}, you should also use a * Note: When using a value of {@code SCALE_FAST}, you should also use a
* subsampling factor of {@code 1.0}, for fast read/scale. * subsampling factor of {@code 1.0}, for fast read/scale.
* Otherwise, use a subsampling factor of {@code 2.0} for better quality. * Otherwise, use a subsampling factor of {@code 2.0} for better quality.
* <p/> * <p/>
* Defaults to {@code SCALE_DEFAULT}. * Defaults to {@code SCALE_DEFAULT}.
*/ */
String ATTRIB_IMAGE_RESAMPLE_ALGORITHM = "com.twelvemonkeys.servlet.image.ImageServletResponse.IMAGE_RESAMPLE_ALGORITHM"; 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". * 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. * If not set, the default format is that of the original image.
* *
* @return the image format for this response. * @return the image format for this response.
* @see #setOutputContentType(String) * @see #setOutputContentType(String)
*/ */
String getOutputContentType(); String getOutputContentType();
/** /**
* Sets the image format for this response, such as "image/gif" or "image/jpeg". * Sets the image format for this response, such as "image/gif" or "image/jpeg".
* <p/> * <p/>
* As an example, a custom filter could do content negotiation based on the * 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. * 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. * If not set, the default format is that of the original image.
* *
* @param pImageFormat the image format for this response. * @param pImageFormat the image format for this response.
*/ */
void setOutputContentType(String pImageFormat); void setOutputContentType(String pImageFormat);
//TODO: ?? void setCompressionQuality(float pQualityFactor); //TODO: ?? void setCompressionQuality(float pQualityFactor);
//TODO: ?? float getCompressionQuality(); //TODO: ?? float getCompressionQuality();
/** /**
* Writes the image to the original {@code ServletOutputStream}. * Writes the image to the original {@code ServletOutputStream}.
* If no format is {@linkplain #setOutputContentType(String) set} in this response, * If no format is {@linkplain #setOutputContentType(String) set} in this response,
* the image is encoded in the same format as the original image. * the image is encoded in the same format as the original image.
* *
* @throws java.io.IOException if an I/O exception occurs during writing * @throws java.io.IOException if an I/O exception occurs during writing
*/ */
void flush() throws IOException; void flush() throws IOException;
/** /**
* Gets the decoded image from the response. * Gets the decoded image from the response.
* *
* @return a {@code BufferedImage} or {@code null} if the image could not be read. * @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 * @throws java.io.IOException if an I/O exception occurs during reading
*/ */
BufferedImage getImage() throws IOException; BufferedImage getImage() throws IOException;
/** /**
* Sets the image for this response. * Sets the image for this response.
* *
* @param pImage the new response image. * @param pImage the new response image.
*/ */
void setImage(RenderedImage pImage); void setImage(RenderedImage pImage);
} }

View File

@@ -1,46 +1,46 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.servlet.image; package com.twelvemonkeys.servlet.image;
import javax.servlet.ServletRequest; import javax.servlet.ServletRequest;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage; import java.awt.image.RenderedImage;
/** /**
* An {@code ImageFilter} that does nothing. Useful for debugging purposes. * An {@code ImageFilter} that does nothing. Useful for debugging purposes.
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: NullImageFilter.java $ * @version $Id: NullImageFilter.java $
* *
*/ */
public final class NullImageFilter extends ImageFilter { public final class NullImageFilter extends ImageFilter {
protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) { protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) {
return pImage; return pImage;
} }
} }

View File

@@ -1,202 +1,202 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.servlet.image; package com.twelvemonkeys.servlet.image;
import com.twelvemonkeys.image.ImageUtil; import com.twelvemonkeys.image.ImageUtil;
import com.twelvemonkeys.lang.StringUtil; import com.twelvemonkeys.lang.StringUtil;
import com.twelvemonkeys.servlet.ServletUtil; import com.twelvemonkeys.servlet.ServletUtil;
import javax.servlet.ServletRequest; import javax.servlet.ServletRequest;
import java.awt.*; import java.awt.*;
import java.awt.geom.AffineTransform; import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage; import java.awt.image.RenderedImage;
/** /**
* This Servlet is able to render a cropped part of an image. * This Servlet is able to render a cropped part of an image.
* *
* <P><HR><P> * <P><HR><P>
* *
* <A name="parameters"></A><STRONG>Parameters:</STRONG><BR> * <A name="parameters"></A><STRONG>Parameters:</STRONG><BR>
* <DL> * <DL>
* <DT>{@code cropX}</DT> * <DT>{@code cropX}</DT>
* <DD>integer, the new left edge of the image. * <DD>integer, the new left edge of the image.
* <DT>{@code cropY}</DT> * <DT>{@code cropY}</DT>
* <DD>integer, the new top of the image. * <DD>integer, the new top of the image.
* <DT>{@code cropWidth}</DT> * <DT>{@code cropWidth}</DT>
* <DD>integer, the new width of the image. * <DD>integer, the new width of the image.
* <DT>{@code cropHeight}</DT> * <DT>{@code cropHeight}</DT>
* <DD>integer, the new height of the image. * <DD>integer, the new height of the image.
* <!-- * <!--
* <DT>{@code cropUniform}</DT> * <DT>{@code cropUniform}</DT>
* <DD>boolean, wether or not uniform scalnig should be used. Default is * <DD>boolean, wether or not uniform scalnig should be used. Default is
* {@code true}. * {@code true}.
* <DT>{@code cropUnits}</DT> * <DT>{@code cropUnits}</DT>
* <DD>string, one of {@code PIXELS}, {@code PERCENT}. * <DD>string, one of {@code PIXELS}, {@code PERCENT}.
* {@code PIXELS} is default. --> * {@code PIXELS} is default. -->
* *
* *
* </DL> * </DL>
* *
* @example * @example
* JPEG: * JPEG:
* &lt;IMG src="/scale/test.jpg?image=http://www.iconmedialab.com/images/random/home_image_12.jpg&width=500&uniform=true"&gt; * &lt;IMG src="/scale/test.jpg?image=http://www.iconmedialab.com/images/random/home_image_12.jpg&width=500&uniform=true"&gt;
* *
* PNG: * PNG:
* &lt;IMG src="/scale/test.png?cache=false&image=http://www.iconmedialab.com/images/random/home_image_12.jpg&width=50&units=PERCENT"&gt; * &lt;IMG src="/scale/test.png?cache=false&image=http://www.iconmedialab.com/images/random/home_image_12.jpg&width=50&units=PERCENT"&gt;
* *
* @todo Correct rounding errors, resulting in black borders when rotating 90 * @todo Correct rounding errors, resulting in black borders when rotating 90
* degrees, and one of width or height is odd length... * degrees, and one of width or height is odd length...
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $ * @author last modified by $Author: haku $
* @version $Id: RotateFilter.java#1 $ * @version $Id: RotateFilter.java#1 $
*/ */
public class RotateFilter extends ImageFilter { public class RotateFilter extends ImageFilter {
/** {@code angle}*/ /** {@code angle}*/
protected final static String PARAM_ANGLE = "angle"; protected final static String PARAM_ANGLE = "angle";
/** {@code angleUnits (RADIANS|DEGREES)}*/ /** {@code angleUnits (RADIANS|DEGREES)}*/
protected final static String PARAM_ANGLE_UNITS = "angleUnits"; protected final static String PARAM_ANGLE_UNITS = "angleUnits";
/** {@code crop}*/ /** {@code crop}*/
protected final static String PARAM_CROP = "rotateCrop"; protected final static String PARAM_CROP = "rotateCrop";
/** {@code bgcolor}*/ /** {@code bgcolor}*/
protected final static String PARAM_BGCOLOR = "rotateBgcolor"; protected final static String PARAM_BGCOLOR = "rotateBgcolor";
/** {@code degrees}*/ /** {@code degrees}*/
private final static String ANGLE_DEGREES = "degrees"; private final static String ANGLE_DEGREES = "degrees";
/** {@code radians}*/ /** {@code radians}*/
//private final static String ANGLE_RADIANS = "radians"; //private final static String ANGLE_RADIANS = "radians";
/** /**
* Reads the image from the requested URL, rotates it, and returns * Reads the image from the requested URL, rotates it, and returns
* it in the * it in the
* Servlet stream. See above for details on parameters. * Servlet stream. See above for details on parameters.
*/ */
protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) { protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) {
// Get angle // Get angle
double ang = getAngle(pRequest); double ang = getAngle(pRequest);
// Get bounds // Get bounds
Rectangle2D rect = getBounds(pRequest, pImage, ang); Rectangle2D rect = getBounds(pRequest, pImage, ang);
int width = (int) rect.getWidth(); int width = (int) rect.getWidth();
int height = (int) rect.getHeight(); int height = (int) rect.getHeight();
// Create result image // Create result image
BufferedImage res = ImageUtil.createTransparent(width, height, BufferedImage.TYPE_INT_ARGB); BufferedImage res = ImageUtil.createTransparent(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = res.createGraphics(); Graphics2D g = res.createGraphics();
// Get background color and clear // Get background color and clear
String str = pRequest.getParameter(PARAM_BGCOLOR); String str = pRequest.getParameter(PARAM_BGCOLOR);
if (!StringUtil.isEmpty(str)) { if (!StringUtil.isEmpty(str)) {
Color bgcolor = StringUtil.toColor(str); Color bgcolor = StringUtil.toColor(str);
g.setBackground(bgcolor); g.setBackground(bgcolor);
g.clearRect(0, 0, width, height); g.clearRect(0, 0, width, height);
} }
// Set mHints (why do I always get jagged edgdes?) // Set mHints (why do I always get jagged edgdes?)
RenderingHints hints = new RenderingHints(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY); RenderingHints hints = new RenderingHints(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
hints.add(new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY)); hints.add(new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY));
hints.add(new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)); hints.add(new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON));
hints.add(new RenderingHints(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC)); hints.add(new RenderingHints(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC));
g.setRenderingHints(hints); g.setRenderingHints(hints);
// Rotate around center // Rotate around center
AffineTransform at = AffineTransform AffineTransform at = AffineTransform
.getRotateInstance(ang, width / 2.0, height / 2.0); .getRotateInstance(ang, width / 2.0, height / 2.0);
// Move to center // Move to center
at.translate(width / 2.0 - pImage.getWidth() / 2.0, at.translate(width / 2.0 - pImage.getWidth() / 2.0,
height / 2.0 - pImage.getHeight() / 2.0); height / 2.0 - pImage.getHeight() / 2.0);
// Draw it, centered // Draw it, centered
g.drawImage(pImage, at, null); g.drawImage(pImage, at, null);
return res; return res;
} }
/** /**
* Gets the angle of rotation. * Gets the angle of rotation.
*/ */
private double getAngle(ServletRequest pReq) { private double getAngle(ServletRequest pReq) {
double angle = 0.0; double angle = 0.0;
String str = pReq.getParameter(PARAM_ANGLE); String str = pReq.getParameter(PARAM_ANGLE);
if (!StringUtil.isEmpty(str)) { if (!StringUtil.isEmpty(str)) {
angle = Double.parseDouble(str); angle = Double.parseDouble(str);
// Convert to radians, if needed // Convert to radians, if needed
str = pReq.getParameter(PARAM_ANGLE_UNITS); str = pReq.getParameter(PARAM_ANGLE_UNITS);
if (!StringUtil.isEmpty(str) if (!StringUtil.isEmpty(str)
&& ANGLE_DEGREES.equalsIgnoreCase(str)) { && ANGLE_DEGREES.equalsIgnoreCase(str)) {
angle = Math.toRadians(angle); angle = Math.toRadians(angle);
} }
} }
return angle; return angle;
} }
/** /**
* Get the bounding rectangle of the rotated image. * Get the bounding rectangle of the rotated image.
*/ */
private Rectangle2D getBounds(ServletRequest pReq, BufferedImage pImage, private Rectangle2D getBounds(ServletRequest pReq, BufferedImage pImage,
double pAng) { double pAng) {
// Get dimensions of original image // Get dimensions of original image
int width = pImage.getWidth(); // loads the image int width = pImage.getWidth(); // loads the image
int height = pImage.getHeight(); int height = pImage.getHeight();
// Test if we want to crop image (default) // Test if we want to crop image (default)
// if true // if true
// - find the largest bounding box INSIDE the rotated image, // - find the largest bounding box INSIDE the rotated image,
// that matches the original proportions (nearest 90deg) // that matches the original proportions (nearest 90deg)
// (scale up to fit dimensions?) // (scale up to fit dimensions?)
// else // else
// - find the smallest bounding box OUTSIDE the rotated image. // - find the smallest bounding box OUTSIDE the rotated image.
// - that matches the original proportions (nearest 90deg) ? // - that matches the original proportions (nearest 90deg) ?
// (scale down to fit dimensions?) // (scale down to fit dimensions?)
AffineTransform at = AffineTransform at =
AffineTransform.getRotateInstance(pAng, width / 2.0, height / 2.0); AffineTransform.getRotateInstance(pAng, width / 2.0, height / 2.0);
Rectangle2D orig = new Rectangle(width, height); Rectangle2D orig = new Rectangle(width, height);
Shape rotated = at.createTransformedShape(orig); Shape rotated = at.createTransformedShape(orig);
if (ServletUtil.getBooleanParameter(pReq, PARAM_CROP, false)) { if (ServletUtil.getBooleanParameter(pReq, PARAM_CROP, false)) {
// TODO: Inside box // TODO: Inside box
return rotated.getBounds2D(); return rotated.getBounds2D();
} }
else { else {
return rotated.getBounds2D(); return rotated.getBounds2D();
} }
} }
} }

View File

@@ -1,322 +1,322 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the * * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.servlet.image; package com.twelvemonkeys.servlet.image;
import com.twelvemonkeys.image.ImageUtil; import com.twelvemonkeys.image.ImageUtil;
import com.twelvemonkeys.lang.StringUtil; import com.twelvemonkeys.lang.StringUtil;
import com.twelvemonkeys.servlet.ServletUtil; import com.twelvemonkeys.servlet.ServletUtil;
import javax.servlet.ServletRequest; import javax.servlet.ServletRequest;
import java.awt.*; import java.awt.*;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage; import java.awt.image.RenderedImage;
import java.lang.reflect.Field; import java.lang.reflect.Field;
/** /**
* This filter renders a scaled version of an image read from a * This filter renders a scaled version of an image read from a
* given URL. The image can be output as a GIF, JPEG or PNG image * given URL. The image can be output as a GIF, JPEG or PNG image
* or similar<!--, * or similar<!--,
* with optional caching of the rendered image files-->. * with optional caching of the rendered image files-->.
* <P> * <P>
* <P><HR><P> * <P><HR><P>
* <p/> * <p/>
* <A name="parameters"></A><STRONG>Parameters:</STRONG><BR> * <A name="parameters"></A><STRONG>Parameters:</STRONG><BR>
* <DL> * <DL>
* <DT>{@code scaleX}</DT> * <DT>{@code scaleX}</DT>
* <DD>integer, the new width of the image. * <DD>integer, the new width of the image.
* <DT>{@code scaleY}</DT> * <DT>{@code scaleY}</DT>
* <DD>integer, the new height of the image. * <DD>integer, the new height of the image.
* <DT>{@code scaleUniform}</DT> * <DT>{@code scaleUniform}</DT>
* <DD>boolean, wether or not uniform scalnig should be used. Default is * <DD>boolean, wether or not uniform scalnig should be used. Default is
* {@code true}. * {@code true}.
* <DT>{@code scaleUnits}</DT> * <DT>{@code scaleUnits}</DT>
* <DD>string, one of {@code PIXELS}, {@code PERCENT}. * <DD>string, one of {@code PIXELS}, {@code PERCENT}.
* {@code PIXELS} is default. * {@code PIXELS} is default.
* <DT>{@code scaleQuality}</DT> * <DT>{@code scaleQuality}</DT>
* <DD>string, one of {@code SCALE_SMOOTH}, {@code SCALE_FAST}, * <DD>string, one of {@code SCALE_SMOOTH}, {@code SCALE_FAST},
* {@code SCALE_REPLICATE}, {@code SCALE_AREA_AVERAGING}. * {@code SCALE_REPLICATE}, {@code SCALE_AREA_AVERAGING}.
* {@code SCALE_DEFAULT} is default (see * {@code SCALE_DEFAULT} is default (see
* {@link java.awt.Image#getScaledInstance(int,int,int)}, {@link java.awt.Image} * {@link java.awt.Image#getScaledInstance(int,int,int)}, {@link java.awt.Image}
* for more details). * for more details).
* </DL> * </DL>
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $ * @author last modified by $Author: haku $
* @version $Id: ScaleFilter.java#1 $ * @version $Id: ScaleFilter.java#1 $
* *
* @example &lt;IMG src="/scale/test.jpg?scaleX=500&scaleUniform=false"&gt; * @example &lt;IMG src="/scale/test.jpg?scaleX=500&scaleUniform=false"&gt;
* @example &lt;IMG src="/scale/test.png?scaleY=50&scaleUnits=PERCENT"&gt; * @example &lt;IMG src="/scale/test.png?scaleY=50&scaleUnits=PERCENT"&gt;
*/ */
public class ScaleFilter extends ImageFilter { public class ScaleFilter extends ImageFilter {
/** /**
* Width and height are absolute pixels. The default. * Width and height are absolute pixels. The default.
*/ */
public static final int UNITS_PIXELS = 1; public static final int UNITS_PIXELS = 1;
/** /**
* Width and height are percentage of original width and height. * Width and height are percentage of original width and height.
*/ */
public static final int UNITS_PERCENT = 5; public static final int UNITS_PERCENT = 5;
/** /**
* Ahh, good choice! * Ahh, good choice!
*/ */
//private static final int UNITS_METRIC = 42; //private static final int UNITS_METRIC = 42;
/** /**
* The root of all evil... * The root of all evil...
*/ */
//private static final int UNITS_INCHES = 666; //private static final int UNITS_INCHES = 666;
/** /**
* Unknown units. <!-- Oops, what now? --> * Unknown units. <!-- Oops, what now? -->
*/ */
public static final int UNITS_UNKNOWN = 0; public static final int UNITS_UNKNOWN = 0;
/** /**
* {@code scaleQuality} * {@code scaleQuality}
*/ */
protected final static String PARAM_SCALE_QUALITY = "scaleQuality"; protected final static String PARAM_SCALE_QUALITY = "scaleQuality";
/** /**
* {@code scaleUnits} * {@code scaleUnits}
*/ */
protected final static String PARAM_SCALE_UNITS = "scaleUnits"; protected final static String PARAM_SCALE_UNITS = "scaleUnits";
/** /**
* {@code scaleUniform} * {@code scaleUniform}
*/ */
protected final static String PARAM_SCALE_UNIFORM = "scaleUniform"; protected final static String PARAM_SCALE_UNIFORM = "scaleUniform";
/** /**
* {@code scaleX} * {@code scaleX}
*/ */
protected final static String PARAM_SCALE_X = "scaleX"; protected final static String PARAM_SCALE_X = "scaleX";
/** /**
* {@code scaleY} * {@code scaleY}
*/ */
protected final static String PARAM_SCALE_Y = "scaleY"; protected final static String PARAM_SCALE_Y = "scaleY";
/** /**
* {@code image} * {@code image}
*/ */
protected final static String PARAM_IMAGE = "image"; protected final static String PARAM_IMAGE = "image";
/** */ /** */
protected int defaultScaleQuality = Image.SCALE_DEFAULT; protected int defaultScaleQuality = Image.SCALE_DEFAULT;
/** /**
* Reads the image from the requested URL, scales it, and returns it in the * Reads the image from the requested URL, scales it, and returns it in the
* Servlet stream. See above for details on parameters. * Servlet stream. See above for details on parameters.
*/ */
protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) { protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) {
// Get quality setting // Get quality setting
// SMOOTH | FAST | REPLICATE | DEFAULT | AREA_AVERAGING // SMOOTH | FAST | REPLICATE | DEFAULT | AREA_AVERAGING
// See Image (mHints) // See Image (mHints)
int quality = getQuality(pRequest.getParameter(PARAM_SCALE_QUALITY)); int quality = getQuality(pRequest.getParameter(PARAM_SCALE_QUALITY));
// Get units, default is pixels // Get units, default is pixels
// PIXELS | PERCENT | METRIC | INCHES // PIXELS | PERCENT | METRIC | INCHES
int units = getUnits(pRequest.getParameter(PARAM_SCALE_UNITS)); int units = getUnits(pRequest.getParameter(PARAM_SCALE_UNITS));
if (units == UNITS_UNKNOWN) { if (units == UNITS_UNKNOWN) {
log("Unknown units for scale, returning original."); log("Unknown units for scale, returning original.");
return pImage; return pImage;
} }
// Use uniform scaling? Default is true // Use uniform scaling? Default is true
boolean uniformScale = ServletUtil.getBooleanParameter(pRequest, PARAM_SCALE_UNIFORM, true); boolean uniformScale = ServletUtil.getBooleanParameter(pRequest, PARAM_SCALE_UNIFORM, true);
// Get dimensions // Get dimensions
int width = ServletUtil.getIntParameter(pRequest, PARAM_SCALE_X, -1); int width = ServletUtil.getIntParameter(pRequest, PARAM_SCALE_X, -1);
int height = ServletUtil.getIntParameter(pRequest, PARAM_SCALE_Y, -1); int height = ServletUtil.getIntParameter(pRequest, PARAM_SCALE_Y, -1);
// Get dimensions for scaled image // Get dimensions for scaled image
Dimension dim = getDimensions(pImage, width, height, units, uniformScale); Dimension dim = getDimensions(pImage, width, height, units, uniformScale);
width = (int) dim.getWidth(); width = (int) dim.getWidth();
height = (int) dim.getHeight(); height = (int) dim.getHeight();
// Return scaled instance directly // Return scaled instance directly
return ImageUtil.createScaled(pImage, width, height, quality); return ImageUtil.createScaled(pImage, width, height, quality);
} }
/** /**
* Gets the quality constant for the scaling, from the string argument. * Gets the quality constant for the scaling, from the string argument.
* *
* @param pQualityStr The string representation of the scale quality * @param pQualityStr The string representation of the scale quality
* constant. * constant.
* @return The matching quality constant, or the default quality if none * @return The matching quality constant, or the default quality if none
* was found. * was found.
* @see java.awt.Image * @see java.awt.Image
* @see java.awt.Image#getScaledInstance(int,int,int) * @see java.awt.Image#getScaledInstance(int,int,int)
*/ */
protected int getQuality(String pQualityStr) { protected int getQuality(String pQualityStr) {
if (!StringUtil.isEmpty(pQualityStr)) { if (!StringUtil.isEmpty(pQualityStr)) {
try { try {
// Get quality constant from Image using reflection // Get quality constant from Image using reflection
Class cl = Image.class; Class cl = Image.class;
Field field = cl.getField(pQualityStr.toUpperCase()); Field field = cl.getField(pQualityStr.toUpperCase());
return field.getInt(null); return field.getInt(null);
} }
catch (IllegalAccessException ia) { catch (IllegalAccessException ia) {
log("Unable to get quality.", ia); log("Unable to get quality.", ia);
} }
catch (NoSuchFieldException nsf) { catch (NoSuchFieldException nsf) {
log("Unable to get quality.", nsf); log("Unable to get quality.", nsf);
} }
} }
return defaultScaleQuality; return defaultScaleQuality;
} }
public void setDefaultScaleQuality(String pDefaultScaleQuality) { public void setDefaultScaleQuality(String pDefaultScaleQuality) {
defaultScaleQuality = getQuality(pDefaultScaleQuality); defaultScaleQuality = getQuality(pDefaultScaleQuality);
} }
/** /**
* Gets the units constant for the width and height arguments, from the * Gets the units constant for the width and height arguments, from the
* given string argument. * given string argument.
* *
* @param pUnitStr The string representation of the units constant, * @param pUnitStr The string representation of the units constant,
* can be one of "PIXELS" or "PERCENT". * can be one of "PIXELS" or "PERCENT".
* @return The mathcing units constant, or UNITS_UNKNOWN if none was found. * @return The mathcing units constant, or UNITS_UNKNOWN if none was found.
*/ */
protected int getUnits(String pUnitStr) { protected int getUnits(String pUnitStr) {
if (StringUtil.isEmpty(pUnitStr) if (StringUtil.isEmpty(pUnitStr)
|| pUnitStr.equalsIgnoreCase("PIXELS")) { || pUnitStr.equalsIgnoreCase("PIXELS")) {
return UNITS_PIXELS; return UNITS_PIXELS;
} }
else if (pUnitStr.equalsIgnoreCase("PERCENT")) { else if (pUnitStr.equalsIgnoreCase("PERCENT")) {
return UNITS_PERCENT; return UNITS_PERCENT;
} }
else { else {
return UNITS_UNKNOWN; return UNITS_UNKNOWN;
} }
} }
/** /**
* Gets the dimensions (height and width) of the scaled image. The * Gets the dimensions (height and width) of the scaled image. The
* dimensions are computed based on the old image's dimensions, the units * dimensions are computed based on the old image's dimensions, the units
* used for specifying new dimensions and whether or not uniform scaling * used for specifying new dimensions and whether or not uniform scaling
* should be used (se algorithm below). * should be used (se algorithm below).
* *
* @param pImage the image to be scaled * @param pImage the image to be scaled
* @param pWidth the new width of the image, or -1 if unknown * @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 pHeight the new height of the image, or -1 if unknown
* @param pUnits the constant specifying units for width and height * @param pUnits the constant specifying units for width and height
* parameter (UNITS_PIXELS or UNITS_PERCENT) * parameter (UNITS_PIXELS or UNITS_PERCENT)
* @param pUniformScale boolean specifying uniform scale or not * @param pUniformScale boolean specifying uniform scale or not
* @return a Dimension object, with the correct width and heigth * @return a Dimension object, with the correct width and heigth
* in pixels, for the scaled version of the image. * in pixels, for the scaled version of the image.
*/ */
protected Dimension getDimensions(Image pImage, int pWidth, int pHeight, protected Dimension getDimensions(Image pImage, int pWidth, int pHeight,
int pUnits, boolean pUniformScale) { int pUnits, boolean pUniformScale) {
// If uniform, make sure width and height are scaled the same ammount // If uniform, make sure width and height are scaled the same ammount
// (use ONLY height or ONLY width). // (use ONLY height or ONLY width).
// //
// Algoritm: // Algoritm:
// if uniform // if uniform
// if newHeight not set // if newHeight not set
// find ratio newWidth / oldWidth // find ratio newWidth / oldWidth
// oldHeight *= ratio // oldHeight *= ratio
// else if newWidth not set // else if newWidth not set
// find ratio newWidth / oldWidth // find ratio newWidth / oldWidth
// oldHeight *= ratio // oldHeight *= ratio
// else // else
// find both ratios and use the smallest one // find both ratios and use the smallest one
// (this will be the largest version of the image that fits // (this will be the largest version of the image that fits
// inside the rectangle given) // inside the rectangle given)
// (if PERCENT, just use smallest percentage). // (if PERCENT, just use smallest percentage).
// //
// If units is percent, we only need old height and width // If units is percent, we only need old height and width
int oldWidth = ImageUtil.getWidth(pImage); int oldWidth = ImageUtil.getWidth(pImage);
int oldHeight = ImageUtil.getHeight(pImage); int oldHeight = ImageUtil.getHeight(pImage);
float ratio; float ratio;
if (pUnits == UNITS_PERCENT) { if (pUnits == UNITS_PERCENT) {
if (pWidth >= 0 && pHeight >= 0) { if (pWidth >= 0 && pHeight >= 0) {
// Non-uniform // Non-uniform
pWidth = (int) ((float) oldWidth * (float) pWidth / 100f); pWidth = (int) ((float) oldWidth * (float) pWidth / 100f);
pHeight = (int) ((float) oldHeight * (float) pHeight / 100f); pHeight = (int) ((float) oldHeight * (float) pHeight / 100f);
} }
else if (pWidth >= 0) { else if (pWidth >= 0) {
// Find ratio from pWidth // Find ratio from pWidth
ratio = (float) pWidth / 100f; ratio = (float) pWidth / 100f;
pWidth = (int) ((float) oldWidth * ratio); pWidth = (int) ((float) oldWidth * ratio);
pHeight = (int) ((float) oldHeight * ratio); pHeight = (int) ((float) oldHeight * ratio);
} }
else if (pHeight >= 0) { else if (pHeight >= 0) {
// Find ratio from pHeight // Find ratio from pHeight
ratio = (float) pHeight / 100f; ratio = (float) pHeight / 100f;
pWidth = (int) ((float) oldWidth * ratio); pWidth = (int) ((float) oldWidth * ratio);
pHeight = (int) ((float) oldHeight * ratio); pHeight = (int) ((float) oldHeight * ratio);
} }
// Else: No scale // Else: No scale
} }
else if (pUnits == UNITS_PIXELS) { else if (pUnits == UNITS_PIXELS) {
if (pUniformScale) { if (pUniformScale) {
if (pWidth >= 0 && pHeight >= 0) { if (pWidth >= 0 && pHeight >= 0) {
// Compute both ratios // Compute both ratios
ratio = (float) pWidth / (float) oldWidth; ratio = (float) pWidth / (float) oldWidth;
float heightRatio = (float) pHeight / (float) oldHeight; float heightRatio = (float) pHeight / (float) oldHeight;
// Find the largest ratio, and use that for both // Find the largest ratio, and use that for both
if (heightRatio < ratio) { if (heightRatio < ratio) {
ratio = heightRatio; ratio = heightRatio;
pWidth = (int) ((float) oldWidth * ratio); pWidth = (int) ((float) oldWidth * ratio);
} }
else { else {
pHeight = (int) ((float) oldHeight * ratio); pHeight = (int) ((float) oldHeight * ratio);
} }
} }
else if (pWidth >= 0) { else if (pWidth >= 0) {
// Find ratio from pWidth // Find ratio from pWidth
ratio = (float) pWidth / (float) oldWidth; ratio = (float) pWidth / (float) oldWidth;
pHeight = (int) ((float) oldHeight * ratio); pHeight = (int) ((float) oldHeight * ratio);
} }
else if (pHeight >= 0) { else if (pHeight >= 0) {
// Find ratio from pHeight // Find ratio from pHeight
ratio = (float) pHeight / (float) oldHeight; ratio = (float) pHeight / (float) oldHeight;
pWidth = (int) ((float) oldWidth * ratio); pWidth = (int) ((float) oldWidth * ratio);
} }
// Else: No scale // Else: No scale
} }
} }
// Default is no scale, just work as a proxy // Default is no scale, just work as a proxy
if (pWidth < 0) { if (pWidth < 0) {
pWidth = oldWidth; pWidth = oldWidth;
} }
if (pHeight < 0) { if (pHeight < 0) {
pHeight = oldHeight; pHeight = oldHeight;
} }
// Create new Dimension object and return // Create new Dimension object and return
return new Dimension(pWidth, pHeight); return new Dimension(pWidth, pHeight);
} }
} }

View File

@@ -1,154 +1,154 @@
package com.twelvemonkeys.servlet.image; package com.twelvemonkeys.servlet.image;
import com.twelvemonkeys.servlet.ServletUtil; import com.twelvemonkeys.servlet.ServletUtil;
import javax.servlet.ServletRequest; import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; import javax.servlet.ServletResponse;
import javax.servlet.FilterChain; import javax.servlet.FilterChain;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import java.awt.image.RenderedImage; import java.awt.image.RenderedImage;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.awt.*; import java.awt.*;
import java.io.IOException; import java.io.IOException;
/** /**
* A {@link javax.servlet.Filter} that extracts request parameters, and sets the * A {@link javax.servlet.Filter} that extracts request parameters, and sets the
* corresponding request attributes from {@link ImageServletResponse}. * corresponding request attributes from {@link ImageServletResponse}.
* Only affects how the image is decoded, and must be applied before any * Only affects how the image is decoded, and must be applied before any
* other image filters in the chain. * other image filters in the chain.
* <p/> * <p/>
* @see ImageServletResponse#ATTRIB_SIZE * @see ImageServletResponse#ATTRIB_SIZE
* @see ImageServletResponse#ATTRIB_AOI * @see ImageServletResponse#ATTRIB_AOI
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: SourceRenderFilter.java#1 $ * @version $Id: SourceRenderFilter.java#1 $
*/ */
public class SourceRenderFilter extends ImageFilter { public class SourceRenderFilter extends ImageFilter {
private String sizeWidthParam = "size.w"; private String sizeWidthParam = "size.w";
private String sizeHeightParam = "size.h"; private String sizeHeightParam = "size.h";
private String sizePercentParam = "size.percent"; private String sizePercentParam = "size.percent";
private String sizeUniformParam = "size.uniform"; private String sizeUniformParam = "size.uniform";
private String regionWidthParam = "aoi.w"; private String regionWidthParam = "aoi.w";
private String regionHeightParam = "aoi.h"; private String regionHeightParam = "aoi.h";
private String regionLeftParam = "aoi.x"; private String regionLeftParam = "aoi.x";
private String regionTopParam = "aoi.y"; private String regionTopParam = "aoi.y";
private String regionPercentParam = "aoi.percent"; private String regionPercentParam = "aoi.percent";
private String regionUniformParam = "aoi.uniform"; private String regionUniformParam = "aoi.uniform";
public void setRegionHeightParam(String pRegionHeightParam) { public void setRegionHeightParam(String pRegionHeightParam) {
regionHeightParam = pRegionHeightParam; regionHeightParam = pRegionHeightParam;
} }
public void setRegionWidthParam(String pRegionWidthParam) { public void setRegionWidthParam(String pRegionWidthParam) {
regionWidthParam = pRegionWidthParam; regionWidthParam = pRegionWidthParam;
} }
public void setRegionLeftParam(String pRegionLeftParam) { public void setRegionLeftParam(String pRegionLeftParam) {
regionLeftParam = pRegionLeftParam; regionLeftParam = pRegionLeftParam;
} }
public void setRegionTopParam(String pRegionTopParam) { public void setRegionTopParam(String pRegionTopParam) {
regionTopParam = pRegionTopParam; regionTopParam = pRegionTopParam;
} }
public void setSizeHeightParam(String pSizeHeightParam) { public void setSizeHeightParam(String pSizeHeightParam) {
sizeHeightParam = pSizeHeightParam; sizeHeightParam = pSizeHeightParam;
} }
public void setSizeWidthParam(String pSizeWidthParam) { public void setSizeWidthParam(String pSizeWidthParam) {
sizeWidthParam = pSizeWidthParam; sizeWidthParam = pSizeWidthParam;
} }
public void setRegionPercentParam(String pRegionPercentParam) { public void setRegionPercentParam(String pRegionPercentParam) {
regionPercentParam = pRegionPercentParam; regionPercentParam = pRegionPercentParam;
} }
public void setRegionUniformParam(String pRegionUniformParam) { public void setRegionUniformParam(String pRegionUniformParam) {
regionUniformParam = pRegionUniformParam; regionUniformParam = pRegionUniformParam;
} }
public void setSizePercentParam(String pSizePercentParam) { public void setSizePercentParam(String pSizePercentParam) {
sizePercentParam = pSizePercentParam; sizePercentParam = pSizePercentParam;
} }
public void setSizeUniformParam(String pSizeUniformParam) { public void setSizeUniformParam(String pSizeUniformParam) {
sizeUniformParam = pSizeUniformParam; sizeUniformParam = pSizeUniformParam;
} }
public void init() throws ServletException { public void init() throws ServletException {
if (triggerParams == null) { if (triggerParams == null) {
// Add all params as triggers // Add all params as triggers
triggerParams = new String[]{sizeWidthParam, sizeHeightParam, triggerParams = new String[]{sizeWidthParam, sizeHeightParam,
sizeUniformParam, sizePercentParam, sizeUniformParam, sizePercentParam,
regionLeftParam, regionTopParam, regionLeftParam, regionTopParam,
regionWidthParam, regionHeightParam, regionWidthParam, regionHeightParam,
regionUniformParam, regionPercentParam}; regionUniformParam, regionPercentParam};
} }
} }
/** /**
* Extracts request parameters, and sets the corresponding request * Extracts request parameters, and sets the corresponding request
* attributes if specified. * attributes if specified.
* *
* @param pRequest * @param pRequest
* @param pResponse * @param pResponse
* @param pChain * @param pChain
* @throws IOException * @throws IOException
* @throws ServletException * @throws ServletException
*/ */
protected void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain) throws IOException, ServletException { protected void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain) throws IOException, ServletException {
// TODO: Max size configuration, to avoid DOS attacks? OutOfMemory // TODO: Max size configuration, to avoid DOS attacks? OutOfMemory
// Size parameters // Size parameters
int width = ServletUtil.getIntParameter(pRequest, sizeWidthParam, -1); int width = ServletUtil.getIntParameter(pRequest, sizeWidthParam, -1);
int height = ServletUtil.getIntParameter(pRequest, sizeHeightParam, -1); int height = ServletUtil.getIntParameter(pRequest, sizeHeightParam, -1);
if (width > 0 || height > 0) { if (width > 0 || height > 0) {
pRequest.setAttribute(ImageServletResponse.ATTRIB_SIZE, new Dimension(width, height)); pRequest.setAttribute(ImageServletResponse.ATTRIB_SIZE, new Dimension(width, height));
} }
// Size uniform/percent // Size uniform/percent
boolean uniform = ServletUtil.getBooleanParameter(pRequest, sizeUniformParam, true); boolean uniform = ServletUtil.getBooleanParameter(pRequest, sizeUniformParam, true);
if (!uniform) { if (!uniform) {
pRequest.setAttribute(ImageServletResponse.ATTRIB_SIZE_UNIFORM, Boolean.FALSE); pRequest.setAttribute(ImageServletResponse.ATTRIB_SIZE_UNIFORM, Boolean.FALSE);
} }
boolean percent = ServletUtil.getBooleanParameter(pRequest, sizePercentParam, false); boolean percent = ServletUtil.getBooleanParameter(pRequest, sizePercentParam, false);
if (percent) { if (percent) {
pRequest.setAttribute(ImageServletResponse.ATTRIB_SIZE_PERCENT, Boolean.TRUE); pRequest.setAttribute(ImageServletResponse.ATTRIB_SIZE_PERCENT, Boolean.TRUE);
} }
// Area of interest parameters // Area of interest parameters
int x = ServletUtil.getIntParameter(pRequest, regionLeftParam, -1); // Default is center int x = ServletUtil.getIntParameter(pRequest, regionLeftParam, -1); // Default is center
int y = ServletUtil.getIntParameter(pRequest, regionTopParam, -1); // Default is center int y = ServletUtil.getIntParameter(pRequest, regionTopParam, -1); // Default is center
width = ServletUtil.getIntParameter(pRequest, regionWidthParam, -1); width = ServletUtil.getIntParameter(pRequest, regionWidthParam, -1);
height = ServletUtil.getIntParameter(pRequest, regionHeightParam, -1); height = ServletUtil.getIntParameter(pRequest, regionHeightParam, -1);
if (width > 0 || height > 0) { if (width > 0 || height > 0) {
pRequest.setAttribute(ImageServletResponse.ATTRIB_AOI, new Rectangle(x, y, width, height)); pRequest.setAttribute(ImageServletResponse.ATTRIB_AOI, new Rectangle(x, y, width, height));
} }
// AOI uniform/percent // AOI uniform/percent
uniform = ServletUtil.getBooleanParameter(pRequest, regionUniformParam, false); uniform = ServletUtil.getBooleanParameter(pRequest, regionUniformParam, false);
if (uniform) { if (uniform) {
pRequest.setAttribute(ImageServletResponse.ATTRIB_SIZE_UNIFORM, Boolean.TRUE); pRequest.setAttribute(ImageServletResponse.ATTRIB_SIZE_UNIFORM, Boolean.TRUE);
} }
percent = ServletUtil.getBooleanParameter(pRequest, regionPercentParam, false); percent = ServletUtil.getBooleanParameter(pRequest, regionPercentParam, false);
if (percent) { if (percent) {
pRequest.setAttribute(ImageServletResponse.ATTRIB_SIZE_PERCENT, Boolean.TRUE); pRequest.setAttribute(ImageServletResponse.ATTRIB_SIZE_PERCENT, Boolean.TRUE);
} }
super.doFilterImpl(pRequest, pResponse, pChain); super.doFilterImpl(pRequest, pResponse, pChain);
} }
/** /**
* This implementation does no filtering, and simply returns the image * This implementation does no filtering, and simply returns the image
* passed in. * passed in.
* *
* @param pImage * @param pImage
* @param pRequest * @param pRequest
* @param pResponse * @param pResponse
* @return {@code pImage} * @return {@code pImage}
*/ */
protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) { protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) {
return pImage; return pImage;
} }
} }

View File

@@ -1,33 +1,33 @@
/** /**
* Contains various image-outputting filters, that should run under any * Contains various image-outputting filters, that should run under any
* servlet engine. * servlet engine.
* <P> * <P>
* Some of these methods may require use of the native graphics libraries * Some of these methods may require use of the native graphics libraries
* supported by the JVM, like the X libraries on Unix systems, and should be * supported by the JVM, like the X libraries on Unix systems, and should be
* run with JRE <STRONG>1.4</STRONG> or later, and with the option: * run with JRE <STRONG>1.4</STRONG> or later, and with the option:
* <DL> * <DL>
* <DD>{@code -Djawa.awt.headless=true}</DD> * <DD>{@code -Djawa.awt.headless=true}</DD>
* </DL> * </DL>
* See the document * See the document
* <A href="http://java.sun.com/j2se/1.4/docs/guide/awt/AWTChanges.html#headless">AWT Enhancements</A> and bugtraq report * <A href="http://java.sun.com/j2se/1.4/docs/guide/awt/AWTChanges.html#headless">AWT Enhancements</A> and bugtraq report
* <A href="http://developer.java.sun.com/developer/bugParade/bugs/4281163.html">4281163</A> for more information on this issue. * <A href="http://developer.java.sun.com/developer/bugParade/bugs/4281163.html">4281163</A> for more information on this issue.
* <P> * <P>
* If you cannot use JRE 1.4 or later, or do not want to use the X * If you cannot use JRE 1.4 or later, or do not want to use the X
* libraries, one possibility is to use the * libraries, one possibility is to use the
* <A href="http://www.eteks.com/pja/en/">PJA package</A> (com.eteks.pja), * <A href="http://www.eteks.com/pja/en/">PJA package</A> (com.eteks.pja),
* and start the JVM with the following options: * and start the JVM with the following options:
* <DL> * <DL>
* <DD>{@code -Xbootclasspath/a:&lt;path to pja.jar&gt;}</DD> * <DD>{@code -Xbootclasspath/a:&lt;path to pja.jar&gt;}</DD>
* <DD>{@code -Dawt.toolkit=com.eteks.awt.PJAToolkit}</DD> * <DD>{@code -Dawt.toolkit=com.eteks.awt.PJAToolkit}</DD>
* <DD>{@code -Djava.awt.graphicsenv=com.eteks.java2d.PJAGraphicsEnvironment}</DD> * <DD>{@code -Djava.awt.graphicsenv=com.eteks.java2d.PJAGraphicsEnvironment}</DD>
* <DD>{@code -Djava.awt.fonts=&lt;path where True Type fonts files will be loaded from&gt;}</DD> * <DD>{@code -Djava.awt.fonts=&lt;path where True Type fonts files will be loaded from&gt;}</DD>
* </DL> * </DL>
* <P> * <P>
* Please note that creation of PNG images (from bytes or URL's) are only * Please note that creation of PNG images (from bytes or URL's) are only
* supported in JRE 1.3 and later, trying to load them from an earlier version, * supported in JRE 1.3 and later, trying to load them from an earlier version,
* will result in errors. * will result in errors.
* *
* @see com.twelvemonkeys.servlet.image.ImageServlet * @see com.twelvemonkeys.servlet.image.ImageServlet
* @see com.twelvemonkeys.servlet.image.ImagePainterServlet * @see com.twelvemonkeys.servlet.image.ImagePainterServlet
*/ */
package com.twelvemonkeys.servlet.image; package com.twelvemonkeys.servlet.image;

View File

@@ -1,4 +1,4 @@
/** /**
* Contains servlet support classes. * Contains servlet support classes.
*/ */
package com.twelvemonkeys.servlet; package com.twelvemonkeys.servlet;

View File

@@ -1,438 +1,438 @@
package com.twelvemonkeys.servlet; package com.twelvemonkeys.servlet;
import com.twelvemonkeys.lang.ObjectAbstractTestCase; import com.twelvemonkeys.lang.ObjectAbstractTestCase;
import org.junit.Test; import org.junit.Test;
import javax.servlet.*; import javax.servlet.*;
import java.io.*; import java.io.*;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.util.*; import java.util.*;
import static org.junit.Assert.*; import static org.junit.Assert.*;
/** /**
* FilterAbstractTestCase * FilterAbstractTestCase
* <p/> * <p/>
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $ * @author last modified by $Author: haku $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/FilterAbstractTestCase.java#1 $ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/FilterAbstractTestCase.java#1 $
*/ */
public abstract class FilterAbstractTestCase extends ObjectAbstractTestCase { public abstract class FilterAbstractTestCase extends ObjectAbstractTestCase {
protected Object makeObject() { protected Object makeObject() {
return makeFilter(); return makeFilter();
} }
protected abstract Filter makeFilter(); protected abstract Filter makeFilter();
// TODO: Is it a good thing to have an API like this? // TODO: Is it a good thing to have an API like this?
protected FilterConfig makeFilterConfig() { protected FilterConfig makeFilterConfig() {
return makeFilterConfig(new HashMap()); return makeFilterConfig(new HashMap());
} }
protected FilterConfig makeFilterConfig(Map pParams) { protected FilterConfig makeFilterConfig(Map pParams) {
return new MockFilterConfig(pParams); return new MockFilterConfig(pParams);
} }
protected ServletRequest makeRequest() { protected ServletRequest makeRequest() {
return new MockServletRequest(); return new MockServletRequest();
} }
protected ServletResponse makeResponse() { protected ServletResponse makeResponse() {
return new MockServletResponse(); return new MockServletResponse();
} }
protected FilterChain makeFilterChain() { protected FilterChain makeFilterChain() {
return new MockFilterChain(); return new MockFilterChain();
} }
@Test @Test
public void testInitNull() { public void testInitNull() {
Filter filter = makeFilter(); Filter filter = makeFilter();
// The spec seems to be a little unclear on this issue, but anyway, // The spec seems to be a little unclear on this issue, but anyway,
// the container should never invoke init(null)... // the container should never invoke init(null)...
try { try {
filter.init(null); filter.init(null);
fail("Should throw Exception on init(null)"); fail("Should throw Exception on init(null)");
} }
catch (IllegalArgumentException e) { catch (IllegalArgumentException e) {
// Good // Good
} }
catch (NullPointerException e) { catch (NullPointerException e) {
// Bad (but not unreasonable) // Bad (but not unreasonable)
} }
catch (ServletException e) { catch (ServletException e) {
// Hmmm.. The jury is still out. // Hmmm.. The jury is still out.
} }
} }
@Test @Test
public void testInit() { public void testInit() {
Filter filter = makeFilter(); Filter filter = makeFilter();
try { try {
filter.init(makeFilterConfig()); filter.init(makeFilterConfig());
} }
catch (ServletException e) { catch (ServletException e) {
assertNotNull(e.getMessage()); assertNotNull(e.getMessage());
} }
finally { finally {
filter.destroy(); filter.destroy();
} }
} }
@Test @Test
public void testLifeCycle() throws ServletException { public void testLifeCycle() throws ServletException {
Filter filter = makeFilter(); Filter filter = makeFilter();
try { try {
filter.init(makeFilterConfig()); filter.init(makeFilterConfig());
} }
finally { finally {
filter.destroy(); filter.destroy();
} }
} }
@Test @Test
public void testFilterBasic() throws ServletException, IOException { public void testFilterBasic() throws ServletException, IOException {
Filter filter = makeFilter(); Filter filter = makeFilter();
try { try {
filter.init(makeFilterConfig()); filter.init(makeFilterConfig());
filter.doFilter(makeRequest(), makeResponse(), makeFilterChain()); filter.doFilter(makeRequest(), makeResponse(), makeFilterChain());
} }
finally { finally {
filter.destroy(); filter.destroy();
} }
} }
@Test @Test
public void testDestroy() { public void testDestroy() {
// TODO: Implement // TODO: Implement
} }
static class MockFilterConfig implements FilterConfig { static class MockFilterConfig implements FilterConfig {
private final Map<String, String> params; private final Map<String, String> params;
MockFilterConfig(Map<String, String> pParams) { MockFilterConfig(Map<String, String> pParams) {
if (pParams == null) { if (pParams == null) {
throw new IllegalArgumentException("params == null"); throw new IllegalArgumentException("params == null");
} }
params = pParams; params = pParams;
} }
public String getFilterName() { public String getFilterName() {
return "mock-filter"; return "mock-filter";
} }
public String getInitParameter(String pName) { public String getInitParameter(String pName) {
return params.get(pName); return params.get(pName);
} }
public Enumeration getInitParameterNames() { public Enumeration getInitParameterNames() {
return Collections.enumeration(params.keySet()); return Collections.enumeration(params.keySet());
} }
public ServletContext getServletContext() { public ServletContext getServletContext() {
return new MockServletContext(); return new MockServletContext();
} }
private static class MockServletContext implements ServletContext { private static class MockServletContext implements ServletContext {
private final Map<String, Object> attributes; private final Map<String, Object> attributes;
private final Map<String, String> params; private final Map<String, String> params;
MockServletContext() { MockServletContext() {
attributes = new HashMap<String, Object>(); attributes = new HashMap<String, Object>();
params = new HashMap<String, String>(); params = new HashMap<String, String>();
} }
public Object getAttribute(String s) { public Object getAttribute(String s) {
return attributes.get(s); return attributes.get(s);
} }
public Enumeration getAttributeNames() { public Enumeration getAttributeNames() {
return Collections.enumeration(attributes.keySet()); return Collections.enumeration(attributes.keySet());
} }
public ServletContext getContext(String s) { public ServletContext getContext(String s) {
return null; // TODO: Implement return null; // TODO: Implement
} }
public String getInitParameter(String s) { public String getInitParameter(String s) {
return (String) params.get(s); return (String) params.get(s);
} }
public Enumeration getInitParameterNames() { public Enumeration getInitParameterNames() {
return Collections.enumeration(params.keySet()); return Collections.enumeration(params.keySet());
} }
public int getMajorVersion() { public int getMajorVersion() {
return 0; // TODO: Implement return 0; // TODO: Implement
} }
public String getMimeType(String s) { public String getMimeType(String s) {
return null; // TODO: Implement return null; // TODO: Implement
} }
public int getMinorVersion() { public int getMinorVersion() {
return 0; // TODO: Implement return 0; // TODO: Implement
} }
public RequestDispatcher getNamedDispatcher(String s) { public RequestDispatcher getNamedDispatcher(String s) {
return null; // TODO: Implement return null; // TODO: Implement
} }
public String getRealPath(String s) { public String getRealPath(String s) {
return null; // TODO: Implement return null; // TODO: Implement
} }
public RequestDispatcher getRequestDispatcher(String s) { public RequestDispatcher getRequestDispatcher(String s) {
return null; // TODO: Implement return null; // TODO: Implement
} }
public URL getResource(String s) throws MalformedURLException { public URL getResource(String s) throws MalformedURLException {
return null; // TODO: Implement return null; // TODO: Implement
} }
public InputStream getResourceAsStream(String s) { public InputStream getResourceAsStream(String s) {
return null; // TODO: Implement return null; // TODO: Implement
} }
public Set getResourcePaths(String s) { public Set getResourcePaths(String s) {
return null; // TODO: Implement return null; // TODO: Implement
} }
public String getServerInfo() { public String getServerInfo() {
return null; // TODO: Implement return null; // TODO: Implement
} }
public Servlet getServlet(String s) throws ServletException { public Servlet getServlet(String s) throws ServletException {
return null; // TODO: Implement return null; // TODO: Implement
} }
public String getServletContextName() { public String getServletContextName() {
return "mock"; return "mock";
} }
public Enumeration getServletNames() { public Enumeration getServletNames() {
return null; // TODO: Implement return null; // TODO: Implement
} }
public Enumeration getServlets() { public Enumeration getServlets() {
return null; // TODO: Implement return null; // TODO: Implement
} }
public void log(Exception exception, String s) { public void log(Exception exception, String s) {
} }
public void log(String s) { public void log(String s) {
} }
public void log(String s, Throwable throwable) { public void log(String s, Throwable throwable) {
} }
public void removeAttribute(String s) { public void removeAttribute(String s) {
attributes.remove(s); attributes.remove(s);
} }
public void setAttribute(String s, Object obj) { public void setAttribute(String s, Object obj) {
attributes.put(s, obj); attributes.put(s, obj);
} }
} }
} }
static class MockServletRequest implements ServletRequest { static class MockServletRequest implements ServletRequest {
final private Map<String, Object> attributes; final private Map<String, Object> attributes;
public MockServletRequest() { public MockServletRequest() {
attributes = new HashMap<String, Object>(); attributes = new HashMap<String, Object>();
} }
public Object getAttribute(String pKey) { public Object getAttribute(String pKey) {
return attributes.get(pKey); return attributes.get(pKey);
} }
public Enumeration getAttributeNames() { public Enumeration getAttributeNames() {
return Collections.enumeration(attributes.keySet()); return Collections.enumeration(attributes.keySet());
} }
public String getCharacterEncoding() { public String getCharacterEncoding() {
return null; // TODO: Implement return null; // TODO: Implement
} }
public void setCharacterEncoding(String pMessage) throws UnsupportedEncodingException { public void setCharacterEncoding(String pMessage) throws UnsupportedEncodingException {
// TODO: Implement // TODO: Implement
} }
public int getContentLength() { public int getContentLength() {
return 0; // TODO: Implement return 0; // TODO: Implement
} }
public String getContentType() { public String getContentType() {
return null; // TODO: Implement return null; // TODO: Implement
} }
public ServletInputStream getInputStream() throws IOException { public ServletInputStream getInputStream() throws IOException {
return null; // TODO: Implement return null; // TODO: Implement
} }
public String getParameter(String pMessage) { public String getParameter(String pMessage) {
return null; // TODO: Implement return null; // TODO: Implement
} }
public Enumeration getParameterNames() { public Enumeration getParameterNames() {
return null; // TODO: Implement return null; // TODO: Implement
} }
public String[] getParameterValues(String pMessage) { public String[] getParameterValues(String pMessage) {
return new String[0]; // TODO: Implement return new String[0]; // TODO: Implement
} }
public Map getParameterMap() { public Map getParameterMap() {
return null; // TODO: Implement return null; // TODO: Implement
} }
public String getProtocol() { public String getProtocol() {
return null; // TODO: Implement return null; // TODO: Implement
} }
public String getScheme() { public String getScheme() {
return null; // TODO: Implement return null; // TODO: Implement
} }
public String getServerName() { public String getServerName() {
return null; // TODO: Implement return null; // TODO: Implement
} }
public int getServerPort() { public int getServerPort() {
return 0; // TODO: Implement return 0; // TODO: Implement
} }
public BufferedReader getReader() throws IOException { public BufferedReader getReader() throws IOException {
return null; // TODO: Implement return null; // TODO: Implement
} }
public String getRemoteAddr() { public String getRemoteAddr() {
return null; // TODO: Implement return null; // TODO: Implement
} }
public String getRemoteHost() { public String getRemoteHost() {
return null; // TODO: Implement return null; // TODO: Implement
} }
public void setAttribute(String pKey, Object pValue) { public void setAttribute(String pKey, Object pValue) {
attributes.put(pKey, pValue); attributes.put(pKey, pValue);
} }
public void removeAttribute(String pKey) { public void removeAttribute(String pKey) {
attributes.remove(pKey); attributes.remove(pKey);
} }
public Locale getLocale() { public Locale getLocale() {
return null; // TODO: Implement return null; // TODO: Implement
} }
public Enumeration getLocales() { public Enumeration getLocales() {
return null; // TODO: Implement return null; // TODO: Implement
} }
public boolean isSecure() { public boolean isSecure() {
return false; // TODO: Implement return false; // TODO: Implement
} }
public RequestDispatcher getRequestDispatcher(String pMessage) { public RequestDispatcher getRequestDispatcher(String pMessage) {
return null; // TODO: Implement return null; // TODO: Implement
} }
public String getRealPath(String pMessage) { public String getRealPath(String pMessage) {
return null; // TODO: Implement return null; // TODO: Implement
} }
public int getRemotePort() { public int getRemotePort() {
throw new UnsupportedOperationException("Method getRemotePort not implemented");// TODO: Implement throw new UnsupportedOperationException("Method getRemotePort not implemented");// TODO: Implement
} }
public String getLocalName() { public String getLocalName() {
throw new UnsupportedOperationException("Method getLocalName not implemented");// TODO: Implement throw new UnsupportedOperationException("Method getLocalName not implemented");// TODO: Implement
} }
public String getLocalAddr() { public String getLocalAddr() {
throw new UnsupportedOperationException("Method getLocalAddr not implemented");// TODO: Implement throw new UnsupportedOperationException("Method getLocalAddr not implemented");// TODO: Implement
} }
public int getLocalPort() { public int getLocalPort() {
throw new UnsupportedOperationException("Method getLocalPort not implemented");// TODO: Implement throw new UnsupportedOperationException("Method getLocalPort not implemented");// TODO: Implement
} }
} }
static class MockServletResponse implements ServletResponse { static class MockServletResponse implements ServletResponse {
public void flushBuffer() throws IOException { public void flushBuffer() throws IOException {
// TODO: Implement // TODO: Implement
} }
public int getBufferSize() { public int getBufferSize() {
return 0; // TODO: Implement return 0; // TODO: Implement
} }
public String getCharacterEncoding() { public String getCharacterEncoding() {
return null; // TODO: Implement return null; // TODO: Implement
} }
public String getContentType() { public String getContentType() {
throw new UnsupportedOperationException("Method getContentType not implemented");// TODO: Implement throw new UnsupportedOperationException("Method getContentType not implemented");// TODO: Implement
} }
public Locale getLocale() { public Locale getLocale() {
return null; // TODO: Implement return null; // TODO: Implement
} }
public ServletOutputStream getOutputStream() throws IOException { public ServletOutputStream getOutputStream() throws IOException {
return null; // TODO: Implement return null; // TODO: Implement
} }
public PrintWriter getWriter() throws IOException { public PrintWriter getWriter() throws IOException {
return null; // TODO: Implement return null; // TODO: Implement
} }
public void setCharacterEncoding(String charset) { public void setCharacterEncoding(String charset) {
throw new UnsupportedOperationException("Method setCharacterEncoding not implemented");// TODO: Implement throw new UnsupportedOperationException("Method setCharacterEncoding not implemented");// TODO: Implement
} }
public boolean isCommitted() { public boolean isCommitted() {
return false; // TODO: Implement return false; // TODO: Implement
} }
public void reset() { public void reset() {
// TODO: Implement // TODO: Implement
} }
public void resetBuffer() { public void resetBuffer() {
// TODO: Implement // TODO: Implement
} }
public void setBufferSize(int pLength) { public void setBufferSize(int pLength) {
// TODO: Implement // TODO: Implement
} }
public void setContentLength(int pLength) { public void setContentLength(int pLength) {
// TODO: Implement // TODO: Implement
} }
public void setContentType(String pMessage) { public void setContentType(String pMessage) {
// TODO: Implement // TODO: Implement
} }
public void setLocale(Locale pLocale) { public void setLocale(Locale pLocale) {
// TODO: Implement // TODO: Implement
} }
} }
static class MockFilterChain implements FilterChain { static class MockFilterChain implements FilterChain {
public void doFilter(ServletRequest pRequest, ServletResponse pResponse) throws IOException, ServletException { public void doFilter(ServletRequest pRequest, ServletResponse pResponse) throws IOException, ServletException {
// TODO: Implement // TODO: Implement
} }
} }
} }

View File

@@ -1,157 +1,157 @@
package com.twelvemonkeys.servlet; package com.twelvemonkeys.servlet;
import org.junit.Test; import org.junit.Test;
import javax.servlet.*; import javax.servlet.*;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import static org.junit.Assert.*; import static org.junit.Assert.*;
/** /**
* GenericFilterTestCase * GenericFilterTestCase
* <p/> * <p/>
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $ * @author last modified by $Author: haku $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/GenericFilterTestCase.java#1 $ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/GenericFilterTestCase.java#1 $
*/ */
public final class GenericFilterTestCase extends FilterAbstractTestCase { public final class GenericFilterTestCase extends FilterAbstractTestCase {
protected Filter makeFilter() { protected Filter makeFilter() {
return new GenericFilterImpl(); return new GenericFilterImpl();
} }
@Test @Test
public void testInitOncePerRequest() { public void testInitOncePerRequest() {
// Default FALSE // Default FALSE
GenericFilter filter = new GenericFilterImpl(); GenericFilter filter = new GenericFilterImpl();
try { try {
filter.init(makeFilterConfig()); filter.init(makeFilterConfig());
} }
catch (ServletException e) { catch (ServletException e) {
fail(e.getMessage()); fail(e.getMessage());
} }
assertFalse("OncePerRequest should default to false", filter.oncePerRequest); assertFalse("OncePerRequest should default to false", filter.oncePerRequest);
filter.destroy(); filter.destroy();
// TRUE // TRUE
filter = new GenericFilterImpl(); filter = new GenericFilterImpl();
Map<String, String> params = new HashMap<String, String>(); Map<String, String> params = new HashMap<String, String>();
params.put("once-per-request", "true"); params.put("once-per-request", "true");
try { try {
filter.init(makeFilterConfig(params)); filter.init(makeFilterConfig(params));
} }
catch (ServletException e) { catch (ServletException e) {
fail(e.getMessage()); fail(e.getMessage());
} }
assertTrue("oncePerRequest should be true", filter.oncePerRequest); assertTrue("oncePerRequest should be true", filter.oncePerRequest);
filter.destroy(); filter.destroy();
// TRUE // TRUE
filter = new GenericFilterImpl(); filter = new GenericFilterImpl();
params = new HashMap<String, String>(); params = new HashMap<String, String>();
params.put("oncePerRequest", "true"); params.put("oncePerRequest", "true");
try { try {
filter.init(makeFilterConfig(params)); filter.init(makeFilterConfig(params));
} }
catch (ServletException e) { catch (ServletException e) {
fail(e.getMessage()); fail(e.getMessage());
} }
assertTrue("oncePerRequest should be true", filter.oncePerRequest); assertTrue("oncePerRequest should be true", filter.oncePerRequest);
filter.destroy(); filter.destroy();
} }
@Test @Test
public void testFilterOnlyOnce() { public void testFilterOnlyOnce() {
final GenericFilterImpl filter = new GenericFilterImpl(); final GenericFilterImpl filter = new GenericFilterImpl();
filter.setOncePerRequest(true); filter.setOncePerRequest(true);
try { try {
filter.init(makeFilterConfig()); filter.init(makeFilterConfig());
} }
catch (ServletException e) { catch (ServletException e) {
fail(e.getMessage()); fail(e.getMessage());
} }
FilterChain chain = new MyFilterChain(new Filter[] {filter, filter, filter}); FilterChain chain = new MyFilterChain(new Filter[] {filter, filter, filter});
try { try {
chain.doFilter(makeRequest(), makeResponse()); chain.doFilter(makeRequest(), makeResponse());
} }
catch (IOException e) { catch (IOException e) {
fail(e.getMessage()); fail(e.getMessage());
} }
catch (ServletException e) { catch (ServletException e) {
fail(e.getMessage()); fail(e.getMessage());
} }
assertEquals("Filter was invoked more than once!", 1, filter.invocationCount); assertEquals("Filter was invoked more than once!", 1, filter.invocationCount);
filter.destroy(); filter.destroy();
} }
@Test @Test
public void testFilterMultiple() { public void testFilterMultiple() {
final GenericFilterImpl filter = new GenericFilterImpl(); final GenericFilterImpl filter = new GenericFilterImpl();
try { try {
filter.init(makeFilterConfig()); filter.init(makeFilterConfig());
} }
catch (ServletException e) { catch (ServletException e) {
fail(e.getMessage()); fail(e.getMessage());
} }
FilterChain chain = new MyFilterChain(new Filter[] { FilterChain chain = new MyFilterChain(new Filter[] {
filter, filter, filter, filter, filter filter, filter, filter, filter, filter
}); });
try { try {
chain.doFilter(makeRequest(), makeResponse()); chain.doFilter(makeRequest(), makeResponse());
} }
catch (IOException e) { catch (IOException e) {
fail(e.getMessage()); fail(e.getMessage());
} }
catch (ServletException e) { catch (ServletException e) {
fail(e.getMessage()); fail(e.getMessage());
} }
assertEquals("Filter was invoked not invoked five times!", 5, filter.invocationCount); assertEquals("Filter was invoked not invoked five times!", 5, filter.invocationCount);
filter.destroy(); filter.destroy();
} }
private static class GenericFilterImpl extends GenericFilter { private static class GenericFilterImpl extends GenericFilter {
int invocationCount; int invocationCount;
protected void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain) throws IOException, ServletException { protected void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain) throws IOException, ServletException {
invocationCount++; invocationCount++;
pChain.doFilter(pRequest, pResponse); pChain.doFilter(pRequest, pResponse);
} }
} }
private static class MyFilterChain implements FilterChain { private static class MyFilterChain implements FilterChain {
Filter[] mFilters; Filter[] mFilters;
int mCurrentFilter; int mCurrentFilter;
public MyFilterChain(Filter[] pFilters) { public MyFilterChain(Filter[] pFilters) {
if (pFilters == null) { if (pFilters == null) {
throw new IllegalArgumentException("filters == null"); throw new IllegalArgumentException("filters == null");
} }
mFilters = pFilters; mFilters = pFilters;
mCurrentFilter = 0; mCurrentFilter = 0;
} }
public void doFilter(ServletRequest pRequest, ServletResponse pResponse) throws IOException, ServletException { public void doFilter(ServletRequest pRequest, ServletResponse pResponse) throws IOException, ServletException {
if (mCurrentFilter < mFilters.length) { if (mCurrentFilter < mFilters.length) {
mFilters[mCurrentFilter++].doFilter(pRequest, pResponse, this); mFilters[mCurrentFilter++].doFilter(pRequest, pResponse, this);
} }
} }
} }
} }

View File

@@ -1,199 +1,199 @@
package com.twelvemonkeys.servlet; package com.twelvemonkeys.servlet;
import com.twelvemonkeys.util.MapAbstractTestCase; import com.twelvemonkeys.util.MapAbstractTestCase;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.junit.runners.Suite; import org.junit.runners.Suite;
import javax.servlet.*; import javax.servlet.*;
import java.io.InputStream; import java.io.InputStream;
import java.io.Serializable; import java.io.Serializable;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.util.*; import java.util.*;
/** /**
* ServletConfigMapAdapterTestCase * ServletConfigMapAdapterTestCase
* <p/> * <p/>
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/ServletConfigMapAdapterTestCase.java#3 $ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/ServletConfigMapAdapterTestCase.java#3 $
*/ */
@RunWith(Suite.class) @RunWith(Suite.class)
@Suite.SuiteClasses({AbstractServletConfigMapAdapterTest.ServletConfigMapTest.class, AbstractServletConfigMapAdapterTest.FilterConfigMapTest.class, AbstractServletConfigMapAdapterTest.ServletContextMapTest.class}) @Suite.SuiteClasses({AbstractServletConfigMapAdapterTest.ServletConfigMapTest.class, AbstractServletConfigMapAdapterTest.FilterConfigMapTest.class, AbstractServletConfigMapAdapterTest.ServletContextMapTest.class})
public final class ServletConfigMapAdapterTest { public final class ServletConfigMapAdapterTest {
} }
abstract class AbstractServletConfigMapAdapterTest extends MapAbstractTestCase { abstract class AbstractServletConfigMapAdapterTest extends MapAbstractTestCase {
public boolean isPutAddSupported() { public boolean isPutAddSupported() {
return false; return false;
} }
public boolean isPutChangeSupported() { public boolean isPutChangeSupported() {
return false; return false;
} }
public boolean isRemoveSupported() { public boolean isRemoveSupported() {
return false; return false;
} }
public boolean isSetValueSupported() { public boolean isSetValueSupported() {
return false; return false;
} }
private static class TestConfig implements ServletConfig, FilterConfig, ServletContext, Serializable, Cloneable { private static class TestConfig implements ServletConfig, FilterConfig, ServletContext, Serializable, Cloneable {
Map map = new HashMap(); Map map = new HashMap();
public String getServletName() { public String getServletName() {
return "dummy"; // Not needed for this test return "dummy"; // Not needed for this test
} }
public String getFilterName() { public String getFilterName() {
return getServletName(); return getServletName();
} }
public String getServletContextName() { public String getServletContextName() {
return getServletName(); return getServletName();
} }
public ServletContext getServletContext() { public ServletContext getServletContext() {
throw new UnsupportedOperationException("Method getSerlvetContext not implemented"); throw new UnsupportedOperationException("Method getSerlvetContext not implemented");
} }
public String getInitParameter(String s) { public String getInitParameter(String s) {
return (String) map.get(s); return (String) map.get(s);
} }
public Enumeration getInitParameterNames() { public Enumeration getInitParameterNames() {
//noinspection unchecked //noinspection unchecked
return Collections.enumeration(map.keySet()); return Collections.enumeration(map.keySet());
} }
public ServletContext getContext(String uripath) { public ServletContext getContext(String uripath) {
throw new UnsupportedOperationException("Method getContext not implemented"); throw new UnsupportedOperationException("Method getContext not implemented");
} }
public int getMajorVersion() { public int getMajorVersion() {
throw new UnsupportedOperationException("Method getMajorVersion not implemented"); throw new UnsupportedOperationException("Method getMajorVersion not implemented");
} }
public int getMinorVersion() { public int getMinorVersion() {
throw new UnsupportedOperationException("Method getMinorVersion not implemented"); throw new UnsupportedOperationException("Method getMinorVersion not implemented");
} }
public String getMimeType(String file) { public String getMimeType(String file) {
throw new UnsupportedOperationException("Method getMimeType not implemented"); throw new UnsupportedOperationException("Method getMimeType not implemented");
} }
public Set getResourcePaths(String path) { public Set getResourcePaths(String path) {
throw new UnsupportedOperationException("Method getResourcePaths not implemented"); throw new UnsupportedOperationException("Method getResourcePaths not implemented");
} }
public URL getResource(String path) throws MalformedURLException { public URL getResource(String path) throws MalformedURLException {
throw new UnsupportedOperationException("Method getResource not implemented"); throw new UnsupportedOperationException("Method getResource not implemented");
} }
public InputStream getResourceAsStream(String path) { public InputStream getResourceAsStream(String path) {
throw new UnsupportedOperationException("Method getResourceAsStream not implemented"); throw new UnsupportedOperationException("Method getResourceAsStream not implemented");
} }
public RequestDispatcher getRequestDispatcher(String path) { public RequestDispatcher getRequestDispatcher(String path) {
throw new UnsupportedOperationException("Method getRequestDispatcher not implemented"); throw new UnsupportedOperationException("Method getRequestDispatcher not implemented");
} }
public RequestDispatcher getNamedDispatcher(String name) { public RequestDispatcher getNamedDispatcher(String name) {
throw new UnsupportedOperationException("Method getNamedDispatcher not implemented"); throw new UnsupportedOperationException("Method getNamedDispatcher not implemented");
} }
public Servlet getServlet(String name) throws ServletException { public Servlet getServlet(String name) throws ServletException {
throw new UnsupportedOperationException("Method getServlet not implemented"); throw new UnsupportedOperationException("Method getServlet not implemented");
} }
public Enumeration getServlets() { public Enumeration getServlets() {
throw new UnsupportedOperationException("Method getServlets not implemented"); throw new UnsupportedOperationException("Method getServlets not implemented");
} }
public Enumeration getServletNames() { public Enumeration getServletNames() {
throw new UnsupportedOperationException("Method getServletNames not implemented"); throw new UnsupportedOperationException("Method getServletNames not implemented");
} }
public void log(String msg) { public void log(String msg) {
throw new UnsupportedOperationException("Method log not implemented"); throw new UnsupportedOperationException("Method log not implemented");
} }
public void log(Exception exception, String msg) { public void log(Exception exception, String msg) {
throw new UnsupportedOperationException("Method log not implemented"); throw new UnsupportedOperationException("Method log not implemented");
} }
public void log(String message, Throwable throwable) { public void log(String message, Throwable throwable) {
throw new UnsupportedOperationException("Method log not implemented"); throw new UnsupportedOperationException("Method log not implemented");
} }
public String getRealPath(String path) { public String getRealPath(String path) {
throw new UnsupportedOperationException("Method getRealPath not implemented"); throw new UnsupportedOperationException("Method getRealPath not implemented");
} }
public String getServerInfo() { public String getServerInfo() {
throw new UnsupportedOperationException("Method getServerInfo not implemented"); throw new UnsupportedOperationException("Method getServerInfo not implemented");
} }
public Object getAttribute(String name) { public Object getAttribute(String name) {
throw new UnsupportedOperationException("Method getAttribute not implemented"); throw new UnsupportedOperationException("Method getAttribute not implemented");
} }
public Enumeration getAttributeNames() { public Enumeration getAttributeNames() {
throw new UnsupportedOperationException("Method getAttributeNames not implemented"); throw new UnsupportedOperationException("Method getAttributeNames not implemented");
} }
public void setAttribute(String name, Object object) { public void setAttribute(String name, Object object) {
throw new UnsupportedOperationException("Method setAttribute not implemented"); throw new UnsupportedOperationException("Method setAttribute not implemented");
} }
public void removeAttribute(String name) { public void removeAttribute(String name) {
throw new UnsupportedOperationException("Method removeAttribute not implemented"); throw new UnsupportedOperationException("Method removeAttribute not implemented");
} }
} }
public static final class ServletConfigMapTest extends AbstractServletConfigMapAdapterTest { public static final class ServletConfigMapTest extends AbstractServletConfigMapAdapterTest {
public Map makeEmptyMap() { public Map makeEmptyMap() {
ServletConfig config = new TestConfig(); ServletConfig config = new TestConfig();
return new ServletConfigMapAdapter(config); return new ServletConfigMapAdapter(config);
} }
public Map makeFullMap() { public Map makeFullMap() {
ServletConfig config = new TestConfig(); ServletConfig config = new TestConfig();
addSampleMappings(((TestConfig) config).map); addSampleMappings(((TestConfig) config).map);
return new ServletConfigMapAdapter(config); return new ServletConfigMapAdapter(config);
} }
} }
public static final class FilterConfigMapTest extends AbstractServletConfigMapAdapterTest { public static final class FilterConfigMapTest extends AbstractServletConfigMapAdapterTest {
public Map makeEmptyMap() { public Map makeEmptyMap() {
FilterConfig config = new TestConfig(); FilterConfig config = new TestConfig();
return new ServletConfigMapAdapter(config); return new ServletConfigMapAdapter(config);
} }
public Map makeFullMap() { public Map makeFullMap() {
FilterConfig config = new TestConfig(); FilterConfig config = new TestConfig();
addSampleMappings(((TestConfig) config).map); addSampleMappings(((TestConfig) config).map);
return new ServletConfigMapAdapter(config); return new ServletConfigMapAdapter(config);
} }
} }
public static final class ServletContextMapTest extends AbstractServletConfigMapAdapterTest { public static final class ServletContextMapTest extends AbstractServletConfigMapAdapterTest {
public Map makeEmptyMap() { public Map makeEmptyMap() {
ServletContext config = new TestConfig(); ServletContext config = new TestConfig();
return new ServletConfigMapAdapter(config); return new ServletConfigMapAdapter(config);
} }
public Map makeFullMap() { public Map makeFullMap() {
FilterConfig config = new TestConfig(); FilterConfig config = new TestConfig();
addSampleMappings(((TestConfig) config).map); addSampleMappings(((TestConfig) config).map);
return new ServletConfigMapAdapter(config); return new ServletConfigMapAdapter(config);
} }
} }
} }

View File

@@ -1,95 +1,95 @@
package com.twelvemonkeys.servlet; package com.twelvemonkeys.servlet;
import com.twelvemonkeys.util.MapAbstractTestCase; import com.twelvemonkeys.util.MapAbstractTestCase;
import org.mockito.Mockito; import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock; import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer; import org.mockito.stubbing.Answer;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import java.util.*; import java.util.*;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
/** /**
* ServletConfigMapAdapterTestCase * ServletConfigMapAdapterTestCase
* <p/> * <p/>
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: ServletHeadersMapAdapterTestCase.java#1 $ * @version $Id: ServletHeadersMapAdapterTestCase.java#1 $
*/ */
public class ServletHeadersMapAdapterTest extends MapAbstractTestCase { public class ServletHeadersMapAdapterTest extends MapAbstractTestCase {
private static final List<String> HEADER_VALUE_ETAG = Arrays.asList("\"1234567890abcdef\""); private static final List<String> HEADER_VALUE_ETAG = Arrays.asList("\"1234567890abcdef\"");
private static final List<String> HEADER_VALUE_DATE = Arrays.asList(new Date().toString()); private static final List<String> HEADER_VALUE_DATE = Arrays.asList(new Date().toString());
private static final List<String> HEADER_VALUE_FOO = Arrays.asList("one", "two"); private static final List<String> HEADER_VALUE_FOO = Arrays.asList("one", "two");
public boolean isPutAddSupported() { public boolean isPutAddSupported() {
return false; return false;
} }
public boolean isPutChangeSupported() { public boolean isPutChangeSupported() {
return false; return false;
} }
public boolean isRemoveSupported() { public boolean isRemoveSupported() {
return false; return false;
} }
public boolean isSetValueSupported() { public boolean isSetValueSupported() {
return false; return false;
} }
@Override @Override
public boolean isTestSerialization() { public boolean isTestSerialization() {
return false; return false;
} }
public Map makeEmptyMap() { public Map makeEmptyMap() {
HttpServletRequest request = Mockito.mock(HttpServletRequest.class); HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
when(request.getHeaderNames()).thenAnswer(returnEnumeration(Collections.emptyList())); when(request.getHeaderNames()).thenAnswer(returnEnumeration(Collections.emptyList()));
return new ServletHeadersMapAdapter(request); return new ServletHeadersMapAdapter(request);
} }
@Override @Override
public Map makeFullMap() { public Map makeFullMap() {
HttpServletRequest request = Mockito.mock(HttpServletRequest.class); HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
when(request.getHeaderNames()).thenAnswer(returnEnumeration(Arrays.asList(getSampleKeys()))); when(request.getHeaderNames()).thenAnswer(returnEnumeration(Arrays.asList(getSampleKeys())));
when(request.getHeaders("Date")).thenAnswer(returnEnumeration(HEADER_VALUE_DATE)); when(request.getHeaders("Date")).thenAnswer(returnEnumeration(HEADER_VALUE_DATE));
when(request.getHeaders("ETag")).thenAnswer(returnEnumeration(HEADER_VALUE_ETAG)); when(request.getHeaders("ETag")).thenAnswer(returnEnumeration(HEADER_VALUE_ETAG));
when(request.getHeaders("X-Foo")).thenAnswer(returnEnumeration(HEADER_VALUE_FOO)); when(request.getHeaders("X-Foo")).thenAnswer(returnEnumeration(HEADER_VALUE_FOO));
return new ServletHeadersMapAdapter(request); return new ServletHeadersMapAdapter(request);
} }
@Override @Override
public Object[] getSampleKeys() { public Object[] getSampleKeys() {
return new String[] {"Date", "ETag", "X-Foo"}; return new String[] {"Date", "ETag", "X-Foo"};
} }
@Override @Override
public Object[] getSampleValues() { public Object[] getSampleValues() {
return new Object[] {HEADER_VALUE_DATE, HEADER_VALUE_ETAG, HEADER_VALUE_FOO}; return new Object[] {HEADER_VALUE_DATE, HEADER_VALUE_ETAG, HEADER_VALUE_FOO};
} }
@Override @Override
public Object[] getNewSampleValues() { public Object[] getNewSampleValues() {
// Needs to be same length but different values // Needs to be same length but different values
return new Object[3]; return new Object[3];
} }
protected static <T> ReturnNewEnumeration<T> returnEnumeration(final Collection<T> collection) { protected static <T> ReturnNewEnumeration<T> returnEnumeration(final Collection<T> collection) {
return new ReturnNewEnumeration<T>(collection); return new ReturnNewEnumeration<T>(collection);
} }
private static class ReturnNewEnumeration<T> implements Answer<Enumeration<T>> { private static class ReturnNewEnumeration<T> implements Answer<Enumeration<T>> {
private final Collection<T> collection; private final Collection<T> collection;
private ReturnNewEnumeration(final Collection<T> collection) { private ReturnNewEnumeration(final Collection<T> collection) {
this.collection = collection; this.collection = collection;
} }
public Enumeration<T> answer(InvocationOnMock invocation) throws Throwable { public Enumeration<T> answer(InvocationOnMock invocation) throws Throwable {
return Collections.enumeration(collection); return Collections.enumeration(collection);
} }
} }
} }

View File

@@ -1,96 +1,96 @@
package com.twelvemonkeys.servlet; package com.twelvemonkeys.servlet;
import com.twelvemonkeys.util.MapAbstractTestCase; import com.twelvemonkeys.util.MapAbstractTestCase;
import org.mockito.Mockito; import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock; import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer; import org.mockito.stubbing.Answer;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import java.util.*; import java.util.*;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
/** /**
* ServletConfigMapAdapterTestCase * ServletConfigMapAdapterTestCase
* <p/> * <p/>
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/ServletParametersMapAdapterTestCase.java#1 $ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/ServletParametersMapAdapterTestCase.java#1 $
*/ */
public class ServletParametersMapAdapterTest extends MapAbstractTestCase { public class ServletParametersMapAdapterTest extends MapAbstractTestCase {
private static final List<String> PARAM_VALUE_ETAG = Arrays.asList("\"1234567890abcdef\""); private static final List<String> PARAM_VALUE_ETAG = Arrays.asList("\"1234567890abcdef\"");
private static final List<String> PARAM_VALUE_DATE = Arrays.asList(new Date().toString()); private static final List<String> PARAM_VALUE_DATE = Arrays.asList(new Date().toString());
private static final List<String> PARAM_VALUE_FOO = Arrays.asList("one", "two"); private static final List<String> PARAM_VALUE_FOO = Arrays.asList("one", "two");
public boolean isPutAddSupported() { public boolean isPutAddSupported() {
return false; return false;
} }
public boolean isPutChangeSupported() { public boolean isPutChangeSupported() {
return false; return false;
} }
public boolean isRemoveSupported() { public boolean isRemoveSupported() {
return false; return false;
} }
public boolean isSetValueSupported() { public boolean isSetValueSupported() {
return false; return false;
} }
@Override @Override
public boolean isTestSerialization() { public boolean isTestSerialization() {
return false; return false;
} }
public Map makeEmptyMap() { public Map makeEmptyMap() {
HttpServletRequest request = Mockito.mock(HttpServletRequest.class); HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
when(request.getParameterNames()).thenAnswer(returnEnumeration(Collections.emptyList())); when(request.getParameterNames()).thenAnswer(returnEnumeration(Collections.emptyList()));
return new ServletParametersMapAdapter(request); return new ServletParametersMapAdapter(request);
} }
@Override @Override
public Map makeFullMap() { public Map makeFullMap() {
HttpServletRequest request = Mockito.mock(HttpServletRequest.class); HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
when(request.getParameterNames()).thenAnswer(returnEnumeration(Arrays.asList("tag", "date", "foo"))); when(request.getParameterNames()).thenAnswer(returnEnumeration(Arrays.asList("tag", "date", "foo")));
when(request.getParameterValues("date")).thenReturn(PARAM_VALUE_DATE.toArray(new String[PARAM_VALUE_DATE.size()])); when(request.getParameterValues("date")).thenReturn(PARAM_VALUE_DATE.toArray(new String[PARAM_VALUE_DATE.size()]));
when(request.getParameterValues("tag")).thenReturn(PARAM_VALUE_ETAG.toArray(new String[PARAM_VALUE_ETAG.size()])); when(request.getParameterValues("tag")).thenReturn(PARAM_VALUE_ETAG.toArray(new String[PARAM_VALUE_ETAG.size()]));
when(request.getParameterValues("foo")).thenReturn(PARAM_VALUE_FOO.toArray(new String[PARAM_VALUE_FOO.size()])); when(request.getParameterValues("foo")).thenReturn(PARAM_VALUE_FOO.toArray(new String[PARAM_VALUE_FOO.size()]));
return new ServletParametersMapAdapter(request); return new ServletParametersMapAdapter(request);
} }
@Override @Override
public Object[] getSampleKeys() { public Object[] getSampleKeys() {
return new String[] {"date", "tag", "foo"}; return new String[] {"date", "tag", "foo"};
} }
@Override @Override
public Object[] getSampleValues() { public Object[] getSampleValues() {
return new Object[] {PARAM_VALUE_DATE, PARAM_VALUE_ETAG, PARAM_VALUE_FOO}; return new Object[] {PARAM_VALUE_DATE, PARAM_VALUE_ETAG, PARAM_VALUE_FOO};
} }
@Override @Override
public Object[] getNewSampleValues() { public Object[] getNewSampleValues() {
// Needs to be same length but different values // Needs to be same length but different values
return new Object[3]; return new Object[3];
} }
protected static <T> ReturnNewEnumeration<T> returnEnumeration(final Collection<T> collection) { protected static <T> ReturnNewEnumeration<T> returnEnumeration(final Collection<T> collection) {
return new ReturnNewEnumeration<T>(collection); return new ReturnNewEnumeration<T>(collection);
} }
private static class ReturnNewEnumeration<T> implements Answer<Enumeration<T>> { private static class ReturnNewEnumeration<T> implements Answer<Enumeration<T>> {
private final Collection<T> collection; private final Collection<T> collection;
private ReturnNewEnumeration(final Collection<T> collection) { private ReturnNewEnumeration(final Collection<T> collection) {
this.collection = collection; this.collection = collection;
} }
public Enumeration<T> answer(InvocationOnMock invocation) throws Throwable { public Enumeration<T> answer(InvocationOnMock invocation) throws Throwable {
return Collections.enumeration(collection); return Collections.enumeration(collection);
} }
} }
} }

View File

@@ -1,23 +1,23 @@
package com.twelvemonkeys.servlet; package com.twelvemonkeys.servlet;
import com.twelvemonkeys.lang.ObjectAbstractTestCase; import com.twelvemonkeys.lang.ObjectAbstractTestCase;
import javax.servlet.ServletResponse; import javax.servlet.ServletResponse;
/** /**
* ServletResponseAbsrtactTestCase * ServletResponseAbsrtactTestCase
* <p/> * <p/>
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $ * @author last modified by $Author: haku $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/ServletResponseAbsrtactTestCase.java#1 $ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/ServletResponseAbsrtactTestCase.java#1 $
*/ */
public abstract class ServletResponseAbsrtactTestCase extends ObjectAbstractTestCase { public abstract class ServletResponseAbsrtactTestCase extends ObjectAbstractTestCase {
protected Object makeObject() { protected Object makeObject() {
return makeServletResponse(); return makeServletResponse();
} }
protected abstract ServletResponse makeServletResponse(); protected abstract ServletResponse makeServletResponse();
// TODO: Implement // TODO: Implement
} }

View File

@@ -1,115 +1,115 @@
package com.twelvemonkeys.servlet; package com.twelvemonkeys.servlet;
import com.twelvemonkeys.io.OutputStreamAbstractTestCase; import com.twelvemonkeys.io.OutputStreamAbstractTestCase;
import org.junit.Test; import org.junit.Test;
import javax.servlet.Filter; import javax.servlet.Filter;
import javax.servlet.ServletResponse; import javax.servlet.ServletResponse;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import static org.junit.Assert.*; import static org.junit.Assert.*;
/** /**
* TrimWhiteSpaceFilterTestCase * TrimWhiteSpaceFilterTestCase
* <p/> * <p/>
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $ * @author last modified by $Author: haku $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/TrimWhiteSpaceFilterTestCase.java#1 $ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/TrimWhiteSpaceFilterTestCase.java#1 $
*/ */
public class TrimWhiteSpaceFilterTestCase extends FilterAbstractTestCase { public class TrimWhiteSpaceFilterTestCase extends FilterAbstractTestCase {
protected Filter makeFilter() { protected Filter makeFilter() {
return new TrimWhiteSpaceFilter(); return new TrimWhiteSpaceFilter();
} }
public static final class TrimWSFilterOutputStreamTestCase extends OutputStreamAbstractTestCase { public static final class TrimWSFilterOutputStreamTestCase extends OutputStreamAbstractTestCase {
protected OutputStream makeObject() { protected OutputStream makeObject() {
// NOTE: ByteArrayOutputStream does not implement flush or close... // NOTE: ByteArrayOutputStream does not implement flush or close...
return makeOutputStream(new ByteArrayOutputStream(16)); return makeOutputStream(new ByteArrayOutputStream(16));
} }
protected OutputStream makeOutputStream(OutputStream pWrapped) { protected OutputStream makeOutputStream(OutputStream pWrapped) {
return new TrimWhiteSpaceFilter.TrimWSFilterOutputStream(pWrapped); return new TrimWhiteSpaceFilter.TrimWSFilterOutputStream(pWrapped);
} }
@Test @Test
public void testTrimWSOnlyWS() throws IOException { public void testTrimWSOnlyWS() throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream(64); ByteArrayOutputStream out = new ByteArrayOutputStream(64);
OutputStream trim = makeOutputStream(out); OutputStream trim = makeOutputStream(out);
String input = " \n\n\t \t" + (char) 0x0a + ' ' + (char) 0x0d + "\r "; String input = " \n\n\t \t" + (char) 0x0a + ' ' + (char) 0x0d + "\r ";
trim.write(input.getBytes()); trim.write(input.getBytes());
trim.flush(); trim.flush();
trim.close(); trim.close();
assertEquals("Should be trimmed", "\"\"", '"' + new String(out.toByteArray()) + '"'); assertEquals("Should be trimmed", "\"\"", '"' + new String(out.toByteArray()) + '"');
} }
@Test @Test
public void testTrimWSLeading() throws IOException { public void testTrimWSLeading() throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream(64); ByteArrayOutputStream out = new ByteArrayOutputStream(64);
OutputStream trim = makeOutputStream(out); OutputStream trim = makeOutputStream(out);
byte[] input = " \n<?xml version=\"1.0\"?>\n\t <not-really-well-formed/> \t".getBytes(); byte[] input = " \n<?xml version=\"1.0\"?>\n\t <not-really-well-formed/> \t".getBytes();
String trimmed = "<?xml version=\"1.0\"?>\n<not-really-well-formed/> "; // TODO: This is pr spec (the trailing space). But probably quite stupid... String trimmed = "<?xml version=\"1.0\"?>\n<not-really-well-formed/> "; // TODO: This is pr spec (the trailing space). But probably quite stupid...
trim.write(input); trim.write(input);
trim.flush(); trim.flush();
trim.close(); trim.close();
assertEquals("Should be trimmed", '"' + trimmed + '"', '"' + new String(out.toByteArray()) + '"'); assertEquals("Should be trimmed", '"' + trimmed + '"', '"' + new String(out.toByteArray()) + '"');
} }
@Test @Test
public void testTrimWSOffsetLength() throws IOException { public void testTrimWSOffsetLength() throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream(64); ByteArrayOutputStream out = new ByteArrayOutputStream(64);
OutputStream trim = makeOutputStream(out); OutputStream trim = makeOutputStream(out);
// Kindly generated by http://lipsum.org/ :-) // Kindly generated by http://lipsum.org/ :-)
byte[] input = (" \n\tLorem ipsum dolor sit amet, consectetuer adipiscing elit.\n\r\n\r" + byte[] input = (" \n\tLorem ipsum dolor sit amet, consectetuer adipiscing elit.\n\r\n\r" +
"Etiam arcu neque, \n\rmalesuada blandit,\t\n\r\n\r\n\n\n\r\n\r\r\n\n\t rutrum quis, molestie at, diam.\n" + "Etiam arcu neque, \n\rmalesuada blandit,\t\n\r\n\r\n\n\n\r\n\r\r\n\n\t rutrum quis, molestie at, diam.\n" +
" Nulla elementum elementum eros.\n \t\t\n\r" + " Nulla elementum elementum eros.\n \t\t\n\r" +
"Ut rhoncus, turpis in pellentesque volutpat, sapien sem accumsan augue, a scelerisque nibh erat vel magna.\n" + "Ut rhoncus, turpis in pellentesque volutpat, sapien sem accumsan augue, a scelerisque nibh erat vel magna.\n" +
" Phasellus diam orci, dignissim et, gravida vitae, venenatis eu, elit.\n" + " Phasellus diam orci, dignissim et, gravida vitae, venenatis eu, elit.\n" +
"\t\t\tSuspendisse dictum enim at nisl. Integer magna erat, viverra sit amet, consectetuer nec, accumsan ut, mi.\n" + "\t\t\tSuspendisse dictum enim at nisl. Integer magna erat, viverra sit amet, consectetuer nec, accumsan ut, mi.\n" +
"\n\r\r\r\n\rNunc ultricies \n\n\n consectetuer mauris. " + "\n\r\r\r\n\rNunc ultricies \n\n\n consectetuer mauris. " +
"Nulla lectus mauris, viverra ac, pulvinar a, commodo quis, nulla.\n " + "Nulla lectus mauris, viverra ac, pulvinar a, commodo quis, nulla.\n " +
"Ut eget nulla. In est dolor, convallis \t non, tincidunt \tvestibulum, porttitor et, eros.\n " + "Ut eget nulla. In est dolor, convallis \t non, tincidunt \tvestibulum, porttitor et, eros.\n " +
"\t\t \t \n\rDonec vehicula ultrices nisl.").getBytes(); "\t\t \t \n\rDonec vehicula ultrices nisl.").getBytes();
String trimmed = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\n" + String trimmed = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\n" +
"Etiam arcu neque, malesuada blandit,\trutrum quis, molestie at, diam.\n" + "Etiam arcu neque, malesuada blandit,\trutrum quis, molestie at, diam.\n" +
"Nulla elementum elementum eros.\n" + "Nulla elementum elementum eros.\n" +
"Ut rhoncus, turpis in pellentesque volutpat, sapien sem accumsan augue, a scelerisque nibh erat vel magna.\n" + "Ut rhoncus, turpis in pellentesque volutpat, sapien sem accumsan augue, a scelerisque nibh erat vel magna.\n" +
"Phasellus diam orci, dignissim et, gravida vitae, venenatis eu, elit.\n" + "Phasellus diam orci, dignissim et, gravida vitae, venenatis eu, elit.\n" +
"Suspendisse dictum enim at nisl. Integer magna erat, viverra sit amet, consectetuer nec, accumsan ut, mi.\n" + "Suspendisse dictum enim at nisl. Integer magna erat, viverra sit amet, consectetuer nec, accumsan ut, mi.\n" +
"Nunc ultricies consectetuer mauris. Nulla lectus mauris, viverra ac, pulvinar a, commodo quis, nulla.\n" + "Nunc ultricies consectetuer mauris. Nulla lectus mauris, viverra ac, pulvinar a, commodo quis, nulla.\n" +
"Ut eget nulla. In est dolor, convallis non, tincidunt vestibulum, porttitor et, eros.\n" + "Ut eget nulla. In est dolor, convallis non, tincidunt vestibulum, porttitor et, eros.\n" +
"Donec vehicula ultrices nisl."; "Donec vehicula ultrices nisl.";
int chunkLenght = 5; int chunkLenght = 5;
int bytesLeft = input.length; int bytesLeft = input.length;
while (bytesLeft > chunkLenght) { while (bytesLeft > chunkLenght) {
trim.write(input, input.length - bytesLeft, chunkLenght); trim.write(input, input.length - bytesLeft, chunkLenght);
bytesLeft -= chunkLenght; bytesLeft -= chunkLenght;
} }
trim.write(input, input.length - bytesLeft, bytesLeft); trim.write(input, input.length - bytesLeft, bytesLeft);
trim.flush(); trim.flush();
trim.close(); trim.close();
assertEquals("Should be trimmed", '"' + trimmed + '"', '"' + new String(out.toByteArray()) + '"'); assertEquals("Should be trimmed", '"' + trimmed + '"', '"' + new String(out.toByteArray()) + '"');
} }
// TODO: Test that we DON'T remove too much... // TODO: Test that we DON'T remove too much...
} }
public static final class TrimWSServletResponseWrapperTestCase extends ServletResponseAbsrtactTestCase { public static final class TrimWSServletResponseWrapperTestCase extends ServletResponseAbsrtactTestCase {
protected ServletResponse makeServletResponse() { protected ServletResponse makeServletResponse() {
return new TrimWhiteSpaceFilter.TrimWSServletResponseWrapper(new MockServletResponse()); return new TrimWhiteSpaceFilter.TrimWSServletResponseWrapper(new MockServletResponse());
} }
} }
} }

View File

@@ -0,0 +1,341 @@
package com.twelvemonkeys.servlet.image.aoi;
import com.twelvemonkeys.servlet.image.aoi.DefaultAreaOfInterest;
import com.twelvemonkeys.servlet.image.aoi.UniformAreaOfInterest;
import org.junit.Test;
import java.awt.*;
import static org.junit.Assert.assertEquals;
/**
* @author <a href="mailto:erlend@escenic.com">Erlend Hamnaberg</a>
* @version $Revision: $
*/
public class AreaOfInterestTestCase {
private static final Dimension SQUARE_200_200 = new Dimension(200, 200);
private static final Dimension PORTRAIT_100_200 = new Dimension(100, 200);
private static final Dimension LANDSCAPE_200_100 = new Dimension(200, 100);
private static final Dimension SQUARE_100_100 = new Dimension(100, 100);
// -----------------------------------------------------------------------------------------------------------------
// Absolute AOI
// -----------------------------------------------------------------------------------------------------------------
@Test
public void testGetAOIAbsolute() {
assertEquals(new Rectangle(10, 10, 100, 100), new DefaultAreaOfInterest(SQUARE_200_200).getAOI(10, 10, 100, 100));
}
@Test
public void testGetAOIAbsoluteOverflowX() {
assertEquals(new Rectangle(10, 10, 90, 100), new DefaultAreaOfInterest(PORTRAIT_100_200).getAOI(10, 10, 100, 100));
}
@Test
public void testGetAOIAbsoluteOverflowW() {
assertEquals(new Rectangle(0, 10, 100, 100), new DefaultAreaOfInterest(PORTRAIT_100_200).getAOI(0, 10, 110, 100));
}
@Test
public void testGetAOIAbsoluteOverflowY() {
assertEquals(new Rectangle(10, 10, 100, 90), new DefaultAreaOfInterest(LANDSCAPE_200_100).getAOI(10, 10, 100, 100));
}
@Test
public void testGetAOIAbsoluteOverflowH() {
assertEquals(new Rectangle(10, 0, 100, 100), new DefaultAreaOfInterest(LANDSCAPE_200_100).getAOI(10, 0, 100, 110));
}
// -----------------------------------------------------------------------------------------------------------------
// Uniform AOI centered
// -----------------------------------------------------------------------------------------------------------------
@Test
public void testGetAOIUniformCenteredS2SUp() {
assertEquals(new Rectangle(0, 0, 100, 100), new UniformAreaOfInterest(SQUARE_100_100).getAOI(-1, -1, 333, 333));
}
@Test
public void testGetAOIUniformCenteredS2SDown() {
assertEquals(new Rectangle(0, 0, 100, 100), new UniformAreaOfInterest(SQUARE_100_100).getAOI(-1, -1, 33, 33));
}
@Test
public void testGetAOIUniformCenteredS2SNormalized() {
assertEquals(new Rectangle(0, 0, 100, 100), new UniformAreaOfInterest(SQUARE_100_100).getAOI(-1, -1, 100, 100));
}
@Test
public void testGetAOIUniformCenteredS2W() {
assertEquals(new Rectangle(0, 25, 100, 50), new UniformAreaOfInterest(SQUARE_100_100).getAOI(-1, -1, 200, 100));
}
@Test
public void testGetAOIUniformCenteredS2WNormalized() {
assertEquals(new Rectangle(0, 25, 100, 50), new UniformAreaOfInterest(SQUARE_100_100).getAOI(-1, -1, 100, 50));
}
@Test
public void testGetAOIUniformCenteredS2N() {
assertEquals(new Rectangle(25, 0, 50, 100), new UniformAreaOfInterest(SQUARE_100_100).getAOI(-1, -1, 100, 200));
}
@Test
public void testGetAOIUniformCenteredS2NNormalized() {
assertEquals(new Rectangle(25, 0, 50, 100), new UniformAreaOfInterest(SQUARE_100_100).getAOI(-1, -1, 50, 100));
}
@Test
public void testGetAOIUniformCenteredW2S() {
assertEquals(new Rectangle(50, 0, 100, 100), new UniformAreaOfInterest(LANDSCAPE_200_100).getAOI(-1, -1, 333, 333));
}
@Test
public void testGetAOIUniformCenteredW2SNormalized() {
assertEquals(new Rectangle(50, 0, 100, 100), new UniformAreaOfInterest(LANDSCAPE_200_100).getAOI(-1, -1, 100, 100));
}
@Test
public void testGetAOIUniformCenteredW2W() {
assertEquals(new Rectangle(0, 0, 200, 100), new UniformAreaOfInterest(LANDSCAPE_200_100).getAOI(-1, -1, 100, 50));
}
@Test
public void testGetAOIUniformCenteredW2WW() {
assertEquals(new Rectangle(0, 25, 200, 50), new UniformAreaOfInterest(LANDSCAPE_200_100).getAOI(-1, -1, 200, 50));
}
@Test
public void testGetAOIUniformCenteredW2WN() {
assertEquals(new Rectangle(25, 0, 150, 100), new UniformAreaOfInterest(LANDSCAPE_200_100).getAOI(-1, -1, 75, 50));
}
@Test
public void testGetAOIUniformCenteredW2WNNormalized() {
assertEquals(new Rectangle(25, 0, 150, 100), new UniformAreaOfInterest(LANDSCAPE_200_100).getAOI(-1, -1, 150, 100));
}
@Test
public void testGetAOIUniformCenteredW2WNormalized() {
assertEquals(new Rectangle(0, 0, 200, 100), new UniformAreaOfInterest(LANDSCAPE_200_100).getAOI(-1, -1, 200, 100));
}
@Test
public void testGetAOIUniformCenteredW2N() {
assertEquals(new Rectangle(75, 0, 50, 100), new UniformAreaOfInterest(LANDSCAPE_200_100).getAOI(-1, -1, 100, 200));
}
@Test
public void testGetAOIUniformCenteredW2NNormalized() {
assertEquals(new Rectangle(75, 0, 50, 100), new UniformAreaOfInterest(LANDSCAPE_200_100).getAOI(-1, -1, 50, 100));
}
@Test
public void testGetAOIUniformCenteredN2S() {
assertEquals(new Rectangle(0, 50, 100, 100), new UniformAreaOfInterest(PORTRAIT_100_200).getAOI(-1, -1, 333, 333));
}
@Test
public void testGetAOIUniformCenteredN2SNormalized() {
assertEquals(new Rectangle(0, 50, 100, 100), new UniformAreaOfInterest(PORTRAIT_100_200).getAOI(-1, -1, 100, 100));
}
@Test
public void testGetAOIUniformCenteredN2W() {
assertEquals(new Rectangle(0, 75, 100, 50), new UniformAreaOfInterest(PORTRAIT_100_200).getAOI(-1, -1, 200, 100));
}
@Test
public void testGetAOIUniformCenteredN2WNormalized() {
assertEquals(new Rectangle(0, 75, 100, 50), new UniformAreaOfInterest(PORTRAIT_100_200).getAOI(-1, -1, 100, 50));
}
@Test
public void testGetAOIUniformCenteredN2N() {
assertEquals(new Rectangle(0, 0, 100, 200), new UniformAreaOfInterest(PORTRAIT_100_200).getAOI(-1, -1, 50, 100));
}
@Test
public void testGetAOIUniformCenteredN2NN() {
assertEquals(new Rectangle(25, 0, 50, 200), new UniformAreaOfInterest(PORTRAIT_100_200).getAOI(-1, -1, 25, 100));
}
@Test
public void testGetAOIUniformCenteredN2NW() {
assertEquals(new Rectangle(0, 33, 100, 133), new UniformAreaOfInterest(PORTRAIT_100_200).getAOI(-1, -1, 75, 100));
}
@Test
public void testGetAOIUniformCenteredN2NWNormalized() {
assertEquals(new Rectangle(0, 37, 100, 125), new UniformAreaOfInterest(PORTRAIT_100_200).getAOI(-1, -1, 100, 125));
}
@Test
public void testGetAOIUniformCenteredN2NNormalized() {
assertEquals(new Rectangle(0, 0, 100, 200), new UniformAreaOfInterest(PORTRAIT_100_200).getAOI(-1, -1, 100, 200));
}
// -----------------------------------------------------------------------------------------------------------------
// Absolute AOI centered
// -----------------------------------------------------------------------------------------------------------------
@Test
public void testGetAOICenteredS2SUp() {
assertEquals(new Rectangle(0, 0, 100, 100), new DefaultAreaOfInterest(SQUARE_100_100).getAOI(-1, -1, 333, 333));
}
@Test
public void testGetAOICenteredS2SDown() {
assertEquals(new Rectangle(33, 33, 33, 33), new DefaultAreaOfInterest(SQUARE_100_100).getAOI(-1, -1, 33, 33));
}
@Test
public void testGetAOICenteredS2SSame() {
assertEquals(new Rectangle(0, 0, 100, 100), new DefaultAreaOfInterest(SQUARE_100_100).getAOI(-1, -1, 100, 100));
}
@Test
public void testGetAOICenteredS2WOverflow() {
assertEquals(new Rectangle(0, 0, 100, 100), new DefaultAreaOfInterest(SQUARE_100_100).getAOI(-1, -1, 200, 100));
}
@Test
public void testGetAOICenteredS2W() {
assertEquals(new Rectangle(40, 45, 20, 10), new DefaultAreaOfInterest(SQUARE_100_100).getAOI(-1, -1, 20, 10));
}
@Test
public void testGetAOICenteredS2WMax() {
assertEquals(new Rectangle(0, 25, 100, 50), new DefaultAreaOfInterest(SQUARE_100_100).getAOI(-1, -1, 100, 50));
}
@Test
public void testGetAOICenteredS2NOverflow() {
assertEquals(new Rectangle(0, 0, 100, 100), new DefaultAreaOfInterest(SQUARE_100_100).getAOI(-1, -1, 100, 200));
}
@Test
public void testGetAOICenteredS2N() {
assertEquals(new Rectangle(45, 40, 10, 20), new DefaultAreaOfInterest(SQUARE_100_100).getAOI(-1, -1, 10, 20));
}
@Test
public void testGetAOICenteredS2NMax() {
assertEquals(new Rectangle(25, 0, 50, 100), new DefaultAreaOfInterest(SQUARE_100_100).getAOI(-1, -1, 50, 100));
}
@Test
public void testGetAOICenteredW2SOverflow() {
assertEquals(new Rectangle(0, 0, 200, 100), new DefaultAreaOfInterest(LANDSCAPE_200_100).getAOI(-1, -1, 333, 333));
}
@Test
public void testGetAOICenteredW2S() {
assertEquals(new Rectangle(75, 25, 50, 50), new DefaultAreaOfInterest(LANDSCAPE_200_100).getAOI(-1, -1, 50, 50));
}
@Test
public void testGetAOICenteredW2SMax() {
assertEquals(new Rectangle(50, 0, 100, 100), new DefaultAreaOfInterest(LANDSCAPE_200_100).getAOI(-1, -1, 100, 100));
}
@Test
public void testGetAOICenteredW2WOverflow() {
assertEquals(new Rectangle(0, 0, 200, 100), new DefaultAreaOfInterest(LANDSCAPE_200_100).getAOI(-1, -1, 300, 200));
}
@Test
public void testGetAOICenteredW2W() {
assertEquals(new Rectangle(50, 25, 100, 50), new DefaultAreaOfInterest(LANDSCAPE_200_100).getAOI(-1, -1, 100, 50));
}
@Test
public void testGetAOICenteredW2WW() {
assertEquals(new Rectangle(10, 40, 180, 20), new DefaultAreaOfInterest(LANDSCAPE_200_100).getAOI(-1, -1, 180, 20));
}
@Test
public void testGetAOICenteredW2WN() {
assertEquals(new Rectangle(62, 25, 75, 50), new DefaultAreaOfInterest(LANDSCAPE_200_100).getAOI(-1, -1, 75, 50));
}
@Test
public void testGetAOICenteredW2WSame() {
assertEquals(new Rectangle(0, 0, 200, 100), new DefaultAreaOfInterest(LANDSCAPE_200_100).getAOI(-1, -1, 200, 100));
}
@Test
public void testGetAOICenteredW2NOverflow() {
assertEquals(new Rectangle(50, 0, 100, 100), new DefaultAreaOfInterest(LANDSCAPE_200_100).getAOI(-1, -1, 100, 200));
}
@Test
public void testGetAOICenteredW2N() {
assertEquals(new Rectangle(83, 25, 33, 50), new DefaultAreaOfInterest(LANDSCAPE_200_100).getAOI(-1, -1, 33, 50));
}
@Test
public void testGetAOICenteredW2NMax() {
assertEquals(new Rectangle(75, 0, 50, 100), new DefaultAreaOfInterest(LANDSCAPE_200_100).getAOI(-1, -1, 50, 100));
}
@Test
public void testGetAOICenteredN2S() {
assertEquals(new Rectangle(33, 83, 33, 33), new DefaultAreaOfInterest(PORTRAIT_100_200).getAOI(-1, -1, 33, 33));
}
@Test
public void testGetAOICenteredN2SMax() {
assertEquals(new Rectangle(0, 50, 100, 100), new DefaultAreaOfInterest(PORTRAIT_100_200).getAOI(-1, -1, 100, 100));
}
@Test
public void testGetAOICenteredN2WOverflow() {
assertEquals(new Rectangle(0, 50, 100, 100), new DefaultAreaOfInterest(PORTRAIT_100_200).getAOI(-1, -1, 200, 100));
}
@Test
public void testGetAOICenteredN2W() {
assertEquals(new Rectangle(40, 95, 20, 10), new DefaultAreaOfInterest(PORTRAIT_100_200).getAOI(-1, -1, 20, 10));
}
@Test
public void testGetAOICenteredN2WMax() {
assertEquals(new Rectangle(0, 75, 100, 50), new DefaultAreaOfInterest(PORTRAIT_100_200).getAOI(-1, -1, 100, 50));
}
@Test
public void testGetAOICenteredN2N() {
assertEquals(new Rectangle(45, 90, 10, 20), new DefaultAreaOfInterest(PORTRAIT_100_200).getAOI(-1, -1, 10, 20));
}
@Test
public void testGetAOICenteredN2NSame() {
assertEquals(new Rectangle(0, 0, 100, 200), new DefaultAreaOfInterest(PORTRAIT_100_200).getAOI(-1, -1, 100, 200));
}
@Test
public void testGetAOICenteredN2NN() {
assertEquals(new Rectangle(37, 50, 25, 100), new DefaultAreaOfInterest(PORTRAIT_100_200).getAOI(-1, -1, 25, 100));
}
@Test
public void testGetAOICenteredN2NW() {
assertEquals(new Rectangle(12, 50, 75, 100), new DefaultAreaOfInterest(PORTRAIT_100_200).getAOI(-1, -1, 75, 100));
}
@Test
public void testGetAOICenteredN2NWMax() {
assertEquals(new Rectangle(0, 37, 100, 125), new DefaultAreaOfInterest(PORTRAIT_100_200).getAOI(-1, -1, 100, 125));
}
@Test
public void testGetAOICenteredN2NMax() {
assertEquals(new Rectangle(0, 0, 100, 200), new DefaultAreaOfInterest(PORTRAIT_100_200).getAOI(-1, -1, 100, 200));
}
}