From 5131e02e6c14e12d10c059c976543394cd939146 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=98yvind=20L=C3=B8kling?=
- * @author Eirik Torske
- */
-@Deprecated
-public class DebugUtil {
-
- // Constants
-
- /** Field PRINTSTREAM_IS_NULL_ERROR_MESSAGE */
- public static final String PRINTSTREAM_IS_NULL_ERROR_MESSAGE = "PrintStream is null";
-
- /** Field OBJECT_IS_NULL_ERROR_MESSAGE */
- public static final String OBJECT_IS_NULL_ERROR_MESSAGE = "Object is null";
-
- /** Field INTARRAY_IS_NULL_ERROR_MESSAGE */
- public static final String INTARRAY_IS_NULL_ERROR_MESSAGE = "int array is null";
-
- /** Field STRINGARRAY_IS_NULL_ERROR_MESSAGE */
- public static final String STRINGARRAY_IS_NULL_ERROR_MESSAGE = "String array is null";
-
- /** Field ENUMERATION_IS_NULL_ERROR_MESSAGE */
- public static final String ENUMERATION_IS_NULL_ERROR_MESSAGE = "Enumeration is null";
-
- /** Field COLLECTION_IS_NULL_ERROR_MESSAGE */
- public static final String COLLECTION_IS_NULL_ERROR_MESSAGE = "Collection is null";
-
- /** Field COLLECTION_IS_EMPTY_ERROR_MESSAGE */
- public static final String COLLECTION_IS_EMPTY_ERROR_MESSAGE = "Collection contains no elements";
-
- /** Field MAP_IS_NULL_ERROR_MESSAGE */
- public static final String MAP_IS_NULL_ERROR_MESSAGE = "Map is null";
-
- /** Field MAP_IS_EMPTY_ERROR_MESSAGE */
- public static final String MAP_IS_EMPTY_ERROR_MESSAGE = "Map contains no elements";
-
- /** Field PROPERTIES_IS_NULL_ERROR_MESSAGE */
- public static final String PROPERTIES_IS_NULL_ERROR_MESSAGE = "Properties is null";
-
- /** Field PROPERTIES_IS_EMPTY_ERROR_MESSAGE */
- public static final String PROPERTIES_IS_EMPTY_ERROR_MESSAGE = "Properties contains no elements";
-
- /** Field CALENDAR_IS_NULL_ERROR_MESSAGE */
- public static final String CALENDAR_IS_NULL_ERROR_MESSAGE = "Calendar is null";
-
- /** Field CALENDAR_CAUSATION_ERROR_MESSAGE */
- public static final String CALENDAR_CAUSATION_ERROR_MESSAGE = "The causation of the calendars is wrong";
-
- /** Field TIMEDIFFERENCES_IS_NULL_ERROR_MESSAGE */
- public static final String TIMEDIFFERENCES_IS_NULL_ERROR_MESSAGE = "Inner TimeDifference object is null";
-
- /** Field TIMEDIFFERENCES_WRONG_DATATYPE_ERROR_MESSAGE */
- public static final String TIMEDIFFERENCES_WRONG_DATATYPE_ERROR_MESSAGE =
- "Element in TimeDifference collection is not a TimeDifference object";
-
- /** Field DEBUG */
- public static final String DEBUG = "**** external debug: ";
-
- /** Field INFO */
- public static final String INFO = "**** external info: ";
-
- /** Field WARNING */
- public static final String WARNING = "**** external warning: ";
-
- /** Field ERROR */
- public static final String ERROR = "**** external error: ";
-
- /**
- * Builds a prefix message to be used in front of info messages for identification purposes.
- * The message format is:
- *
- * @param pObject the {@code java.lang.Object} to be debugged. If the object ia a {@code java.lang.String} object, it is assumed that it is the class name given directly.
- * @return a prefix for an info message.
- */
- public static String getPrefixInfoMessage(final Object pObject) {
-
- StringBuilder buffer = new StringBuilder();
-
- buffer.append(INFO);
- buffer.append(getTimestamp());
- buffer.append(" ");
- if (pObject == null) {
- buffer.append("[unknown class]");
- } else {
- if (pObject instanceof String) {
- buffer.append((String) pObject);
- } else {
- buffer.append(getClassName(pObject));
- }
- }
- buffer.append(": ");
- return buffer.toString();
- }
-
- /**
- * Builds a prefix message to be used in front of debug messages for identification purposes.
- * The message format is:
- *
- * @param pObject the {@code java.lang.Object} to be debugged. If the object ia a {@code java.lang.String} object, it is assumed that it is the class name given directly.
- * @return a prefix for a debug message.
- */
- public static String getPrefixDebugMessage(final Object pObject) {
-
- StringBuilder buffer = new StringBuilder();
-
- buffer.append(DEBUG);
- buffer.append(getTimestamp());
- buffer.append(" ");
- if (pObject == null) {
- buffer.append("[unknown class]");
- } else {
- if (pObject instanceof String) {
- buffer.append((String) pObject);
- } else {
- buffer.append(getClassName(pObject));
- }
- }
- buffer.append(": ");
- return buffer.toString();
- }
-
- /**
- * Builds a prefix message to be used in front of warning messages for identification purposes.
- * The message format is:
- *
- * @param pObject the {@code java.lang.Object} to be debugged. If the object ia a {@code java.lang.String} object, it is assumed that it is the class name given directly.
- * @return a prefix for a warning message.
- */
- public static String getPrefixWarningMessage(final Object pObject) {
-
- StringBuilder buffer = new StringBuilder();
-
- buffer.append(WARNING);
- buffer.append(getTimestamp());
- buffer.append(" ");
- if (pObject == null) {
- buffer.append("[unknown class]");
- } else {
- if (pObject instanceof String) {
- buffer.append((String) pObject);
- } else {
- buffer.append(getClassName(pObject));
- }
- }
- buffer.append(": ");
- return buffer.toString();
- }
-
- /**
- * Builds a prefix message to be used in front of error messages for identification purposes.
- * The message format is:
- *
- * @param pObject the {@code java.lang.Object} to be debugged. If the object ia a {@code java.lang.String} object, it is assumed that it is the class name given directly.
- * @return a prefix for an error message.
- */
- public static String getPrefixErrorMessage(final Object pObject) {
-
- StringBuilder buffer = new StringBuilder();
-
- buffer.append(ERROR);
- buffer.append(getTimestamp());
- buffer.append(" ");
- if (pObject == null) {
- buffer.append("[unknown class]");
- } else {
- if (pObject instanceof String) {
- buffer.append((String) pObject);
- } else {
- buffer.append(getClassName(pObject));
- }
- }
- buffer.append(": ");
- return buffer.toString();
- }
-
- /**
- * The "default" method that invokes a given method of an object and prints the results to a {@code java.io.PrintStream}.
- * @param pObject the {@code java.lang.Object} to be printed.
- * @param pMethodName a {@code java.lang.String} holding the name of the method to be invoked.
- * @param pPrintStream the {@code java.io.PrintStream} for flushing the results.
- */
- public static void printDebug(final Object pObject, final String pMethodName, final PrintStream pPrintStream) {
-
- if (pPrintStream == null) {
- System.err.println(PRINTSTREAM_IS_NULL_ERROR_MESSAGE);
- return;
- }
- if (pObject == null) {
- pPrintStream.println(OBJECT_IS_NULL_ERROR_MESSAGE);
- return;
- }
- if (!StringUtil.isEmpty(pMethodName)) {
- try {
- Method objectMethod = pObject.getClass().getMethod(pMethodName, null);
- Object retVal = objectMethod.invoke(pObject, null);
-
- if (retVal != null) {
- printDebug(retVal, null, pPrintStream);
- } else {
- throw new Exception();
- }
- } catch (Exception e) {
-
- // Default
- pPrintStream.println(pObject.toString());
- }
- } else { // Ultimate default
- pPrintStream.println(pObject.toString());
- }
- }
-
- /**
- * Prints the object's {@code toString()} method to a {@code java.io.PrintStream}.
- *
- * @param pObject the {@code java.lang.Object} to be printed.
- * @param pPrintStream the {@code java.io.PrintStream} for flushing the results.
- */
- public static void printDebug(final Object pObject, final PrintStream pPrintStream) {
- printDebug(pObject, null, pPrintStream);
- }
-
- /**
- * Prints the object's {@code toString()} method to {@code System.out}.
- *
- * @param pObject the {@code java.lang.Object} to be printed.
- */
- public static void printDebug(final Object pObject) {
- printDebug(pObject, System.out);
- }
-
- /**
- * Prints a line break.
- */
- public static void printDebug() {
- System.out.println();
- }
-
- /**
- * Prints a primitive {@code boolean} to {@code System.out}.
- *
- * @param pBoolean the {@code boolean} to be printed.
- */
- public static void printDebug(final boolean pBoolean) {
- printDebug(new Boolean(pBoolean).toString());
- }
-
- /**
- * Prints a primitive {@code int} to {@code System.out}.
- *
- *
- * @param pInt
- */
- public static void printDebug(final int pInt) {
- printDebug(new Integer(pInt).toString());
- }
-
- /**
- * Prints the content of a {@code int[]} to a {@code java.io.PrintStream}.
- *
- * @param pIntArray the {@code int[]} to be printed.
- * @param pPrintStream the {@code java.io.PrintStream} for flushing the results.
- */
- public static void printDebug(final int[] pIntArray, final PrintStream pPrintStream) {
-
- if (pPrintStream == null) {
- System.err.println(PRINTSTREAM_IS_NULL_ERROR_MESSAGE);
- return;
- }
- if (pIntArray == null) {
- pPrintStream.println(INTARRAY_IS_NULL_ERROR_MESSAGE);
- return;
- }
- for (int i = 0; i < pIntArray.length; i++) {
- pPrintStream.println(pIntArray[i]);
- }
- }
-
- /**
- * Prints the content of a {@code int[]} to {@code System.out}.
- *
- * @param pIntArray the {@code int[]} to be printed.
- */
- public static void printDebug(final int[] pIntArray) {
- printDebug(pIntArray, System.out);
- }
-
- /**
- * Prints a number of character check methods from the {@code java.lang.Character} class to a {@code java.io.PrintStream}.
- *
- * @param pChar the {@code java.lang.char} to be debugged.
- * @param pPrintStream the {@code java.io.PrintStream} for flushing the results.
- */
- public static void printDebug(final char pChar, final PrintStream pPrintStream) {
-
- if (pPrintStream == null) {
- System.err.println(PRINTSTREAM_IS_NULL_ERROR_MESSAGE);
- return;
- }
- pPrintStream.println("Character.getNumericValue(pChar): " + Character.getNumericValue(pChar));
- pPrintStream.println("Character.getType(pChar): " + Character.getType(pChar));
- pPrintStream.println("pChar.hashCode(): " + new Character(pChar).hashCode());
- pPrintStream.println("Character.isDefined(pChar): " + Character.isDefined(pChar));
- pPrintStream.println("Character.isDigit(pChar): " + Character.isDigit(pChar));
- pPrintStream.println("Character.isIdentifierIgnorable(pChar): " + Character.isIdentifierIgnorable(pChar));
- pPrintStream.println("Character.isISOControl(pChar): " + Character.isISOControl(pChar));
- pPrintStream.println("Character.isJavaIdentifierPart(pChar): " + Character.isJavaIdentifierPart(pChar));
- pPrintStream.println("Character.isJavaIdentifierStart(pChar): " + Character.isJavaIdentifierStart(pChar));
- pPrintStream.println("Character.isLetter(pChar): " + Character.isLetter(pChar));
- pPrintStream.println("Character.isLetterOrDigit(pChar): " + Character.isLetterOrDigit(pChar));
- pPrintStream.println("Character.isLowerCase(pChar): " + Character.isLowerCase(pChar));
- pPrintStream.println("Character.isSpaceChar(pChar): " + Character.isSpaceChar(pChar));
- pPrintStream.println("Character.isTitleCase(pChar): " + Character.isTitleCase(pChar));
- pPrintStream.println("Character.isUnicodeIdentifierPart(pChar): " + Character.isUnicodeIdentifierPart(pChar));
- pPrintStream.println("Character.isUnicodeIdentifierStart(pChar): " + Character.isUnicodeIdentifierStart(pChar));
- pPrintStream.println("Character.isUpperCase(pChar): " + Character.isUpperCase(pChar));
- pPrintStream.println("Character.isWhitespace(pChar): " + Character.isWhitespace(pChar));
- pPrintStream.println("pChar.toString(): " + new Character(pChar).toString());
- }
-
- /**
- * Prints a number of character check methods from the {@code java.lang.Character} class to {@code System.out}.
- *
- * @param pChar the {@code java.lang.char} to be debugged.
- */
- public static void printDebug(final char pChar) {
- printDebug(pChar, System.out);
- }
-
- /**
- * Prints the content of a {@code java.lang.String[]} to a {@code java.io.PrintStream}.
- *
- * @param pStringArray the {@code java.lang.String[]} to be printed.
- * @param pPrintStream the {@code java.io.PrintStream} for flushing the results.
- */
- public static void printDebug(final String[] pStringArray, final PrintStream pPrintStream) {
-
- if (pPrintStream == null) {
- System.err.println(PRINTSTREAM_IS_NULL_ERROR_MESSAGE);
- return;
- }
- if (pStringArray == null) {
- pPrintStream.println(STRINGARRAY_IS_NULL_ERROR_MESSAGE);
- return;
- }
- for (int i = 0; i < pStringArray.length; i++) {
- pPrintStream.println(pStringArray[i]);
- }
- }
-
- /**
- * Prints the content of a {@code java.lang.String[]} to {@code System.out}.
- *
- * @param pStringArray the {@code java.lang.String[]} to be printed.
- */
- public static void printDebug(final String[] pStringArray) {
- printDebug(pStringArray, System.out);
- }
-
- /**
- * Invokes a given method of every element in a {@code java.util.Enumeration} and prints the results to a {@code java.io.PrintStream}.
- * The method to be invoked must have no formal parameters.
- *
- * If an exception is throwed during the method invocation, the element's {@code toString()} method is called.
- * For bulk data types, recursive invocations and invocations of other methods in this class, are used.
- *
- * @param pEnumeration the {@code java.util.Enumeration} to be printed.
- * @param pMethodName a {@code java.lang.String} holding the name of the method to be invoked on each collection element.
- * @param pPrintStream the {@code java.io.PrintStream} for flushing the results.
- * @see {@code java.util.Enumeration}
- */
- public static void printDebug(final Enumeration pEnumeration, final String pMethodName, final PrintStream pPrintStream) {
-
- if (pPrintStream == null) {
- System.err.println(PRINTSTREAM_IS_NULL_ERROR_MESSAGE);
- return;
- }
- if (pEnumeration == null) {
- pPrintStream.println(ENUMERATION_IS_NULL_ERROR_MESSAGE);
- return;
- }
- while (pEnumeration.hasMoreElements()) {
- printDebug(pEnumeration.nextElement(), pMethodName, pPrintStream);
- }
- }
-
- /**
- * Invokes a given method of every element in a {@code java.util.Enumeration} and prints the results to a {@code java.io.PrintStream}.
- * The method to be invoked must have no formal parameters.
- *
- * If an exception is throwed during the method invocation, the element's {@code toString()} method is called.
- * For bulk data types, recursive invocations and invocations of other methods in this class, are used.
- *
- * @param pEnumeration the {@code java.util.Enumeration} to be printed.
- * @param pMethodName a {@code java.lang.String} holding the name of the method to be invoked on each collection element.
- * @see {@code java.util.Enumeration}
- */
- public static void printDebug(final Enumeration pEnumeration, final String pMethodName) {
- printDebug(pEnumeration, pMethodName, System.out);
- }
-
- /**
- * Invokes a given method of every element in a {@code java.util.Enumeration} and prints the results to a {@code java.io.PrintStream}.
- * The method to be invoked must have no formal parameters. The default is calling an element's {@code toString()} method.
- *
- * If an exception is throwed during the method invocation, the element's {@code toString()} method is called.
- * For bulk data types, recursive invocations and invocations of other methods in this class, are used.
- *
- * @param pEnumeration the {@code java.util.Enumeration} to be printed.
- * @see {@code java.util.Enumeration}
- */
- public static void printDebug(final Enumeration pEnumeration) {
- printDebug(pEnumeration, null, System.out);
- }
-
- /**
- * Invokes a given method of every element in a {@code java.util.Collection} and prints the results to a {@code java.io.PrintStream}.
- * The method to be invoked must have no formal parameters.
- *
- * If an exception is throwed during the method invocation, the element's {@code toString()} method is called.
- * For bulk data types, recursive invocations and invocations of other methods in this class, are used.
- *
- * Be aware that the {@code Collection} interface embraces a large portion of the bulk data types in the {@code java.util} package,
- * e.g. {@code List}, {@code Set}, {@code Vector} and {@code HashSet}.
- *
- * For debugging of arrays, use the method {@code java.util.Arrays.asList(Object[])} method for converting the object array to a list before calling this method.
- *
- * @param pCollection the {@code java.util.Collection} to be printed.
- * @param pMethodName a {@code java.lang.String} holding the name of the method to be invoked on each collection element.
- * @param pPrintStream the {@code java.io.PrintStream} for flushing the results.
- * @see {@code java.util.Collection}
- */
- public static void printDebug(final Collection pCollection, final String pMethodName, final PrintStream pPrintStream) {
-
- if (pPrintStream == null) {
- System.err.println(PRINTSTREAM_IS_NULL_ERROR_MESSAGE);
- return;
- }
- if (pCollection == null) {
- pPrintStream.println(COLLECTION_IS_NULL_ERROR_MESSAGE);
- return;
- } else if (pCollection.isEmpty()) {
- pPrintStream.println(COLLECTION_IS_EMPTY_ERROR_MESSAGE);
- return;
- }
- for (Iterator i = pCollection.iterator(); i.hasNext(); ) {
- printDebug(i.next(), pMethodName, pPrintStream);
- }
- }
-
- /**
- * Invokes a given method of every element in a {@code java.util.Collection} and prints the results to {@code System.out}.
- * The method to be invoked must have no formal parameters.
- *
- * If an exception is throwed during the method invocation, the element's {@code toString()} method is called.
- * For bulk data types, recursive invocations and invocations of other methods in this class, are used.
- *
- * Be aware that the {@code Collection} interface embraces a large portion of the bulk data types in the {@code java.util} package,
- * e.g. {@code List}, {@code Set}, {@code Vector} and {@code HashSet}.
- *
- * For debugging of arrays, use the method {@code java.util.Arrays.asList(Object[])} method for converting the object array to a list before calling this method.
- *
- * @param pCollection the {@code java.util.Collection} to be printed.
- * @param pMethodName a {@code java.lang.String} holding the name of the method to be invoked on each collection element.
- * @see {@code java.util.Collection}
- */
- public static void printDebug(final Collection pCollection, final String pMethodName) {
- printDebug(pCollection, pMethodName, System.out);
- }
-
- /**
- * Prints the content of a {@code java.util.Collection} to a {@code java.io.PrintStream}.
- *
- * Not all data types are supported so far. The default is calling an element's {@code toString()} method.
- * For bulk data types, recursive invocations and invocations of other methods in this class, are used.
- *
- * Be aware that the {@code Collection} interface embraces a large portion of the bulk data types in the {@code java.util} package,
- * e.g. {@code List}, {@code Set}, {@code Vector} and {@code HashSet}.
- *
- * For debugging of arrays, use the method {@code java.util.Arrays.asList(Object[])} method for converting the object array to a list before calling this method.
- *
- * @param pCollection the {@code java.util.Collection} to be printed.
- * @param pPrintStream the {@code java.io.PrintStream} for flushing the results.
- * @see {@code java.util.Collection}
- */
- public static void printDebug(final Collection pCollection, final PrintStream pPrintStream) {
- printDebug(pCollection, null, pPrintStream);
- }
-
- /**
- * Prints the content of a {@code java.util.Collection} to {@code System.out}.
- *
- * Not all data types are supported so far. The default is calling an element's {@code toString()} method.
- * For bulk data types, recursive invocations and invocations of other methods in this class, are used.
- *
- * Be aware that the {@code Collection} interface embraces a large portion of the bulk data types in the {@code java.util} package,
- * e.g. {@code List}, {@code Set}, {@code Vector} and {@code HashSet}.
- *
- * For debugging of arrays, use the method {@code java.util.Arrays.asList(Object[])} method for converting the object array to a list before calling this method.
- *
- * @param pCollection the {@code java.util.Collection} to be printed.
- * @see {@code java.util.Collection}
- */
- public static void printDebug(final Collection pCollection) {
- printDebug(pCollection, System.out);
- }
-
- /**
- * Invokes a given method of every object in a {@code java.util.Map} and prints the results to a {@code java.io.PrintStream}.
- * The method called must have no formal parameters.
- *
- * If an exception is throwed during the method invocation, the element's {@code toString()} method is called.
- * For bulk data types, recursive invocations and invocations of other methods in this class, are used.
- *
- * @param pMap the {@code java.util.Map} to be printed.
- * @param pMethodName a {@code java.lang.String} holding the name of the method to be invoked on each mapped object.
- * @param pPrintStream the {@code java.io.PrintStream} for flushing the results.
- * @see {@code java.util.Map}
- */
- public static void printDebug(final Map pMap, final String pMethodName, final PrintStream pPrintStream) {
-
- if (pPrintStream == null) {
- System.err.println(PRINTSTREAM_IS_NULL_ERROR_MESSAGE);
- return;
- }
- if (pMap == null) {
- pPrintStream.println(MAP_IS_NULL_ERROR_MESSAGE);
- return;
- } else if (pMap.isEmpty()) {
- pPrintStream.println(MAP_IS_EMPTY_ERROR_MESSAGE);
- return;
- }
- Object mKeyObject;
- Object mEntryObject;
-
- for (Iterator i = pMap.keySet().iterator(); i.hasNext(); ) {
- mKeyObject = i.next();
- mEntryObject = pMap.get(mKeyObject);
- if ((mKeyObject instanceof String) && (mEntryObject instanceof String)) {
- pPrintStream.println((String) mKeyObject + ": " + mEntryObject);
- } else if ((mKeyObject instanceof String) && (mEntryObject instanceof List)) {
- printDebug((List) mEntryObject, pPrintStream);
- } else if ((mKeyObject instanceof String) && (mEntryObject instanceof Set)) {
- printDebug((Set) mEntryObject, pPrintStream);
- } else if (mKeyObject instanceof String) {
- if (!StringUtil.isEmpty(pMethodName)) {
- try {
- Method objectMethod = mEntryObject.getClass().getMethod(pMethodName, null);
- Object retVal = objectMethod.invoke(mEntryObject, null);
-
- if (retVal != null) {
- pPrintStream.println((String) mKeyObject + ": " + retVal.toString());
- } else { // Default execution
- throw new Exception();
- }
- } catch (Exception e) {
-
- // Default
- pPrintStream.println((String) mKeyObject + ": " + mEntryObject.toString());
- }
- } else { // Default
- pPrintStream.println((String) mKeyObject + ": " + mEntryObject.toString());
- }
- } else if ((mKeyObject instanceof Integer) && (mEntryObject instanceof String)) {
- pPrintStream.println((Integer) mKeyObject + ": " + mEntryObject);
- } else if ((mKeyObject instanceof Integer) && (mEntryObject instanceof List)) {
- printDebug((List) mEntryObject, pPrintStream);
- } else if ((mKeyObject instanceof String) && (mEntryObject instanceof Set)) {
- printDebug((Set) mEntryObject, pPrintStream);
- } else if (mKeyObject instanceof Integer) {
- if (!StringUtil.isEmpty(pMethodName)) {
- try {
- Method objectMethod = mEntryObject.getClass().getMethod(pMethodName, null);
- Object retVal = objectMethod.invoke(mEntryObject, null);
-
- if (retVal != null) {
- pPrintStream.println((Integer) mKeyObject + ": " + retVal.toString());
- } else { // Default execution
- throw new Exception();
- }
- } catch (Exception e) {
-
- // Default
- pPrintStream.println((Integer) mKeyObject + ": " + mEntryObject.toString());
- }
- } else { // Default
- pPrintStream.println((Integer) mKeyObject + ": " + mEntryObject.toString());
- }
- }
-
- // More..
- //else if
- }
- }
-
- /**
- * Invokes a given method of every object in a {@code java.util.Map} to {@code System.out}.
- * The method called must have no formal parameters.
- *
- * If an exception is throwed during the method invocation, the element's {@code toString()} method is called.
- * @param pMap the {@code java.util.Map} to be printed.
- * @param pMethodName a {@code java.lang.String} holding the name of the method to be invoked on each mapped object.
- * @see {@code java.util.Map}
- */
- public static void printDebug(final Map pMap, final String pMethodName) {
- printDebug(pMap, pMethodName, System.out);
- }
-
- /**
- * Prints the content of a {@code java.util.Map} to a {@code java.io.PrintStream}.
- *
- * Not all data types are supported so far. The default is calling an element's {@code toString()} method.
- * @param pMap the {@code java.util.Map} to be printed.
- * @param pPrintStream the {@code java.io.PrintStream} for flushing the results.
- * @see {@code java.util.Map}
- */
- public static void printDebug(final Map pMap, final PrintStream pPrintStream) {
- printDebug(pMap, null, pPrintStream);
- }
-
- /**
- * Prints the content of a {@code java.util.Map} to {@code System.out}.
- *
- * Not all data types are supported so far. The default is calling an element's {@code toString()} method.
- * @param pMap the {@code java.util.Map} to be printed.
- * @see {@code java.util.Map}
- */
- public static void printDebug(final Map pMap) {
- printDebug(pMap, System.out);
- }
-
- /**
- * Prints the content of a {@code java.util.Properties} to a {@code java.io.PrintStream}.
- *
- * @param pProperties the {@code java.util.Properties} to be printed.
- * @param pPrintStream the {@code java.io.PrintStream} for flushing the results.
- * @see {@code java.util.Properties}
- */
- public static void printDebug(final Properties pProperties, final PrintStream pPrintStream) {
-
- if (pPrintStream == null) {
- System.err.println(PRINTSTREAM_IS_NULL_ERROR_MESSAGE);
- return;
- }
- if (pProperties == null) {
- pPrintStream.println(PROPERTIES_IS_NULL_ERROR_MESSAGE);
- return;
- } else if (pProperties.isEmpty()) {
- pPrintStream.println(PROPERTIES_IS_EMPTY_ERROR_MESSAGE);
- return;
- }
- for (Enumeration e = pProperties.propertyNames(); e.hasMoreElements(); ) {
- String key = (String) e.nextElement();
-
- pPrintStream.println(key + ": " + pProperties.getProperty(key));
- }
- }
-
- /**
- * Prints the content of a {@code java.util.Properties} to {@code System.out}.
- *
- * @param pProperties the {@code java.util.Properties} to be printed.
- * @see {@code java.util.Properties}
- */
- public static void printDebug(final Properties pProperties) {
- printDebug(pProperties, System.out);
- }
-
- // Timestamp utilities
-
- /**
- * Prints out the calendar time.
- *
- * @param pCalendar the {@code java.util.Calendar} object from which to extract the date information.
- * @param pPrintStream the {@code java.io.PrintStream} for flushing the results.
- * @see {@code java.util.Calendar}
- */
- public static void printTimestamp(final Calendar pCalendar, final PrintStream pPrintStream) {
-
- if (pPrintStream == null) {
- System.err.println(PRINTSTREAM_IS_NULL_ERROR_MESSAGE);
- return;
- }
- pPrintStream.println(getTimestamp(pCalendar));
- }
-
- /**
- * Prints out the system time.
- *
- * @param pPrintStream the {@code java.io.PrintStream} for flushing the results.
- * @see {@code java.util.GregorianCalendar}
- */
- public static void printTimestamp(final PrintStream pPrintStream) {
-
- GregorianCalendar cal = new GregorianCalendar();
-
- printTimestamp(cal, pPrintStream);
- }
-
- /**
- * Prints out the system time to {@code System.out}.
- */
- public static void printTimestamp() {
- printTimestamp(System.out);
- }
-
- /**
- * Returns a presentation of the date based on the given milliseconds.
- *
- * @param pMilliseconds The specified number of milliseconds since the standard base time known as "the epoch", namely January 1, 1970, 00:00:00 GMT.
- * @return a presentation of the calendar time.
- * @see {@code java.util.Calendar}
- */
- public static String getTimestamp(final String pMilliseconds) {
- return getTimestamp(Long.parseLong(pMilliseconds));
- }
-
- /**
- * Returns a presentation of the date based on the given milliseconds.
- *
- * @param pMilliseconds The specified number of milliseconds since the standard base time known as "the epoch", namely January 1, 1970, 00:00:00 GMT.
- * @return a presentation of the calendar time.
- * @see {@code java.util.Calendar}
- */
- public static String getTimestamp(final long pMilliseconds) {
-
- java.util.Date date = new java.util.Date(pMilliseconds);
- java.util.Calendar calendar = new GregorianCalendar();
-
- calendar.setTime(date);
- return getTimestamp(calendar);
- }
-
- /**
- * Returns a presentation of the given calendar's time.
- *
- * @param pCalendar the {@code java.util.Calendar} object from which to extract the date information.
- * @return a presentation of the calendar time.
- * @see {@code java.util.Calendar}
- */
- public static String getTimestamp(final Calendar pCalendar) {
- return buildTimestamp(pCalendar);
- }
-
- /**
- * @return a presentation of the system time.
- */
- public static String getTimestamp() {
-
- GregorianCalendar cal = new GregorianCalendar();
-
- return getTimestamp(cal);
- }
-
- /**
- * Builds a presentation of the given calendar's time. This method contains the common timestamp format used in this class.
- * @return a presentation of the calendar time.
- */
- protected static String buildTimestamp(final Calendar pCalendar) {
-
- if (pCalendar == null) {
- return CALENDAR_IS_NULL_ERROR_MESSAGE;
- }
-
- // The timestamp format
- StringBuilder timestamp = new StringBuilder();
-
- //timestamp.append(DateUtil.getMonthName(new Integer(pCalendar.get(Calendar.MONTH)).toString(), "0", "us", "MEDIUM", false) + " ");
- timestamp.append(DateFormat.getDateInstance(DateFormat.MEDIUM).format(pCalendar.getTime()));
-
- //timestamp.append(pCalendar.get(Calendar.DAY_OF_MONTH) + " ");
- timestamp.append(" ");
- timestamp.append(StringUtil.pad(new Integer(pCalendar.get(Calendar.HOUR_OF_DAY)).toString(), 2, "0", true) + ":");
- timestamp.append(StringUtil.pad(new Integer(pCalendar.get(Calendar.MINUTE)).toString(), 2, "0", true) + ":");
- timestamp.append(StringUtil.pad(new Integer(pCalendar.get(Calendar.SECOND)).toString(), 2, "0", true) + ":");
- timestamp.append(StringUtil.pad(new Integer(pCalendar.get(Calendar.MILLISECOND)).toString(), 3, "0", true));
- return timestamp.toString();
- }
-
- /**
- * Builds the time difference between two millisecond representations.
- *
- * This method is to be used with small time intervals between 0 ms up to a couple of minutes.
- *
- * @param pStartTime the start time.
- * @param pEndTime the end time.
- * @return the time difference in milliseconds.
- */
- public static String buildTimeDifference(final long pStartTime, final long pEndTime) {
-
- //return pEndTime - pStartTime;
- StringBuilder retVal = new StringBuilder();
-
- // The time difference in milliseconds
- long timeDifference = pEndTime - pStartTime;
-
- if (timeDifference < 1000) {
- retVal.append(timeDifference);
- retVal.append(" ms");
- } else {
- long seconds = timeDifference / 1000;
-
- timeDifference = timeDifference % 1000;
- retVal.append(seconds);
- retVal.append("s ");
- retVal.append(timeDifference);
- retVal.append("ms");
- }
-
- //return retVal.toString() + " (original timeDifference: " + new String(new Long(pEndTime - pStartTime).toString()) + ")";
- return retVal.toString();
- }
-
- /**
- * Builds the time difference between the given time and present time.
- *
- * This method is to be used with small time intervals between 0 ms up to a couple of minutes.
- *
- * @param pStartTime the start time.
- * @return the time difference in milliseconds.
- */
- public static String buildTimeDifference(final long pStartTime) {
-
- long presentTime = System.currentTimeMillis();
-
- return buildTimeDifference(pStartTime, presentTime);
- }
-
- /**
- * Prints out the difference between two millisecond representations.
- * The start time is subtracted from the end time.
- *
- * @param pStartTime the start time.
- * @param pEndTime the end time.
- * @param pPrintStream the {@code java.io.PrintStream} for flushing the results.
- */
- public static void printTimeDifference(final long pStartTime, final long pEndTime, final PrintStream pPrintStream) {
-
- if (pPrintStream == null) {
- System.err.println(PRINTSTREAM_IS_NULL_ERROR_MESSAGE);
- return;
- }
- pPrintStream.println(buildTimeDifference(pStartTime, pEndTime));
- }
-
- /**
- * Prints out the difference between two millisecond representations.
- * The start time is subtracted from the end time.
- *
- * @param pStartTime the start time.
- * @param pEndTime the end time.
- */
- public static void printTimeDifference(final long pStartTime, final long pEndTime) {
- printTimeDifference(pStartTime, pEndTime, System.out);
- }
-
- /**
- * Prints out the difference between the given time and present time.
- * The start time is subtracted from the present time.
- *
- * @param pStartTime the start time.
- * @param pPrintStream the {@code java.io.PrintStream} for flushing the results.
- */
- public static void printTimeDifference(final long pStartTime, final PrintStream pPrintStream) {
- printTimeDifference(pStartTime, System.currentTimeMillis(), pPrintStream);
- }
-
- /**
- * Prints out the difference between the given time and present time to {@code System.out}.
- * The start time is subtracted from the present time.
- *
- * usage:
- *
- * @param pStartTime the start time.
- */
- public static void printTimeDifference(final long pStartTime) {
- printTimeDifference(pStartTime, System.out);
- }
-
- /**
- * Builds a string representing the difference between two calendar times.
- * The first calendar object is subtracted from the second one.
- *
- * This method is to be used with time intervals between 0 ms up to several hours.
- *
- * @param pStartCalendar the first {@code java.util.Calendar}.
- * @param pEndCalendar the second {@code java.util.Calendar}.
- * @return a string representation of the time difference.
- * @see {@code java.util.Calendar}
- */
- public static String buildTimeDifference(final Calendar pStartCalendar, final Calendar pEndCalendar) {
-
- if (pStartCalendar == null) {
- return CALENDAR_IS_NULL_ERROR_MESSAGE;
- }
- if (pEndCalendar == null) {
- return CALENDAR_IS_NULL_ERROR_MESSAGE;
- }
- if (pEndCalendar.before(pStartCalendar)) {
- return CALENDAR_CAUSATION_ERROR_MESSAGE;
- }
- int dateDiff = pEndCalendar.get(Calendar.DATE) - pStartCalendar.get(Calendar.DATE);
- int hourDiff = pEndCalendar.get(Calendar.HOUR_OF_DAY) - pStartCalendar.get(Calendar.HOUR_OF_DAY);
- int minuteDiff = pEndCalendar.get(Calendar.MINUTE) - pStartCalendar.get(Calendar.MINUTE);
- int secondDiff = pEndCalendar.get(Calendar.SECOND) - pStartCalendar.get(Calendar.SECOND);
- int milliSecondDiff = pEndCalendar.get(Calendar.MILLISECOND) - pStartCalendar.get(Calendar.MILLISECOND);
-
- if (milliSecondDiff < 0) {
- secondDiff--;
- milliSecondDiff += 1000;
- }
- if (secondDiff < 0) {
- minuteDiff--;
- secondDiff += 60;
- }
- if (minuteDiff < 0) {
- hourDiff--;
- minuteDiff += 60;
- }
- while (dateDiff > 0) {
- dateDiff--;
- hourDiff += 24;
- }
-
- // Time difference presentation format
- StringBuilder buffer = new StringBuilder();
-
- if ((hourDiff == 0) && (minuteDiff == 0) && (secondDiff == 0)) {
- buffer.append(milliSecondDiff);
- buffer.append("ms");
- } else if ((hourDiff == 0) && (minuteDiff == 0)) {
- buffer.append(secondDiff);
- buffer.append("s ");
- buffer.append(milliSecondDiff);
- buffer.append("ms");
- } else if (hourDiff == 0) {
- buffer.append(minuteDiff);
- buffer.append("m ");
- buffer.append(secondDiff);
- buffer.append(",");
- buffer.append(milliSecondDiff);
- buffer.append("s");
- } else {
- buffer.append(hourDiff);
- buffer.append("h ");
- buffer.append(minuteDiff);
- buffer.append("m ");
- buffer.append(secondDiff);
- buffer.append(",");
- buffer.append(milliSecondDiff);
- buffer.append("s");
- }
- return buffer.toString();
- }
-
- /**
- * Prints out the difference between to calendar times.
- * The first calendar object is subtracted from the second one.
- *
- * @param pStartCalendar the first {@code java.util.Calendar}.
- * @param pEndCalendar the second {@code java.util.Calendar}.
- * @param pPrintStream the {@code java.io.PrintStream} for flushing the results.
- * @see {@code java.util.Calendar}
- */
- public static void printTimeDifference(final Calendar pStartCalendar, final Calendar pEndCalendar, final PrintStream pPrintStream) {
-
- if (pPrintStream == null) {
- System.err.println(PRINTSTREAM_IS_NULL_ERROR_MESSAGE);
- return;
- }
- pPrintStream.println(buildTimeDifference(pStartCalendar, pEndCalendar));
- }
-
- /**
- * Prints out the difference between to calendar times two {@code System.out}.
- * The first calendar object is subtracted from the second one.
- *
- * @param pStartCalendar the first {@code java.util.Calendar}.
- * @param pEndCalendar the second {@code java.util.Calendar}.
- * @see {@code java.util.Calendar}
- */
- public static void printTimeDifference(final Calendar pStartCalendar, final Calendar pEndCalendar) {
- printTimeDifference(pStartCalendar, pEndCalendar, System.out);
- }
-
- /**
- * Prints out the difference between the given calendar time and present time.
- *
- * @param pStartCalendar the {@code java.util.Calendar} to compare with present time.
- * @param pPrintStream the {@code java.io.PrintStream} for flushing the results.
- * @see {@code java.util.Calendar}
- */
- public static void printTimeDifference(final Calendar pStartCalendar, final PrintStream pPrintStream) {
-
- GregorianCalendar endCalendar = new GregorianCalendar();
-
- printTimeDifference(pStartCalendar, endCalendar, pPrintStream);
- }
-
- /**
- * Prints out the difference between the given calendar time and present time to {@code System.out}.
- *
- * usage:
- *
- * @param pStartCalendar the {@code java.util.Calendar} to compare with present time.
- * @see {@code java.util.Calendar}
- */
- public static void printTimeDifference(final Calendar pStartCalendar) {
-
- GregorianCalendar endCalendar = new GregorianCalendar();
-
- printTimeDifference(pStartCalendar, endCalendar);
- }
-
- /**
- * Prints out a {@code com.iml.oslo.eito.util.DebugUtil.TimeDifference} object.
- *
- * @param pTimeDifference the {@code com.twelvemonkeys.util.DebugUtil.TimeDifference} to investigate.
- * @param pPrintStream the {@code java.io.PrintStream} for flushing the results.
- */
- public static void printTimeDifference(final TimeDifference pTimeDifference, final PrintStream pPrintStream) {
- printTimeDifference(pTimeDifference.getStartCalendar(), pTimeDifference.getEndCalendar(), pPrintStream);
- }
-
- /**
- * Prints out a {@code com.iml.oslo.eito.util.DebugUtil.TimeDifference} object to {@code System.out}.
- *
- * @param pTimeDifference the {@code com.twelvemonkeys.util.DebugUtil.TimeDifference} to investigate.
- */
- public static void printTimeDifference(final TimeDifference pTimeDifference) {
- printTimeDifference(pTimeDifference.getStartCalendar(), pTimeDifference.getEndCalendar(), System.out);
- }
-
- /**
- * A convenience class for embracing two {@code java.util.Calendar} objects.
- * The class is used for building a collection of time differences according to the {@code printTimeAverage} method.
- */
- public static class TimeDifference {
-
- Calendar mStartCalendar;
- Calendar mEndCalendar;
-
- /**
- * Constructor TimeDifference
- *
- *
- */
- public TimeDifference() {}
-
- /**
- * Constructor TimeDifference
- *
- *
- * @param pStartCalendar
- * @param pEndCalendar
- *
- */
- public TimeDifference(final Calendar pStartCalendar, final Calendar pEndCalendar) {
- this.mStartCalendar = pStartCalendar;
- this.mEndCalendar = pEndCalendar;
- }
-
- /**
- * Method setStartCalendar
- *
- *
- * @param pStartCalendar
- *
- */
- public void setStartCalendar(Calendar pStartCalendar) {
- this.mStartCalendar = pStartCalendar;
- }
-
- /**
- * Method getStartCalendar
- *
- *
- * @return
- *
- */
- public Calendar getStartCalendar() {
- return this.mStartCalendar;
- }
-
- /**
- * Method setEndCalendar
- *
- *
- * @param pEndCalendar
- *
- */
- public void setEndCalendar(Calendar pEndCalendar) {
- this.mEndCalendar = pEndCalendar;
- }
-
- /**
- * Method getEndCalendar
- *
- *
- * @return
- *
- */
- public Calendar getEndCalendar() {
- return this.mEndCalendar;
- }
- }
-
- /**
- * Prints out the average time difference from a collection of {@code com.twelvemonkeys.util.DebugUtil.TimeDifference} objects.
- *
- *
- * @param pTimeDifferences
- * @param pPrintStream the {@code java.io.PrintStream} for flushing the results.
- */
- public static void printTimeAverage(final Collection pTimeDifferences, final PrintStream pPrintStream) {
-
- if (pPrintStream == null) {
- System.err.println(PRINTSTREAM_IS_NULL_ERROR_MESSAGE);
- return;
- }
- if (pTimeDifferences == null) {
- pPrintStream.println(TIMEDIFFERENCES_IS_NULL_ERROR_MESSAGE);
- return;
- }
- Object o;
- TimeDifference timeDifference;
- Calendar startCalendar = null;
- Calendar endCalendar = null;
- Calendar totalStartCalendar = null;
- Calendar totalEndCalendar = null;
- long startCalendarMilliSeconds, endCalendarMilliSeconds;
- List timeDifferenceList = new Vector();
- Iterator i = pTimeDifferences.iterator();
-
- if (i.hasNext()) {
- o = i.next();
- if (!(o instanceof TimeDifference)) {
- pPrintStream.println(TIMEDIFFERENCES_WRONG_DATATYPE_ERROR_MESSAGE);
- return;
- }
- timeDifference = (TimeDifference) o;
- startCalendar = timeDifference.getStartCalendar();
- totalStartCalendar = startCalendar;
- endCalendar = timeDifference.getEndCalendar();
- startCalendarMilliSeconds = startCalendar.getTime().getTime();
- endCalendarMilliSeconds = endCalendar.getTime().getTime();
- timeDifferenceList.add(new Long(endCalendarMilliSeconds - startCalendarMilliSeconds));
- }
- while (i.hasNext()) {
- o = i.next();
- if (!(o instanceof TimeDifference)) {
- pPrintStream.println(TIMEDIFFERENCES_WRONG_DATATYPE_ERROR_MESSAGE);
- return;
- }
- timeDifference = (TimeDifference) o;
- startCalendar = timeDifference.getStartCalendar();
- endCalendar = timeDifference.getEndCalendar();
- startCalendarMilliSeconds = startCalendar.getTime().getTime();
- endCalendarMilliSeconds = endCalendar.getTime().getTime();
- timeDifferenceList.add(new Long(endCalendarMilliSeconds - startCalendarMilliSeconds));
- }
- totalEndCalendar = endCalendar;
- int numberOfElements = timeDifferenceList.size();
- long timeDifferenceElement;
- long timeDifferenceSum = 0;
-
- for (Iterator i2 = timeDifferenceList.iterator(); i2.hasNext(); ) {
- timeDifferenceElement = ((Long) i2.next()).longValue();
- timeDifferenceSum += timeDifferenceElement;
- }
-
- // Total elapsed time
- String totalElapsedTime = buildTimeDifference(totalStartCalendar, totalEndCalendar);
-
- // Time average presentation format
- pPrintStream.println("Average time difference: " + timeDifferenceSum / numberOfElements + "ms (" + numberOfElements
- + " elements, total elapsed time: " + totalElapsedTime + ")");
- }
-
- /**
- * Prints out the average time difference from a collection of {@code com.twelvemonkeys.util.DebugUtil.TimeDifference} objects to {@code System.out}.
- *
- *
- * @param pTimeDifferences
- */
- public static void printTimeAverage(final Collection pTimeDifferences) {
- printTimeAverage(pTimeDifferences, System.out);
- }
-
- // Reflective methods
-
- /**
- * Prints the top-wrapped class name of a {@code java.lang.Object} to a {@code java.io.PrintStream}.
- *
- * @param pObject the {@code java.lang.Object} to be printed.
- * @param pPrintStream the {@code java.io.PrintStream} for flushing the results.
- * @see {@code java.lang.Class}
- */
- public static void printClassName(final Object pObject, final PrintStream pPrintStream) {
-
- if (pPrintStream == null) {
- System.err.println(PRINTSTREAM_IS_NULL_ERROR_MESSAGE);
- return;
- }
- pPrintStream.println(getClassName(pObject));
- }
-
- /**
- * Prints the top-wrapped class name of a {@code java.lang.Object} to {@code System.out}.
- *
- * @param pObject the {@code java.lang.Object} to be printed.
- * @see {@code java.lang.Class}
- */
- public static void printClassName(final Object pObject) {
- printClassName(pObject, System.out);
- }
-
- /**
- * Builds the top-wrapped class name of a {@code java.lang.Object}.
- *
- * @param pObject the {@code java.lang.Object} to be analysed.
- * @return the object's class name.
- * @see {@code java.lang.Class}
- */
- public static String getClassName(final Object pObject) {
-
- if (pObject == null) {
- return OBJECT_IS_NULL_ERROR_MESSAGE;
- }
- return pObject.getClass().getName();
- }
-
- /**
- * Prints javadoc-like, the top wrapped class fields and methods of a {@code java.lang.Object} to a {@code java.io.PrintStream}.
- *
- * @param pObject the {@code java.lang.Object} to be analysed.
- * @param pObjectName the name of the object instance, for identification purposes.
- * @param pPrintStream the {@code java.io.PrintStream} for flushing the results.
- * @see {@code java.lang.Class}
- * @see {@code java.lang.reflect.Modifier}
- * @see {@code java.lang.reflect.Field}
- * @see {@code java.lang.reflect.Constructor}
- * @see {@code java.lang.reflect.Method}
- */
- public static void printClassDetails(final Object pObject, final String pObjectName, final PrintStream pPrintStream) {
-
- if (pPrintStream == null) {
- System.err.println(PRINTSTREAM_IS_NULL_ERROR_MESSAGE);
- return;
- }
- pPrintStream.println(getClassDetails(pObject, pObjectName));
- }
-
- /**
- * Prints javadoc-like, the top wrapped class fields and methods of a {@code java.lang.Object} to {@code System.out}.
- *
- * @param pObject the {@code java.lang.Object} to be analysed.
- * @param pObjectName the name of the object instance, for identification purposes.
- * @see {@code java.lang.Class}
- * @see {@code java.lang.reflect.Modifier}
- * @see {@code java.lang.reflect.Field}
- * @see {@code java.lang.reflect.Constructor}
- * @see {@code java.lang.reflect.Method}
- */
- public static void printClassDetails(final Object pObject, final String pObjectName) {
- printClassDetails(pObject, pObjectName, System.out);
- }
-
- /**
- * Prints javadoc-like, the top wrapped class fields and methods of a {@code java.lang.Object} to {@code System.out}.
- *
- * @param pObject the {@code java.lang.Object} to be analysed.
- * @see {@code java.lang.Class}
- * @see {@code java.lang.reflect.Modifier}
- * @see {@code java.lang.reflect.Field}
- * @see {@code java.lang.reflect.Constructor}
- * @see {@code java.lang.reflect.Method}
- */
- public static void printClassDetails(final Object pObject) {
- printClassDetails(pObject, null, System.out);
- }
-
- /**
- * Prints javadoc-like, the top wrapped class fields and methods of a {@code java.lang.Object} to a {@code java.io.PrintStream}.
- *
- * @param pObject the {@code java.lang.Object} to be analysed.
- * @param pPrintStream the {@code java.io.PrintStream} for flushing the results.
- * @see {@code java.lang.Class}
- * @see {@code java.lang.reflect.Modifier}
- * @see {@code java.lang.reflect.Field}
- * @see {@code java.lang.reflect.Constructor}
- * @see {@code java.lang.reflect.Method}
- */
- public static void printClassDetails(final Object pObject, final PrintStream pPrintStream) {
- printClassDetails(pObject, null, pPrintStream);
- }
-
- /**
- * Builds a javadoc-like presentation of the top wrapped class fields and methods of a {@code java.lang.Object}.
- *
- * @param pObject the {@code java.lang.Object} to be analysed.
- * @return a listing of the object's class details.
- * @see {@code java.lang.Class}
- * @see {@code java.lang.reflect.Modifier}
- * @see {@code java.lang.reflect.Field}
- * @see {@code java.lang.reflect.Constructor}
- * @see {@code java.lang.reflect.Method}
- */
- public static String getClassDetails(final Object pObject) {
- return getClassDetails(pObject, null);
- }
-
- /**
- * Builds a javadoc-like presentation of the top wrapped class fields and methods of a {@code java.lang.Object}.
- *
- * @param pObject the {@code java.lang.Object} to be analysed.
- * @param pObjectName the name of the object instance, for identification purposes.
- * @return a listing of the object's class details.
- * @see {@code java.lang.Class}
- * @see {@code java.lang.reflect.Modifier}
- * @see {@code java.lang.reflect.Field}
- * @see {@code java.lang.reflect.Constructor}
- * @see {@code java.lang.reflect.Method}
- */
- public static String getClassDetails(final Object pObject, final String pObjectName) {
-
- if (pObject == null) {
- return OBJECT_IS_NULL_ERROR_MESSAGE;
- }
- final String endOfLine = System.getProperty("line.separator");
- final String dividerLine = "---------------------------------------------------------";
- Class c = pObject.getClass();
- StringTokenizer tokenizedString;
- String str;
- String className = new String();
- String superClassName = new String();
- StringBuilder buffer = new StringBuilder();
-
- // Heading
- buffer.append(endOfLine);
- buffer.append("**** class details");
- if (!StringUtil.isEmpty(pObjectName)) {
- buffer.append(" for \"" + pObjectName + "\"");
- }
- buffer.append(" ****");
- buffer.append(endOfLine);
-
- // Package
- Package p = c.getPackage();
-
- if (p != null) {
- buffer.append(p.getName());
- }
- buffer.append(endOfLine);
-
- // Class or Interface
- if (c.isInterface()) {
- buffer.append("I n t e r f a c e ");
- } else {
- buffer.append("C l a s s ");
- }
- str = c.getName();
- tokenizedString = new StringTokenizer(str, ".");
- while (tokenizedString.hasMoreTokens()) {
- className = tokenizedString.nextToken().trim();
- }
- str = new String();
- char[] charArray = className.toCharArray();
-
- for (int i = 0; i < charArray.length; i++) {
- str += charArray[i] + " ";
- }
- buffer.append(str);
- buffer.append(endOfLine);
- buffer.append(endOfLine);
-
- // Class Hierarch
- List classNameList = new Vector();
-
- classNameList.add(c.getName());
- Class superclass = c.getSuperclass();
-
- while (superclass != null) {
- classNameList.add(superclass.getName());
- superclass = superclass.getSuperclass();
- }
- Object[] classNameArray = classNameList.toArray();
- int counter = 0;
-
- for (int i = classNameArray.length - 1; i >= 0; i--) {
- for (int j = 0; j < counter; j++) {
- buffer.append(" ");
- }
- if (counter > 0) {
- buffer.append("|");
- buffer.append(endOfLine);
- }
- for (int j = 0; j < counter; j++) {
- buffer.append(" ");
- }
- if (counter > 0) {
- buffer.append("+-");
- }
- buffer.append((String) classNameArray[i]);
- buffer.append(endOfLine);
- counter++;
- }
-
- // Divider
- buffer.append(endOfLine);
- buffer.append(dividerLine);
- buffer.append(endOfLine);
- buffer.append(endOfLine);
-
- // Profile
- int classModifier = c.getModifiers();
-
- buffer.append(Modifier.toString(classModifier) + " ");
- if (c.isInterface()) {
- buffer.append("Interface ");
- } else {
- buffer.append("Class ");
- }
- buffer.append(className);
- buffer.append(endOfLine);
- if ((classNameArray != null) && (classNameArray[classNameArray.length - 2] != null)) {
- str = (String) classNameArray[classNameArray.length - 2];
- tokenizedString = new StringTokenizer(str, ".");
- while (tokenizedString.hasMoreTokens()) {
- superClassName = tokenizedString.nextToken().trim();
- }
- buffer.append("extends " + superClassName);
- buffer.append(endOfLine);
- }
- if (!c.isInterface()) {
- Class[] interfaces = c.getInterfaces();
-
- if ((interfaces != null) && (interfaces.length > 0)) {
- buffer.append("implements ");
- str = interfaces[0].getName();
- tokenizedString = new StringTokenizer(str, ".");
- while (tokenizedString.hasMoreTokens()) {
- str = tokenizedString.nextToken().trim();
- }
- buffer.append(str);
- for (int i = 1; i < interfaces.length; i++) {
- str = interfaces[i].getName();
- tokenizedString = new StringTokenizer(str, ".");
- while (tokenizedString.hasMoreTokens()) {
- str = tokenizedString.nextToken().trim();
- }
- buffer.append(", " + str);
- }
- buffer.append(endOfLine);
- }
- }
-
- // Divider
- buffer.append(endOfLine);
- buffer.append(dividerLine);
- buffer.append(endOfLine);
- buffer.append(endOfLine);
-
- // Fields
- buffer.append("F I E L D S U M M A R Y");
- buffer.append(endOfLine);
- Field[] fields = c.getFields();
-
- if (fields != null) {
- for (int i = 0; i < fields.length; i++) {
- buffer.append(Modifier.toString(fields[i].getType().getModifiers()) + " ");
- str = fields[i].getType().getName();
- tokenizedString = new StringTokenizer(str, ".");
- while (tokenizedString.hasMoreTokens()) {
- str = tokenizedString.nextToken().trim();
- }
- buffer.append(str + " ");
- buffer.append(fields[i].getName());
- buffer.append(endOfLine);
- }
- }
- buffer.append(endOfLine);
-
- // Constructors
- buffer.append("C O N S T R U C T O R S U M M A R Y");
- buffer.append(endOfLine);
- Constructor[] constructors = c.getConstructors();
-
- if (constructors != null) {
- for (int i = 0; i < constructors.length; i++) {
- buffer.append(className + "(");
- Class[] parameterTypes = constructors[i].getParameterTypes();
-
- if (parameterTypes != null) {
- if (parameterTypes.length > 0) {
- str = parameterTypes[0].getName();
- tokenizedString = new StringTokenizer(str, ".");
- while (tokenizedString.hasMoreTokens()) {
- str = tokenizedString.nextToken().trim();
- }
- buffer.append(str);
- for (int j = 1; j < parameterTypes.length; j++) {
- str = parameterTypes[j].getName();
- tokenizedString = new StringTokenizer(str, ".");
- while (tokenizedString.hasMoreTokens()) {
- str = tokenizedString.nextToken().trim();
- }
- buffer.append(", " + str);
- }
- }
- }
- buffer.append(")");
- buffer.append(endOfLine);
- }
- }
- buffer.append(endOfLine);
-
- // Methods
- buffer.append("M E T H O D S U M M A R Y");
- buffer.append(endOfLine);
- Method[] methods = c.getMethods();
-
- if (methods != null) {
- for (int i = 0; i < methods.length; i++) {
- buffer.append(Modifier.toString(methods[i].getModifiers()) + " ");
- str = methods[i].getReturnType().getName();
- tokenizedString = new StringTokenizer(str, ".");
- while (tokenizedString.hasMoreTokens()) {
- str = tokenizedString.nextToken().trim();
- }
- buffer.append(str + " ");
- buffer.append(methods[i].getName() + "(");
- Class[] parameterTypes = methods[i].getParameterTypes();
-
- if ((parameterTypes != null) && (parameterTypes.length > 0)) {
- if (parameterTypes[0] != null) {
- str = parameterTypes[0].getName();
- tokenizedString = new StringTokenizer(str, ".");
- while (tokenizedString.hasMoreTokens()) {
- str = tokenizedString.nextToken().trim();
- }
-
- // array bugfix
- if (str.charAt(str.length() - 1) == ';') {
- str = str.substring(0, str.length() - 1) + "[]";
- }
- buffer.append(str);
- for (int j = 1; j < parameterTypes.length; j++) {
- str = parameterTypes[j].getName();
- tokenizedString = new StringTokenizer(str, ".");
- while (tokenizedString.hasMoreTokens()) {
- str = tokenizedString.nextToken().trim();
- }
- buffer.append(", " + str);
- }
- }
- }
- buffer.append(")");
- buffer.append(endOfLine);
- }
- }
- buffer.append(endOfLine);
-
- // Ending
- buffer.append("**** class details");
- if (!StringUtil.isEmpty(pObjectName)) {
- buffer.append(" for \"" + pObjectName + "\"");
- }
- buffer.append(" end ****");
- buffer.append(endOfLine);
- return buffer.toString();
- }
-
- /**
- * Prettyprints a large number.
- *
- *
- * @param pBigNumber
- * @return prettyprinted number with dot-separation each 10e3.
- */
- public static String getLargeNumber(final long pBigNumber) {
-
- StringBuilder buffer = new StringBuilder(new Long(pBigNumber).toString());
- char[] number = new Long(pBigNumber).toString().toCharArray();
- int reverseIndex = 0;
-
- for (int i = number.length; i >= 0; i--) {
- reverseIndex++;
- if ((reverseIndex % 3 == 0) && (i > 1)) {
- buffer = buffer.insert(i - 1, '.');
- }
- }
- return buffer.toString();
- }
-
- /**
- * Prettyprints milliseconds to ?day(s) ?h ?m ?s ?ms.
- *
- *
- * @param pMilliseconds
- * @return prettyprinted time duration.
- */
- public static String getTimeInterval(final long pMilliseconds) {
-
- long timeIntervalMilliseconds = pMilliseconds;
- long timeIntervalSeconds = 0;
- long timeIntervalMinutes = 0;
- long timeIntervalHours = 0;
- long timeIntervalDays = 0;
- boolean printMilliseconds = true;
- boolean printSeconds = false;
- boolean printMinutes = false;
- boolean printHours = false;
- boolean printDays = false;
- final long MILLISECONDS_IN_SECOND = 1000;
- final long MILLISECONDS_IN_MINUTE = 60 * MILLISECONDS_IN_SECOND; // 60000
- final long MILLISECONDS_IN_HOUR = 60 * MILLISECONDS_IN_MINUTE; // 3600000
- final long MILLISECONDS_IN_DAY = 24 * MILLISECONDS_IN_HOUR; // 86400000
- StringBuilder timeIntervalBuffer = new StringBuilder();
-
- // Days
- if (timeIntervalMilliseconds >= MILLISECONDS_IN_DAY) {
- timeIntervalDays = timeIntervalMilliseconds / MILLISECONDS_IN_DAY;
- timeIntervalMilliseconds = timeIntervalMilliseconds % MILLISECONDS_IN_DAY;
- printDays = true;
- printHours = true;
- printMinutes = true;
- printSeconds = true;
- }
-
- // Hours
- if (timeIntervalMilliseconds >= MILLISECONDS_IN_HOUR) {
- timeIntervalHours = timeIntervalMilliseconds / MILLISECONDS_IN_HOUR;
- timeIntervalMilliseconds = timeIntervalMilliseconds % MILLISECONDS_IN_HOUR;
- printHours = true;
- printMinutes = true;
- printSeconds = true;
- }
-
- // Minutes
- if (timeIntervalMilliseconds >= MILLISECONDS_IN_MINUTE) {
- timeIntervalMinutes = timeIntervalMilliseconds / MILLISECONDS_IN_MINUTE;
- timeIntervalMilliseconds = timeIntervalMilliseconds % MILLISECONDS_IN_MINUTE;
- printMinutes = true;
- printSeconds = true;
- }
-
- // Seconds
- if (timeIntervalMilliseconds >= MILLISECONDS_IN_SECOND) {
- timeIntervalSeconds = timeIntervalMilliseconds / MILLISECONDS_IN_SECOND;
- timeIntervalMilliseconds = timeIntervalMilliseconds % MILLISECONDS_IN_SECOND;
- printSeconds = true;
- }
-
- // Prettyprint
- if (printDays) {
- timeIntervalBuffer.append(timeIntervalDays);
- if (timeIntervalDays > 1) {
- timeIntervalBuffer.append("days ");
- } else {
- timeIntervalBuffer.append("day ");
- }
- }
- if (printHours) {
- timeIntervalBuffer.append(timeIntervalHours);
- timeIntervalBuffer.append("h ");
- }
- if (printMinutes) {
- timeIntervalBuffer.append(timeIntervalMinutes);
- timeIntervalBuffer.append("m ");
- }
- if (printSeconds) {
- timeIntervalBuffer.append(timeIntervalSeconds);
- timeIntervalBuffer.append("s ");
- }
- if (printMilliseconds) {
- timeIntervalBuffer.append(timeIntervalMilliseconds);
- timeIntervalBuffer.append("ms");
- }
- return timeIntervalBuffer.toString();
- }
-}
-
-
-/*--- Formatted in Sun Java Convention Style on ma, des 1, '03 ---*/
-
-
-/*------ Formatted by Jindent 3.23 Basic 1.0 --- http://www.jindent.de ------*/
+/****************************************************
+ * *
+ * (c) 2000-2003 TwelveMonkeys *
+ * All rights reserved *
+ * http://www.twelvemonkeys.no *
+ * *
+ * $RCSfile: DebugUtil.java,v $
+ * @version $Revision: #2 $
+ * $Date: 2009/06/19 $
+ * *
+ * @author Last modified by: $Author: haku $
+ * *
+ ****************************************************/
+
+
+
+/*
+ * Produced (p) 2002 TwelveMonkeys
+ * Address : Svovelstikka 1, Box 6432 Etterstad, 0605 Oslo, Norway.
+ * Phone : +47 22 57 70 00
+ * Fax : +47 22 57 70 70
+ */
+package com.twelvemonkeys.util;
+
+
+import com.twelvemonkeys.lang.StringUtil;
+
+import java.io.PrintStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.text.DateFormat;
+import java.util.*;
+
+
+/**
+ * A utility class to simplify debugging.
+ * This includes viewing generic data structures, printing timestamps, printing object info and more...
+ * NB! Only use this class for instrumentation purposes
+ *
+ * @author Eirik Torske
+ */
+@Deprecated
+public class DebugUtil {
+
+ // Constants
+
+ /** Field PRINTSTREAM_IS_NULL_ERROR_MESSAGE */
+ public static final String PRINTSTREAM_IS_NULL_ERROR_MESSAGE = "PrintStream is null";
+
+ /** Field OBJECT_IS_NULL_ERROR_MESSAGE */
+ public static final String OBJECT_IS_NULL_ERROR_MESSAGE = "Object is null";
+
+ /** Field INTARRAY_IS_NULL_ERROR_MESSAGE */
+ public static final String INTARRAY_IS_NULL_ERROR_MESSAGE = "int array is null";
+
+ /** Field STRINGARRAY_IS_NULL_ERROR_MESSAGE */
+ public static final String STRINGARRAY_IS_NULL_ERROR_MESSAGE = "String array is null";
+
+ /** Field ENUMERATION_IS_NULL_ERROR_MESSAGE */
+ public static final String ENUMERATION_IS_NULL_ERROR_MESSAGE = "Enumeration is null";
+
+ /** Field COLLECTION_IS_NULL_ERROR_MESSAGE */
+ public static final String COLLECTION_IS_NULL_ERROR_MESSAGE = "Collection is null";
+
+ /** Field COLLECTION_IS_EMPTY_ERROR_MESSAGE */
+ public static final String COLLECTION_IS_EMPTY_ERROR_MESSAGE = "Collection contains no elements";
+
+ /** Field MAP_IS_NULL_ERROR_MESSAGE */
+ public static final String MAP_IS_NULL_ERROR_MESSAGE = "Map is null";
+
+ /** Field MAP_IS_EMPTY_ERROR_MESSAGE */
+ public static final String MAP_IS_EMPTY_ERROR_MESSAGE = "Map contains no elements";
+
+ /** Field PROPERTIES_IS_NULL_ERROR_MESSAGE */
+ public static final String PROPERTIES_IS_NULL_ERROR_MESSAGE = "Properties is null";
+
+ /** Field PROPERTIES_IS_EMPTY_ERROR_MESSAGE */
+ public static final String PROPERTIES_IS_EMPTY_ERROR_MESSAGE = "Properties contains no elements";
+
+ /** Field CALENDAR_IS_NULL_ERROR_MESSAGE */
+ public static final String CALENDAR_IS_NULL_ERROR_MESSAGE = "Calendar is null";
+
+ /** Field CALENDAR_CAUSATION_ERROR_MESSAGE */
+ public static final String CALENDAR_CAUSATION_ERROR_MESSAGE = "The causation of the calendars is wrong";
+
+ /** Field TIMEDIFFERENCES_IS_NULL_ERROR_MESSAGE */
+ public static final String TIMEDIFFERENCES_IS_NULL_ERROR_MESSAGE = "Inner TimeDifference object is null";
+
+ /** Field TIMEDIFFERENCES_WRONG_DATATYPE_ERROR_MESSAGE */
+ public static final String TIMEDIFFERENCES_WRONG_DATATYPE_ERROR_MESSAGE =
+ "Element in TimeDifference collection is not a TimeDifference object";
+
+ /** Field DEBUG */
+ public static final String DEBUG = "**** external debug: ";
+
+ /** Field INFO */
+ public static final String INFO = "**** external info: ";
+
+ /** Field WARNING */
+ public static final String WARNING = "**** external warning: ";
+
+ /** Field ERROR */
+ public static final String ERROR = "**** external error: ";
+
+ /**
+ * Builds a prefix message to be used in front of info messages for identification purposes.
+ * The message format is:
+ *
+ * @param pObject the {@code java.lang.Object} to be debugged. If the object ia a {@code java.lang.String} object, it is assumed that it is the class name given directly.
+ * @return a prefix for an info message.
+ */
+ public static String getPrefixInfoMessage(final Object pObject) {
+
+ StringBuilder buffer = new StringBuilder();
+
+ buffer.append(INFO);
+ buffer.append(getTimestamp());
+ buffer.append(" ");
+ if (pObject == null) {
+ buffer.append("[unknown class]");
+ } else {
+ if (pObject instanceof String) {
+ buffer.append((String) pObject);
+ } else {
+ buffer.append(getClassName(pObject));
+ }
+ }
+ buffer.append(": ");
+ return buffer.toString();
+ }
+
+ /**
+ * Builds a prefix message to be used in front of debug messages for identification purposes.
+ * The message format is:
+ *
+ * @param pObject the {@code java.lang.Object} to be debugged. If the object ia a {@code java.lang.String} object, it is assumed that it is the class name given directly.
+ * @return a prefix for a debug message.
+ */
+ public static String getPrefixDebugMessage(final Object pObject) {
+
+ StringBuilder buffer = new StringBuilder();
+
+ buffer.append(DEBUG);
+ buffer.append(getTimestamp());
+ buffer.append(" ");
+ if (pObject == null) {
+ buffer.append("[unknown class]");
+ } else {
+ if (pObject instanceof String) {
+ buffer.append((String) pObject);
+ } else {
+ buffer.append(getClassName(pObject));
+ }
+ }
+ buffer.append(": ");
+ return buffer.toString();
+ }
+
+ /**
+ * Builds a prefix message to be used in front of warning messages for identification purposes.
+ * The message format is:
+ *
+ * @param pObject the {@code java.lang.Object} to be debugged. If the object ia a {@code java.lang.String} object, it is assumed that it is the class name given directly.
+ * @return a prefix for a warning message.
+ */
+ public static String getPrefixWarningMessage(final Object pObject) {
+
+ StringBuilder buffer = new StringBuilder();
+
+ buffer.append(WARNING);
+ buffer.append(getTimestamp());
+ buffer.append(" ");
+ if (pObject == null) {
+ buffer.append("[unknown class]");
+ } else {
+ if (pObject instanceof String) {
+ buffer.append((String) pObject);
+ } else {
+ buffer.append(getClassName(pObject));
+ }
+ }
+ buffer.append(": ");
+ return buffer.toString();
+ }
+
+ /**
+ * Builds a prefix message to be used in front of error messages for identification purposes.
+ * The message format is:
+ *
+ * @param pObject the {@code java.lang.Object} to be debugged. If the object ia a {@code java.lang.String} object, it is assumed that it is the class name given directly.
+ * @return a prefix for an error message.
+ */
+ public static String getPrefixErrorMessage(final Object pObject) {
+
+ StringBuilder buffer = new StringBuilder();
+
+ buffer.append(ERROR);
+ buffer.append(getTimestamp());
+ buffer.append(" ");
+ if (pObject == null) {
+ buffer.append("[unknown class]");
+ } else {
+ if (pObject instanceof String) {
+ buffer.append((String) pObject);
+ } else {
+ buffer.append(getClassName(pObject));
+ }
+ }
+ buffer.append(": ");
+ return buffer.toString();
+ }
+
+ /**
+ * The "default" method that invokes a given method of an object and prints the results to a {@code java.io.PrintStream}.
+ * @param pObject the {@code java.lang.Object} to be printed.
+ * @param pMethodName a {@code java.lang.String} holding the name of the method to be invoked.
+ * @param pPrintStream the {@code java.io.PrintStream} for flushing the results.
+ */
+ public static void printDebug(final Object pObject, final String pMethodName, final PrintStream pPrintStream) {
+
+ if (pPrintStream == null) {
+ System.err.println(PRINTSTREAM_IS_NULL_ERROR_MESSAGE);
+ return;
+ }
+ if (pObject == null) {
+ pPrintStream.println(OBJECT_IS_NULL_ERROR_MESSAGE);
+ return;
+ }
+ if (!StringUtil.isEmpty(pMethodName)) {
+ try {
+ Method objectMethod = pObject.getClass().getMethod(pMethodName, null);
+ Object retVal = objectMethod.invoke(pObject, null);
+
+ if (retVal != null) {
+ printDebug(retVal, null, pPrintStream);
+ } else {
+ throw new Exception();
+ }
+ } catch (Exception e) {
+
+ // Default
+ pPrintStream.println(pObject.toString());
+ }
+ } else { // Ultimate default
+ pPrintStream.println(pObject.toString());
+ }
+ }
+
+ /**
+ * Prints the object's {@code toString()} method to a {@code java.io.PrintStream}.
+ *
+ * @param pObject the {@code java.lang.Object} to be printed.
+ * @param pPrintStream the {@code java.io.PrintStream} for flushing the results.
+ */
+ public static void printDebug(final Object pObject, final PrintStream pPrintStream) {
+ printDebug(pObject, null, pPrintStream);
+ }
+
+ /**
+ * Prints the object's {@code toString()} method to {@code System.out}.
+ *
+ * @param pObject the {@code java.lang.Object} to be printed.
+ */
+ public static void printDebug(final Object pObject) {
+ printDebug(pObject, System.out);
+ }
+
+ /**
+ * Prints a line break.
+ */
+ public static void printDebug() {
+ System.out.println();
+ }
+
+ /**
+ * Prints a primitive {@code boolean} to {@code System.out}.
+ *
+ * @param pBoolean the {@code boolean} to be printed.
+ */
+ public static void printDebug(final boolean pBoolean) {
+ printDebug(new Boolean(pBoolean).toString());
+ }
+
+ /**
+ * Prints a primitive {@code int} to {@code System.out}.
+ *
+ *
+ * @param pInt
+ */
+ public static void printDebug(final int pInt) {
+ printDebug(new Integer(pInt).toString());
+ }
+
+ /**
+ * Prints the content of a {@code int[]} to a {@code java.io.PrintStream}.
+ *
+ * @param pIntArray the {@code int[]} to be printed.
+ * @param pPrintStream the {@code java.io.PrintStream} for flushing the results.
+ */
+ public static void printDebug(final int[] pIntArray, final PrintStream pPrintStream) {
+
+ if (pPrintStream == null) {
+ System.err.println(PRINTSTREAM_IS_NULL_ERROR_MESSAGE);
+ return;
+ }
+ if (pIntArray == null) {
+ pPrintStream.println(INTARRAY_IS_NULL_ERROR_MESSAGE);
+ return;
+ }
+ for (int i = 0; i < pIntArray.length; i++) {
+ pPrintStream.println(pIntArray[i]);
+ }
+ }
+
+ /**
+ * Prints the content of a {@code int[]} to {@code System.out}.
+ *
+ * @param pIntArray the {@code int[]} to be printed.
+ */
+ public static void printDebug(final int[] pIntArray) {
+ printDebug(pIntArray, System.out);
+ }
+
+ /**
+ * Prints a number of character check methods from the {@code java.lang.Character} class to a {@code java.io.PrintStream}.
+ *
+ * @param pChar the {@code java.lang.char} to be debugged.
+ * @param pPrintStream the {@code java.io.PrintStream} for flushing the results.
+ */
+ public static void printDebug(final char pChar, final PrintStream pPrintStream) {
+
+ if (pPrintStream == null) {
+ System.err.println(PRINTSTREAM_IS_NULL_ERROR_MESSAGE);
+ return;
+ }
+ pPrintStream.println("Character.getNumericValue(pChar): " + Character.getNumericValue(pChar));
+ pPrintStream.println("Character.getType(pChar): " + Character.getType(pChar));
+ pPrintStream.println("pChar.hashCode(): " + new Character(pChar).hashCode());
+ pPrintStream.println("Character.isDefined(pChar): " + Character.isDefined(pChar));
+ pPrintStream.println("Character.isDigit(pChar): " + Character.isDigit(pChar));
+ pPrintStream.println("Character.isIdentifierIgnorable(pChar): " + Character.isIdentifierIgnorable(pChar));
+ pPrintStream.println("Character.isISOControl(pChar): " + Character.isISOControl(pChar));
+ pPrintStream.println("Character.isJavaIdentifierPart(pChar): " + Character.isJavaIdentifierPart(pChar));
+ pPrintStream.println("Character.isJavaIdentifierStart(pChar): " + Character.isJavaIdentifierStart(pChar));
+ pPrintStream.println("Character.isLetter(pChar): " + Character.isLetter(pChar));
+ pPrintStream.println("Character.isLetterOrDigit(pChar): " + Character.isLetterOrDigit(pChar));
+ pPrintStream.println("Character.isLowerCase(pChar): " + Character.isLowerCase(pChar));
+ pPrintStream.println("Character.isSpaceChar(pChar): " + Character.isSpaceChar(pChar));
+ pPrintStream.println("Character.isTitleCase(pChar): " + Character.isTitleCase(pChar));
+ pPrintStream.println("Character.isUnicodeIdentifierPart(pChar): " + Character.isUnicodeIdentifierPart(pChar));
+ pPrintStream.println("Character.isUnicodeIdentifierStart(pChar): " + Character.isUnicodeIdentifierStart(pChar));
+ pPrintStream.println("Character.isUpperCase(pChar): " + Character.isUpperCase(pChar));
+ pPrintStream.println("Character.isWhitespace(pChar): " + Character.isWhitespace(pChar));
+ pPrintStream.println("pChar.toString(): " + new Character(pChar).toString());
+ }
+
+ /**
+ * Prints a number of character check methods from the {@code java.lang.Character} class to {@code System.out}.
+ *
+ * @param pChar the {@code java.lang.char} to be debugged.
+ */
+ public static void printDebug(final char pChar) {
+ printDebug(pChar, System.out);
+ }
+
+ /**
+ * Prints the content of a {@code java.lang.String[]} to a {@code java.io.PrintStream}.
+ *
+ * @param pStringArray the {@code java.lang.String[]} to be printed.
+ * @param pPrintStream the {@code java.io.PrintStream} for flushing the results.
+ */
+ public static void printDebug(final String[] pStringArray, final PrintStream pPrintStream) {
+
+ if (pPrintStream == null) {
+ System.err.println(PRINTSTREAM_IS_NULL_ERROR_MESSAGE);
+ return;
+ }
+ if (pStringArray == null) {
+ pPrintStream.println(STRINGARRAY_IS_NULL_ERROR_MESSAGE);
+ return;
+ }
+ for (int i = 0; i < pStringArray.length; i++) {
+ pPrintStream.println(pStringArray[i]);
+ }
+ }
+
+ /**
+ * Prints the content of a {@code java.lang.String[]} to {@code System.out}.
+ *
+ * @param pStringArray the {@code java.lang.String[]} to be printed.
+ */
+ public static void printDebug(final String[] pStringArray) {
+ printDebug(pStringArray, System.out);
+ }
+
+ /**
+ * Invokes a given method of every element in a {@code java.util.Enumeration} and prints the results to a {@code java.io.PrintStream}.
+ * The method to be invoked must have no formal parameters.
+ *
+ * If an exception is throwed during the method invocation, the element's {@code toString()} method is called.
+ * For bulk data types, recursive invocations and invocations of other methods in this class, are used.
+ *
+ * @param pEnumeration the {@code java.util.Enumeration} to be printed.
+ * @param pMethodName a {@code java.lang.String} holding the name of the method to be invoked on each collection element.
+ * @param pPrintStream the {@code java.io.PrintStream} for flushing the results.
+ * @see {@code java.util.Enumeration}
+ */
+ public static void printDebug(final Enumeration pEnumeration, final String pMethodName, final PrintStream pPrintStream) {
+
+ if (pPrintStream == null) {
+ System.err.println(PRINTSTREAM_IS_NULL_ERROR_MESSAGE);
+ return;
+ }
+ if (pEnumeration == null) {
+ pPrintStream.println(ENUMERATION_IS_NULL_ERROR_MESSAGE);
+ return;
+ }
+ while (pEnumeration.hasMoreElements()) {
+ printDebug(pEnumeration.nextElement(), pMethodName, pPrintStream);
+ }
+ }
+
+ /**
+ * Invokes a given method of every element in a {@code java.util.Enumeration} and prints the results to a {@code java.io.PrintStream}.
+ * The method to be invoked must have no formal parameters.
+ *
+ * If an exception is throwed during the method invocation, the element's {@code toString()} method is called.
+ * For bulk data types, recursive invocations and invocations of other methods in this class, are used.
+ *
+ * @param pEnumeration the {@code java.util.Enumeration} to be printed.
+ * @param pMethodName a {@code java.lang.String} holding the name of the method to be invoked on each collection element.
+ * @see {@code java.util.Enumeration}
+ */
+ public static void printDebug(final Enumeration pEnumeration, final String pMethodName) {
+ printDebug(pEnumeration, pMethodName, System.out);
+ }
+
+ /**
+ * Invokes a given method of every element in a {@code java.util.Enumeration} and prints the results to a {@code java.io.PrintStream}.
+ * The method to be invoked must have no formal parameters. The default is calling an element's {@code toString()} method.
+ *
+ * If an exception is throwed during the method invocation, the element's {@code toString()} method is called.
+ * For bulk data types, recursive invocations and invocations of other methods in this class, are used.
+ *
+ * @param pEnumeration the {@code java.util.Enumeration} to be printed.
+ * @see {@code java.util.Enumeration}
+ */
+ public static void printDebug(final Enumeration pEnumeration) {
+ printDebug(pEnumeration, null, System.out);
+ }
+
+ /**
+ * Invokes a given method of every element in a {@code java.util.Collection} and prints the results to a {@code java.io.PrintStream}.
+ * The method to be invoked must have no formal parameters.
+ *
+ * If an exception is throwed during the method invocation, the element's {@code toString()} method is called.
+ * For bulk data types, recursive invocations and invocations of other methods in this class, are used.
+ *
+ * Be aware that the {@code Collection} interface embraces a large portion of the bulk data types in the {@code java.util} package,
+ * e.g. {@code List}, {@code Set}, {@code Vector} and {@code HashSet}.
+ *
+ * For debugging of arrays, use the method {@code java.util.Arrays.asList(Object[])} method for converting the object array to a list before calling this method.
+ *
+ * @param pCollection the {@code java.util.Collection} to be printed.
+ * @param pMethodName a {@code java.lang.String} holding the name of the method to be invoked on each collection element.
+ * @param pPrintStream the {@code java.io.PrintStream} for flushing the results.
+ * @see {@code java.util.Collection}
+ */
+ public static void printDebug(final Collection pCollection, final String pMethodName, final PrintStream pPrintStream) {
+
+ if (pPrintStream == null) {
+ System.err.println(PRINTSTREAM_IS_NULL_ERROR_MESSAGE);
+ return;
+ }
+ if (pCollection == null) {
+ pPrintStream.println(COLLECTION_IS_NULL_ERROR_MESSAGE);
+ return;
+ } else if (pCollection.isEmpty()) {
+ pPrintStream.println(COLLECTION_IS_EMPTY_ERROR_MESSAGE);
+ return;
+ }
+ for (Iterator i = pCollection.iterator(); i.hasNext(); ) {
+ printDebug(i.next(), pMethodName, pPrintStream);
+ }
+ }
+
+ /**
+ * Invokes a given method of every element in a {@code java.util.Collection} and prints the results to {@code System.out}.
+ * The method to be invoked must have no formal parameters.
+ *
+ * If an exception is throwed during the method invocation, the element's {@code toString()} method is called.
+ * For bulk data types, recursive invocations and invocations of other methods in this class, are used.
+ *
+ * Be aware that the {@code Collection} interface embraces a large portion of the bulk data types in the {@code java.util} package,
+ * e.g. {@code List}, {@code Set}, {@code Vector} and {@code HashSet}.
+ *
+ * For debugging of arrays, use the method {@code java.util.Arrays.asList(Object[])} method for converting the object array to a list before calling this method.
+ *
+ * @param pCollection the {@code java.util.Collection} to be printed.
+ * @param pMethodName a {@code java.lang.String} holding the name of the method to be invoked on each collection element.
+ * @see {@code java.util.Collection}
+ */
+ public static void printDebug(final Collection pCollection, final String pMethodName) {
+ printDebug(pCollection, pMethodName, System.out);
+ }
+
+ /**
+ * Prints the content of a {@code java.util.Collection} to a {@code java.io.PrintStream}.
+ *
+ * Not all data types are supported so far. The default is calling an element's {@code toString()} method.
+ * For bulk data types, recursive invocations and invocations of other methods in this class, are used.
+ *
+ * Be aware that the {@code Collection} interface embraces a large portion of the bulk data types in the {@code java.util} package,
+ * e.g. {@code List}, {@code Set}, {@code Vector} and {@code HashSet}.
+ *
+ * For debugging of arrays, use the method {@code java.util.Arrays.asList(Object[])} method for converting the object array to a list before calling this method.
+ *
+ * @param pCollection the {@code java.util.Collection} to be printed.
+ * @param pPrintStream the {@code java.io.PrintStream} for flushing the results.
+ * @see {@code java.util.Collection}
+ */
+ public static void printDebug(final Collection pCollection, final PrintStream pPrintStream) {
+ printDebug(pCollection, null, pPrintStream);
+ }
+
+ /**
+ * Prints the content of a {@code java.util.Collection} to {@code System.out}.
+ *
+ * Not all data types are supported so far. The default is calling an element's {@code toString()} method.
+ * For bulk data types, recursive invocations and invocations of other methods in this class, are used.
+ *
+ * Be aware that the {@code Collection} interface embraces a large portion of the bulk data types in the {@code java.util} package,
+ * e.g. {@code List}, {@code Set}, {@code Vector} and {@code HashSet}.
+ *
+ * For debugging of arrays, use the method {@code java.util.Arrays.asList(Object[])} method for converting the object array to a list before calling this method.
+ *
+ * @param pCollection the {@code java.util.Collection} to be printed.
+ * @see {@code java.util.Collection}
+ */
+ public static void printDebug(final Collection pCollection) {
+ printDebug(pCollection, System.out);
+ }
+
+ /**
+ * Invokes a given method of every object in a {@code java.util.Map} and prints the results to a {@code java.io.PrintStream}.
+ * The method called must have no formal parameters.
+ *
+ * If an exception is throwed during the method invocation, the element's {@code toString()} method is called.
+ * For bulk data types, recursive invocations and invocations of other methods in this class, are used.
+ *
+ * @param pMap the {@code java.util.Map} to be printed.
+ * @param pMethodName a {@code java.lang.String} holding the name of the method to be invoked on each mapped object.
+ * @param pPrintStream the {@code java.io.PrintStream} for flushing the results.
+ * @see {@code java.util.Map}
+ */
+ public static void printDebug(final Map pMap, final String pMethodName, final PrintStream pPrintStream) {
+
+ if (pPrintStream == null) {
+ System.err.println(PRINTSTREAM_IS_NULL_ERROR_MESSAGE);
+ return;
+ }
+ if (pMap == null) {
+ pPrintStream.println(MAP_IS_NULL_ERROR_MESSAGE);
+ return;
+ } else if (pMap.isEmpty()) {
+ pPrintStream.println(MAP_IS_EMPTY_ERROR_MESSAGE);
+ return;
+ }
+ Object mKeyObject;
+ Object mEntryObject;
+
+ for (Iterator i = pMap.keySet().iterator(); i.hasNext(); ) {
+ mKeyObject = i.next();
+ mEntryObject = pMap.get(mKeyObject);
+ if ((mKeyObject instanceof String) && (mEntryObject instanceof String)) {
+ pPrintStream.println((String) mKeyObject + ": " + mEntryObject);
+ } else if ((mKeyObject instanceof String) && (mEntryObject instanceof List)) {
+ printDebug((List) mEntryObject, pPrintStream);
+ } else if ((mKeyObject instanceof String) && (mEntryObject instanceof Set)) {
+ printDebug((Set) mEntryObject, pPrintStream);
+ } else if (mKeyObject instanceof String) {
+ if (!StringUtil.isEmpty(pMethodName)) {
+ try {
+ Method objectMethod = mEntryObject.getClass().getMethod(pMethodName, null);
+ Object retVal = objectMethod.invoke(mEntryObject, null);
+
+ if (retVal != null) {
+ pPrintStream.println((String) mKeyObject + ": " + retVal.toString());
+ } else { // Default execution
+ throw new Exception();
+ }
+ } catch (Exception e) {
+
+ // Default
+ pPrintStream.println((String) mKeyObject + ": " + mEntryObject.toString());
+ }
+ } else { // Default
+ pPrintStream.println((String) mKeyObject + ": " + mEntryObject.toString());
+ }
+ } else if ((mKeyObject instanceof Integer) && (mEntryObject instanceof String)) {
+ pPrintStream.println((Integer) mKeyObject + ": " + mEntryObject);
+ } else if ((mKeyObject instanceof Integer) && (mEntryObject instanceof List)) {
+ printDebug((List) mEntryObject, pPrintStream);
+ } else if ((mKeyObject instanceof String) && (mEntryObject instanceof Set)) {
+ printDebug((Set) mEntryObject, pPrintStream);
+ } else if (mKeyObject instanceof Integer) {
+ if (!StringUtil.isEmpty(pMethodName)) {
+ try {
+ Method objectMethod = mEntryObject.getClass().getMethod(pMethodName, null);
+ Object retVal = objectMethod.invoke(mEntryObject, null);
+
+ if (retVal != null) {
+ pPrintStream.println((Integer) mKeyObject + ": " + retVal.toString());
+ } else { // Default execution
+ throw new Exception();
+ }
+ } catch (Exception e) {
+
+ // Default
+ pPrintStream.println((Integer) mKeyObject + ": " + mEntryObject.toString());
+ }
+ } else { // Default
+ pPrintStream.println((Integer) mKeyObject + ": " + mEntryObject.toString());
+ }
+ }
+
+ // More..
+ //else if
+ }
+ }
+
+ /**
+ * Invokes a given method of every object in a {@code java.util.Map} to {@code System.out}.
+ * The method called must have no formal parameters.
+ *
+ * If an exception is throwed during the method invocation, the element's {@code toString()} method is called.
+ * @param pMap the {@code java.util.Map} to be printed.
+ * @param pMethodName a {@code java.lang.String} holding the name of the method to be invoked on each mapped object.
+ * @see {@code java.util.Map}
+ */
+ public static void printDebug(final Map pMap, final String pMethodName) {
+ printDebug(pMap, pMethodName, System.out);
+ }
+
+ /**
+ * Prints the content of a {@code java.util.Map} to a {@code java.io.PrintStream}.
+ *
+ * Not all data types are supported so far. The default is calling an element's {@code toString()} method.
+ * @param pMap the {@code java.util.Map} to be printed.
+ * @param pPrintStream the {@code java.io.PrintStream} for flushing the results.
+ * @see {@code java.util.Map}
+ */
+ public static void printDebug(final Map pMap, final PrintStream pPrintStream) {
+ printDebug(pMap, null, pPrintStream);
+ }
+
+ /**
+ * Prints the content of a {@code java.util.Map} to {@code System.out}.
+ *
+ * Not all data types are supported so far. The default is calling an element's {@code toString()} method.
+ * @param pMap the {@code java.util.Map} to be printed.
+ * @see {@code java.util.Map}
+ */
+ public static void printDebug(final Map pMap) {
+ printDebug(pMap, System.out);
+ }
+
+ /**
+ * Prints the content of a {@code java.util.Properties} to a {@code java.io.PrintStream}.
+ *
+ * @param pProperties the {@code java.util.Properties} to be printed.
+ * @param pPrintStream the {@code java.io.PrintStream} for flushing the results.
+ * @see {@code java.util.Properties}
+ */
+ public static void printDebug(final Properties pProperties, final PrintStream pPrintStream) {
+
+ if (pPrintStream == null) {
+ System.err.println(PRINTSTREAM_IS_NULL_ERROR_MESSAGE);
+ return;
+ }
+ if (pProperties == null) {
+ pPrintStream.println(PROPERTIES_IS_NULL_ERROR_MESSAGE);
+ return;
+ } else if (pProperties.isEmpty()) {
+ pPrintStream.println(PROPERTIES_IS_EMPTY_ERROR_MESSAGE);
+ return;
+ }
+ for (Enumeration e = pProperties.propertyNames(); e.hasMoreElements(); ) {
+ String key = (String) e.nextElement();
+
+ pPrintStream.println(key + ": " + pProperties.getProperty(key));
+ }
+ }
+
+ /**
+ * Prints the content of a {@code java.util.Properties} to {@code System.out}.
+ *
+ * @param pProperties the {@code java.util.Properties} to be printed.
+ * @see {@code java.util.Properties}
+ */
+ public static void printDebug(final Properties pProperties) {
+ printDebug(pProperties, System.out);
+ }
+
+ // Timestamp utilities
+
+ /**
+ * Prints out the calendar time.
+ *
+ * @param pCalendar the {@code java.util.Calendar} object from which to extract the date information.
+ * @param pPrintStream the {@code java.io.PrintStream} for flushing the results.
+ * @see {@code java.util.Calendar}
+ */
+ public static void printTimestamp(final Calendar pCalendar, final PrintStream pPrintStream) {
+
+ if (pPrintStream == null) {
+ System.err.println(PRINTSTREAM_IS_NULL_ERROR_MESSAGE);
+ return;
+ }
+ pPrintStream.println(getTimestamp(pCalendar));
+ }
+
+ /**
+ * Prints out the system time.
+ *
+ * @param pPrintStream the {@code java.io.PrintStream} for flushing the results.
+ * @see {@code java.util.GregorianCalendar}
+ */
+ public static void printTimestamp(final PrintStream pPrintStream) {
+
+ GregorianCalendar cal = new GregorianCalendar();
+
+ printTimestamp(cal, pPrintStream);
+ }
+
+ /**
+ * Prints out the system time to {@code System.out}.
+ */
+ public static void printTimestamp() {
+ printTimestamp(System.out);
+ }
+
+ /**
+ * Returns a presentation of the date based on the given milliseconds.
+ *
+ * @param pMilliseconds The specified number of milliseconds since the standard base time known as "the epoch", namely January 1, 1970, 00:00:00 GMT.
+ * @return a presentation of the calendar time.
+ * @see {@code java.util.Calendar}
+ */
+ public static String getTimestamp(final String pMilliseconds) {
+ return getTimestamp(Long.parseLong(pMilliseconds));
+ }
+
+ /**
+ * Returns a presentation of the date based on the given milliseconds.
+ *
+ * @param pMilliseconds The specified number of milliseconds since the standard base time known as "the epoch", namely January 1, 1970, 00:00:00 GMT.
+ * @return a presentation of the calendar time.
+ * @see {@code java.util.Calendar}
+ */
+ public static String getTimestamp(final long pMilliseconds) {
+
+ java.util.Date date = new java.util.Date(pMilliseconds);
+ java.util.Calendar calendar = new GregorianCalendar();
+
+ calendar.setTime(date);
+ return getTimestamp(calendar);
+ }
+
+ /**
+ * Returns a presentation of the given calendar's time.
+ *
+ * @param pCalendar the {@code java.util.Calendar} object from which to extract the date information.
+ * @return a presentation of the calendar time.
+ * @see {@code java.util.Calendar}
+ */
+ public static String getTimestamp(final Calendar pCalendar) {
+ return buildTimestamp(pCalendar);
+ }
+
+ /**
+ * @return a presentation of the system time.
+ */
+ public static String getTimestamp() {
+
+ GregorianCalendar cal = new GregorianCalendar();
+
+ return getTimestamp(cal);
+ }
+
+ /**
+ * Builds a presentation of the given calendar's time. This method contains the common timestamp format used in this class.
+ * @return a presentation of the calendar time.
+ */
+ protected static String buildTimestamp(final Calendar pCalendar) {
+
+ if (pCalendar == null) {
+ return CALENDAR_IS_NULL_ERROR_MESSAGE;
+ }
+
+ // The timestamp format
+ StringBuilder timestamp = new StringBuilder();
+
+ //timestamp.append(DateUtil.getMonthName(new Integer(pCalendar.get(Calendar.MONTH)).toString(), "0", "us", "MEDIUM", false) + " ");
+ timestamp.append(DateFormat.getDateInstance(DateFormat.MEDIUM).format(pCalendar.getTime()));
+
+ //timestamp.append(pCalendar.get(Calendar.DAY_OF_MONTH) + " ");
+ timestamp.append(" ");
+ timestamp.append(StringUtil.pad(new Integer(pCalendar.get(Calendar.HOUR_OF_DAY)).toString(), 2, "0", true) + ":");
+ timestamp.append(StringUtil.pad(new Integer(pCalendar.get(Calendar.MINUTE)).toString(), 2, "0", true) + ":");
+ timestamp.append(StringUtil.pad(new Integer(pCalendar.get(Calendar.SECOND)).toString(), 2, "0", true) + ":");
+ timestamp.append(StringUtil.pad(new Integer(pCalendar.get(Calendar.MILLISECOND)).toString(), 3, "0", true));
+ return timestamp.toString();
+ }
+
+ /**
+ * Builds the time difference between two millisecond representations.
+ *
+ * This method is to be used with small time intervals between 0 ms up to a couple of minutes.
+ *
+ * @param pStartTime the start time.
+ * @param pEndTime the end time.
+ * @return the time difference in milliseconds.
+ */
+ public static String buildTimeDifference(final long pStartTime, final long pEndTime) {
+
+ //return pEndTime - pStartTime;
+ StringBuilder retVal = new StringBuilder();
+
+ // The time difference in milliseconds
+ long timeDifference = pEndTime - pStartTime;
+
+ if (timeDifference < 1000) {
+ retVal.append(timeDifference);
+ retVal.append(" ms");
+ } else {
+ long seconds = timeDifference / 1000;
+
+ timeDifference = timeDifference % 1000;
+ retVal.append(seconds);
+ retVal.append("s ");
+ retVal.append(timeDifference);
+ retVal.append("ms");
+ }
+
+ //return retVal.toString() + " (original timeDifference: " + new String(new Long(pEndTime - pStartTime).toString()) + ")";
+ return retVal.toString();
+ }
+
+ /**
+ * Builds the time difference between the given time and present time.
+ *
+ * This method is to be used with small time intervals between 0 ms up to a couple of minutes.
+ *
+ * @param pStartTime the start time.
+ * @return the time difference in milliseconds.
+ */
+ public static String buildTimeDifference(final long pStartTime) {
+
+ long presentTime = System.currentTimeMillis();
+
+ return buildTimeDifference(pStartTime, presentTime);
+ }
+
+ /**
+ * Prints out the difference between two millisecond representations.
+ * The start time is subtracted from the end time.
+ *
+ * @param pStartTime the start time.
+ * @param pEndTime the end time.
+ * @param pPrintStream the {@code java.io.PrintStream} for flushing the results.
+ */
+ public static void printTimeDifference(final long pStartTime, final long pEndTime, final PrintStream pPrintStream) {
+
+ if (pPrintStream == null) {
+ System.err.println(PRINTSTREAM_IS_NULL_ERROR_MESSAGE);
+ return;
+ }
+ pPrintStream.println(buildTimeDifference(pStartTime, pEndTime));
+ }
+
+ /**
+ * Prints out the difference between two millisecond representations.
+ * The start time is subtracted from the end time.
+ *
+ * @param pStartTime the start time.
+ * @param pEndTime the end time.
+ */
+ public static void printTimeDifference(final long pStartTime, final long pEndTime) {
+ printTimeDifference(pStartTime, pEndTime, System.out);
+ }
+
+ /**
+ * Prints out the difference between the given time and present time.
+ * The start time is subtracted from the present time.
+ *
+ * @param pStartTime the start time.
+ * @param pPrintStream the {@code java.io.PrintStream} for flushing the results.
+ */
+ public static void printTimeDifference(final long pStartTime, final PrintStream pPrintStream) {
+ printTimeDifference(pStartTime, System.currentTimeMillis(), pPrintStream);
+ }
+
+ /**
+ * Prints out the difference between the given time and present time to {@code System.out}.
+ * The start time is subtracted from the present time.
+ *
+ * usage:
+ *
+ * @param pStartTime the start time.
+ */
+ public static void printTimeDifference(final long pStartTime) {
+ printTimeDifference(pStartTime, System.out);
+ }
+
+ /**
+ * Builds a string representing the difference between two calendar times.
+ * The first calendar object is subtracted from the second one.
+ *
+ * This method is to be used with time intervals between 0 ms up to several hours.
+ *
+ * @param pStartCalendar the first {@code java.util.Calendar}.
+ * @param pEndCalendar the second {@code java.util.Calendar}.
+ * @return a string representation of the time difference.
+ * @see {@code java.util.Calendar}
+ */
+ public static String buildTimeDifference(final Calendar pStartCalendar, final Calendar pEndCalendar) {
+
+ if (pStartCalendar == null) {
+ return CALENDAR_IS_NULL_ERROR_MESSAGE;
+ }
+ if (pEndCalendar == null) {
+ return CALENDAR_IS_NULL_ERROR_MESSAGE;
+ }
+ if (pEndCalendar.before(pStartCalendar)) {
+ return CALENDAR_CAUSATION_ERROR_MESSAGE;
+ }
+ int dateDiff = pEndCalendar.get(Calendar.DATE) - pStartCalendar.get(Calendar.DATE);
+ int hourDiff = pEndCalendar.get(Calendar.HOUR_OF_DAY) - pStartCalendar.get(Calendar.HOUR_OF_DAY);
+ int minuteDiff = pEndCalendar.get(Calendar.MINUTE) - pStartCalendar.get(Calendar.MINUTE);
+ int secondDiff = pEndCalendar.get(Calendar.SECOND) - pStartCalendar.get(Calendar.SECOND);
+ int milliSecondDiff = pEndCalendar.get(Calendar.MILLISECOND) - pStartCalendar.get(Calendar.MILLISECOND);
+
+ if (milliSecondDiff < 0) {
+ secondDiff--;
+ milliSecondDiff += 1000;
+ }
+ if (secondDiff < 0) {
+ minuteDiff--;
+ secondDiff += 60;
+ }
+ if (minuteDiff < 0) {
+ hourDiff--;
+ minuteDiff += 60;
+ }
+ while (dateDiff > 0) {
+ dateDiff--;
+ hourDiff += 24;
+ }
+
+ // Time difference presentation format
+ StringBuilder buffer = new StringBuilder();
+
+ if ((hourDiff == 0) && (minuteDiff == 0) && (secondDiff == 0)) {
+ buffer.append(milliSecondDiff);
+ buffer.append("ms");
+ } else if ((hourDiff == 0) && (minuteDiff == 0)) {
+ buffer.append(secondDiff);
+ buffer.append("s ");
+ buffer.append(milliSecondDiff);
+ buffer.append("ms");
+ } else if (hourDiff == 0) {
+ buffer.append(minuteDiff);
+ buffer.append("m ");
+ buffer.append(secondDiff);
+ buffer.append(",");
+ buffer.append(milliSecondDiff);
+ buffer.append("s");
+ } else {
+ buffer.append(hourDiff);
+ buffer.append("h ");
+ buffer.append(minuteDiff);
+ buffer.append("m ");
+ buffer.append(secondDiff);
+ buffer.append(",");
+ buffer.append(milliSecondDiff);
+ buffer.append("s");
+ }
+ return buffer.toString();
+ }
+
+ /**
+ * Prints out the difference between to calendar times.
+ * The first calendar object is subtracted from the second one.
+ *
+ * @param pStartCalendar the first {@code java.util.Calendar}.
+ * @param pEndCalendar the second {@code java.util.Calendar}.
+ * @param pPrintStream the {@code java.io.PrintStream} for flushing the results.
+ * @see {@code java.util.Calendar}
+ */
+ public static void printTimeDifference(final Calendar pStartCalendar, final Calendar pEndCalendar, final PrintStream pPrintStream) {
+
+ if (pPrintStream == null) {
+ System.err.println(PRINTSTREAM_IS_NULL_ERROR_MESSAGE);
+ return;
+ }
+ pPrintStream.println(buildTimeDifference(pStartCalendar, pEndCalendar));
+ }
+
+ /**
+ * Prints out the difference between to calendar times two {@code System.out}.
+ * The first calendar object is subtracted from the second one.
+ *
+ * @param pStartCalendar the first {@code java.util.Calendar}.
+ * @param pEndCalendar the second {@code java.util.Calendar}.
+ * @see {@code java.util.Calendar}
+ */
+ public static void printTimeDifference(final Calendar pStartCalendar, final Calendar pEndCalendar) {
+ printTimeDifference(pStartCalendar, pEndCalendar, System.out);
+ }
+
+ /**
+ * Prints out the difference between the given calendar time and present time.
+ *
+ * @param pStartCalendar the {@code java.util.Calendar} to compare with present time.
+ * @param pPrintStream the {@code java.io.PrintStream} for flushing the results.
+ * @see {@code java.util.Calendar}
+ */
+ public static void printTimeDifference(final Calendar pStartCalendar, final PrintStream pPrintStream) {
+
+ GregorianCalendar endCalendar = new GregorianCalendar();
+
+ printTimeDifference(pStartCalendar, endCalendar, pPrintStream);
+ }
+
+ /**
+ * Prints out the difference between the given calendar time and present time to {@code System.out}.
+ *
+ * usage:
+ *
+ * @param pStartCalendar the {@code java.util.Calendar} to compare with present time.
+ * @see {@code java.util.Calendar}
+ */
+ public static void printTimeDifference(final Calendar pStartCalendar) {
+
+ GregorianCalendar endCalendar = new GregorianCalendar();
+
+ printTimeDifference(pStartCalendar, endCalendar);
+ }
+
+ /**
+ * Prints out a {@code com.iml.oslo.eito.util.DebugUtil.TimeDifference} object.
+ *
+ * @param pTimeDifference the {@code com.twelvemonkeys.util.DebugUtil.TimeDifference} to investigate.
+ * @param pPrintStream the {@code java.io.PrintStream} for flushing the results.
+ */
+ public static void printTimeDifference(final TimeDifference pTimeDifference, final PrintStream pPrintStream) {
+ printTimeDifference(pTimeDifference.getStartCalendar(), pTimeDifference.getEndCalendar(), pPrintStream);
+ }
+
+ /**
+ * Prints out a {@code com.iml.oslo.eito.util.DebugUtil.TimeDifference} object to {@code System.out}.
+ *
+ * @param pTimeDifference the {@code com.twelvemonkeys.util.DebugUtil.TimeDifference} to investigate.
+ */
+ public static void printTimeDifference(final TimeDifference pTimeDifference) {
+ printTimeDifference(pTimeDifference.getStartCalendar(), pTimeDifference.getEndCalendar(), System.out);
+ }
+
+ /**
+ * A convenience class for embracing two {@code java.util.Calendar} objects.
+ * The class is used for building a collection of time differences according to the {@code printTimeAverage} method.
+ */
+ public static class TimeDifference {
+
+ Calendar mStartCalendar;
+ Calendar mEndCalendar;
+
+ /**
+ * Constructor TimeDifference
+ *
+ *
+ */
+ public TimeDifference() {}
+
+ /**
+ * Constructor TimeDifference
+ *
+ *
+ * @param pStartCalendar
+ * @param pEndCalendar
+ *
+ */
+ public TimeDifference(final Calendar pStartCalendar, final Calendar pEndCalendar) {
+ this.mStartCalendar = pStartCalendar;
+ this.mEndCalendar = pEndCalendar;
+ }
+
+ /**
+ * Method setStartCalendar
+ *
+ *
+ * @param pStartCalendar
+ *
+ */
+ public void setStartCalendar(Calendar pStartCalendar) {
+ this.mStartCalendar = pStartCalendar;
+ }
+
+ /**
+ * Method getStartCalendar
+ *
+ *
+ * @return
+ *
+ */
+ public Calendar getStartCalendar() {
+ return this.mStartCalendar;
+ }
+
+ /**
+ * Method setEndCalendar
+ *
+ *
+ * @param pEndCalendar
+ *
+ */
+ public void setEndCalendar(Calendar pEndCalendar) {
+ this.mEndCalendar = pEndCalendar;
+ }
+
+ /**
+ * Method getEndCalendar
+ *
+ *
+ * @return
+ *
+ */
+ public Calendar getEndCalendar() {
+ return this.mEndCalendar;
+ }
+ }
+
+ /**
+ * Prints out the average time difference from a collection of {@code com.twelvemonkeys.util.DebugUtil.TimeDifference} objects.
+ *
+ *
+ * @param pTimeDifferences
+ * @param pPrintStream the {@code java.io.PrintStream} for flushing the results.
+ */
+ public static void printTimeAverage(final Collection pTimeDifferences, final PrintStream pPrintStream) {
+
+ if (pPrintStream == null) {
+ System.err.println(PRINTSTREAM_IS_NULL_ERROR_MESSAGE);
+ return;
+ }
+ if (pTimeDifferences == null) {
+ pPrintStream.println(TIMEDIFFERENCES_IS_NULL_ERROR_MESSAGE);
+ return;
+ }
+ Object o;
+ TimeDifference timeDifference;
+ Calendar startCalendar = null;
+ Calendar endCalendar = null;
+ Calendar totalStartCalendar = null;
+ Calendar totalEndCalendar = null;
+ long startCalendarMilliSeconds, endCalendarMilliSeconds;
+ List timeDifferenceList = new Vector();
+ Iterator i = pTimeDifferences.iterator();
+
+ if (i.hasNext()) {
+ o = i.next();
+ if (!(o instanceof TimeDifference)) {
+ pPrintStream.println(TIMEDIFFERENCES_WRONG_DATATYPE_ERROR_MESSAGE);
+ return;
+ }
+ timeDifference = (TimeDifference) o;
+ startCalendar = timeDifference.getStartCalendar();
+ totalStartCalendar = startCalendar;
+ endCalendar = timeDifference.getEndCalendar();
+ startCalendarMilliSeconds = startCalendar.getTime().getTime();
+ endCalendarMilliSeconds = endCalendar.getTime().getTime();
+ timeDifferenceList.add(new Long(endCalendarMilliSeconds - startCalendarMilliSeconds));
+ }
+ while (i.hasNext()) {
+ o = i.next();
+ if (!(o instanceof TimeDifference)) {
+ pPrintStream.println(TIMEDIFFERENCES_WRONG_DATATYPE_ERROR_MESSAGE);
+ return;
+ }
+ timeDifference = (TimeDifference) o;
+ startCalendar = timeDifference.getStartCalendar();
+ endCalendar = timeDifference.getEndCalendar();
+ startCalendarMilliSeconds = startCalendar.getTime().getTime();
+ endCalendarMilliSeconds = endCalendar.getTime().getTime();
+ timeDifferenceList.add(new Long(endCalendarMilliSeconds - startCalendarMilliSeconds));
+ }
+ totalEndCalendar = endCalendar;
+ int numberOfElements = timeDifferenceList.size();
+ long timeDifferenceElement;
+ long timeDifferenceSum = 0;
+
+ for (Iterator i2 = timeDifferenceList.iterator(); i2.hasNext(); ) {
+ timeDifferenceElement = ((Long) i2.next()).longValue();
+ timeDifferenceSum += timeDifferenceElement;
+ }
+
+ // Total elapsed time
+ String totalElapsedTime = buildTimeDifference(totalStartCalendar, totalEndCalendar);
+
+ // Time average presentation format
+ pPrintStream.println("Average time difference: " + timeDifferenceSum / numberOfElements + "ms (" + numberOfElements
+ + " elements, total elapsed time: " + totalElapsedTime + ")");
+ }
+
+ /**
+ * Prints out the average time difference from a collection of {@code com.twelvemonkeys.util.DebugUtil.TimeDifference} objects to {@code System.out}.
+ *
+ *
+ * @param pTimeDifferences
+ */
+ public static void printTimeAverage(final Collection pTimeDifferences) {
+ printTimeAverage(pTimeDifferences, System.out);
+ }
+
+ // Reflective methods
+
+ /**
+ * Prints the top-wrapped class name of a {@code java.lang.Object} to a {@code java.io.PrintStream}.
+ *
+ * @param pObject the {@code java.lang.Object} to be printed.
+ * @param pPrintStream the {@code java.io.PrintStream} for flushing the results.
+ * @see {@code java.lang.Class}
+ */
+ public static void printClassName(final Object pObject, final PrintStream pPrintStream) {
+
+ if (pPrintStream == null) {
+ System.err.println(PRINTSTREAM_IS_NULL_ERROR_MESSAGE);
+ return;
+ }
+ pPrintStream.println(getClassName(pObject));
+ }
+
+ /**
+ * Prints the top-wrapped class name of a {@code java.lang.Object} to {@code System.out}.
+ *
+ * @param pObject the {@code java.lang.Object} to be printed.
+ * @see {@code java.lang.Class}
+ */
+ public static void printClassName(final Object pObject) {
+ printClassName(pObject, System.out);
+ }
+
+ /**
+ * Builds the top-wrapped class name of a {@code java.lang.Object}.
+ *
+ * @param pObject the {@code java.lang.Object} to be analysed.
+ * @return the object's class name.
+ * @see {@code java.lang.Class}
+ */
+ public static String getClassName(final Object pObject) {
+
+ if (pObject == null) {
+ return OBJECT_IS_NULL_ERROR_MESSAGE;
+ }
+ return pObject.getClass().getName();
+ }
+
+ /**
+ * Prints javadoc-like, the top wrapped class fields and methods of a {@code java.lang.Object} to a {@code java.io.PrintStream}.
+ *
+ * @param pObject the {@code java.lang.Object} to be analysed.
+ * @param pObjectName the name of the object instance, for identification purposes.
+ * @param pPrintStream the {@code java.io.PrintStream} for flushing the results.
+ * @see {@code java.lang.Class}
+ * @see {@code java.lang.reflect.Modifier}
+ * @see {@code java.lang.reflect.Field}
+ * @see {@code java.lang.reflect.Constructor}
+ * @see {@code java.lang.reflect.Method}
+ */
+ public static void printClassDetails(final Object pObject, final String pObjectName, final PrintStream pPrintStream) {
+
+ if (pPrintStream == null) {
+ System.err.println(PRINTSTREAM_IS_NULL_ERROR_MESSAGE);
+ return;
+ }
+ pPrintStream.println(getClassDetails(pObject, pObjectName));
+ }
+
+ /**
+ * Prints javadoc-like, the top wrapped class fields and methods of a {@code java.lang.Object} to {@code System.out}.
+ *
+ * @param pObject the {@code java.lang.Object} to be analysed.
+ * @param pObjectName the name of the object instance, for identification purposes.
+ * @see {@code java.lang.Class}
+ * @see {@code java.lang.reflect.Modifier}
+ * @see {@code java.lang.reflect.Field}
+ * @see {@code java.lang.reflect.Constructor}
+ * @see {@code java.lang.reflect.Method}
+ */
+ public static void printClassDetails(final Object pObject, final String pObjectName) {
+ printClassDetails(pObject, pObjectName, System.out);
+ }
+
+ /**
+ * Prints javadoc-like, the top wrapped class fields and methods of a {@code java.lang.Object} to {@code System.out}.
+ *
+ * @param pObject the {@code java.lang.Object} to be analysed.
+ * @see {@code java.lang.Class}
+ * @see {@code java.lang.reflect.Modifier}
+ * @see {@code java.lang.reflect.Field}
+ * @see {@code java.lang.reflect.Constructor}
+ * @see {@code java.lang.reflect.Method}
+ */
+ public static void printClassDetails(final Object pObject) {
+ printClassDetails(pObject, null, System.out);
+ }
+
+ /**
+ * Prints javadoc-like, the top wrapped class fields and methods of a {@code java.lang.Object} to a {@code java.io.PrintStream}.
+ *
+ * @param pObject the {@code java.lang.Object} to be analysed.
+ * @param pPrintStream the {@code java.io.PrintStream} for flushing the results.
+ * @see {@code java.lang.Class}
+ * @see {@code java.lang.reflect.Modifier}
+ * @see {@code java.lang.reflect.Field}
+ * @see {@code java.lang.reflect.Constructor}
+ * @see {@code java.lang.reflect.Method}
+ */
+ public static void printClassDetails(final Object pObject, final PrintStream pPrintStream) {
+ printClassDetails(pObject, null, pPrintStream);
+ }
+
+ /**
+ * Builds a javadoc-like presentation of the top wrapped class fields and methods of a {@code java.lang.Object}.
+ *
+ * @param pObject the {@code java.lang.Object} to be analysed.
+ * @return a listing of the object's class details.
+ * @see {@code java.lang.Class}
+ * @see {@code java.lang.reflect.Modifier}
+ * @see {@code java.lang.reflect.Field}
+ * @see {@code java.lang.reflect.Constructor}
+ * @see {@code java.lang.reflect.Method}
+ */
+ public static String getClassDetails(final Object pObject) {
+ return getClassDetails(pObject, null);
+ }
+
+ /**
+ * Builds a javadoc-like presentation of the top wrapped class fields and methods of a {@code java.lang.Object}.
+ *
+ * @param pObject the {@code java.lang.Object} to be analysed.
+ * @param pObjectName the name of the object instance, for identification purposes.
+ * @return a listing of the object's class details.
+ * @see {@code java.lang.Class}
+ * @see {@code java.lang.reflect.Modifier}
+ * @see {@code java.lang.reflect.Field}
+ * @see {@code java.lang.reflect.Constructor}
+ * @see {@code java.lang.reflect.Method}
+ */
+ public static String getClassDetails(final Object pObject, final String pObjectName) {
+
+ if (pObject == null) {
+ return OBJECT_IS_NULL_ERROR_MESSAGE;
+ }
+ final String endOfLine = System.getProperty("line.separator");
+ final String dividerLine = "---------------------------------------------------------";
+ Class c = pObject.getClass();
+ StringTokenizer tokenizedString;
+ String str;
+ String className = new String();
+ String superClassName = new String();
+ StringBuilder buffer = new StringBuilder();
+
+ // Heading
+ buffer.append(endOfLine);
+ buffer.append("**** class details");
+ if (!StringUtil.isEmpty(pObjectName)) {
+ buffer.append(" for \"" + pObjectName + "\"");
+ }
+ buffer.append(" ****");
+ buffer.append(endOfLine);
+
+ // Package
+ Package p = c.getPackage();
+
+ if (p != null) {
+ buffer.append(p.getName());
+ }
+ buffer.append(endOfLine);
+
+ // Class or Interface
+ if (c.isInterface()) {
+ buffer.append("I n t e r f a c e ");
+ } else {
+ buffer.append("C l a s s ");
+ }
+ str = c.getName();
+ tokenizedString = new StringTokenizer(str, ".");
+ while (tokenizedString.hasMoreTokens()) {
+ className = tokenizedString.nextToken().trim();
+ }
+ str = new String();
+ char[] charArray = className.toCharArray();
+
+ for (int i = 0; i < charArray.length; i++) {
+ str += charArray[i] + " ";
+ }
+ buffer.append(str);
+ buffer.append(endOfLine);
+ buffer.append(endOfLine);
+
+ // Class Hierarch
+ List classNameList = new Vector();
+
+ classNameList.add(c.getName());
+ Class superclass = c.getSuperclass();
+
+ while (superclass != null) {
+ classNameList.add(superclass.getName());
+ superclass = superclass.getSuperclass();
+ }
+ Object[] classNameArray = classNameList.toArray();
+ int counter = 0;
+
+ for (int i = classNameArray.length - 1; i >= 0; i--) {
+ for (int j = 0; j < counter; j++) {
+ buffer.append(" ");
+ }
+ if (counter > 0) {
+ buffer.append("|");
+ buffer.append(endOfLine);
+ }
+ for (int j = 0; j < counter; j++) {
+ buffer.append(" ");
+ }
+ if (counter > 0) {
+ buffer.append("+-");
+ }
+ buffer.append((String) classNameArray[i]);
+ buffer.append(endOfLine);
+ counter++;
+ }
+
+ // Divider
+ buffer.append(endOfLine);
+ buffer.append(dividerLine);
+ buffer.append(endOfLine);
+ buffer.append(endOfLine);
+
+ // Profile
+ int classModifier = c.getModifiers();
+
+ buffer.append(Modifier.toString(classModifier) + " ");
+ if (c.isInterface()) {
+ buffer.append("Interface ");
+ } else {
+ buffer.append("Class ");
+ }
+ buffer.append(className);
+ buffer.append(endOfLine);
+ if ((classNameArray != null) && (classNameArray[classNameArray.length - 2] != null)) {
+ str = (String) classNameArray[classNameArray.length - 2];
+ tokenizedString = new StringTokenizer(str, ".");
+ while (tokenizedString.hasMoreTokens()) {
+ superClassName = tokenizedString.nextToken().trim();
+ }
+ buffer.append("extends " + superClassName);
+ buffer.append(endOfLine);
+ }
+ if (!c.isInterface()) {
+ Class[] interfaces = c.getInterfaces();
+
+ if ((interfaces != null) && (interfaces.length > 0)) {
+ buffer.append("implements ");
+ str = interfaces[0].getName();
+ tokenizedString = new StringTokenizer(str, ".");
+ while (tokenizedString.hasMoreTokens()) {
+ str = tokenizedString.nextToken().trim();
+ }
+ buffer.append(str);
+ for (int i = 1; i < interfaces.length; i++) {
+ str = interfaces[i].getName();
+ tokenizedString = new StringTokenizer(str, ".");
+ while (tokenizedString.hasMoreTokens()) {
+ str = tokenizedString.nextToken().trim();
+ }
+ buffer.append(", " + str);
+ }
+ buffer.append(endOfLine);
+ }
+ }
+
+ // Divider
+ buffer.append(endOfLine);
+ buffer.append(dividerLine);
+ buffer.append(endOfLine);
+ buffer.append(endOfLine);
+
+ // Fields
+ buffer.append("F I E L D S U M M A R Y");
+ buffer.append(endOfLine);
+ Field[] fields = c.getFields();
+
+ if (fields != null) {
+ for (int i = 0; i < fields.length; i++) {
+ buffer.append(Modifier.toString(fields[i].getType().getModifiers()) + " ");
+ str = fields[i].getType().getName();
+ tokenizedString = new StringTokenizer(str, ".");
+ while (tokenizedString.hasMoreTokens()) {
+ str = tokenizedString.nextToken().trim();
+ }
+ buffer.append(str + " ");
+ buffer.append(fields[i].getName());
+ buffer.append(endOfLine);
+ }
+ }
+ buffer.append(endOfLine);
+
+ // Constructors
+ buffer.append("C O N S T R U C T O R S U M M A R Y");
+ buffer.append(endOfLine);
+ Constructor[] constructors = c.getConstructors();
+
+ if (constructors != null) {
+ for (int i = 0; i < constructors.length; i++) {
+ buffer.append(className + "(");
+ Class[] parameterTypes = constructors[i].getParameterTypes();
+
+ if (parameterTypes != null) {
+ if (parameterTypes.length > 0) {
+ str = parameterTypes[0].getName();
+ tokenizedString = new StringTokenizer(str, ".");
+ while (tokenizedString.hasMoreTokens()) {
+ str = tokenizedString.nextToken().trim();
+ }
+ buffer.append(str);
+ for (int j = 1; j < parameterTypes.length; j++) {
+ str = parameterTypes[j].getName();
+ tokenizedString = new StringTokenizer(str, ".");
+ while (tokenizedString.hasMoreTokens()) {
+ str = tokenizedString.nextToken().trim();
+ }
+ buffer.append(", " + str);
+ }
+ }
+ }
+ buffer.append(")");
+ buffer.append(endOfLine);
+ }
+ }
+ buffer.append(endOfLine);
+
+ // Methods
+ buffer.append("M E T H O D S U M M A R Y");
+ buffer.append(endOfLine);
+ Method[] methods = c.getMethods();
+
+ if (methods != null) {
+ for (int i = 0; i < methods.length; i++) {
+ buffer.append(Modifier.toString(methods[i].getModifiers()) + " ");
+ str = methods[i].getReturnType().getName();
+ tokenizedString = new StringTokenizer(str, ".");
+ while (tokenizedString.hasMoreTokens()) {
+ str = tokenizedString.nextToken().trim();
+ }
+ buffer.append(str + " ");
+ buffer.append(methods[i].getName() + "(");
+ Class[] parameterTypes = methods[i].getParameterTypes();
+
+ if ((parameterTypes != null) && (parameterTypes.length > 0)) {
+ if (parameterTypes[0] != null) {
+ str = parameterTypes[0].getName();
+ tokenizedString = new StringTokenizer(str, ".");
+ while (tokenizedString.hasMoreTokens()) {
+ str = tokenizedString.nextToken().trim();
+ }
+
+ // array bugfix
+ if (str.charAt(str.length() - 1) == ';') {
+ str = str.substring(0, str.length() - 1) + "[]";
+ }
+ buffer.append(str);
+ for (int j = 1; j < parameterTypes.length; j++) {
+ str = parameterTypes[j].getName();
+ tokenizedString = new StringTokenizer(str, ".");
+ while (tokenizedString.hasMoreTokens()) {
+ str = tokenizedString.nextToken().trim();
+ }
+ buffer.append(", " + str);
+ }
+ }
+ }
+ buffer.append(")");
+ buffer.append(endOfLine);
+ }
+ }
+ buffer.append(endOfLine);
+
+ // Ending
+ buffer.append("**** class details");
+ if (!StringUtil.isEmpty(pObjectName)) {
+ buffer.append(" for \"" + pObjectName + "\"");
+ }
+ buffer.append(" end ****");
+ buffer.append(endOfLine);
+ return buffer.toString();
+ }
+
+ /**
+ * Prettyprints a large number.
+ *
+ *
+ * @param pBigNumber
+ * @return prettyprinted number with dot-separation each 10e3.
+ */
+ public static String getLargeNumber(final long pBigNumber) {
+
+ StringBuilder buffer = new StringBuilder(new Long(pBigNumber).toString());
+ char[] number = new Long(pBigNumber).toString().toCharArray();
+ int reverseIndex = 0;
+
+ for (int i = number.length; i >= 0; i--) {
+ reverseIndex++;
+ if ((reverseIndex % 3 == 0) && (i > 1)) {
+ buffer = buffer.insert(i - 1, '.');
+ }
+ }
+ return buffer.toString();
+ }
+
+ /**
+ * Prettyprints milliseconds to ?day(s) ?h ?m ?s ?ms.
+ *
+ *
+ * @param pMilliseconds
+ * @return prettyprinted time duration.
+ */
+ public static String getTimeInterval(final long pMilliseconds) {
+
+ long timeIntervalMilliseconds = pMilliseconds;
+ long timeIntervalSeconds = 0;
+ long timeIntervalMinutes = 0;
+ long timeIntervalHours = 0;
+ long timeIntervalDays = 0;
+ boolean printMilliseconds = true;
+ boolean printSeconds = false;
+ boolean printMinutes = false;
+ boolean printHours = false;
+ boolean printDays = false;
+ final long MILLISECONDS_IN_SECOND = 1000;
+ final long MILLISECONDS_IN_MINUTE = 60 * MILLISECONDS_IN_SECOND; // 60000
+ final long MILLISECONDS_IN_HOUR = 60 * MILLISECONDS_IN_MINUTE; // 3600000
+ final long MILLISECONDS_IN_DAY = 24 * MILLISECONDS_IN_HOUR; // 86400000
+ StringBuilder timeIntervalBuffer = new StringBuilder();
+
+ // Days
+ if (timeIntervalMilliseconds >= MILLISECONDS_IN_DAY) {
+ timeIntervalDays = timeIntervalMilliseconds / MILLISECONDS_IN_DAY;
+ timeIntervalMilliseconds = timeIntervalMilliseconds % MILLISECONDS_IN_DAY;
+ printDays = true;
+ printHours = true;
+ printMinutes = true;
+ printSeconds = true;
+ }
+
+ // Hours
+ if (timeIntervalMilliseconds >= MILLISECONDS_IN_HOUR) {
+ timeIntervalHours = timeIntervalMilliseconds / MILLISECONDS_IN_HOUR;
+ timeIntervalMilliseconds = timeIntervalMilliseconds % MILLISECONDS_IN_HOUR;
+ printHours = true;
+ printMinutes = true;
+ printSeconds = true;
+ }
+
+ // Minutes
+ if (timeIntervalMilliseconds >= MILLISECONDS_IN_MINUTE) {
+ timeIntervalMinutes = timeIntervalMilliseconds / MILLISECONDS_IN_MINUTE;
+ timeIntervalMilliseconds = timeIntervalMilliseconds % MILLISECONDS_IN_MINUTE;
+ printMinutes = true;
+ printSeconds = true;
+ }
+
+ // Seconds
+ if (timeIntervalMilliseconds >= MILLISECONDS_IN_SECOND) {
+ timeIntervalSeconds = timeIntervalMilliseconds / MILLISECONDS_IN_SECOND;
+ timeIntervalMilliseconds = timeIntervalMilliseconds % MILLISECONDS_IN_SECOND;
+ printSeconds = true;
+ }
+
+ // Prettyprint
+ if (printDays) {
+ timeIntervalBuffer.append(timeIntervalDays);
+ if (timeIntervalDays > 1) {
+ timeIntervalBuffer.append("days ");
+ } else {
+ timeIntervalBuffer.append("day ");
+ }
+ }
+ if (printHours) {
+ timeIntervalBuffer.append(timeIntervalHours);
+ timeIntervalBuffer.append("h ");
+ }
+ if (printMinutes) {
+ timeIntervalBuffer.append(timeIntervalMinutes);
+ timeIntervalBuffer.append("m ");
+ }
+ if (printSeconds) {
+ timeIntervalBuffer.append(timeIntervalSeconds);
+ timeIntervalBuffer.append("s ");
+ }
+ if (printMilliseconds) {
+ timeIntervalBuffer.append(timeIntervalMilliseconds);
+ timeIntervalBuffer.append("ms");
+ }
+ return timeIntervalBuffer.toString();
+ }
+}
+
+
+/*--- Formatted in Sun Java Convention Style on ma, des 1, '03 ---*/
+
+
+/*------ Formatted by Jindent 3.23 Basic 1.0 --- http://www.jindent.de ------*/
diff --git a/twelvemonkeys-sandbox/src/main/java/com/twelvemonkeys/util/regex/REWildcardStringParser.java b/twelvemonkeys-sandbox/src/main/java/com/twelvemonkeys/util/regex/REWildcardStringParser.java
index 9a1c1b9d..d2bbb8de 100755
--- a/twelvemonkeys-sandbox/src/main/java/com/twelvemonkeys/util/regex/REWildcardStringParser.java
+++ b/twelvemonkeys-sandbox/src/main/java/com/twelvemonkeys/util/regex/REWildcardStringParser.java
@@ -1,398 +1,398 @@
-/*
- * Copyright (c) 2008, Harald Kuhr
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name "TwelveMonkeys" nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package com.twelvemonkeys.util.regex;
-
-import com.twelvemonkeys.util.DebugUtil;
-
-import java.io.PrintStream;
-import java.util.regex.Pattern;
-import java.util.regex.PatternSyntaxException;
-
-/**
- * This class parses arbitrary strings against a wildcard string mask provided.
- * The wildcard characters are '*' and '?'.
- *
- * The string masks provided are treated as case sensitive.
- *
- * This task is performed based on regular expression techniques.
- * 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.
- *
- * This class uses the Regexp package from Apache's Jakarta Project, links below.
- *
- *
- *
- * Examples of usage:
- *
- * @author Eirik Torske
- * @see Jakarta Regexp
- * @see {@code org.apache.regexp.RE}
- * @see com.twelvemonkeys.util.regex.WildcardStringParser
- *
- * @todo Rewrite to use this regex package, and not Jakarta directly!
- */
-public class REWildcardStringParser /*extends EntityObject*/ {
-
- // Constants
-
- /** Field 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', 'æ',
- 'ø', 'å', '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', 'Æ', 'Ø', 'Å', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', '_', '-'
- };
-
- /** Field FREE_RANGE_CHARACTER */
- public static final char FREE_RANGE_CHARACTER = '*';
-
- /** Field FREE_PASS_CHARACTER */
- public static final char FREE_PASS_CHARACTER = '?';
-
- // Members
- Pattern mRegexpParser;
- String mStringMask;
- boolean mInitialized = false;
- int mTotalNumberOfStringsParsed;
- boolean mDebugging;
- PrintStream out;
-
- // Properties
- // Constructors
-
- /**
- * Creates a wildcard string parser.
- *
- * @param pStringMask the wildcard string mask.
- */
- public REWildcardStringParser(final String pStringMask) {
- this(pStringMask, false);
- }
-
- /**
- * Creates a wildcard string parser.
- *
- * @param pStringMask the wildcard string mask.
- * @param pDebugging {@code true} will cause debug messages to be emitted to {@code System.out}.
- */
- public REWildcardStringParser(final String pStringMask, final boolean pDebugging) {
- this(pStringMask, pDebugging, System.out);
- }
-
- /**
- * Creates a wildcard string parser.
- *
- * @param pStringMask the wildcard string mask.
- * @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.
- */
- public REWildcardStringParser(final String pStringMask, final boolean pDebugging, final PrintStream pDebuggingPrintStream) {
-
- this.mStringMask = pStringMask;
- this.mDebugging = pDebugging;
- this.out = pDebuggingPrintStream;
- mInitialized = buildRegexpParser();
- }
-
- // Methods
-
- /**
- * 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...
- * @return the corresponding regular expression or {@code null} if an error occurred.
- */
- private String convertWildcardExpressionToRegularExpression(final String pWildcardExpression) {
-
- if (pWildcardExpression == null) {
- if (mDebugging) {
- out.println(DebugUtil.getPrefixDebugMessage(this) + "wildcard expression is null - also returning null as regexp!");
- }
- return null;
- }
- StringBuilder regexpBuffer = new StringBuilder();
- boolean convertingError = false;
-
- for (int i = 0; i < pWildcardExpression.length(); i++) {
- if (convertingError) {
- return null;
- }
-
- // Free-range character '*'
- char stringMaskChar = pWildcardExpression.charAt(i);
-
- if (isFreeRangeCharacter(stringMaskChar)) {
- regexpBuffer.append("(([a-åA-Å0-9]|.|_|-)*)");
- }
-
- // Free-pass character '?'
- else if (isFreePassCharacter(stringMaskChar)) {
- regexpBuffer.append("([a-åA_Å0-9]|.|_|-)");
- }
-
- // Valid characters
- else if (isInAlphabet(stringMaskChar)) {
- regexpBuffer.append(stringMaskChar);
- }
-
- // Invalid character - aborting
- else {
- if (mDebugging) {
- out.println(DebugUtil.getPrefixDebugMessage(this)
- + "one or more characters in string mask are not legal characters - returning null as regexp!");
- }
- convertingError = true;
- }
- }
- return regexpBuffer.toString();
- }
-
- /**
- * Builds the regexp parser.
- */
- private boolean buildRegexpParser() {
-
- // Convert wildcard string mask to regular expression
- String regexp = convertWildcardExpressionToRegularExpression(mStringMask);
-
- if (regexp == null) {
- out.println(DebugUtil.getPrefixErrorMessage(this)
- + "irregularity in regexp conversion - now not able to parse any strings, all strings will be rejected!");
- return false;
- }
-
- // Instantiate a regular expression parser
- try {
- mRegexpParser = Pattern.compile(regexp);
- }
- catch (PatternSyntaxException e) {
- if (mDebugging) {
- out.println(DebugUtil.getPrefixErrorMessage(this) + "RESyntaxException \"" + e.getMessage()
- + "\" caught - now not able to parse any strings, all strings will be rejected!");
- }
- if (mDebugging) {
- e.printStackTrace(System.err);
- }
- return false;
- }
- if (mDebugging) {
- out.println(DebugUtil.getPrefixDebugMessage(this) + "regular expression parser from regular expression " + regexp
- + " extracted from wildcard string mask " + mStringMask + ".");
- }
- return true;
- }
-
- /**
- * Simple check of the string to be parsed.
- */
- private boolean checkStringToBeParsed(final String pStringToBeParsed) {
-
- // Check for nullness
- if (pStringToBeParsed == null) {
- if (mDebugging) {
- out.println(DebugUtil.getPrefixDebugMessage(this) + "string to be parsed is null - rejection!");
- }
- return false;
- }
-
- // Check if valid character (element in alphabet)
- for (int i = 0; i < pStringToBeParsed.length(); i++) {
- if (!isInAlphabet(pStringToBeParsed.charAt(i))) {
- if (mDebugging) {
- out.println(DebugUtil.getPrefixDebugMessage(this)
- + "one or more characters in string to be parsed are not legal characters - rejection!");
- }
- return false;
- }
- }
- return true;
- }
-
- /**
- * 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) {
-
- for (int i = 0; i < ALPHABET.length; i++) {
- if (pCharToCheck == ALPHABET[i]) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Tests if a certain character is the designated "free-range" character ('*').
- */
- public static boolean isFreeRangeCharacter(final char pCharToCheck) {
- return pCharToCheck == FREE_RANGE_CHARACTER;
- }
-
- /**
- * Tests if a certain character is the designated "free-pass" character ('?').
- */
- public static boolean isFreePassCharacter(final char pCharToCheck) {
- return pCharToCheck == FREE_PASS_CHARACTER;
- }
-
- /**
- * Tests if a certain character is a wildcard character ('*' or '?').
- */
- public static boolean isWildcardCharacter(final char pCharToCheck) {
- return ((isFreeRangeCharacter(pCharToCheck)) || (isFreePassCharacter(pCharToCheck)));
- }
-
- /**
- * Gets the string mask that was used when building the parser atomaton.
- *
- * @return the string mask used for building the parser automaton.
- */
- public String getStringMask() {
- return mStringMask;
- }
-
- /**
- * Parses a string.
- *
- *
- * @param pStringToBeParsed
- * @return {@code true} if and only if the string are accepted by the parser.
- */
- public boolean parseString(final String pStringToBeParsed) {
-
- if (mDebugging) {
- out.println();
- }
- if (mDebugging) {
- out.println(DebugUtil.getPrefixDebugMessage(this) + "parsing \"" + pStringToBeParsed + "\"...");
- }
-
- // Update statistics
- mTotalNumberOfStringsParsed++;
-
- // Check string to be parsed
- if (!checkStringToBeParsed(pStringToBeParsed)) {
- return false;
- }
-
- // Perform parsing and return accetance/rejection flag
- if (mInitialized) {
- return mRegexpParser.matcher(pStringToBeParsed).matches();
- } else {
- out.println(DebugUtil.getPrefixErrorMessage(this) + "trying to use non-initialized parser - string rejected!");
- }
- return false;
- }
-
- /*
- * Overriding mandatory methods from EntityObject's.
- */
-
- /**
- * Method toString
- *
- *
- * @return
- *
- */
- public String toString() {
-
- StringBuilder buffer = new StringBuilder();
-
- buffer.append(DebugUtil.getClassName(this));
- buffer.append(": String mask ");
- buffer.append(mStringMask);
- buffer.append("\n");
- return buffer.toString();
- }
-
- // Just taking the lazy, easy and dangerous way out
-
- /**
- * Method equals
- *
- *
- * @param pObject
- *
- * @return
- *
- */
- public boolean equals(Object pObject) {
-
- if (pObject instanceof REWildcardStringParser) {
- REWildcardStringParser externalParser = (REWildcardStringParser) pObject;
-
- return (externalParser.mStringMask == this.mStringMask);
- }
- return ((Object) this).equals(pObject);
- }
-
- // Just taking the lazy, easy and dangerous way out
-
- /**
- * Method hashCode
- *
- *
- * @return
- *
- */
- public int hashCode() {
- return ((Object) this).hashCode();
- }
-
- protected Object clone() throws CloneNotSupportedException {
- return new REWildcardStringParser(mStringMask);
- }
-
- // Just taking the lazy, easy and dangerous way out
- protected void finalize() throws Throwable {}
-}
-
-
-/*--- Formatted in Sun Java Convention Style on ma, des 1, '03 ---*/
-
-
-/*------ Formatted by Jindent 3.23 Basic 1.0 --- http://www.jindent.de ------*/
+/*
+ * Copyright (c) 2008, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name "TwelveMonkeys" nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.twelvemonkeys.util.regex;
+
+import com.twelvemonkeys.util.DebugUtil;
+
+import java.io.PrintStream;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+/**
+ * This class parses arbitrary strings against a wildcard string mask provided.
+ * The wildcard characters are '*' and '?'.
+ *
+ * The string masks provided are treated as case sensitive.
+ *
+ * This task is performed based on regular expression techniques.
+ * 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.
+ *
+ * This class uses the Regexp package from Apache's Jakarta Project, links below.
+ *
+ *
+ *
+ * Examples of usage:
+ *
+ * @author Eirik Torske
+ * @see Jakarta Regexp
+ * @see {@code org.apache.regexp.RE}
+ * @see com.twelvemonkeys.util.regex.WildcardStringParser
+ *
+ * @todo Rewrite to use this regex package, and not Jakarta directly!
+ */
+public class REWildcardStringParser /*extends EntityObject*/ {
+
+ // Constants
+
+ /** Field 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', 'æ',
+ 'ø', 'å', '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', 'Æ', 'Ø', 'Å', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', '_', '-'
+ };
+
+ /** Field FREE_RANGE_CHARACTER */
+ public static final char FREE_RANGE_CHARACTER = '*';
+
+ /** Field FREE_PASS_CHARACTER */
+ public static final char FREE_PASS_CHARACTER = '?';
+
+ // Members
+ Pattern mRegexpParser;
+ String mStringMask;
+ boolean mInitialized = false;
+ int mTotalNumberOfStringsParsed;
+ boolean mDebugging;
+ PrintStream out;
+
+ // Properties
+ // Constructors
+
+ /**
+ * Creates a wildcard string parser.
+ *
+ * @param pStringMask the wildcard string mask.
+ */
+ public REWildcardStringParser(final String pStringMask) {
+ this(pStringMask, false);
+ }
+
+ /**
+ * Creates a wildcard string parser.
+ *
+ * @param pStringMask the wildcard string mask.
+ * @param pDebugging {@code true} will cause debug messages to be emitted to {@code System.out}.
+ */
+ public REWildcardStringParser(final String pStringMask, final boolean pDebugging) {
+ this(pStringMask, pDebugging, System.out);
+ }
+
+ /**
+ * Creates a wildcard string parser.
+ *
+ * @param pStringMask the wildcard string mask.
+ * @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.
+ */
+ public REWildcardStringParser(final String pStringMask, final boolean pDebugging, final PrintStream pDebuggingPrintStream) {
+
+ this.mStringMask = pStringMask;
+ this.mDebugging = pDebugging;
+ this.out = pDebuggingPrintStream;
+ mInitialized = buildRegexpParser();
+ }
+
+ // Methods
+
+ /**
+ * 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...
+ * @return the corresponding regular expression or {@code null} if an error occurred.
+ */
+ private String convertWildcardExpressionToRegularExpression(final String pWildcardExpression) {
+
+ if (pWildcardExpression == null) {
+ if (mDebugging) {
+ out.println(DebugUtil.getPrefixDebugMessage(this) + "wildcard expression is null - also returning null as regexp!");
+ }
+ return null;
+ }
+ StringBuilder regexpBuffer = new StringBuilder();
+ boolean convertingError = false;
+
+ for (int i = 0; i < pWildcardExpression.length(); i++) {
+ if (convertingError) {
+ return null;
+ }
+
+ // Free-range character '*'
+ char stringMaskChar = pWildcardExpression.charAt(i);
+
+ if (isFreeRangeCharacter(stringMaskChar)) {
+ regexpBuffer.append("(([a-åA-Å0-9]|.|_|-)*)");
+ }
+
+ // Free-pass character '?'
+ else if (isFreePassCharacter(stringMaskChar)) {
+ regexpBuffer.append("([a-åA_Å0-9]|.|_|-)");
+ }
+
+ // Valid characters
+ else if (isInAlphabet(stringMaskChar)) {
+ regexpBuffer.append(stringMaskChar);
+ }
+
+ // Invalid character - aborting
+ else {
+ if (mDebugging) {
+ out.println(DebugUtil.getPrefixDebugMessage(this)
+ + "one or more characters in string mask are not legal characters - returning null as regexp!");
+ }
+ convertingError = true;
+ }
+ }
+ return regexpBuffer.toString();
+ }
+
+ /**
+ * Builds the regexp parser.
+ */
+ private boolean buildRegexpParser() {
+
+ // Convert wildcard string mask to regular expression
+ String regexp = convertWildcardExpressionToRegularExpression(mStringMask);
+
+ if (regexp == null) {
+ out.println(DebugUtil.getPrefixErrorMessage(this)
+ + "irregularity in regexp conversion - now not able to parse any strings, all strings will be rejected!");
+ return false;
+ }
+
+ // Instantiate a regular expression parser
+ try {
+ mRegexpParser = Pattern.compile(regexp);
+ }
+ catch (PatternSyntaxException e) {
+ if (mDebugging) {
+ out.println(DebugUtil.getPrefixErrorMessage(this) + "RESyntaxException \"" + e.getMessage()
+ + "\" caught - now not able to parse any strings, all strings will be rejected!");
+ }
+ if (mDebugging) {
+ e.printStackTrace(System.err);
+ }
+ return false;
+ }
+ if (mDebugging) {
+ out.println(DebugUtil.getPrefixDebugMessage(this) + "regular expression parser from regular expression " + regexp
+ + " extracted from wildcard string mask " + mStringMask + ".");
+ }
+ return true;
+ }
+
+ /**
+ * Simple check of the string to be parsed.
+ */
+ private boolean checkStringToBeParsed(final String pStringToBeParsed) {
+
+ // Check for nullness
+ if (pStringToBeParsed == null) {
+ if (mDebugging) {
+ out.println(DebugUtil.getPrefixDebugMessage(this) + "string to be parsed is null - rejection!");
+ }
+ return false;
+ }
+
+ // Check if valid character (element in alphabet)
+ for (int i = 0; i < pStringToBeParsed.length(); i++) {
+ if (!isInAlphabet(pStringToBeParsed.charAt(i))) {
+ if (mDebugging) {
+ out.println(DebugUtil.getPrefixDebugMessage(this)
+ + "one or more characters in string to be parsed are not legal characters - rejection!");
+ }
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * 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) {
+
+ for (int i = 0; i < ALPHABET.length; i++) {
+ if (pCharToCheck == ALPHABET[i]) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Tests if a certain character is the designated "free-range" character ('*').
+ */
+ public static boolean isFreeRangeCharacter(final char pCharToCheck) {
+ return pCharToCheck == FREE_RANGE_CHARACTER;
+ }
+
+ /**
+ * Tests if a certain character is the designated "free-pass" character ('?').
+ */
+ public static boolean isFreePassCharacter(final char pCharToCheck) {
+ return pCharToCheck == FREE_PASS_CHARACTER;
+ }
+
+ /**
+ * Tests if a certain character is a wildcard character ('*' or '?').
+ */
+ public static boolean isWildcardCharacter(final char pCharToCheck) {
+ return ((isFreeRangeCharacter(pCharToCheck)) || (isFreePassCharacter(pCharToCheck)));
+ }
+
+ /**
+ * Gets the string mask that was used when building the parser atomaton.
+ *
+ * @return the string mask used for building the parser automaton.
+ */
+ public String getStringMask() {
+ return mStringMask;
+ }
+
+ /**
+ * Parses a string.
+ *
+ *
+ * @param pStringToBeParsed
+ * @return {@code true} if and only if the string are accepted by the parser.
+ */
+ public boolean parseString(final String pStringToBeParsed) {
+
+ if (mDebugging) {
+ out.println();
+ }
+ if (mDebugging) {
+ out.println(DebugUtil.getPrefixDebugMessage(this) + "parsing \"" + pStringToBeParsed + "\"...");
+ }
+
+ // Update statistics
+ mTotalNumberOfStringsParsed++;
+
+ // Check string to be parsed
+ if (!checkStringToBeParsed(pStringToBeParsed)) {
+ return false;
+ }
+
+ // Perform parsing and return accetance/rejection flag
+ if (mInitialized) {
+ return mRegexpParser.matcher(pStringToBeParsed).matches();
+ } else {
+ out.println(DebugUtil.getPrefixErrorMessage(this) + "trying to use non-initialized parser - string rejected!");
+ }
+ return false;
+ }
+
+ /*
+ * Overriding mandatory methods from EntityObject's.
+ */
+
+ /**
+ * Method toString
+ *
+ *
+ * @return
+ *
+ */
+ public String toString() {
+
+ StringBuilder buffer = new StringBuilder();
+
+ buffer.append(DebugUtil.getClassName(this));
+ buffer.append(": String mask ");
+ buffer.append(mStringMask);
+ buffer.append("\n");
+ return buffer.toString();
+ }
+
+ // Just taking the lazy, easy and dangerous way out
+
+ /**
+ * Method equals
+ *
+ *
+ * @param pObject
+ *
+ * @return
+ *
+ */
+ public boolean equals(Object pObject) {
+
+ if (pObject instanceof REWildcardStringParser) {
+ REWildcardStringParser externalParser = (REWildcardStringParser) pObject;
+
+ return (externalParser.mStringMask == this.mStringMask);
+ }
+ return ((Object) this).equals(pObject);
+ }
+
+ // Just taking the lazy, easy and dangerous way out
+
+ /**
+ * Method hashCode
+ *
+ *
+ * @return
+ *
+ */
+ public int hashCode() {
+ return ((Object) this).hashCode();
+ }
+
+ protected Object clone() throws CloneNotSupportedException {
+ return new REWildcardStringParser(mStringMask);
+ }
+
+ // Just taking the lazy, easy and dangerous way out
+ protected void finalize() throws Throwable {}
+}
+
+
+/*--- Formatted in Sun Java Convention Style on ma, des 1, '03 ---*/
+
+
+/*------ Formatted by Jindent 3.23 Basic 1.0 --- http://www.jindent.de ------*/
diff --git a/twelvemonkeys-sandbox/src/test/java/com/twelvemonkeys/io/enc/DeflateEncoderTestCase.java b/twelvemonkeys-sandbox/src/test/java/com/twelvemonkeys/io/enc/DeflateEncoderTestCase.java
index d2e501ff..a1666ff4 100644
--- a/twelvemonkeys-sandbox/src/test/java/com/twelvemonkeys/io/enc/DeflateEncoderTestCase.java
+++ b/twelvemonkeys-sandbox/src/test/java/com/twelvemonkeys/io/enc/DeflateEncoderTestCase.java
@@ -1,18 +1,18 @@
-package com.twelvemonkeys.io.enc;
-
-/**
- * DeflateEncoderTest
- *
- *
- * @author Harald Kuhr
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageServletResponseImpl.java#10 $
- *
- */
-// TODO: Refactor out HTTP specifcs (if possible).
-// TODO: Is it a good ide to throw IIOException?
-class ImageServletResponseImpl extends HttpServletResponseWrapper implements ImageServletResponse {
-
- private final ServletRequest mOriginalRequest;
- private final ServletContext mContext;
- private final ServletResponseStreamDelegate mStreamDelegate;
-
- private FastByteArrayOutputStream mBufferedOut;
-
- private RenderedImage mImage;
- private String mOutputContentType;
-
- private String mOriginalContentType;
- private int mOriginalContentLength = -1;
-
- /**
- * Creates an {@code ImageServletResponseImpl}.
- *
- * @param pRequest the request
- * @param pResponse the response
- * @param pContext the servlet context
- */
- public ImageServletResponseImpl(final HttpServletRequest pRequest, final HttpServletResponse pResponse, final ServletContext pContext) {
- super(pResponse);
- mOriginalRequest = pRequest;
- mStreamDelegate = new ServletResponseStreamDelegate(pResponse) {
- @Override
- protected OutputStream createOutputStream() throws IOException {
- if (mOriginalContentLength >= 0) {
- mBufferedOut = new FastByteArrayOutputStream(mOriginalContentLength);
- }
- else {
- mBufferedOut = new FastByteArrayOutputStream(0);
- }
-
- return mBufferedOut;
- }
- };
- mContext = pContext;
- }
-
- /**
- * Creates an {@code ImageServletResponseImpl}.
- *
- * @param pRequest the request
- * @param pResponse the response
- * @param pContext the servlet context
- *
- * @throws ClassCastException if {@code pRequest} is not an {@link javax.servlet.http.HttpServletRequest} or
- * {@code pResponse} is not an {@link javax.servlet.http.HttpServletResponse}.
- */
- public ImageServletResponseImpl(final ServletRequest pRequest, final ServletResponse pResponse, final ServletContext pContext) {
- // Cheat for now...
- this((HttpServletRequest) pRequest, (HttpServletResponse) pResponse, pContext);
- }
-
- /**
- * Called by the container, do not invoke.
- *
- * @param pMimeType the content (MIME) type
- */
- public void setContentType(final String pMimeType) {
- // Throw exception is already set
- if (mOriginalContentType != null) {
- throw new IllegalStateException("ContentType already set.");
- }
-
- mOriginalContentType = pMimeType;
- }
-
- /**
- * Called by the container. Do not invoke.
- *
- * @return the response's {@code OutputStream}
- * @throws IOException
- */
- public ServletOutputStream getOutputStream() throws IOException {
- return mStreamDelegate.getOutputStream();
- }
-
- /**
- * Called by the container. Do not invoke.
- *
- * @return the response's {@code PrintWriter}
- * @throws IOException
- */
- public PrintWriter getWriter() throws IOException {
- return mStreamDelegate.getWriter();
- }
-
- /**
- * Called by the container. Do not invoke.
- *
- * @param pLength the content length
- */
- public void setContentLength(final int pLength) {
- if (mOriginalContentLength != -1) {
- throw new IllegalStateException("ContentLength already set.");
- }
-
- mOriginalContentLength = pLength;
- }
-
- /**
- * Writes the image to the original {@code ServletOutputStream}.
- * If no format is set in this response, the image is encoded in the same
- * format as the original image.
- *
- * @throws IOException if an I/O exception occurs during writing
- */
- public void flush() throws IOException {
- String outputType = getOutputContentType();
-
- // Force transcoding, if no other filtering is done
- if (!outputType.equals(mOriginalContentType)) {
- getImage();
- }
-
- // For known formats that don't support transparency, convert to opaque
- if (("image/jpeg".equals(outputType) || "image/jpg".equals(outputType)
- || "image/bmp".equals(outputType) || "image/x-bmp".equals(outputType)) &&
- mImage.getColorModel().getTransparency() != Transparency.OPAQUE) {
- mImage = ImageUtil.toBuffered(mImage, BufferedImage.TYPE_INT_RGB);
- }
-
- if (mImage != null) {
- Iterator writers = ImageIO.getImageWritersByMIMEType(outputType);
- if (writers.hasNext()) {
- super.setContentType(outputType);
- OutputStream out = super.getOutputStream();
-
- ImageWriter writer = (ImageWriter) writers.next();
- try {
- ImageWriteParam param = writer.getDefaultWriteParam();
-
- Float requestQuality = (Float) mOriginalRequest.getAttribute(ImageServletResponse.ATTRIB_OUTPUT_QUALITY);
-
- // The default JPEG quality is not good enough, so always apply compression
- if ((requestQuality != null || "jpeg".equalsIgnoreCase(getFormatNameSafe(writer))) && param.canWriteCompressed()) {
- param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
- param.setCompressionQuality(requestQuality != null ? requestQuality : 0.8f);
- }
-
- ImageOutputStream stream = ImageIO.createImageOutputStream(out);
-
- writer.setOutput(stream);
- try {
- writer.write(null, new IIOImage(mImage, null, null), param);
- }
- finally {
- stream.close();
- }
- }
- finally {
- writer.dispose();
- out.flush();
- }
- }
- else {
- mContext.log("ERROR: No writer for content-type: " + outputType);
- throw new IIOException("Unable to transcode image: No suitable image writer found (content-type: " + outputType + ").");
- }
- }
- else {
- super.setContentType(mOriginalContentType);
- ServletOutputStream out = super.getOutputStream();
- try {
- mBufferedOut.writeTo(out);
- }
- finally {
- out.flush();
- }
- }
- }
-
- private String getFormatNameSafe(final ImageWriter pWriter) {
- try {
- return pWriter.getOriginatingProvider().getFormatNames()[0];
- }
- catch (RuntimeException e) {
- // NPE, AIOOBE, etc..
- return null;
- }
- }
-
- public String getOutputContentType() {
- return mOutputContentType != null ? mOutputContentType : mOriginalContentType;
- }
-
- public void setOutputContentType(final String pImageFormat) {
- mOutputContentType = pImageFormat;
- }
-
- /**
- * Sets the image for this response.
- *
- * @param pImage the {@code RenderedImage} that will be written to the
- * response stream
- */
- public void setImage(final RenderedImage pImage) {
- mImage = pImage;
- }
-
- /**
- * Gets the decoded image from the response.
- *
- * @return a {@code BufferedImage} or {@code null} if the image could
- * not be read.
- *
- * @throws java.io.IOException if an I/O exception occurs during reading
- */
- public BufferedImage getImage() throws IOException {
- if (mImage == null) {
- // No content, no image
- if (mBufferedOut == null) {
- return null;
- }
-
- // Read from the byte buffer
- InputStream byteStream = mBufferedOut.createInputStream();
- ImageInputStream input = null;
- try {
- input = ImageIO.createImageInputStream(byteStream);
- Iterator readers = ImageIO.getImageReaders(input);
- if (readers.hasNext()) {
- // Get the correct reader
- ImageReader reader = (ImageReader) readers.next();
- try {
- reader.setInput(input);
-
- ImageReadParam param = reader.getDefaultReadParam();
-
- // Get default size
- int originalWidth = reader.getWidth(0);
- int originalHeight = reader.getHeight(0);
-
- // Extract AOI from request
- Rectangle aoi = extractAOIFromRequest(originalWidth, originalHeight);
- if (aoi != null) {
- param.setSourceRegion(aoi);
- originalWidth = aoi.width;
- originalHeight = aoi.height;
- }
-
- // If possible, extract size from request
- Dimension size = extractSizeFromRequest(originalWidth, originalHeight);
- double readSubSamplingFactor = getReadSubsampleFactorFromRequest();
- if (size != null) {
- //System.out.println("Size: " + size);
- if (param.canSetSourceRenderSize()) {
- param.setSourceRenderSize(size);
- }
- else {
- int subX = (int) Math.max(originalWidth / (double) (size.width * readSubSamplingFactor), 1.0);
- int subY = (int) Math.max(originalHeight / (double) (size.height * readSubSamplingFactor), 1.0);
-
- if (subX > 1 || subY > 1) {
- param.setSourceSubsampling(subX, subY, subX > 1 ? subX / 2 : 0, subY > 1 ? subY / 2 : 0);
- }
- }
- }
-
- // Need base URI for SVG with links/stylesheets etc
- maybeSetBaseURIFromRequest(param);
-
- // Finally, read the image using the supplied parameter
- BufferedImage image = reader.read(0, param);
-
- // If reader doesn't support dynamic sizing, scale now
- if (image != null && size != null
- && (image.getWidth() != size.width || image.getHeight() != size.height)) {
-
- int resampleAlgorithm = getResampleAlgorithmFromRequest();
- // NOTE: Only use createScaled if IndexColorModel,
- // as it's more expensive due to color conversion
- if (image.getColorModel() instanceof IndexColorModel) {
- image = ImageUtil.createScaled(image, size.width, size.height, resampleAlgorithm);
- }
- else {
- image = ImageUtil.createResampled(image, size.width, size.height, resampleAlgorithm);
- }
- }
-
- // Fill bgcolor behind image, if transparent
- extractAndSetBackgroundColor(image);
-
- // Set image
- mImage = image;
- }
- finally {
- reader.dispose();
- }
- }
- else {
- mContext.log("ERROR: No suitable image reader found (content-type: " + mOriginalContentType + ").");
- mContext.log("ERROR: Available formats: " + getFormatsString());
-
- throw new IIOException("Unable to transcode image: No suitable image reader found (content-type: " + mOriginalContentType + ").");
- }
-
- // Free resources, as the image is now either read, or unreadable
- mBufferedOut = null;
- }
- finally {
- if (input != null) {
- input.close();
- }
- }
- }
-
- // Image is usually a BufferedImage, but may also be a RenderedImage
- return mImage != null ? ImageUtil.toBuffered(mImage) : null;
- }
-
- private int getResampleAlgorithmFromRequest() {
- int resampleAlgoithm;
-
- Object algorithm = mOriginalRequest.getAttribute(ATTRIB_IMAGE_RESAMPLE_ALGORITHM);
- if (algorithm instanceof Integer && ((Integer) algorithm == Image.SCALE_SMOOTH || (Integer) algorithm == Image.SCALE_FAST || (Integer) algorithm == Image.SCALE_DEFAULT)) {
- resampleAlgoithm = (Integer) algorithm;
- }
- else {
- if (algorithm != null) {
- mContext.log("WARN: Illegal image resampling algorithm: " + algorithm);
- }
- resampleAlgoithm = BufferedImage.SCALE_DEFAULT;
- }
-
- return resampleAlgoithm;
- }
-
- private double getReadSubsampleFactorFromRequest() {
- double subsampleFactor;
-
- Object factor = mOriginalRequest.getAttribute(ATTRIB_READ_SUBSAMPLING_FACTOR);
- if (factor instanceof Number && ((Number) factor).doubleValue() >= 1.0) {
- subsampleFactor = ((Number) factor).doubleValue();
- }
- else {
- if (factor != null) {
- mContext.log("WARN: Illegal read subsampling factor: " + factor);
- }
- subsampleFactor = 2.0;
- }
-
- return subsampleFactor;
- }
-
- private void extractAndSetBackgroundColor(final BufferedImage pImage) {
- // TODO: bgColor request attribute instead of parameter?
- if (pImage.getColorModel().hasAlpha()) {
- String bgColor = mOriginalRequest.getParameter("bg.color");
- if (bgColor != null) {
- Color color = StringUtil.toColor(bgColor);
-
- Graphics2D g = pImage.createGraphics();
- try {
- g.setColor(color);
- g.setComposite(AlphaComposite.DstOver);
- g.fillRect(0, 0, pImage.getWidth(), pImage.getHeight());
- }
- finally {
- g.dispose();
- }
- }
- }
- }
-
- private static String getFormatsString() {
- String[] formats = ImageIO.getReaderFormatNames();
- StringBuilder buf = new StringBuilder();
- for (int i = 0; i < formats.length; i++) {
- String format = formats[i];
- if (i > 0) {
- buf.append(", ");
- }
- buf.append(format);
- }
- return buf.toString();
- }
-
- private void maybeSetBaseURIFromRequest(final ImageReadParam pParam) {
- if (mOriginalRequest instanceof HttpServletRequest) {
- try {
- // If there's a setBaseURI method, we'll try to use that (uses reflection, to avoid dependency on plugins)
- Method setBaseURI;
- try {
- setBaseURI = pParam.getClass().getMethod("setBaseURI", String.class);
- }
- catch (NoSuchMethodException ignore) {
- return;
- }
-
- // Get URL for resource and set as base
- String baseURI = ServletUtil.getContextRelativeURI((HttpServletRequest) mOriginalRequest);
-
- URL resourceURL = mContext.getResource(baseURI);
- if (resourceURL == null) {
- resourceURL = ServletUtil.getRealURL(mContext, baseURI);
- }
-
- if (resourceURL != null) {
- setBaseURI.invoke(pParam, resourceURL.toExternalForm());
- }
- else {
- mContext.log("WARN: Resource URL not found for URI: " + baseURI);
- }
- }
- catch (Exception e) {
- mContext.log("WARN: Could not set base URI: ", e);
- }
- }
- }
-
- private Dimension extractSizeFromRequest(final int pDefaultWidth, final int pDefaultHeight) {
- // TODO: Allow extraction from request parameters
- /*
- int sizeW = ServletUtil.getIntParameter(mOriginalRequest, "size.w", -1);
- int sizeH = ServletUtil.getIntParameter(mOriginalRequest, "size.h", -1);
- boolean sizePercent = ServletUtil.getBooleanParameter(mOriginalRequest, "size.percent", false);
- boolean sizeUniform = ServletUtil.getBooleanParameter(mOriginalRequest, "size.uniform", true);
- */
- Dimension size = (Dimension) mOriginalRequest.getAttribute(ATTRIB_SIZE);
- int sizeW = size != null ? size.width : -1;
- int sizeH = size != null ? size.height : -1;
-
- Boolean b = (Boolean) mOriginalRequest.getAttribute(ATTRIB_SIZE_PERCENT);
- boolean sizePercent = b != null && b; // default: false
-
- b = (Boolean) mOriginalRequest.getAttribute(ATTRIB_SIZE_UNIFORM);
- boolean sizeUniform = b == null || b; // default: true
-
- if (sizeW >= 0 || sizeH >= 0) {
- size = getSize(pDefaultWidth, pDefaultHeight, sizeW, sizeH, sizePercent, sizeUniform);
- }
-
- return size;
- }
-
- private Rectangle extractAOIFromRequest(final int pDefaultWidth, final int pDefaultHeight) {
- // TODO: Allow extraction from request parameters
- /*
- int aoiX = ServletUtil.getIntParameter(mOriginalRequest, "aoi.x", -1);
- int aoiY = ServletUtil.getIntParameter(mOriginalRequest, "aoi.y", -1);
- int aoiW = ServletUtil.getIntParameter(mOriginalRequest, "aoi.w", -1);
- int aoiH = ServletUtil.getIntParameter(mOriginalRequest, "aoi.h", -1);
- boolean aoiPercent = ServletUtil.getBooleanParameter(mOriginalRequest, "aoi.percent", false);
- boolean aoiUniform = ServletUtil.getBooleanParameter(mOriginalRequest, "aoi.uniform", false);
- */
- Rectangle aoi = (Rectangle) mOriginalRequest.getAttribute(ATTRIB_AOI);
- int aoiX = aoi != null ? aoi.x : -1;
- int aoiY = aoi != null ? aoi.y : -1;
- int aoiW = aoi != null ? aoi.width : -1;
- int aoiH = aoi != null ? aoi.height : -1;
-
- Boolean b = (Boolean) mOriginalRequest.getAttribute(ATTRIB_AOI_PERCENT);
- boolean aoiPercent = b != null && b; // default: false
-
- b = (Boolean) mOriginalRequest.getAttribute(ATTRIB_AOI_UNIFORM);
- boolean aoiUniform = b != null && b; // default: false
-
- if (aoiX >= 0 || aoiY >= 0 || aoiW >= 0 || aoiH >= 0) {
- aoi = getAOI(pDefaultWidth, pDefaultHeight, aoiX, aoiY, aoiW, aoiH, aoiPercent, aoiUniform);
- return aoi;
- }
-
- return null;
- }
-
- // TODO: Move these to ImageUtil or similar, as they are often used...
- // TODO: Consider separate methods for percent and pixels
- /**
- * Gets the dimensions (height and width) of the scaled image. The
- * dimensions are computed based on the old image's dimensions, the units
- * used for specifying new dimensions and whether or not uniform scaling
- * should be used (se algorithm below).
- *
- * @param pOriginalWidth the original width of the image
- * @param pOriginalHeight the original height of the image
- * @param pWidth the new width of the image, or -1 if unknown
- * @param pHeight the new height of the image, or -1 if unknown
- * @param pPercent the constant specifying units for width and height
- * parameter (UNITS_PIXELS or UNITS_PERCENT)
- * @param pUniformScale boolean specifying uniform scale or not
- * @return a Dimension object, with the correct width and heigth
- * in pixels, for the scaled version of the image.
- */
- protected static Dimension getSize(int pOriginalWidth, int pOriginalHeight,
- int pWidth, int pHeight,
- boolean pPercent, boolean pUniformScale) {
-
- // If uniform, make sure width and height are scaled the same ammount
- // (use ONLY height or ONLY width).
- //
- // Algoritm:
- // if uniform
- // if newHeight not set
- // find ratio newWidth / oldWidth
- // oldHeight *= ratio
- // else if newWidth not set
- // find ratio newWidth / oldWidth
- // oldHeight *= ratio
- // else
- // find both ratios and use the smallest one
- // (this will be the largest version of the image that fits
- // inside the rectangle given)
- // (if PERCENT, just use smallest percentage).
- //
- // If units is percent, we only need old height and width
-
- float ratio;
-
- if (pPercent) {
- if (pWidth >= 0 && pHeight >= 0) {
- // Non-uniform
- pWidth = Math.round((float) pOriginalWidth * (float) pWidth / 100f);
- pHeight = Math.round((float) pOriginalHeight * (float) pHeight / 100f);
- }
- else if (pWidth >= 0) {
- // Find ratio from pWidth
- ratio = (float) pWidth / 100f;
- pWidth = Math.round((float) pOriginalWidth * ratio);
- pHeight = Math.round((float) pOriginalHeight * ratio);
- }
- else if (pHeight >= 0) {
- // Find ratio from pHeight
- ratio = (float) pHeight / 100f;
- pWidth = Math.round((float) pOriginalWidth * ratio);
- pHeight = Math.round((float) pOriginalHeight * ratio);
- }
- // Else: No scale
- }
- else {
- if (pUniformScale) {
- if (pWidth >= 0 && pHeight >= 0) {
- // Compute both ratios
- ratio = (float) pWidth / (float) pOriginalWidth;
- float heightRatio = (float) pHeight / (float) pOriginalHeight;
-
- // Find the largest ratio, and use that for both
- if (heightRatio < ratio) {
- ratio = heightRatio;
- pWidth = Math.round((float) pOriginalWidth * ratio);
- }
- else {
- pHeight = Math.round((float) pOriginalHeight * ratio);
- }
-
- }
- else if (pWidth >= 0) {
- // Find ratio from pWidth
- ratio = (float) pWidth / (float) pOriginalWidth;
- pHeight = Math.round((float) pOriginalHeight * ratio);
- }
- else if (pHeight >= 0) {
- // Find ratio from pHeight
- ratio = (float) pHeight / (float) pOriginalHeight;
- pWidth = Math.round((float) pOriginalWidth * ratio);
- }
- // Else: No scale
- }
- }
-
- // Default is no scale, just work as a proxy
- if (pWidth < 0) {
- pWidth = pOriginalWidth;
- }
- if (pHeight < 0) {
- pHeight = pOriginalHeight;
- }
-
- // Create new Dimension object and return
- return new Dimension(pWidth, pHeight);
- }
-
- protected static Rectangle getAOI(int pOriginalWidth, int pOriginalHeight,
- int pX, int pY, int pWidth, int pHeight,
- boolean pPercent, boolean pUniform) {
- // Algoritm:
- // Try to get x and y (default 0,0).
- // Try to get width and height (default width-x, height-y)
- //
- // If percent, get ratio
- //
- // If uniform
- //
-
- float ratio;
-
- if (pPercent) {
- if (pWidth >= 0 && pHeight >= 0) {
- // Non-uniform
- pWidth = Math.round((float) pOriginalWidth * (float) pWidth / 100f);
- pHeight = Math.round((float) pOriginalHeight * (float) pHeight / 100f);
- }
- else if (pWidth >= 0) {
- // Find ratio from pWidth
- ratio = (float) pWidth / 100f;
- pWidth = Math.round((float) pOriginalWidth * ratio);
- pHeight = Math.round((float) pOriginalHeight * ratio);
-
- }
- else if (pHeight >= 0) {
- // Find ratio from pHeight
- ratio = (float) pHeight / 100f;
- pWidth = Math.round((float) pOriginalWidth * ratio);
- pHeight = Math.round((float) pOriginalHeight * ratio);
- }
- // Else: No crop
- }
- else {
- // Uniform
- if (pUniform) {
- if (pWidth >= 0 && pHeight >= 0) {
- // Compute both ratios
- ratio = (float) pWidth / (float) pHeight;
- float originalRatio = (float) pOriginalWidth / (float) pOriginalHeight;
- if (ratio > originalRatio) {
- pWidth = pOriginalWidth;
- pHeight = Math.round((float) pOriginalWidth / ratio);
- }
- else {
- pHeight = pOriginalHeight;
- pWidth = Math.round((float) pOriginalHeight * ratio);
- }
- }
- else if (pWidth >= 0) {
- // Find ratio from pWidth
- ratio = (float) pWidth / (float) pOriginalWidth;
- pHeight = Math.round((float) pOriginalHeight * ratio);
- }
- else if (pHeight >= 0) {
- // Find ratio from pHeight
- ratio = (float) pHeight / (float) pOriginalHeight;
- pWidth = Math.round((float) pOriginalWidth * ratio);
- }
- // Else: No crop
- }
- }
-
- // Not specified, or outside bounds: Use original dimensions
- if (pWidth < 0 || (pX < 0 && pWidth > pOriginalWidth)
- || (pX >= 0 && (pX + pWidth) > pOriginalWidth)) {
- pWidth = (pX >= 0 ? pOriginalWidth - pX : pOriginalWidth);
- }
- if (pHeight < 0 || (pY < 0 && pHeight > pOriginalHeight)
- || (pY >= 0 && (pY + pHeight) > pOriginalHeight)) {
- pHeight = (pY >= 0 ? pOriginalHeight - pY : pOriginalHeight);
- }
-
- // Center
- if (pX < 0) {
- pX = (pOriginalWidth - pWidth) / 2;
- }
- if (pY < 0) {
- pY = (pOriginalHeight - pHeight) / 2;
- }
-
-// System.out.println("x: " + pX + " y: " + pY
-// + " w: " + pWidth + " h " + pHeight);
-
- return new Rectangle(pX, pY, pWidth, pHeight);
- }
+/*
+ * Copyright (c) 2008, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name "TwelveMonkeys" nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.twelvemonkeys.servlet.image;
+
+import com.twelvemonkeys.image.ImageUtil;
+import com.twelvemonkeys.io.FastByteArrayOutputStream;
+import com.twelvemonkeys.lang.StringUtil;
+import com.twelvemonkeys.servlet.ServletResponseStreamDelegate;
+import com.twelvemonkeys.servlet.ServletUtil;
+
+import javax.imageio.*;
+import javax.imageio.stream.ImageInputStream;
+import javax.imageio.stream.ImageOutputStream;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.awt.image.IndexColorModel;
+import java.awt.image.RenderedImage;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.util.Iterator;
+
+/**
+ * This {@link ImageServletResponse} implementation can be used with image
+ * requests, to have the image immediately decoded to a {@code BufferedImage}.
+ * The image may be optionally subsampled, scaled and/or cropped.
+ * The response also automtically handles writing the image back to the underlying response stream
+ * in the preferred format, when the response is flushed.
+ *
+ *
+ * @author Harald Kuhr
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageServletResponseImpl.java#10 $
+ *
+ */
+// TODO: Refactor out HTTP specifcs (if possible).
+// TODO: Is it a good ide to throw IIOException?
+class ImageServletResponseImpl extends HttpServletResponseWrapper implements ImageServletResponse {
+
+ private final ServletRequest mOriginalRequest;
+ private final ServletContext mContext;
+ private final ServletResponseStreamDelegate mStreamDelegate;
+
+ private FastByteArrayOutputStream mBufferedOut;
+
+ private RenderedImage mImage;
+ private String mOutputContentType;
+
+ private String mOriginalContentType;
+ private int mOriginalContentLength = -1;
+
+ /**
+ * Creates an {@code ImageServletResponseImpl}.
+ *
+ * @param pRequest the request
+ * @param pResponse the response
+ * @param pContext the servlet context
+ */
+ public ImageServletResponseImpl(final HttpServletRequest pRequest, final HttpServletResponse pResponse, final ServletContext pContext) {
+ super(pResponse);
+ mOriginalRequest = pRequest;
+ mStreamDelegate = new ServletResponseStreamDelegate(pResponse) {
+ @Override
+ protected OutputStream createOutputStream() throws IOException {
+ if (mOriginalContentLength >= 0) {
+ mBufferedOut = new FastByteArrayOutputStream(mOriginalContentLength);
+ }
+ else {
+ mBufferedOut = new FastByteArrayOutputStream(0);
+ }
+
+ return mBufferedOut;
+ }
+ };
+ mContext = pContext;
+ }
+
+ /**
+ * Creates an {@code ImageServletResponseImpl}.
+ *
+ * @param pRequest the request
+ * @param pResponse the response
+ * @param pContext the servlet context
+ *
+ * @throws ClassCastException if {@code pRequest} is not an {@link javax.servlet.http.HttpServletRequest} or
+ * {@code pResponse} is not an {@link javax.servlet.http.HttpServletResponse}.
+ */
+ public ImageServletResponseImpl(final ServletRequest pRequest, final ServletResponse pResponse, final ServletContext pContext) {
+ // Cheat for now...
+ this((HttpServletRequest) pRequest, (HttpServletResponse) pResponse, pContext);
+ }
+
+ /**
+ * Called by the container, do not invoke.
+ *
+ * @param pMimeType the content (MIME) type
+ */
+ public void setContentType(final String pMimeType) {
+ // Throw exception is already set
+ if (mOriginalContentType != null) {
+ throw new IllegalStateException("ContentType already set.");
+ }
+
+ mOriginalContentType = pMimeType;
+ }
+
+ /**
+ * Called by the container. Do not invoke.
+ *
+ * @return the response's {@code OutputStream}
+ * @throws IOException
+ */
+ public ServletOutputStream getOutputStream() throws IOException {
+ return mStreamDelegate.getOutputStream();
+ }
+
+ /**
+ * Called by the container. Do not invoke.
+ *
+ * @return the response's {@code PrintWriter}
+ * @throws IOException
+ */
+ public PrintWriter getWriter() throws IOException {
+ return mStreamDelegate.getWriter();
+ }
+
+ /**
+ * Called by the container. Do not invoke.
+ *
+ * @param pLength the content length
+ */
+ public void setContentLength(final int pLength) {
+ if (mOriginalContentLength != -1) {
+ throw new IllegalStateException("ContentLength already set.");
+ }
+
+ mOriginalContentLength = pLength;
+ }
+
+ /**
+ * Writes the image to the original {@code ServletOutputStream}.
+ * If no format is set in this response, the image is encoded in the same
+ * format as the original image.
+ *
+ * @throws IOException if an I/O exception occurs during writing
+ */
+ public void flush() throws IOException {
+ String outputType = getOutputContentType();
+
+ // Force transcoding, if no other filtering is done
+ if (!outputType.equals(mOriginalContentType)) {
+ getImage();
+ }
+
+ // For known formats that don't support transparency, convert to opaque
+ if (("image/jpeg".equals(outputType) || "image/jpg".equals(outputType)
+ || "image/bmp".equals(outputType) || "image/x-bmp".equals(outputType)) &&
+ mImage.getColorModel().getTransparency() != Transparency.OPAQUE) {
+ mImage = ImageUtil.toBuffered(mImage, BufferedImage.TYPE_INT_RGB);
+ }
+
+ if (mImage != null) {
+ Iterator writers = ImageIO.getImageWritersByMIMEType(outputType);
+ if (writers.hasNext()) {
+ super.setContentType(outputType);
+ OutputStream out = super.getOutputStream();
+
+ ImageWriter writer = (ImageWriter) writers.next();
+ try {
+ ImageWriteParam param = writer.getDefaultWriteParam();
+
+ Float requestQuality = (Float) mOriginalRequest.getAttribute(ImageServletResponse.ATTRIB_OUTPUT_QUALITY);
+
+ // The default JPEG quality is not good enough, so always apply compression
+ if ((requestQuality != null || "jpeg".equalsIgnoreCase(getFormatNameSafe(writer))) && param.canWriteCompressed()) {
+ param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
+ param.setCompressionQuality(requestQuality != null ? requestQuality : 0.8f);
+ }
+
+ ImageOutputStream stream = ImageIO.createImageOutputStream(out);
+
+ writer.setOutput(stream);
+ try {
+ writer.write(null, new IIOImage(mImage, null, null), param);
+ }
+ finally {
+ stream.close();
+ }
+ }
+ finally {
+ writer.dispose();
+ out.flush();
+ }
+ }
+ else {
+ mContext.log("ERROR: No writer for content-type: " + outputType);
+ throw new IIOException("Unable to transcode image: No suitable image writer found (content-type: " + outputType + ").");
+ }
+ }
+ else {
+ super.setContentType(mOriginalContentType);
+ ServletOutputStream out = super.getOutputStream();
+ try {
+ mBufferedOut.writeTo(out);
+ }
+ finally {
+ out.flush();
+ }
+ }
+ }
+
+ private String getFormatNameSafe(final ImageWriter pWriter) {
+ try {
+ return pWriter.getOriginatingProvider().getFormatNames()[0];
+ }
+ catch (RuntimeException e) {
+ // NPE, AIOOBE, etc..
+ return null;
+ }
+ }
+
+ public String getOutputContentType() {
+ return mOutputContentType != null ? mOutputContentType : mOriginalContentType;
+ }
+
+ public void setOutputContentType(final String pImageFormat) {
+ mOutputContentType = pImageFormat;
+ }
+
+ /**
+ * Sets the image for this response.
+ *
+ * @param pImage the {@code RenderedImage} that will be written to the
+ * response stream
+ */
+ public void setImage(final RenderedImage pImage) {
+ mImage = pImage;
+ }
+
+ /**
+ * Gets the decoded image from the response.
+ *
+ * @return a {@code BufferedImage} or {@code null} if the image could
+ * not be read.
+ *
+ * @throws java.io.IOException if an I/O exception occurs during reading
+ */
+ public BufferedImage getImage() throws IOException {
+ if (mImage == null) {
+ // No content, no image
+ if (mBufferedOut == null) {
+ return null;
+ }
+
+ // Read from the byte buffer
+ InputStream byteStream = mBufferedOut.createInputStream();
+ ImageInputStream input = null;
+ try {
+ input = ImageIO.createImageInputStream(byteStream);
+ Iterator readers = ImageIO.getImageReaders(input);
+ if (readers.hasNext()) {
+ // Get the correct reader
+ ImageReader reader = (ImageReader) readers.next();
+ try {
+ reader.setInput(input);
+
+ ImageReadParam param = reader.getDefaultReadParam();
+
+ // Get default size
+ int originalWidth = reader.getWidth(0);
+ int originalHeight = reader.getHeight(0);
+
+ // Extract AOI from request
+ Rectangle aoi = extractAOIFromRequest(originalWidth, originalHeight);
+ if (aoi != null) {
+ param.setSourceRegion(aoi);
+ originalWidth = aoi.width;
+ originalHeight = aoi.height;
+ }
+
+ // If possible, extract size from request
+ Dimension size = extractSizeFromRequest(originalWidth, originalHeight);
+ double readSubSamplingFactor = getReadSubsampleFactorFromRequest();
+ if (size != null) {
+ //System.out.println("Size: " + size);
+ if (param.canSetSourceRenderSize()) {
+ param.setSourceRenderSize(size);
+ }
+ else {
+ int subX = (int) Math.max(originalWidth / (double) (size.width * readSubSamplingFactor), 1.0);
+ int subY = (int) Math.max(originalHeight / (double) (size.height * readSubSamplingFactor), 1.0);
+
+ if (subX > 1 || subY > 1) {
+ param.setSourceSubsampling(subX, subY, subX > 1 ? subX / 2 : 0, subY > 1 ? subY / 2 : 0);
+ }
+ }
+ }
+
+ // Need base URI for SVG with links/stylesheets etc
+ maybeSetBaseURIFromRequest(param);
+
+ // Finally, read the image using the supplied parameter
+ BufferedImage image = reader.read(0, param);
+
+ // If reader doesn't support dynamic sizing, scale now
+ if (image != null && size != null
+ && (image.getWidth() != size.width || image.getHeight() != size.height)) {
+
+ int resampleAlgorithm = getResampleAlgorithmFromRequest();
+ // NOTE: Only use createScaled if IndexColorModel,
+ // as it's more expensive due to color conversion
+ if (image.getColorModel() instanceof IndexColorModel) {
+ image = ImageUtil.createScaled(image, size.width, size.height, resampleAlgorithm);
+ }
+ else {
+ image = ImageUtil.createResampled(image, size.width, size.height, resampleAlgorithm);
+ }
+ }
+
+ // Fill bgcolor behind image, if transparent
+ extractAndSetBackgroundColor(image);
+
+ // Set image
+ mImage = image;
+ }
+ finally {
+ reader.dispose();
+ }
+ }
+ else {
+ mContext.log("ERROR: No suitable image reader found (content-type: " + mOriginalContentType + ").");
+ mContext.log("ERROR: Available formats: " + getFormatsString());
+
+ throw new IIOException("Unable to transcode image: No suitable image reader found (content-type: " + mOriginalContentType + ").");
+ }
+
+ // Free resources, as the image is now either read, or unreadable
+ mBufferedOut = null;
+ }
+ finally {
+ if (input != null) {
+ input.close();
+ }
+ }
+ }
+
+ // Image is usually a BufferedImage, but may also be a RenderedImage
+ return mImage != null ? ImageUtil.toBuffered(mImage) : null;
+ }
+
+ private int getResampleAlgorithmFromRequest() {
+ int resampleAlgoithm;
+
+ Object algorithm = mOriginalRequest.getAttribute(ATTRIB_IMAGE_RESAMPLE_ALGORITHM);
+ if (algorithm instanceof Integer && ((Integer) algorithm == Image.SCALE_SMOOTH || (Integer) algorithm == Image.SCALE_FAST || (Integer) algorithm == Image.SCALE_DEFAULT)) {
+ resampleAlgoithm = (Integer) algorithm;
+ }
+ else {
+ if (algorithm != null) {
+ mContext.log("WARN: Illegal image resampling algorithm: " + algorithm);
+ }
+ resampleAlgoithm = BufferedImage.SCALE_DEFAULT;
+ }
+
+ return resampleAlgoithm;
+ }
+
+ private double getReadSubsampleFactorFromRequest() {
+ double subsampleFactor;
+
+ Object factor = mOriginalRequest.getAttribute(ATTRIB_READ_SUBSAMPLING_FACTOR);
+ if (factor instanceof Number && ((Number) factor).doubleValue() >= 1.0) {
+ subsampleFactor = ((Number) factor).doubleValue();
+ }
+ else {
+ if (factor != null) {
+ mContext.log("WARN: Illegal read subsampling factor: " + factor);
+ }
+ subsampleFactor = 2.0;
+ }
+
+ return subsampleFactor;
+ }
+
+ private void extractAndSetBackgroundColor(final BufferedImage pImage) {
+ // TODO: bgColor request attribute instead of parameter?
+ if (pImage.getColorModel().hasAlpha()) {
+ String bgColor = mOriginalRequest.getParameter("bg.color");
+ if (bgColor != null) {
+ Color color = StringUtil.toColor(bgColor);
+
+ Graphics2D g = pImage.createGraphics();
+ try {
+ g.setColor(color);
+ g.setComposite(AlphaComposite.DstOver);
+ g.fillRect(0, 0, pImage.getWidth(), pImage.getHeight());
+ }
+ finally {
+ g.dispose();
+ }
+ }
+ }
+ }
+
+ private static String getFormatsString() {
+ String[] formats = ImageIO.getReaderFormatNames();
+ StringBuilder buf = new StringBuilder();
+ for (int i = 0; i < formats.length; i++) {
+ String format = formats[i];
+ if (i > 0) {
+ buf.append(", ");
+ }
+ buf.append(format);
+ }
+ return buf.toString();
+ }
+
+ private void maybeSetBaseURIFromRequest(final ImageReadParam pParam) {
+ if (mOriginalRequest instanceof HttpServletRequest) {
+ try {
+ // If there's a setBaseURI method, we'll try to use that (uses reflection, to avoid dependency on plugins)
+ Method setBaseURI;
+ try {
+ setBaseURI = pParam.getClass().getMethod("setBaseURI", String.class);
+ }
+ catch (NoSuchMethodException ignore) {
+ return;
+ }
+
+ // Get URL for resource and set as base
+ String baseURI = ServletUtil.getContextRelativeURI((HttpServletRequest) mOriginalRequest);
+
+ URL resourceURL = mContext.getResource(baseURI);
+ if (resourceURL == null) {
+ resourceURL = ServletUtil.getRealURL(mContext, baseURI);
+ }
+
+ if (resourceURL != null) {
+ setBaseURI.invoke(pParam, resourceURL.toExternalForm());
+ }
+ else {
+ mContext.log("WARN: Resource URL not found for URI: " + baseURI);
+ }
+ }
+ catch (Exception e) {
+ mContext.log("WARN: Could not set base URI: ", e);
+ }
+ }
+ }
+
+ private Dimension extractSizeFromRequest(final int pDefaultWidth, final int pDefaultHeight) {
+ // TODO: Allow extraction from request parameters
+ /*
+ int sizeW = ServletUtil.getIntParameter(mOriginalRequest, "size.w", -1);
+ int sizeH = ServletUtil.getIntParameter(mOriginalRequest, "size.h", -1);
+ boolean sizePercent = ServletUtil.getBooleanParameter(mOriginalRequest, "size.percent", false);
+ boolean sizeUniform = ServletUtil.getBooleanParameter(mOriginalRequest, "size.uniform", true);
+ */
+ Dimension size = (Dimension) mOriginalRequest.getAttribute(ATTRIB_SIZE);
+ int sizeW = size != null ? size.width : -1;
+ int sizeH = size != null ? size.height : -1;
+
+ Boolean b = (Boolean) mOriginalRequest.getAttribute(ATTRIB_SIZE_PERCENT);
+ boolean sizePercent = b != null && b; // default: false
+
+ b = (Boolean) mOriginalRequest.getAttribute(ATTRIB_SIZE_UNIFORM);
+ boolean sizeUniform = b == null || b; // default: true
+
+ if (sizeW >= 0 || sizeH >= 0) {
+ size = getSize(pDefaultWidth, pDefaultHeight, sizeW, sizeH, sizePercent, sizeUniform);
+ }
+
+ return size;
+ }
+
+ private Rectangle extractAOIFromRequest(final int pDefaultWidth, final int pDefaultHeight) {
+ // TODO: Allow extraction from request parameters
+ /*
+ int aoiX = ServletUtil.getIntParameter(mOriginalRequest, "aoi.x", -1);
+ int aoiY = ServletUtil.getIntParameter(mOriginalRequest, "aoi.y", -1);
+ int aoiW = ServletUtil.getIntParameter(mOriginalRequest, "aoi.w", -1);
+ int aoiH = ServletUtil.getIntParameter(mOriginalRequest, "aoi.h", -1);
+ boolean aoiPercent = ServletUtil.getBooleanParameter(mOriginalRequest, "aoi.percent", false);
+ boolean aoiUniform = ServletUtil.getBooleanParameter(mOriginalRequest, "aoi.uniform", false);
+ */
+ Rectangle aoi = (Rectangle) mOriginalRequest.getAttribute(ATTRIB_AOI);
+ int aoiX = aoi != null ? aoi.x : -1;
+ int aoiY = aoi != null ? aoi.y : -1;
+ int aoiW = aoi != null ? aoi.width : -1;
+ int aoiH = aoi != null ? aoi.height : -1;
+
+ Boolean b = (Boolean) mOriginalRequest.getAttribute(ATTRIB_AOI_PERCENT);
+ boolean aoiPercent = b != null && b; // default: false
+
+ b = (Boolean) mOriginalRequest.getAttribute(ATTRIB_AOI_UNIFORM);
+ boolean aoiUniform = b != null && b; // default: false
+
+ if (aoiX >= 0 || aoiY >= 0 || aoiW >= 0 || aoiH >= 0) {
+ aoi = getAOI(pDefaultWidth, pDefaultHeight, aoiX, aoiY, aoiW, aoiH, aoiPercent, aoiUniform);
+ return aoi;
+ }
+
+ return null;
+ }
+
+ // TODO: Move these to ImageUtil or similar, as they are often used...
+ // TODO: Consider separate methods for percent and pixels
+ /**
+ * Gets the dimensions (height and width) of the scaled image. The
+ * dimensions are computed based on the old image's dimensions, the units
+ * used for specifying new dimensions and whether or not uniform scaling
+ * should be used (se algorithm below).
+ *
+ * @param pOriginalWidth the original width of the image
+ * @param pOriginalHeight the original height of the image
+ * @param pWidth the new width of the image, or -1 if unknown
+ * @param pHeight the new height of the image, or -1 if unknown
+ * @param pPercent the constant specifying units for width and height
+ * parameter (UNITS_PIXELS or UNITS_PERCENT)
+ * @param pUniformScale boolean specifying uniform scale or not
+ * @return a Dimension object, with the correct width and heigth
+ * in pixels, for the scaled version of the image.
+ */
+ protected static Dimension getSize(int pOriginalWidth, int pOriginalHeight,
+ int pWidth, int pHeight,
+ boolean pPercent, boolean pUniformScale) {
+
+ // If uniform, make sure width and height are scaled the same ammount
+ // (use ONLY height or ONLY width).
+ //
+ // Algoritm:
+ // if uniform
+ // if newHeight not set
+ // find ratio newWidth / oldWidth
+ // oldHeight *= ratio
+ // else if newWidth not set
+ // find ratio newWidth / oldWidth
+ // oldHeight *= ratio
+ // else
+ // find both ratios and use the smallest one
+ // (this will be the largest version of the image that fits
+ // inside the rectangle given)
+ // (if PERCENT, just use smallest percentage).
+ //
+ // If units is percent, we only need old height and width
+
+ float ratio;
+
+ if (pPercent) {
+ if (pWidth >= 0 && pHeight >= 0) {
+ // Non-uniform
+ pWidth = Math.round((float) pOriginalWidth * (float) pWidth / 100f);
+ pHeight = Math.round((float) pOriginalHeight * (float) pHeight / 100f);
+ }
+ else if (pWidth >= 0) {
+ // Find ratio from pWidth
+ ratio = (float) pWidth / 100f;
+ pWidth = Math.round((float) pOriginalWidth * ratio);
+ pHeight = Math.round((float) pOriginalHeight * ratio);
+ }
+ else if (pHeight >= 0) {
+ // Find ratio from pHeight
+ ratio = (float) pHeight / 100f;
+ pWidth = Math.round((float) pOriginalWidth * ratio);
+ pHeight = Math.round((float) pOriginalHeight * ratio);
+ }
+ // Else: No scale
+ }
+ else {
+ if (pUniformScale) {
+ if (pWidth >= 0 && pHeight >= 0) {
+ // Compute both ratios
+ ratio = (float) pWidth / (float) pOriginalWidth;
+ float heightRatio = (float) pHeight / (float) pOriginalHeight;
+
+ // Find the largest ratio, and use that for both
+ if (heightRatio < ratio) {
+ ratio = heightRatio;
+ pWidth = Math.round((float) pOriginalWidth * ratio);
+ }
+ else {
+ pHeight = Math.round((float) pOriginalHeight * ratio);
+ }
+
+ }
+ else if (pWidth >= 0) {
+ // Find ratio from pWidth
+ ratio = (float) pWidth / (float) pOriginalWidth;
+ pHeight = Math.round((float) pOriginalHeight * ratio);
+ }
+ else if (pHeight >= 0) {
+ // Find ratio from pHeight
+ ratio = (float) pHeight / (float) pOriginalHeight;
+ pWidth = Math.round((float) pOriginalWidth * ratio);
+ }
+ // Else: No scale
+ }
+ }
+
+ // Default is no scale, just work as a proxy
+ if (pWidth < 0) {
+ pWidth = pOriginalWidth;
+ }
+ if (pHeight < 0) {
+ pHeight = pOriginalHeight;
+ }
+
+ // Create new Dimension object and return
+ return new Dimension(pWidth, pHeight);
+ }
+
+ protected static Rectangle getAOI(int pOriginalWidth, int pOriginalHeight,
+ int pX, int pY, int pWidth, int pHeight,
+ boolean pPercent, boolean pUniform) {
+ // Algoritm:
+ // Try to get x and y (default 0,0).
+ // Try to get width and height (default width-x, height-y)
+ //
+ // If percent, get ratio
+ //
+ // If uniform
+ //
+
+ float ratio;
+
+ if (pPercent) {
+ if (pWidth >= 0 && pHeight >= 0) {
+ // Non-uniform
+ pWidth = Math.round((float) pOriginalWidth * (float) pWidth / 100f);
+ pHeight = Math.round((float) pOriginalHeight * (float) pHeight / 100f);
+ }
+ else if (pWidth >= 0) {
+ // Find ratio from pWidth
+ ratio = (float) pWidth / 100f;
+ pWidth = Math.round((float) pOriginalWidth * ratio);
+ pHeight = Math.round((float) pOriginalHeight * ratio);
+
+ }
+ else if (pHeight >= 0) {
+ // Find ratio from pHeight
+ ratio = (float) pHeight / 100f;
+ pWidth = Math.round((float) pOriginalWidth * ratio);
+ pHeight = Math.round((float) pOriginalHeight * ratio);
+ }
+ // Else: No crop
+ }
+ else {
+ // Uniform
+ if (pUniform) {
+ if (pWidth >= 0 && pHeight >= 0) {
+ // Compute both ratios
+ ratio = (float) pWidth / (float) pHeight;
+ float originalRatio = (float) pOriginalWidth / (float) pOriginalHeight;
+ if (ratio > originalRatio) {
+ pWidth = pOriginalWidth;
+ pHeight = Math.round((float) pOriginalWidth / ratio);
+ }
+ else {
+ pHeight = pOriginalHeight;
+ pWidth = Math.round((float) pOriginalHeight * ratio);
+ }
+ }
+ else if (pWidth >= 0) {
+ // Find ratio from pWidth
+ ratio = (float) pWidth / (float) pOriginalWidth;
+ pHeight = Math.round((float) pOriginalHeight * ratio);
+ }
+ else if (pHeight >= 0) {
+ // Find ratio from pHeight
+ ratio = (float) pHeight / (float) pOriginalHeight;
+ pWidth = Math.round((float) pOriginalWidth * ratio);
+ }
+ // Else: No crop
+ }
+ }
+
+ // Not specified, or outside bounds: Use original dimensions
+ if (pWidth < 0 || (pX < 0 && pWidth > pOriginalWidth)
+ || (pX >= 0 && (pX + pWidth) > pOriginalWidth)) {
+ pWidth = (pX >= 0 ? pOriginalWidth - pX : pOriginalWidth);
+ }
+ if (pHeight < 0 || (pY < 0 && pHeight > pOriginalHeight)
+ || (pY >= 0 && (pY + pHeight) > pOriginalHeight)) {
+ pHeight = (pY >= 0 ? pOriginalHeight - pY : pOriginalHeight);
+ }
+
+ // Center
+ if (pX < 0) {
+ pX = (pOriginalWidth - pWidth) / 2;
+ }
+ if (pY < 0) {
+ pY = (pOriginalHeight - pHeight) / 2;
+ }
+
+// System.out.println("x: " + pX + " y: " + pY
+// + " w: " + pWidth + " h " + pHeight);
+
+ return new Rectangle(pX, pY, pWidth, pHeight);
+ }
}
\ No newline at end of file
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/NullImageFilter.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/NullImageFilter.java
index aa6e570d..66e2a293 100755
--- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/NullImageFilter.java
+++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/NullImageFilter.java
@@ -1,46 +1,46 @@
-/*
- * Copyright (c) 2008, Harald Kuhr
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name "TwelveMonkeys" nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package com.twelvemonkeys.servlet.image;
-
-import javax.servlet.ServletRequest;
-import java.awt.image.BufferedImage;
-import java.awt.image.RenderedImage;
-
-/**
- * An {@code ImageFilter} that does nothing. Useful for debugging purposes.
- *
- * @author $Author: haku $
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/NullImageFilter.java#2 $
- *
- */
-public final class NullImageFilter extends ImageFilter {
- protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) {
- return pImage;
- }
-}
+/*
+ * Copyright (c) 2008, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name "TwelveMonkeys" nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.twelvemonkeys.servlet.image;
+
+import javax.servlet.ServletRequest;
+import java.awt.image.BufferedImage;
+import java.awt.image.RenderedImage;
+
+/**
+ * An {@code ImageFilter} that does nothing. Useful for debugging purposes.
+ *
+ * @author $Author: haku $
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/NullImageFilter.java#2 $
+ *
+ */
+public final class NullImageFilter extends ImageFilter {
+ protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) {
+ return pImage;
+ }
+}
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/RotateFilter.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/RotateFilter.java
index 49cfc89b..37af808e 100755
--- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/RotateFilter.java
+++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/RotateFilter.java
@@ -1,203 +1,203 @@
-/*
- * Copyright (c) 2008, Harald Kuhr
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name "TwelveMonkeys" nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package com.twelvemonkeys.servlet.image;
-
-import com.twelvemonkeys.image.ImageUtil;
-import com.twelvemonkeys.lang.MathUtil;
-import com.twelvemonkeys.lang.StringUtil;
-import com.twelvemonkeys.servlet.ServletUtil;
-
-import javax.servlet.ServletRequest;
-import java.awt.*;
-import java.awt.geom.AffineTransform;
-import java.awt.geom.Rectangle2D;
-import java.awt.image.BufferedImage;
-import java.awt.image.RenderedImage;
-
-/**
- * This Servlet is able to render a cropped part of an image.
- *
- *
- *
- *
+ *
+ *
- * 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
- * run with JRE 1.4 or later, and with the option:
- *
- * If you cannot use JRE 1.4 for any reason, or do not want to use the X
- * libraries, a possibilty is to use the
- * PJA package (com.eteks.pja),
- * and start the JVM with the following options:
- *
- * 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,
- * will result in errors.
- *
- * @see com.twelvemonkeys.servlet.image.ImageServlet
- * @see com.twelvemonkeys.servlet.image.ImagePainterServlet
- */
+/**
+ * Contains various image-outputting servlets, that should run under any servlet engine. To create your own image servlet, simply subclass the servlet
+ * {@code ImageServlet}. Optionally implement the interface
+ * {@code ImagePainterServlet}, if you want to do painting.
+ *
+ * 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
+ * run with JRE 1.4 or later, and with the option:
+ *
+ * If you cannot use JRE 1.4 for any reason, or do not want to use the X
+ * libraries, a possibilty is to use the
+ * PJA package (com.eteks.pja),
+ * and start the JVM with the following options:
+ *
+ * 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,
+ * will result in errors.
+ *
+ * @see com.twelvemonkeys.servlet.image.ImageServlet
+ * @see com.twelvemonkeys.servlet.image.ImagePainterServlet
+ */
package com.twelvemonkeys.servlet.image;
\ No newline at end of file
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/Droplet.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/Droplet.java
index 9a3d2c1a..5f30f57f 100755
--- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/Droplet.java
+++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/Droplet.java
@@ -1,80 +1,80 @@
-/*
- * Copyright (c) 2002 TwelveMonkeys.
- * All rights reserved.
- *
- * $Log: Droplet.java,v $
- * Revision 1.3 2003/10/06 14:25:19 WMHAKUR
- * Code clean-up only.
- *
- * Revision 1.2 2002/10/18 14:12:16 WMHAKUR
- * Now, it even compiles. :-/
- *
- * Revision 1.1 2002/10/18 14:02:16 WMHAKUR
- * Moved to com.twelvemonkeys.servlet.jsp.droplet
- *
- *
- */
-
-package com.twelvemonkeys.servlet.jsp.droplet;
-
-import java.io.*;
-
-import javax.servlet.*;
-import javax.servlet.http.*;
-import javax.servlet.jsp.*;
-
-import com.twelvemonkeys.servlet.jsp.droplet.taglib.*;
-
-/**
- * Dynamo Droplet like Servlet.
- *
- * @author Harald Kuhr
- * @author last modified by $Author: haku $
- *
- * @version $Revision: #1 $, ($Date: 2008/05/05 $)
- *
- */
-public abstract class Droplet extends HttpServlet implements JspFragment {
-
- // Copy doc
- public abstract void service(PageContext pPageContext)
- throws ServletException, IOException;
-
- /**
- * Services a parameter. Programatically equivalent to the
- *
- * Based on NestingValidator.java,
- * taken from More Servlets and JavaServer Pages
- * from Prentice Hall and Sun Microsystems Press,
- * http://www.moreservlets.com/.
- * © 2002 Marty Hall; may be freely used or adapted.
- *
- * @author Harald Kuhr
- * @author last modified by $Author: haku $
- *
- * @version $Revision: #1 $, ($Date: 2008/05/05 $)
- *
- */
-
-public class NestingValidator extends TagLibraryValidator {
-
- private Vector errors = new Vector();
-
- /**
- *
- */
-
- public ValidationMessage[] validate(String pPrefix,
- String pURI,
- PageData pPage) {
-
- //System.out.println("Validating " + pPrefix + " (" + pURI + ") for "
- // + pPage + ".");
-
- // Pass the parser factory in on the command line with
- // -D to override the use of the Apache parser.
-
- DefaultHandler handler = new NestingHandler(pPrefix, pURI, this);
- SAXParserFactory factory = SAXParserFactory.newInstance();
-
- try {
- // FileUtil.copy(pPage.getInputStream(), System.out);
-
- SAXParser parser = factory.newSAXParser();
- InputSource source =
- new InputSource(pPage.getInputStream());
-
- // Parse, handler will use callback to report errors
- parser.parse(source, handler);
-
-
- }
- catch (Exception e) {
- String errorMessage = e.getMessage();
-
- reportError(errorMessage);
- }
-
- // Return any errors and exceptions, empty array means okay
- return (ValidationMessage[])
- errors.toArray(new ValidationMessage[errors.size()]);
- }
-
- /**
- * Callback method for the handler to report errors
- */
-
- public void reportError(String pMessage) {
- // The first argument to the ValidationMessage
- // constructor can be a tag ID. Since tag IDs
- // are not universally supported, use null for
- // portability. The important part is the second
- // argument: the error message.
- errors.add(new ValidationMessage(null, pMessage));
- }
-}
-
+/*
+ * Copyright (c) 2002 TwelveMonkeys.
+ * All rights reserved.
+ *
+ * $Log: NestingValidator.java,v $
+ * Revision 1.4 2003/08/04 15:26:40 WMHAKUR
+ * Code clean-up.
+ *
+ * Revision 1.3 2002/11/18 14:12:43 WMHAKUR
+ * *** empty log message ***
+ *
+ * Revision 1.2 2002/10/18 14:28:07 WMHAKUR
+ * Fixed package error.
+ *
+ * Revision 1.1 2002/10/18 14:03:09 WMHAKUR
+ * Moved to com.twelvemonkeys.servlet.jsp.droplet.taglib
+ *
+ *
+ */
+
+package com.twelvemonkeys.servlet.jsp.droplet.taglib;
+
+
+import java.util.*;
+
+import javax.servlet.jsp.tagext.*;
+import javax.xml.parsers.*;
+
+import org.xml.sax.*;
+import org.xml.sax.helpers.*;
+
+import com.twelvemonkeys.util.*;
+
+/**
+ * A validator that verifies that tags follow
+ * proper nesting order.
+ *
+ * Based on NestingValidator.java,
+ * taken from More Servlets and JavaServer Pages
+ * from Prentice Hall and Sun Microsystems Press,
+ * http://www.moreservlets.com/.
+ * © 2002 Marty Hall; may be freely used or adapted.
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haku $
+ *
+ * @version $Revision: #1 $, ($Date: 2008/05/05 $)
+ *
+ */
+
+public class NestingValidator extends TagLibraryValidator {
+
+ private Vector errors = new Vector();
+
+ /**
+ *
+ */
+
+ public ValidationMessage[] validate(String pPrefix,
+ String pURI,
+ PageData pPage) {
+
+ //System.out.println("Validating " + pPrefix + " (" + pURI + ") for "
+ // + pPage + ".");
+
+ // Pass the parser factory in on the command line with
+ // -D to override the use of the Apache parser.
+
+ DefaultHandler handler = new NestingHandler(pPrefix, pURI, this);
+ SAXParserFactory factory = SAXParserFactory.newInstance();
+
+ try {
+ // FileUtil.copy(pPage.getInputStream(), System.out);
+
+ SAXParser parser = factory.newSAXParser();
+ InputSource source =
+ new InputSource(pPage.getInputStream());
+
+ // Parse, handler will use callback to report errors
+ parser.parse(source, handler);
+
+
+ }
+ catch (Exception e) {
+ String errorMessage = e.getMessage();
+
+ reportError(errorMessage);
+ }
+
+ // Return any errors and exceptions, empty array means okay
+ return (ValidationMessage[])
+ errors.toArray(new ValidationMessage[errors.size()]);
+ }
+
+ /**
+ * Callback method for the handler to report errors
+ */
+
+ public void reportError(String pMessage) {
+ // The first argument to the ValidationMessage
+ // constructor can be a tag ID. Since tag IDs
+ // are not universally supported, use null for
+ // portability. The important part is the second
+ // argument: the error message.
+ errors.add(new ValidationMessage(null, pMessage));
+ }
+}
+
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/OparamTag.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/OparamTag.java
index 9ef4c596..98ca02db 100755
--- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/OparamTag.java
+++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/OparamTag.java
@@ -1,238 +1,238 @@
-/*
- * Copyright (c) 2002 TwelveMonkeys.
- * All rights reserved.
- *
- * $Log: OparamTag.java,v $
- * Revision 1.4 2003/10/06 14:25:53 WMHAKUR
- * Code clean-up only.
- *
- * Revision 1.3 2002/11/18 14:12:43 WMHAKUR
- * *** empty log message ***
- *
- * Revision 1.2 2002/11/07 12:20:14 WMHAKUR
- * Updated to reflect changes in com.twelvemonkeys.util.*Util
- *
- * Revision 1.1 2002/10/18 14:03:09 WMHAKUR
- * Moved to com.twelvemonkeys.servlet.jsp.droplet.taglib
- *
- *
- */
-
-package com.twelvemonkeys.servlet.jsp.droplet.taglib;
-
-import com.twelvemonkeys.io.FileUtil;
-import com.twelvemonkeys.lang.StringUtil;
-import com.twelvemonkeys.servlet.jsp.droplet.Oparam;
-import com.twelvemonkeys.servlet.jsp.taglib.BodyReaderTag;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.jsp.JspException;
-import javax.servlet.jsp.tagext.BodyTag;
-import javax.servlet.jsp.tagext.Tag;
-import java.io.File;
-import java.io.IOException;
-
-
-/**
- * Open parameter tag that emulates ATG Dynamo JHTML behaviour for JSP.
- *
- * @author Thomas Purcell (CSC Australia)
- * @author Harald Kuhr
- * @author last modified by $Author: haku $
- *
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/OparamTag.java#1 $
- */
-
-public class OparamTag extends BodyReaderTag {
-
- protected final static String COUNTER = "com.twelvemonkeys.servlet.jsp.taglib.OparamTag.counter";
-
-
- private File mSubpage = null;
-
- /**
- * This is the name of the parameter to be inserted into the {@code
- * PageContext.REQUEST_SCOPE} scope.
- */
-
- private String mParameterName = null;
-
- private String mLanguage = null;
-
- private String mPrefix = null;
-
- /**
- * This method allows the JSP page to set the name for the parameter by
- * using the {@code name} tag attribute.
- *
- * @param pName The name for the parameter to insert into the {@code
- * PageContext.REQUEST_SCOPE} scope.
- */
-
- public void setName(String pName) {
- mParameterName = pName;
- }
-
- public void setLanguage(String pLanguage) {
- //System.out.println("setLanguage:"+pLanguage);
- mLanguage = pLanguage;
- }
-
- public void setPrefix(String pPrefix) {
- //System.out.println("setPrefix:"+pPrefix);
- mPrefix = pPrefix;
- }
-
- /**
- * 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.
- *
- * @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.
- * @exception JspException
- */
-
- public int doStartTag() throws JspException {
- //checkEnclosedInIncludeTag(); // Moved to TagLibValidator
-
- // Get request
- HttpServletRequest request = (HttpServletRequest) pageContext.getRequest();
-
- // Get filename
- mSubpage = createFileNameFromRequest(request);
-
- // Get include tag, and add to parameters
- IncludeTag includeTag = (IncludeTag) getParent();
- includeTag.addParameter(mParameterName, new Oparam(mSubpage.getName()));
-
- // if ! subpage.exist || jsp newer than subpage, write new
- File jsp = new File(pageContext.getServletContext()
- .getRealPath(request.getServletPath()));
-
- if (!mSubpage.exists() || jsp.lastModified() > mSubpage.lastModified()) {
- return BodyTag.EVAL_BODY_BUFFERED;
- }
-
- // No need to evaluate body again!
- return Tag.SKIP_BODY;
- }
-
- /**
- * This is the method responsible for actually testing that the tag
- * implemented by this class is enclosed within an {@code IncludeTag}.
- *
- * @exception JspException
- */
- /*
- protected void checkEnclosedInIncludeTag() throws JspException {
- Tag parentTag = getParent();
-
- if ((parentTag != null) && (parentTag instanceof IncludeTag)) {
- return;
- }
-
- String msg = "A class that extends EnclosedIncludeBodyReaderTag " +
- "is not enclosed within an IncludeTag.";
- log(msg);
- throw new JspException(msg);
- }
- */
-
- /**
- * 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
- * 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
- * life cycle just in case a JspException was thrown during the tag
- * execution.
- */
-
- protected void clearServiceState() {
- mParameterName = null;
- }
-
- /**
- * 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
- * request scope session. If any problems occur while loading the body
- * into the session scope then a {@code JspException} will be thrown.
- *
- * @param pContent The body of the tag as a String.
- *
- * @exception JspException
- */
-
- protected void processBody(String pContent) throws JspException {
- // Okay, we have the content, we need to write it to disk somewhere
- String content = pContent;
-
- if (!StringUtil.isEmpty(mLanguage)) {
- content = "<%@page language=\"" + mLanguage + "\" %>" + content;
- }
-
- if (!StringUtil.isEmpty(mPrefix)) {
- content = "<%@taglib uri=\"/twelvemonkeys-common\" prefix=\"" + mPrefix + "\" %>" + content;
- }
-
- // Write the content of the oparam to disk
- try {
- log("Processing subpage " + mSubpage.getPath());
- FileUtil.write(mSubpage, content.getBytes());
-
- }
- catch (IOException ioe) {
- throw new JspException(ioe);
- }
- }
-
- /**
- * Creates a unique filename for each (nested) oparam
- */
- private File createFileNameFromRequest(HttpServletRequest pRequest) {
- //System.out.println("ServletPath" + pRequest.getServletPath());
- String path = pRequest.getServletPath();
-
- // Find last '/'
- int splitIndex = path.lastIndexOf("/");
-
- // Split -> path + name
- String name = path.substring(splitIndex + 1);
- path = path.substring(0, splitIndex);
-
- // Replace special chars in name with '_'
- name = name.replace('.', '_');
- String param = mParameterName.replace('.', '_');
- param = param.replace('/', '_');
- param = param.replace('\\', '_');
- param = param.replace(':', '_');
-
- // tempfile = realPath(path) + name + "_oparam_" + number + ".jsp"
- int count = getOparamCountFromRequest(pRequest);
-
- // 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");
- //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");
- }
-
- /**
- * Gets the current oparam count for this request
- */
- private int getOparamCountFromRequest(HttpServletRequest pRequest) {
- // Use request.attribute for incrementing oparam counter
- Integer count = (Integer) pRequest.getAttribute(COUNTER);
- if (count == null)
- count = new Integer(0);
- else
- count = new Integer(count.intValue() + 1);
-
- // ... and set it back
- pRequest.setAttribute(COUNTER, count);
-
- return count.intValue();
- }
-
-}
+/*
+ * Copyright (c) 2002 TwelveMonkeys.
+ * All rights reserved.
+ *
+ * $Log: OparamTag.java,v $
+ * Revision 1.4 2003/10/06 14:25:53 WMHAKUR
+ * Code clean-up only.
+ *
+ * Revision 1.3 2002/11/18 14:12:43 WMHAKUR
+ * *** empty log message ***
+ *
+ * Revision 1.2 2002/11/07 12:20:14 WMHAKUR
+ * Updated to reflect changes in com.twelvemonkeys.util.*Util
+ *
+ * Revision 1.1 2002/10/18 14:03:09 WMHAKUR
+ * Moved to com.twelvemonkeys.servlet.jsp.droplet.taglib
+ *
+ *
+ */
+
+package com.twelvemonkeys.servlet.jsp.droplet.taglib;
+
+import com.twelvemonkeys.io.FileUtil;
+import com.twelvemonkeys.lang.StringUtil;
+import com.twelvemonkeys.servlet.jsp.droplet.Oparam;
+import com.twelvemonkeys.servlet.jsp.taglib.BodyReaderTag;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.tagext.BodyTag;
+import javax.servlet.jsp.tagext.Tag;
+import java.io.File;
+import java.io.IOException;
+
+
+/**
+ * Open parameter tag that emulates ATG Dynamo JHTML behaviour for JSP.
+ *
+ * @author Thomas Purcell (CSC Australia)
+ * @author Harald Kuhr
+ * @author last modified by $Author: haku $
+ *
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/OparamTag.java#1 $
+ */
+
+public class OparamTag extends BodyReaderTag {
+
+ protected final static String COUNTER = "com.twelvemonkeys.servlet.jsp.taglib.OparamTag.counter";
+
+
+ private File mSubpage = null;
+
+ /**
+ * This is the name of the parameter to be inserted into the {@code
+ * PageContext.REQUEST_SCOPE} scope.
+ */
+
+ private String mParameterName = null;
+
+ private String mLanguage = null;
+
+ private String mPrefix = null;
+
+ /**
+ * This method allows the JSP page to set the name for the parameter by
+ * using the {@code name} tag attribute.
+ *
+ * @param pName The name for the parameter to insert into the {@code
+ * PageContext.REQUEST_SCOPE} scope.
+ */
+
+ public void setName(String pName) {
+ mParameterName = pName;
+ }
+
+ public void setLanguage(String pLanguage) {
+ //System.out.println("setLanguage:"+pLanguage);
+ mLanguage = pLanguage;
+ }
+
+ public void setPrefix(String pPrefix) {
+ //System.out.println("setPrefix:"+pPrefix);
+ mPrefix = pPrefix;
+ }
+
+ /**
+ * 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.
+ *
+ * @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.
+ * @exception JspException
+ */
+
+ public int doStartTag() throws JspException {
+ //checkEnclosedInIncludeTag(); // Moved to TagLibValidator
+
+ // Get request
+ HttpServletRequest request = (HttpServletRequest) pageContext.getRequest();
+
+ // Get filename
+ mSubpage = createFileNameFromRequest(request);
+
+ // Get include tag, and add to parameters
+ IncludeTag includeTag = (IncludeTag) getParent();
+ includeTag.addParameter(mParameterName, new Oparam(mSubpage.getName()));
+
+ // if ! subpage.exist || jsp newer than subpage, write new
+ File jsp = new File(pageContext.getServletContext()
+ .getRealPath(request.getServletPath()));
+
+ if (!mSubpage.exists() || jsp.lastModified() > mSubpage.lastModified()) {
+ return BodyTag.EVAL_BODY_BUFFERED;
+ }
+
+ // No need to evaluate body again!
+ return Tag.SKIP_BODY;
+ }
+
+ /**
+ * This is the method responsible for actually testing that the tag
+ * implemented by this class is enclosed within an {@code IncludeTag}.
+ *
+ * @exception JspException
+ */
+ /*
+ protected void checkEnclosedInIncludeTag() throws JspException {
+ Tag parentTag = getParent();
+
+ if ((parentTag != null) && (parentTag instanceof IncludeTag)) {
+ return;
+ }
+
+ String msg = "A class that extends EnclosedIncludeBodyReaderTag " +
+ "is not enclosed within an IncludeTag.";
+ log(msg);
+ throw new JspException(msg);
+ }
+ */
+
+ /**
+ * 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
+ * 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
+ * life cycle just in case a JspException was thrown during the tag
+ * execution.
+ */
+
+ protected void clearServiceState() {
+ mParameterName = null;
+ }
+
+ /**
+ * 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
+ * request scope session. If any problems occur while loading the body
+ * into the session scope then a {@code JspException} will be thrown.
+ *
+ * @param pContent The body of the tag as a String.
+ *
+ * @exception JspException
+ */
+
+ protected void processBody(String pContent) throws JspException {
+ // Okay, we have the content, we need to write it to disk somewhere
+ String content = pContent;
+
+ if (!StringUtil.isEmpty(mLanguage)) {
+ content = "<%@page language=\"" + mLanguage + "\" %>" + content;
+ }
+
+ if (!StringUtil.isEmpty(mPrefix)) {
+ content = "<%@taglib uri=\"/twelvemonkeys-common\" prefix=\"" + mPrefix + "\" %>" + content;
+ }
+
+ // Write the content of the oparam to disk
+ try {
+ log("Processing subpage " + mSubpage.getPath());
+ FileUtil.write(mSubpage, content.getBytes());
+
+ }
+ catch (IOException ioe) {
+ throw new JspException(ioe);
+ }
+ }
+
+ /**
+ * Creates a unique filename for each (nested) oparam
+ */
+ private File createFileNameFromRequest(HttpServletRequest pRequest) {
+ //System.out.println("ServletPath" + pRequest.getServletPath());
+ String path = pRequest.getServletPath();
+
+ // Find last '/'
+ int splitIndex = path.lastIndexOf("/");
+
+ // Split -> path + name
+ String name = path.substring(splitIndex + 1);
+ path = path.substring(0, splitIndex);
+
+ // Replace special chars in name with '_'
+ name = name.replace('.', '_');
+ String param = mParameterName.replace('.', '_');
+ param = param.replace('/', '_');
+ param = param.replace('\\', '_');
+ param = param.replace(':', '_');
+
+ // tempfile = realPath(path) + name + "_oparam_" + number + ".jsp"
+ int count = getOparamCountFromRequest(pRequest);
+
+ // 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");
+ //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");
+ }
+
+ /**
+ * Gets the current oparam count for this request
+ */
+ private int getOparamCountFromRequest(HttpServletRequest pRequest) {
+ // Use request.attribute for incrementing oparam counter
+ Integer count = (Integer) pRequest.getAttribute(COUNTER);
+ if (count == null)
+ count = new Integer(0);
+ else
+ count = new Integer(count.intValue() + 1);
+
+ // ... and set it back
+ pRequest.setAttribute(COUNTER, count);
+
+ return count.intValue();
+ }
+
+}
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/ParamTag.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/ParamTag.java
index 3f24a48a..047ab084 100755
--- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/ParamTag.java
+++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/ParamTag.java
@@ -1,141 +1,141 @@
-/*
- * Copyright (c) 2002 TwelveMonkeys.
- * All rights reserved.
- *
- * $Log: ParamTag.java,v $
- * Revision 1.2 2003/10/06 14:26:00 WMHAKUR
- * Code clean-up only.
- *
- * Revision 1.1 2002/10/18 14:03:09 WMHAKUR
- * Moved to com.twelvemonkeys.servlet.jsp.droplet.taglib
- *
- *
- */
-
-package com.twelvemonkeys.servlet.jsp.droplet.taglib;
-
-import java.io.IOException;
-
-import javax.servlet.*;
-import javax.servlet.jsp.*;
-import javax.servlet.jsp.tagext.*;
-
-import com.twelvemonkeys.servlet.jsp.droplet.*;
-import com.twelvemonkeys.servlet.jsp.taglib.*;
-
-/**
- * Parameter tag that emulates ATG Dynamo JHTML behaviour for JSP.
- *
- * @author Thomas Purcell (CSC Australia)
- * @author Harald Kuhr
- * @author last modified by $Author: haku $
- *
- * @version $Revision: #1 $, ($Date: 2008/05/05 $)
- *
- */
-
-public class ParamTag extends ExTagSupport {
-
- /**
- * This is the name of the parameter to be inserted into the {@code
- * PageContext.REQUEST_SCOPE} scope.
- */
-
- private String mParameterName;
-
- /**
- * This is the value for the parameter to be inserted into the {@code
- * PageContext.REQUEST_SCOPE} scope.
- */
-
- private Object mParameterValue;
-
- /**
- * This method allows the JSP page to set the name for the parameter by
- * using the {@code name} tag attribute.
- *
- * @param pName The name for the parameter to insert into the {@code
- * PageContext.REQUEST_SCOPE} scope.
- */
-
- public void setName(String pName) {
- mParameterName = pName;
- }
-
- /**
- * This method allows the JSP page to set the value for hte parameter by
- * using the {@code value} tag attribute.
- *
- * @param pValue The value for the parameter to insert into the An abstract base class for tags with some kind of conditional presentation of the tag body. Perform the test required for this particular tag, and either evaluate or skip the body of this tag. Evaluate the remainder of the current page as normal. Release all allocated resources. The condition that must be met in order to display the body of this tag. An abstract base class for tags with some kind of conditional presentation of the tag body. Perform the test required for this particular tag, and either evaluate or skip the body of this tag. Evaluate the remainder of the current page as normal. Release all allocated resources. The condition that must be met in order to display the body of this tag.
- * Custom tag for testing equality of an attribute against a given value.
- * The attribute types supported so far is:
- *
- * **** external info: [timestamp] [class name]:
- *
- *
- * **** external debug: [timestamp] [class name]:
- *
- *
- * **** external warning: [timestamp] [class name]:
- *
- *
- * **** external error: [timestamp] [class name]:
- *
- *
- * The method for invocation must have no formal parameters. If the invoking method does not exist, the {@code toString()} method is called.
- * The {@code toString()} method of the returning object is called.
- *
- * For bulk data types, recursive invocations and invocations of other methods in this class, are used.
- *
- * For bulk data types, recursive invocations and invocations of other methods in this class, are used.
- *
- * For bulk data types, recursive invocations and invocations of other methods in this class, are used.
- *
- * long startTime = System.currentTimeMillis();
- * ...
- * com.iml.oslo.eito.util.DebugUtil.printTimeDifference(startTime);
- *
- *
- * GregorianCalendar startTime = new GregorianCalendar();
- * ...
- * com.iml.oslo.eito.util.DebugUtil.printTimeDifference(startTime);
- *
- *
+ * **** external info: [timestamp] [class name]:
+ *
+ *
+ * **** external debug: [timestamp] [class name]:
+ *
+ *
+ * **** external warning: [timestamp] [class name]:
+ *
+ *
+ * **** external error: [timestamp] [class name]:
+ *
+ *
+ * The method for invocation must have no formal parameters. If the invoking method does not exist, the {@code toString()} method is called.
+ * The {@code toString()} method of the returning object is called.
+ *
+ * For bulk data types, recursive invocations and invocations of other methods in this class, are used.
+ *
+ * For bulk data types, recursive invocations and invocations of other methods in this class, are used.
+ *
+ * For bulk data types, recursive invocations and invocations of other methods in this class, are used.
+ *
+ * long startTime = System.currentTimeMillis();
+ * ...
+ * com.iml.oslo.eito.util.DebugUtil.printTimeDifference(startTime);
+ *
+ *
+ * GregorianCalendar startTime = new GregorianCalendar();
+ * ...
+ * com.iml.oslo.eito.util.DebugUtil.printTimeDifference(startTime);
+ *
+ *
- * Null-valued string masks as well as null valued strings to be parsed, will lead to rejection.
- *
- *
- * The '*' corresponds to ([Union of all characters in the alphabet])*
- * The '?' corresponds to ([Union of all characters in the alphabet])
- * These expressions are not suited for textual representation at all, I must say. Is there any math tags included in HTML?
- *
- *
- * This example will return "Accepted!".
- *
- * REWildcardStringParser parser = new REWildcardStringParser("*_28????.jp*");
- * if (parser.parseString("gupu_280915.jpg")) {
- * System.out.println("Accepted!");
- * } else {
- * System.out.println("Not accepted!");
- * }
- *
- *
- *
+ * Null-valued string masks as well as null valued strings to be parsed, will lead to rejection.
+ *
+ *
+ * The '*' corresponds to ([Union of all characters in the alphabet])*
+ * The '?' corresponds to ([Union of all characters in the alphabet])
+ * These expressions are not suited for textual representation at all, I must say. Is there any math tags included in HTML?
+ *
+ *
+ * This example will return "Accepted!".
+ *
+ * REWildcardStringParser parser = new REWildcardStringParser("*_28????.jp*");
+ * if (parser.parseString("gupu_280915.jpg")) {
+ * System.out.println("Accepted!");
+ * } else {
+ * System.out.println("Not accepted!");
+ * }
+ *
+ *
+ *
- * To run the filter once per request, the {@code filter-mapping} element
- * of the web-descriptor should include a {@code dispatcher} element:
- * <dispatcher>REQUEST</dispatcher>
- *
- */
- protected boolean mOncePerRequest = false;
-
- /**
- * Does nothing.
- */
- public GenericFilter() {}
-
- /**
- * Called by the web container to indicate to a filter that it is being
- * placed into service.
- *
- * This implementation stores the {@code FilterConfig} object it
- * receives from the servlet container for later use.
- * Generally, there's no reason to override this method, override the
- * no-argument {@code init} instead. However, if you are
- * overriding this form of the method,
- * always call {@code super.init(config)}.
- *
- * This implementation will also set all configured key/value pairs, that
- * have a matching setter method annotated with {@link InitParam}.
- *
- * @param pConfig the filter config
- * @throws ServletException if an error occurs during init
- *
- * @see Filter#init
- * @see #init() init
- * @see BeanUtil#configure(Object, java.util.Map, boolean)
- */
- public void init(FilterConfig pConfig) throws ServletException {
- if (pConfig == null) {
- throw new ServletConfigException("filterconfig == null");
- }
-
- // Store filterconfig
- mFilterConfig = pConfig;
-
- // Configure this
- try {
- BeanUtil.configure(this, ServletUtil.asMap(pConfig), true);
- }
- catch (InvocationTargetException e) {
- throw new ServletConfigException("Could not configure " + getFilterName(), e.getCause());
- }
-
- // Create run-once attribute name
- mAttribRunOnce = pConfig.getFilterName() + ATTRIB_RUN_ONCE_EXT;
- log("init (oncePerRequest=" + mOncePerRequest + ", attribRunOnce=" + mAttribRunOnce + ")");
- init();
- }
-
- /**
- * A convenience method which can be overridden so that there's no need to
- * call {@code super.init(config)}.
- *
- * @see #init(FilterConfig)
- *
- * @throws ServletException if an error occurs during init
- */
- public void init() throws ServletException {}
-
- /**
- * The {@code doFilter} method of the Filter is called by the container
- * each time a request/response pair is passed through the chain due to a
- * client request for a resource at the end of the chain.
- *
- * Subclasses should not override this method, but rather the
- * abstract {@link #doFilterImpl doFilterImpl} method.
- *
- * @param pRequest the servlet request
- * @param pResponse the servlet response
- * @param pFilterChain the filter chain
- *
- * @throws IOException
- * @throws ServletException
- *
- * @see Filter#doFilter Filter.doFilter
- * @see #doFilterImpl doFilterImpl
- */
- public final void doFilter(ServletRequest pRequest, ServletResponse pResponse, FilterChain pFilterChain) throws IOException, ServletException {
- // If request filter and allready run, continue chain and return fast
- if (mOncePerRequest && isRunOnce(pRequest)) {
- pFilterChain.doFilter(pRequest, pResponse);
- return;
- }
-
- // Do real filter
- doFilterImpl(pRequest, pResponse, pFilterChain);
- }
-
- /**
- * If request is filtered, returns true, otherwise marks request as filtered
- * and returns false.
- * A return value of false, indicates that the filter has not yet run.
- * A return value of true, indicates that the filter has run for this
- * request, and processing should not contine.
- *
- * Note that the method will mark the request as filtered on first
- * invocation.
- *
- * see #ATTRIB_RUN_ONCE_EXT
- * see #ATTRIB_RUN_ONCE_VALUE
- *
- * @param pRequest the servlet request
- * @return {@code true} if the request is allready filtered, otherwise
- * {@code false}.
- */
- private boolean isRunOnce(ServletRequest pRequest) {
- // If request allready filtered, return true (skip)
- if (pRequest.getAttribute(mAttribRunOnce) == ATTRIB_RUN_ONCE_VALUE) {
- return true;
- }
-
- // Set attribute and return false (continue)
- pRequest.setAttribute(mAttribRunOnce, ATTRIB_RUN_ONCE_VALUE);
- return false;
- }
-
- /**
- * Invoked once, or each time a request/response pair is passed through the
- * chain, depending on the {@link #mOncePerRequest} member variable.
- *
- * @param pRequest the servlet request
- * @param pResponse the servlet response
- * @param pChain the filter chain
- *
- * @throws IOException if an I/O error occurs
- * @throws ServletException if an exception occurs during the filter process
- *
- * @see #mOncePerRequest
- * @see #doFilter doFilter
- * @see Filter#doFilter Filter.doFilter
- */
- protected abstract void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain)
- throws IOException, ServletException;
-
- /**
- * Called by the web container to indicate to a filter that it is being
- * taken out of service.
- *
- * @see Filter#destroy
- */
- public void destroy() {
- log("destroy");
- mFilterConfig = null;
- }
-
- /**
- * Returns the filter-name of this filter as defined in the deployment
- * descriptor.
- *
- * @return the filter-name
- * @see FilterConfig#getFilterName
- */
- public String getFilterName() {
- return mFilterConfig.getFilterName();
- }
-
- /**
- * Returns a reference to the {@link ServletContext} in which the caller is
- * executing.
- *
- * @return the {@code ServletContext} object, used by the caller to
- * interact with its servlet container
- * @see FilterConfig#getServletContext
- * @see ServletContext
- */
- public ServletContext getServletContext() {
- // TODO: Create a servlet context wrapper that lets you log to a log4j appender?
- return mFilterConfig.getServletContext();
- }
-
- /**
- * Returns a {@code String} containing the value of the named
- * initialization parameter, or null if the parameter does not exist.
- *
- * @param pKey a {@code String} specifying the name of the
- * initialization parameter
- * @return a {@code String} containing the value of the initialization
- * parameter
- */
- public String getInitParameter(String pKey) {
- return mFilterConfig.getInitParameter(pKey);
- }
-
- /**
- * Returns the names of the servlet's initialization parameters as an
- * {@code Enumeration} of {@code String} objects, or an empty
- * {@code Enumeration} if the servlet has no initialization parameters.
- *
- * @return an {@code Enumeration} of {@code String} objects
- * containing the mNames of the servlet's initialization parameters
- */
- public Enumeration getInitParameterNames() {
- return mFilterConfig.getInitParameterNames();
- }
-
- /**
- * Writes the specified message to a servlet log file, prepended by the
- * filter's name.
- *
- * @param pMessage the log message
- * @see ServletContext#log(String)
- */
- protected void log(String pMessage) {
- getServletContext().log(getFilterName() + ": " + pMessage);
- }
-
- /**
- * Writes an explanatory message and a stack trace for a given
- * {@code Throwable} to the servlet log file, prepended by the
- * filter's name.
- *
- * @param pMessage the log message
- * @param pThrowable the exception
- * @see ServletContext#log(String,Throwable)
- */
- protected void log(String pMessage, Throwable pThrowable) {
- getServletContext().log(getFilterName() + ": " + pMessage, pThrowable);
- }
-
- /**
- * Initializes the filter.
- *
- * @param pFilterConfig the filter config
- * @see #init init
- *
- * @deprecated For compatibility only, use {@link #init init} instead.
- */
- public void setFilterConfig(FilterConfig pFilterConfig) {
- try {
- init(pFilterConfig);
- }
- catch (ServletException e) {
- log("Error in init(), see stacktrace for details.", e);
- }
- }
-
- /**
- * Gets the {@code FilterConfig} for this filter.
- *
- * @return the {@code FilterConfig} for this filter
- * @see FilterConfig
- */
- public FilterConfig getFilterConfig() {
- return mFilterConfig;
- }
-
- /**
- * Specifies if this filter should run once per request ({@code true}),
- * or for each forward/include resource ({@code false}).
- * Called automatically from the {@code init}-method, with settings
- * from web.xml.
- *
- * @param pOncePerRequest {@code true} if the filter should run only
- * once per request
- * @see #mOncePerRequest
- */
- @InitParam
- public void setOncePerRequest(boolean pOncePerRequest) {
- mOncePerRequest = pOncePerRequest;
- }
-}
+/*
+ * Copyright (c) 2008, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name "TwelveMonkeys" nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.twelvemonkeys.servlet;
+
+import com.twelvemonkeys.lang.BeanUtil;
+
+import javax.servlet.*;
+import java.io.IOException;
+import java.io.Serializable;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Enumeration;
+
+/**
+ * Defines a generic, protocol-independent filter.
+ *
+ * {@code GenericFilter} is inspired by {@link GenericServlet}, and
+ * implements the {@code Filter} and {@code FilterConfig} interfaces.
+ *
+ * {@code GenericFilter} makes writing filters easier. It provides simple
+ * versions of the lifecycle methods {@code init} and {@code destroy}
+ * and of the methods in the {@code FilterConfig} interface.
+ * {@code GenericFilter} also implements the {@code log} methods,
+ * declared in the {@code ServletContext} interface.
+ *
+ * To write a generic filter, you need only override the abstract
+ * {@link #doFilterImpl doFilterImpl} method.
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haku $
+ *
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/GenericFilter.java#1 $
+ *
+ * @see Filter
+ * @see FilterConfig
+ */
+public abstract class GenericFilter implements Filter, FilterConfig, Serializable {
+
+ /**
+ * The filter config.
+ */
+ private transient FilterConfig mFilterConfig = null;
+
+ /**
+ * Makes sure the filter runs once per request
+ *
+ * see #isRunOnce
+ *
+ * @see #mOncePerRequest
+ * see #ATTRIB_RUN_ONCE_VALUE
+ */
+ private final static String ATTRIB_RUN_ONCE_EXT = ".REQUEST_HANDLED";
+
+ /**
+ * Makes sure the filter runs once per request.
+ * Must be configured through init method, as the filter name is not
+ * available before we have a FitlerConfig object.
+ *
+ * see #isRunOnce
+ *
+ * @see #mOncePerRequest
+ * see #ATTRIB_RUN_ONCE_VALUE
+ */
+ private String mAttribRunOnce = null;
+
+ /**
+ * Makes sure the filter runs once per request
+ *
+ * see #isRunOnce
+ *
+ * @see #mOncePerRequest
+ * see #ATTRIB_RUN_ONCE_EXT
+ */
+ private static final Object ATTRIB_RUN_ONCE_VALUE = new Object();
+
+ /**
+ * Indicates if this filter should run once per request ({@code true}),
+ * or for each forward/include resource ({@code false}).
+ *
+ * Set this variable to true, to make sure the filter runs once per request.
+ *
+ * NOTE: As of Servlet 2.4, this field
+ * should always be left to it's default value ({@code false}).
+ *
+ * To run the filter once per request, the {@code filter-mapping} element
+ * of the web-descriptor should include a {@code dispatcher} element:
+ * <dispatcher>REQUEST</dispatcher>
+ *
+ */
+ protected boolean mOncePerRequest = false;
+
+ /**
+ * Does nothing.
+ */
+ public GenericFilter() {}
+
+ /**
+ * Called by the web container to indicate to a filter that it is being
+ * placed into service.
+ *
+ * This implementation stores the {@code FilterConfig} object it
+ * receives from the servlet container for later use.
+ * Generally, there's no reason to override this method, override the
+ * no-argument {@code init} instead. However, if you are
+ * overriding this form of the method,
+ * always call {@code super.init(config)}.
+ *
+ * This implementation will also set all configured key/value pairs, that
+ * have a matching setter method annotated with {@link InitParam}.
+ *
+ * @param pConfig the filter config
+ * @throws ServletException if an error occurs during init
+ *
+ * @see Filter#init
+ * @see #init() init
+ * @see BeanUtil#configure(Object, java.util.Map, boolean)
+ */
+ public void init(FilterConfig pConfig) throws ServletException {
+ if (pConfig == null) {
+ throw new ServletConfigException("filterconfig == null");
+ }
+
+ // Store filterconfig
+ mFilterConfig = pConfig;
+
+ // Configure this
+ try {
+ BeanUtil.configure(this, ServletUtil.asMap(pConfig), true);
+ }
+ catch (InvocationTargetException e) {
+ throw new ServletConfigException("Could not configure " + getFilterName(), e.getCause());
+ }
+
+ // Create run-once attribute name
+ mAttribRunOnce = pConfig.getFilterName() + ATTRIB_RUN_ONCE_EXT;
+ log("init (oncePerRequest=" + mOncePerRequest + ", attribRunOnce=" + mAttribRunOnce + ")");
+ init();
+ }
+
+ /**
+ * A convenience method which can be overridden so that there's no need to
+ * call {@code super.init(config)}.
+ *
+ * @see #init(FilterConfig)
+ *
+ * @throws ServletException if an error occurs during init
+ */
+ public void init() throws ServletException {}
+
+ /**
+ * The {@code doFilter} method of the Filter is called by the container
+ * each time a request/response pair is passed through the chain due to a
+ * client request for a resource at the end of the chain.
+ *
+ * Subclasses should not override this method, but rather the
+ * abstract {@link #doFilterImpl doFilterImpl} method.
+ *
+ * @param pRequest the servlet request
+ * @param pResponse the servlet response
+ * @param pFilterChain the filter chain
+ *
+ * @throws IOException
+ * @throws ServletException
+ *
+ * @see Filter#doFilter Filter.doFilter
+ * @see #doFilterImpl doFilterImpl
+ */
+ public final void doFilter(ServletRequest pRequest, ServletResponse pResponse, FilterChain pFilterChain) throws IOException, ServletException {
+ // If request filter and allready run, continue chain and return fast
+ if (mOncePerRequest && isRunOnce(pRequest)) {
+ pFilterChain.doFilter(pRequest, pResponse);
+ return;
+ }
+
+ // Do real filter
+ doFilterImpl(pRequest, pResponse, pFilterChain);
+ }
+
+ /**
+ * If request is filtered, returns true, otherwise marks request as filtered
+ * and returns false.
+ * A return value of false, indicates that the filter has not yet run.
+ * A return value of true, indicates that the filter has run for this
+ * request, and processing should not contine.
+ *
+ * Note that the method will mark the request as filtered on first
+ * invocation.
+ *
+ * see #ATTRIB_RUN_ONCE_EXT
+ * see #ATTRIB_RUN_ONCE_VALUE
+ *
+ * @param pRequest the servlet request
+ * @return {@code true} if the request is allready filtered, otherwise
+ * {@code false}.
+ */
+ private boolean isRunOnce(ServletRequest pRequest) {
+ // If request allready filtered, return true (skip)
+ if (pRequest.getAttribute(mAttribRunOnce) == ATTRIB_RUN_ONCE_VALUE) {
+ return true;
+ }
+
+ // Set attribute and return false (continue)
+ pRequest.setAttribute(mAttribRunOnce, ATTRIB_RUN_ONCE_VALUE);
+ return false;
+ }
+
+ /**
+ * Invoked once, or each time a request/response pair is passed through the
+ * chain, depending on the {@link #mOncePerRequest} member variable.
+ *
+ * @param pRequest the servlet request
+ * @param pResponse the servlet response
+ * @param pChain the filter chain
+ *
+ * @throws IOException if an I/O error occurs
+ * @throws ServletException if an exception occurs during the filter process
+ *
+ * @see #mOncePerRequest
+ * @see #doFilter doFilter
+ * @see Filter#doFilter Filter.doFilter
+ */
+ protected abstract void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain)
+ throws IOException, ServletException;
+
+ /**
+ * Called by the web container to indicate to a filter that it is being
+ * taken out of service.
+ *
+ * @see Filter#destroy
+ */
+ public void destroy() {
+ log("destroy");
+ mFilterConfig = null;
+ }
+
+ /**
+ * Returns the filter-name of this filter as defined in the deployment
+ * descriptor.
+ *
+ * @return the filter-name
+ * @see FilterConfig#getFilterName
+ */
+ public String getFilterName() {
+ return mFilterConfig.getFilterName();
+ }
+
+ /**
+ * Returns a reference to the {@link ServletContext} in which the caller is
+ * executing.
+ *
+ * @return the {@code ServletContext} object, used by the caller to
+ * interact with its servlet container
+ * @see FilterConfig#getServletContext
+ * @see ServletContext
+ */
+ public ServletContext getServletContext() {
+ // TODO: Create a servlet context wrapper that lets you log to a log4j appender?
+ return mFilterConfig.getServletContext();
+ }
+
+ /**
+ * Returns a {@code String} containing the value of the named
+ * initialization parameter, or null if the parameter does not exist.
+ *
+ * @param pKey a {@code String} specifying the name of the
+ * initialization parameter
+ * @return a {@code String} containing the value of the initialization
+ * parameter
+ */
+ public String getInitParameter(String pKey) {
+ return mFilterConfig.getInitParameter(pKey);
+ }
+
+ /**
+ * Returns the names of the servlet's initialization parameters as an
+ * {@code Enumeration} of {@code String} objects, or an empty
+ * {@code Enumeration} if the servlet has no initialization parameters.
+ *
+ * @return an {@code Enumeration} of {@code String} objects
+ * containing the mNames of the servlet's initialization parameters
+ */
+ public Enumeration getInitParameterNames() {
+ return mFilterConfig.getInitParameterNames();
+ }
+
+ /**
+ * Writes the specified message to a servlet log file, prepended by the
+ * filter's name.
+ *
+ * @param pMessage the log message
+ * @see ServletContext#log(String)
+ */
+ protected void log(String pMessage) {
+ getServletContext().log(getFilterName() + ": " + pMessage);
+ }
+
+ /**
+ * Writes an explanatory message and a stack trace for a given
+ * {@code Throwable} to the servlet log file, prepended by the
+ * filter's name.
+ *
+ * @param pMessage the log message
+ * @param pThrowable the exception
+ * @see ServletContext#log(String,Throwable)
+ */
+ protected void log(String pMessage, Throwable pThrowable) {
+ getServletContext().log(getFilterName() + ": " + pMessage, pThrowable);
+ }
+
+ /**
+ * Initializes the filter.
+ *
+ * @param pFilterConfig the filter config
+ * @see #init init
+ *
+ * @deprecated For compatibility only, use {@link #init init} instead.
+ */
+ public void setFilterConfig(FilterConfig pFilterConfig) {
+ try {
+ init(pFilterConfig);
+ }
+ catch (ServletException e) {
+ log("Error in init(), see stacktrace for details.", e);
+ }
+ }
+
+ /**
+ * Gets the {@code FilterConfig} for this filter.
+ *
+ * @return the {@code FilterConfig} for this filter
+ * @see FilterConfig
+ */
+ public FilterConfig getFilterConfig() {
+ return mFilterConfig;
+ }
+
+ /**
+ * Specifies if this filter should run once per request ({@code true}),
+ * or for each forward/include resource ({@code false}).
+ * Called automatically from the {@code init}-method, with settings
+ * from web.xml.
+ *
+ * @param pOncePerRequest {@code true} if the filter should run only
+ * once per request
+ * @see #mOncePerRequest
+ */
+ @InitParam
+ public void setOncePerRequest(boolean pOncePerRequest) {
+ mOncePerRequest = pOncePerRequest;
+ }
+}
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/GenericServlet.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/GenericServlet.java
index 6a120cc6..60603c2e 100755
--- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/GenericServlet.java
+++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/GenericServlet.java
@@ -1,87 +1,87 @@
-/*
- * Copyright (c) 2008, Harald Kuhr
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name "TwelveMonkeys" nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package com.twelvemonkeys.servlet;
-
-import com.twelvemonkeys.lang.BeanUtil;
-
-import javax.servlet.ServletConfig;
-import javax.servlet.ServletException;
-import java.lang.reflect.InvocationTargetException;
-
-/**
- * Defines a generic, protocol-independent servlet.
- *
- * {@code GenericServlet} has an auto-init system, that automatically invokes
- * the method matching the signature {@code void setX(<Type>)},
- * for every init-parameter {@code x}. Both camelCase and lisp-style paramter
- * naming is supported, lisp-style names will be converted to camelCase.
- * Parameter values are automatically converted from string represenation to
- * most basic types, if neccessary.
- *
- * @author Harald Kuhr
- * @author last modified by $Author: haku $
- *
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/GenericServlet.java#1 $
- */
-public abstract class GenericServlet extends javax.servlet.GenericServlet {
-
- /**
- * Called by the web container to indicate to a servlet that it is being
- * placed into service.
- *
- * This implementation stores the {@code ServletConfig} object it
- * receives from the servlet container for later use. When overriding this
- * form of the method, call {@code super.init(config)}.
- *
- * This implementation will also set all configured key/value pairs, that
- * have a matching setter method annotated with {@link InitParam}.
- *
- * @param pConfig the servlet config
- * @throws ServletException
- *
- * @see javax.servlet.GenericServlet#init
- * @see #init() init
- * @see BeanUtil#configure(Object, java.util.Map, boolean)
- */
- @Override
- public void init(ServletConfig pConfig) throws ServletException {
- if (pConfig == null) {
- throw new ServletConfigException("servletconfig == null");
- }
-
- try {
- BeanUtil.configure(this, ServletUtil.asMap(pConfig), true);
- }
- catch (InvocationTargetException e) {
- throw new ServletConfigException("Could not configure " + getServletName(), e.getCause());
- }
-
- super.init(pConfig);
- }
-}
+/*
+ * Copyright (c) 2008, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name "TwelveMonkeys" nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.twelvemonkeys.servlet;
+
+import com.twelvemonkeys.lang.BeanUtil;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * Defines a generic, protocol-independent servlet.
+ *
+ * {@code GenericServlet} has an auto-init system, that automatically invokes
+ * the method matching the signature {@code void setX(<Type>)},
+ * for every init-parameter {@code x}. Both camelCase and lisp-style paramter
+ * naming is supported, lisp-style names will be converted to camelCase.
+ * Parameter values are automatically converted from string represenation to
+ * most basic types, if neccessary.
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haku $
+ *
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/GenericServlet.java#1 $
+ */
+public abstract class GenericServlet extends javax.servlet.GenericServlet {
+
+ /**
+ * Called by the web container to indicate to a servlet that it is being
+ * placed into service.
+ *
+ * This implementation stores the {@code ServletConfig} object it
+ * receives from the servlet container for later use. When overriding this
+ * form of the method, call {@code super.init(config)}.
+ *
+ * This implementation will also set all configured key/value pairs, that
+ * have a matching setter method annotated with {@link InitParam}.
+ *
+ * @param pConfig the servlet config
+ * @throws ServletException
+ *
+ * @see javax.servlet.GenericServlet#init
+ * @see #init() init
+ * @see BeanUtil#configure(Object, java.util.Map, boolean)
+ */
+ @Override
+ public void init(ServletConfig pConfig) throws ServletException {
+ if (pConfig == null) {
+ throw new ServletConfigException("servletconfig == null");
+ }
+
+ try {
+ BeanUtil.configure(this, ServletUtil.asMap(pConfig), true);
+ }
+ catch (InvocationTargetException e) {
+ throw new ServletConfigException("Could not configure " + getServletName(), e.getCause());
+ }
+
+ super.init(pConfig);
+ }
+}
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/HttpServlet.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/HttpServlet.java
index 2b546317..f4307131 100755
--- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/HttpServlet.java
+++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/HttpServlet.java
@@ -1,87 +1,87 @@
-/*
- * Copyright (c) 2008, Harald Kuhr
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name "TwelveMonkeys" nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package com.twelvemonkeys.servlet;
-
-import com.twelvemonkeys.lang.BeanUtil;
-
-import javax.servlet.ServletConfig;
-import javax.servlet.ServletException;
-import java.lang.reflect.InvocationTargetException;
-
-/**
- * Defines a generic, HTTP specific servlet.
- *
- * {@code HttpServlet} has an auto-init system, that automatically invokes
- * the method matching the signature {@code void setX(<Type>)},
- * for every init-parameter {@code x}. Both camelCase and lisp-style paramter
- * naming is supported, lisp-style names will be converted to camelCase.
- * Parameter values are automatically converted from string represenation to
- * most basic types, if neccessary.
- *
- * @author Harald Kuhr
- * @author last modified by $Author: haku $
- *
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/HttpServlet.java#1 $
- */
-public abstract class HttpServlet extends javax.servlet.http.HttpServlet {
-
- /**
- * Called by the web container to indicate to a servlet that it is being
- * placed into service.
- *
- * This implementation stores the {@code ServletConfig} object it
- * receives from the servlet container for later use. When overriding this
- * form of the method, call {@code super.init(config)}.
- *
- * This implementation will also set all configured key/value pairs, that
- * have a matching setter method annotated with {@link InitParam}.
- *
- * @param pConfig the servlet config
- * @throws ServletException if an error ouccured during init
- *
- * @see javax.servlet.GenericServlet#init
- * @see #init() init
- * @see BeanUtil#configure(Object, java.util.Map, boolean)
- */
- @Override
- public void init(ServletConfig pConfig) throws ServletException {
- if (pConfig == null) {
- throw new ServletConfigException("servletconfig == null");
- }
-
- try {
- BeanUtil.configure(this, ServletUtil.asMap(pConfig), true);
- }
- catch (InvocationTargetException e) {
- throw new ServletConfigException("Could not configure " + getServletName(), e.getCause());
- }
-
- super.init(pConfig);
- }
-}
+/*
+ * Copyright (c) 2008, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name "TwelveMonkeys" nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.twelvemonkeys.servlet;
+
+import com.twelvemonkeys.lang.BeanUtil;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * Defines a generic, HTTP specific servlet.
+ *
+ * {@code HttpServlet} has an auto-init system, that automatically invokes
+ * the method matching the signature {@code void setX(<Type>)},
+ * for every init-parameter {@code x}. Both camelCase and lisp-style paramter
+ * naming is supported, lisp-style names will be converted to camelCase.
+ * Parameter values are automatically converted from string represenation to
+ * most basic types, if neccessary.
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haku $
+ *
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/HttpServlet.java#1 $
+ */
+public abstract class HttpServlet extends javax.servlet.http.HttpServlet {
+
+ /**
+ * Called by the web container to indicate to a servlet that it is being
+ * placed into service.
+ *
+ * This implementation stores the {@code ServletConfig} object it
+ * receives from the servlet container for later use. When overriding this
+ * form of the method, call {@code super.init(config)}.
+ *
+ * This implementation will also set all configured key/value pairs, that
+ * have a matching setter method annotated with {@link InitParam}.
+ *
+ * @param pConfig the servlet config
+ * @throws ServletException if an error ouccured during init
+ *
+ * @see javax.servlet.GenericServlet#init
+ * @see #init() init
+ * @see BeanUtil#configure(Object, java.util.Map, boolean)
+ */
+ @Override
+ public void init(ServletConfig pConfig) throws ServletException {
+ if (pConfig == null) {
+ throw new ServletConfigException("servletconfig == null");
+ }
+
+ try {
+ BeanUtil.configure(this, ServletUtil.asMap(pConfig), true);
+ }
+ catch (InvocationTargetException e) {
+ throw new ServletConfigException("Could not configure " + getServletName(), e.getCause());
+ }
+
+ super.init(pConfig);
+ }
+}
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/OutputStreamAdapter.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/OutputStreamAdapter.java
index 046b5fc6..5dc3e325 100755
--- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/OutputStreamAdapter.java
+++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/OutputStreamAdapter.java
@@ -1,122 +1,122 @@
-/*
- * Copyright (c) 2008, Harald Kuhr
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name "TwelveMonkeys" nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package com.twelvemonkeys.servlet;
-
-import javax.servlet.ServletOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * A {@code ServletOutputStream} implementation backed by a
- * {@link java.io.OutputStream}. For filters that need to buffer the
- * response and do post filtering, it may be used like this:
- * ByteArrayOutputStream buffer = new ByteArraOutputStream();
- * ServletOutputStream adapter = new OutputStreamAdapter(buffer);
- *
- *
- * As a {@code ServletOutputStream} is itself an {@code OutputStream}, this
- * class may also be used as a superclass for wrappers of other
- * {@code ServletOutputStream}s, like this:
- * class FilterServletOutputStream extends OutputStreamAdapter {
- * public FilterServletOutputStream(ServletOutputStream out) {
- * super(out);
- * }
- *
- * public void write(int abyte) {
- * // do filtering...
- * super.write(...);
- * }
- * }
- *
- * ...
- *
- * ServletOutputStream original = response.getOutputStream();
- * ServletOutputStream wrapper = new FilterServletOutputStream(original);
- *
- * @author Harald Kuhr
- * @author $Author: haku $
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/OutputStreamAdapter.java#1 $
- *
- */
-public class OutputStreamAdapter extends ServletOutputStream {
-
- /** The wrapped {@code OutputStream}. */
- protected final OutputStream mOut;
-
- /**
- * Creates an {@code OutputStreamAdapter}.
- *
- * @param pOut the wrapped {@code OutputStream}
- *
- * @throws IllegalArgumentException if {@code pOut} is {@code null}.
- */
- public OutputStreamAdapter(OutputStream pOut) {
- if (pOut == null) {
- throw new IllegalArgumentException("out == null");
- }
- mOut = pOut;
- }
-
- /**
- * Returns the wrapped {@code OutputStream}.
- *
- * @return the wrapped {@code OutputStream}.
- */
- public OutputStream getOutputStream() {
- return mOut;
- }
-
- public String toString() {
- return "ServletOutputStream adapted from " + mOut.toString();
- }
-
- /**
- * Writes a byte to the underlying stream.
- *
- * @param pByte the byte to write.
- *
- * @throws IOException if an error occurs during writing
- */
- public void write(int pByte)
- throws IOException {
- mOut.write(pByte);
- }
-
- // Overide for efficiency
- public void write(byte pBytes[])
- throws IOException {
- mOut.write(pBytes);
- }
-
- // Overide for efficiency
- public void write(byte pBytes[], int pOff, int pLen)
- throws IOException {
- mOut.write(pBytes, pOff, pLen);
- }
-}
+/*
+ * Copyright (c) 2008, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name "TwelveMonkeys" nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.twelvemonkeys.servlet;
+
+import javax.servlet.ServletOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * A {@code ServletOutputStream} implementation backed by a
+ * {@link java.io.OutputStream}. For filters that need to buffer the
+ * response and do post filtering, it may be used like this:
+ * ByteArrayOutputStream buffer = new ByteArraOutputStream();
+ * ServletOutputStream adapter = new OutputStreamAdapter(buffer);
+ *
+ *
+ * As a {@code ServletOutputStream} is itself an {@code OutputStream}, this
+ * class may also be used as a superclass for wrappers of other
+ * {@code ServletOutputStream}s, like this:
+ * class FilterServletOutputStream extends OutputStreamAdapter {
+ * public FilterServletOutputStream(ServletOutputStream out) {
+ * super(out);
+ * }
+ *
+ * public void write(int abyte) {
+ * // do filtering...
+ * super.write(...);
+ * }
+ * }
+ *
+ * ...
+ *
+ * ServletOutputStream original = response.getOutputStream();
+ * ServletOutputStream wrapper = new FilterServletOutputStream(original);
+ *
+ * @author Harald Kuhr
+ * @author $Author: haku $
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/OutputStreamAdapter.java#1 $
+ *
+ */
+public class OutputStreamAdapter extends ServletOutputStream {
+
+ /** The wrapped {@code OutputStream}. */
+ protected final OutputStream mOut;
+
+ /**
+ * Creates an {@code OutputStreamAdapter}.
+ *
+ * @param pOut the wrapped {@code OutputStream}
+ *
+ * @throws IllegalArgumentException if {@code pOut} is {@code null}.
+ */
+ public OutputStreamAdapter(OutputStream pOut) {
+ if (pOut == null) {
+ throw new IllegalArgumentException("out == null");
+ }
+ mOut = pOut;
+ }
+
+ /**
+ * Returns the wrapped {@code OutputStream}.
+ *
+ * @return the wrapped {@code OutputStream}.
+ */
+ public OutputStream getOutputStream() {
+ return mOut;
+ }
+
+ public String toString() {
+ return "ServletOutputStream adapted from " + mOut.toString();
+ }
+
+ /**
+ * Writes a byte to the underlying stream.
+ *
+ * @param pByte the byte to write.
+ *
+ * @throws IOException if an error occurs during writing
+ */
+ public void write(int pByte)
+ throws IOException {
+ mOut.write(pByte);
+ }
+
+ // Overide for efficiency
+ public void write(byte pBytes[])
+ throws IOException {
+ mOut.write(pBytes);
+ }
+
+ // Overide for efficiency
+ public void write(byte pBytes[], int pOff, int pLen)
+ throws IOException {
+ mOut.write(pBytes, pOff, pLen);
+ }
+}
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/ProxyServlet.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/ProxyServlet.java
index 898e7222..6f47968e 100755
--- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/ProxyServlet.java
+++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/ProxyServlet.java
@@ -1,435 +1,435 @@
-/*
- * Copyright (c) 2008, Harald Kuhr
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name "TwelveMonkeys" nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package com.twelvemonkeys.servlet;
-
-import com.twelvemonkeys.io.FileUtil;
-import com.twelvemonkeys.lang.StringUtil;
-
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.ConnectException;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.util.Enumeration;
-
-/**
- * A simple proxy servlet implementation. Supports HTTP and HTTPS.
- *
- * Note: The servlet is not a true HTTP proxy as described in
- * RFC 2616,
- * instead it passes on all incoming HTTP requests to the configured remote
- * server.
- * Useful for bypassing firewalls or to avoid exposing internal network
- * infrastructure to external clients.
- *
- * At the moment, no caching of content is implemented.
- *
- * 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.
- * If the configured remote server is down, or unreachable, the servlet will
- * respond by sending a {@code 502 Bad Gateway} response to the client.
- * Otherwise, the response from the remote server will be tunneled unmodified
- * to the client.
- *
- * @author Harald Kuhr
- * @author last modified by $Author: haku $
- *
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/ProxyServlet.java#1 $
- */
-public class ProxyServlet extends GenericServlet {
-
- /** Remote server host name or IP address */
- protected String mRemoteServer = null;
- /** Remote server port */
- protected int mRemotePort = 80;
- /** Remote server "mount" path */
- protected String mRemotePath = "";
-
- private static final String HTTP_REQUEST_HEADER_HOST = "host";
- private static final String HTTP_RESPONSE_HEADER_SERVER = "server";
- 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
- * name or IP address. No default.
- *
- * @param pRemoteServer
- */
- public void setRemoteServer(String pRemoteServer) {
- mRemoteServer = pRemoteServer;
- }
-
- /**
- * Called by {@code init} to set the remote port. Must be a number.
- * Default is {@code 80}.
- *
- * @param pRemotePort
- */
- public void setRemotePort(String pRemotePort) {
- try {
- mRemotePort = Integer.parseInt(pRemotePort);
- }
- catch (NumberFormatException e) {
- log("RemotePort must be a number!", e);
- }
- }
-
- /**
- * 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.
- * Default is {@code ""}.
- *
- * @param pRemotePath
- */
- public void setRemotePath(String pRemotePath) {
- if (StringUtil.isEmpty(pRemotePath)) {
- pRemotePath = "";
- }
- else if (pRemotePath.charAt(0) != '/') {
- pRemotePath = "/" + pRemotePath;
- }
-
- mRemotePath = pRemotePath;
- }
-
- /**
- * Override {@code service} to use HTTP specifics.
- *
- * @param pRequest
- * @param pResponse
- *
- * @throws ServletException
- * @throws IOException
- *
- * @see #service(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
- */
- public final void service(ServletRequest pRequest, ServletResponse pResponse) throws ServletException, IOException {
- service((HttpServletRequest) pRequest, (HttpServletResponse) pResponse);
- }
-
- /**
- * Services a single request.
- * Supports HTTP and HTTPS.
- *
- * @param pRequest
- * @param pResponse
- *
- * @throws ServletException
- * @throws IOException
- *
- * @see ProxyServlet Class descrition
- */
- protected void service(HttpServletRequest pRequest, HttpServletResponse pResponse) throws ServletException, IOException {
- // Sanity check configuration
- if (mRemoteServer == null) {
- log(MESSAGE_REMOTE_SERVER_NOT_CONFIGURED);
- pResponse.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
- MESSAGE_REMOTE_SERVER_NOT_CONFIGURED);
- return;
- }
-
- HttpURLConnection remoteConnection = null;
- try {
- // Recreate request URI for remote request
- String requestURI = createRemoteRequestURI(pRequest);
- URL remoteURL = new URL(pRequest.getScheme(), mRemoteServer, mRemotePort, requestURI);
-
- // Get connection, with method from original request
- // NOTE: The actual connection is not done before we ask for streams...
- // NOTE: The HttpURLConnection is supposed to handle multiple
- // requests to the same server internally
- String method = pRequest.getMethod();
- remoteConnection = (HttpURLConnection) remoteURL.openConnection();
- remoteConnection.setRequestMethod(method);
-
- // Copy header fields
- copyHeadersFromClient(pRequest, remoteConnection);
-
- // Do proxy specifc stuff?
- // 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
- // persistent connection with an HTTP/1.0 client"
-
- // Copy message body from client to remote server
- copyBodyFromClient(pRequest, remoteConnection);
-
- // Set response status code from remote server to client
- int responseCode = remoteConnection.getResponseCode();
- pResponse.setStatus(responseCode);
- //System.out.println("Response is: " + responseCode + " " + remoteConnection.getResponseMessage());
-
- // Copy header fields back
- copyHeadersToClient(remoteConnection, pResponse);
-
- // More proxy specific stuff?
-
- // Copy message body from remote server to client
- copyBodyToClient(remoteConnection, pResponse);
- }
- catch (ConnectException e) {
- // In case we could not connecto to the remote server
- log("Could not connect to remote server.", e);
- pResponse.sendError(HttpServletResponse.SC_BAD_GATEWAY, e.getMessage());
- }
- finally {
- // Disconnect from server
- // TODO: Should we actually do this?
- if (remoteConnection != null) {
- remoteConnection.disconnect();
- }
- }
- }
-
- /**
- * Copies the message body from the remote server to the client (outgoing
- * {@code HttpServletResponse}).
- *
- * @param pRemoteConnection
- * @param pResponse
- *
- * @throws IOException
- */
- private void copyBodyToClient(HttpURLConnection pRemoteConnection, HttpServletResponse pResponse) throws IOException {
- InputStream fromRemote = null;
- OutputStream toClient = null;
-
- try {
- // Get either input or error stream
- try {
- fromRemote = pRemoteConnection.getInputStream();
- }
- catch (IOException e) {
- // If exception, use errorStream instead
- fromRemote = pRemoteConnection.getErrorStream();
- }
-
- // I guess the stream might be null if there is no response other
- // than headers (Continue, No Content, etc).
- if (fromRemote != null) {
- toClient = pResponse.getOutputStream();
- FileUtil.copy(fromRemote, toClient);
- }
- }
- finally {
- if (fromRemote != null) {
- try {
- fromRemote.close();
- }
- catch (IOException e) {
- log("Stream from remote could not be closed.", e);
- }
- }
- if (toClient != null) {
- try {
- toClient.close();
- }
- catch (IOException e) {
- log("Stream to client could not be closed.", e);
- }
- }
- }
- }
-
- /**
- * Copies the message body from the client (incomming
- * {@code HttpServletRequest}) to the remote server if the request method
- * is {@code POST} or PUT.
- * Otherwise this method does nothing.
- *
- * @param pRequest
- * @param pRemoteConnection
- *
- * @throws java.io.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 (!("POST".equals(pRequest.getMethod()) || "PUT".equals(pRequest.getMethod()))) {
- return;
- }
-
- // NOTE: Setting doOutput to true, will make it a POST request (why?)...
- pRemoteConnection.setDoOutput(true);
-
- // Get streams and do the copying
- InputStream fromClient = null;
- OutputStream toRemote = null;
- try {
- fromClient = pRequest.getInputStream();
- toRemote = pRemoteConnection.getOutputStream();
- FileUtil.copy(fromClient, toRemote);
- }
- finally {
- if (fromClient != null) {
- try {
- fromClient.close();
- }
- catch (IOException e) {
- log("Stream from client could not be closed.", e);
- }
- }
- if (toRemote != null) {
- try {
- toRemote.close();
- }
- catch (IOException e) {
- log("Stream to remote could not be closed.", e);
- }
- }
- }
- }
-
- /**
- * Creates the remote request URI based on the incoming request.
- * The URI will include any query strings etc.
- *
- * @param pRequest
- *
- * @return a {@code String} representing the remote request URI
- */
- private String createRemoteRequestURI(HttpServletRequest pRequest) {
- StringBuilder requestURI = new StringBuilder(mRemotePath);
- requestURI.append(pRequest.getPathInfo());
-
- if (!StringUtil.isEmpty(pRequest.getQueryString())) {
- requestURI.append("?");
- requestURI.append(pRequest.getQueryString());
- }
-
- return requestURI.toString();
- }
-
- /**
- * Copies headers from the remote connection back to the client
- * (the outgoing HttpServletResponse). All headers except the "Server"
- * header are copied.
- *
- * @param pRemoteConnection
- * @param pResponse
- */
- private void copyHeadersToClient(HttpURLConnection pRemoteConnection, HttpServletResponse pResponse) {
- // NOTE: There is no getHeaderFieldCount method or similar...
- // Also, the getHeaderFields() method was introduced in J2SE 1.4, and
- // we want to be 1.2 compatible.
- // So, just try to loop until there are no more headers.
- int i = 0;
- while (true) {
- String key = pRemoteConnection.getHeaderFieldKey(i);
- // NOTE: getHeaderField(String) returns only the last value
- String value = pRemoteConnection.getHeaderField(i);
-
- // If the key is not null, life is simple, and Sun is shining
- // However, the default implementations includes the HTTP response
- // code ("HTTP/1.1 200 Ok" or similar) as a header field with
- // key "null" (why..?)...
- // In addition, we want to skip the original "Server" header
- if (key != null && !HTTP_RESPONSE_HEADER_SERVER.equalsIgnoreCase(key)) {
- //System.out.println("client <<<-- remote: " + key + ": " + value);
- pResponse.setHeader(key, value);
- }
- else if (value == null) {
- // If BOTH key and value is null, there are no more header fields
- break;
- }
-
- i++;
- }
-
- /* 1.4+ version below....
- Map headers = pRemoteConnection.getHeaderFields();
- for (Iterator iterator = headers.entrySet().iterator(); iterator.hasNext();) {
- Map.Entry header = (Map.Entry) iterator.next();
-
- List values = (List) header.getValue();
-
- for (Iterator valueIter = values.iterator(); valueIter.hasNext();) {
- String value = (String) valueIter.next();
- String key = (String) header.getKey();
-
- // Skip the server header
- if (HTTP_RESPONSE_HEADER_SERVER.equalsIgnoreCase(key)) {
- key = null;
- }
-
- // The default implementations includes the HTTP response code
- // ("HTTP/1.1 200 Ok" or similar) as a header field with
- // key "null" (why..?)...
- if (key != null) {
- //System.out.println("client <<<-- remote: " + key + ": " + value);
- pResponse.setHeader(key, value);
- }
- }
- }
- */
- }
-
- /**
- * Copies headers from the client (the incoming {@code HttpServletRequest})
- * to the outgoing connection.
- * All headers except the "Host" header are copied.
- *
- * @param pRequest
- * @param pRemoteConnection
- */
- private void copyHeadersFromClient(HttpServletRequest pRequest, HttpURLConnection pRemoteConnection) {
- Enumeration headerNames = pRequest.getHeaderNames();
- while (headerNames.hasMoreElements()) {
- String headerName = (String) headerNames.nextElement();
- Enumeration headerValues = pRequest.getHeaders(headerName);
-
- // Skip the "host" header, as we want something else
- if (HTTP_REQUEST_HEADER_HOST.equalsIgnoreCase(headerName)) {
- // Skip this header
- headerName = null;
- }
-
- // Set the the header to the remoteConnection
- if (headerName != null) {
- // Convert from multiple line to single line, comma separated, as
- // there seems to be a shortcoming in the URLConneciton API...
- StringBuilder headerValue = new StringBuilder();
- while (headerValues.hasMoreElements()) {
- String value = (String) headerValues.nextElement();
- headerValue.append(value);
- if (headerValues.hasMoreElements()) {
- headerValue.append(", ");
- }
- }
-
- //System.out.println("client -->>> remote: " + headerName + ": " + headerValue);
- pRemoteConnection.setRequestProperty(headerName, headerValue.toString());
- }
- }
- }
-}
+/*
+ * Copyright (c) 2008, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name "TwelveMonkeys" nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.twelvemonkeys.servlet;
+
+import com.twelvemonkeys.io.FileUtil;
+import com.twelvemonkeys.lang.StringUtil;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.ConnectException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.Enumeration;
+
+/**
+ * A simple proxy servlet implementation. Supports HTTP and HTTPS.
+ *
+ * Note: The servlet is not a true HTTP proxy as described in
+ * RFC 2616,
+ * instead it passes on all incoming HTTP requests to the configured remote
+ * server.
+ * Useful for bypassing firewalls or to avoid exposing internal network
+ * infrastructure to external clients.
+ *
+ * At the moment, no caching of content is implemented.
+ *
+ * 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.
+ * If the configured remote server is down, or unreachable, the servlet will
+ * respond by sending a {@code 502 Bad Gateway} response to the client.
+ * Otherwise, the response from the remote server will be tunneled unmodified
+ * to the client.
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haku $
+ *
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/ProxyServlet.java#1 $
+ */
+public class ProxyServlet extends GenericServlet {
+
+ /** Remote server host name or IP address */
+ protected String mRemoteServer = null;
+ /** Remote server port */
+ protected int mRemotePort = 80;
+ /** Remote server "mount" path */
+ protected String mRemotePath = "";
+
+ private static final String HTTP_REQUEST_HEADER_HOST = "host";
+ private static final String HTTP_RESPONSE_HEADER_SERVER = "server";
+ 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
+ * name or IP address. No default.
+ *
+ * @param pRemoteServer
+ */
+ public void setRemoteServer(String pRemoteServer) {
+ mRemoteServer = pRemoteServer;
+ }
+
+ /**
+ * Called by {@code init} to set the remote port. Must be a number.
+ * Default is {@code 80}.
+ *
+ * @param pRemotePort
+ */
+ public void setRemotePort(String pRemotePort) {
+ try {
+ mRemotePort = Integer.parseInt(pRemotePort);
+ }
+ catch (NumberFormatException e) {
+ log("RemotePort must be a number!", e);
+ }
+ }
+
+ /**
+ * 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.
+ * Default is {@code ""}.
+ *
+ * @param pRemotePath
+ */
+ public void setRemotePath(String pRemotePath) {
+ if (StringUtil.isEmpty(pRemotePath)) {
+ pRemotePath = "";
+ }
+ else if (pRemotePath.charAt(0) != '/') {
+ pRemotePath = "/" + pRemotePath;
+ }
+
+ mRemotePath = pRemotePath;
+ }
+
+ /**
+ * Override {@code service} to use HTTP specifics.
+ *
+ * @param pRequest
+ * @param pResponse
+ *
+ * @throws ServletException
+ * @throws IOException
+ *
+ * @see #service(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
+ */
+ public final void service(ServletRequest pRequest, ServletResponse pResponse) throws ServletException, IOException {
+ service((HttpServletRequest) pRequest, (HttpServletResponse) pResponse);
+ }
+
+ /**
+ * Services a single request.
+ * Supports HTTP and HTTPS.
+ *
+ * @param pRequest
+ * @param pResponse
+ *
+ * @throws ServletException
+ * @throws IOException
+ *
+ * @see ProxyServlet Class descrition
+ */
+ protected void service(HttpServletRequest pRequest, HttpServletResponse pResponse) throws ServletException, IOException {
+ // Sanity check configuration
+ if (mRemoteServer == null) {
+ log(MESSAGE_REMOTE_SERVER_NOT_CONFIGURED);
+ pResponse.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+ MESSAGE_REMOTE_SERVER_NOT_CONFIGURED);
+ return;
+ }
+
+ HttpURLConnection remoteConnection = null;
+ try {
+ // Recreate request URI for remote request
+ String requestURI = createRemoteRequestURI(pRequest);
+ URL remoteURL = new URL(pRequest.getScheme(), mRemoteServer, mRemotePort, requestURI);
+
+ // Get connection, with method from original request
+ // NOTE: The actual connection is not done before we ask for streams...
+ // NOTE: The HttpURLConnection is supposed to handle multiple
+ // requests to the same server internally
+ String method = pRequest.getMethod();
+ remoteConnection = (HttpURLConnection) remoteURL.openConnection();
+ remoteConnection.setRequestMethod(method);
+
+ // Copy header fields
+ copyHeadersFromClient(pRequest, remoteConnection);
+
+ // Do proxy specifc stuff?
+ // 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
+ // persistent connection with an HTTP/1.0 client"
+
+ // Copy message body from client to remote server
+ copyBodyFromClient(pRequest, remoteConnection);
+
+ // Set response status code from remote server to client
+ int responseCode = remoteConnection.getResponseCode();
+ pResponse.setStatus(responseCode);
+ //System.out.println("Response is: " + responseCode + " " + remoteConnection.getResponseMessage());
+
+ // Copy header fields back
+ copyHeadersToClient(remoteConnection, pResponse);
+
+ // More proxy specific stuff?
+
+ // Copy message body from remote server to client
+ copyBodyToClient(remoteConnection, pResponse);
+ }
+ catch (ConnectException e) {
+ // In case we could not connecto to the remote server
+ log("Could not connect to remote server.", e);
+ pResponse.sendError(HttpServletResponse.SC_BAD_GATEWAY, e.getMessage());
+ }
+ finally {
+ // Disconnect from server
+ // TODO: Should we actually do this?
+ if (remoteConnection != null) {
+ remoteConnection.disconnect();
+ }
+ }
+ }
+
+ /**
+ * Copies the message body from the remote server to the client (outgoing
+ * {@code HttpServletResponse}).
+ *
+ * @param pRemoteConnection
+ * @param pResponse
+ *
+ * @throws IOException
+ */
+ private void copyBodyToClient(HttpURLConnection pRemoteConnection, HttpServletResponse pResponse) throws IOException {
+ InputStream fromRemote = null;
+ OutputStream toClient = null;
+
+ try {
+ // Get either input or error stream
+ try {
+ fromRemote = pRemoteConnection.getInputStream();
+ }
+ catch (IOException e) {
+ // If exception, use errorStream instead
+ fromRemote = pRemoteConnection.getErrorStream();
+ }
+
+ // I guess the stream might be null if there is no response other
+ // than headers (Continue, No Content, etc).
+ if (fromRemote != null) {
+ toClient = pResponse.getOutputStream();
+ FileUtil.copy(fromRemote, toClient);
+ }
+ }
+ finally {
+ if (fromRemote != null) {
+ try {
+ fromRemote.close();
+ }
+ catch (IOException e) {
+ log("Stream from remote could not be closed.", e);
+ }
+ }
+ if (toClient != null) {
+ try {
+ toClient.close();
+ }
+ catch (IOException e) {
+ log("Stream to client could not be closed.", e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Copies the message body from the client (incomming
+ * {@code HttpServletRequest}) to the remote server if the request method
+ * is {@code POST} or PUT.
+ * Otherwise this method does nothing.
+ *
+ * @param pRequest
+ * @param pRemoteConnection
+ *
+ * @throws java.io.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 (!("POST".equals(pRequest.getMethod()) || "PUT".equals(pRequest.getMethod()))) {
+ return;
+ }
+
+ // NOTE: Setting doOutput to true, will make it a POST request (why?)...
+ pRemoteConnection.setDoOutput(true);
+
+ // Get streams and do the copying
+ InputStream fromClient = null;
+ OutputStream toRemote = null;
+ try {
+ fromClient = pRequest.getInputStream();
+ toRemote = pRemoteConnection.getOutputStream();
+ FileUtil.copy(fromClient, toRemote);
+ }
+ finally {
+ if (fromClient != null) {
+ try {
+ fromClient.close();
+ }
+ catch (IOException e) {
+ log("Stream from client could not be closed.", e);
+ }
+ }
+ if (toRemote != null) {
+ try {
+ toRemote.close();
+ }
+ catch (IOException e) {
+ log("Stream to remote could not be closed.", e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Creates the remote request URI based on the incoming request.
+ * The URI will include any query strings etc.
+ *
+ * @param pRequest
+ *
+ * @return a {@code String} representing the remote request URI
+ */
+ private String createRemoteRequestURI(HttpServletRequest pRequest) {
+ StringBuilder requestURI = new StringBuilder(mRemotePath);
+ requestURI.append(pRequest.getPathInfo());
+
+ if (!StringUtil.isEmpty(pRequest.getQueryString())) {
+ requestURI.append("?");
+ requestURI.append(pRequest.getQueryString());
+ }
+
+ return requestURI.toString();
+ }
+
+ /**
+ * Copies headers from the remote connection back to the client
+ * (the outgoing HttpServletResponse). All headers except the "Server"
+ * header are copied.
+ *
+ * @param pRemoteConnection
+ * @param pResponse
+ */
+ private void copyHeadersToClient(HttpURLConnection pRemoteConnection, HttpServletResponse pResponse) {
+ // NOTE: There is no getHeaderFieldCount method or similar...
+ // Also, the getHeaderFields() method was introduced in J2SE 1.4, and
+ // we want to be 1.2 compatible.
+ // So, just try to loop until there are no more headers.
+ int i = 0;
+ while (true) {
+ String key = pRemoteConnection.getHeaderFieldKey(i);
+ // NOTE: getHeaderField(String) returns only the last value
+ String value = pRemoteConnection.getHeaderField(i);
+
+ // If the key is not null, life is simple, and Sun is shining
+ // However, the default implementations includes the HTTP response
+ // code ("HTTP/1.1 200 Ok" or similar) as a header field with
+ // key "null" (why..?)...
+ // In addition, we want to skip the original "Server" header
+ if (key != null && !HTTP_RESPONSE_HEADER_SERVER.equalsIgnoreCase(key)) {
+ //System.out.println("client <<<-- remote: " + key + ": " + value);
+ pResponse.setHeader(key, value);
+ }
+ else if (value == null) {
+ // If BOTH key and value is null, there are no more header fields
+ break;
+ }
+
+ i++;
+ }
+
+ /* 1.4+ version below....
+ Map headers = pRemoteConnection.getHeaderFields();
+ for (Iterator iterator = headers.entrySet().iterator(); iterator.hasNext();) {
+ Map.Entry header = (Map.Entry) iterator.next();
+
+ List values = (List) header.getValue();
+
+ for (Iterator valueIter = values.iterator(); valueIter.hasNext();) {
+ String value = (String) valueIter.next();
+ String key = (String) header.getKey();
+
+ // Skip the server header
+ if (HTTP_RESPONSE_HEADER_SERVER.equalsIgnoreCase(key)) {
+ key = null;
+ }
+
+ // The default implementations includes the HTTP response code
+ // ("HTTP/1.1 200 Ok" or similar) as a header field with
+ // key "null" (why..?)...
+ if (key != null) {
+ //System.out.println("client <<<-- remote: " + key + ": " + value);
+ pResponse.setHeader(key, value);
+ }
+ }
+ }
+ */
+ }
+
+ /**
+ * Copies headers from the client (the incoming {@code HttpServletRequest})
+ * to the outgoing connection.
+ * All headers except the "Host" header are copied.
+ *
+ * @param pRequest
+ * @param pRemoteConnection
+ */
+ private void copyHeadersFromClient(HttpServletRequest pRequest, HttpURLConnection pRemoteConnection) {
+ Enumeration headerNames = pRequest.getHeaderNames();
+ while (headerNames.hasMoreElements()) {
+ String headerName = (String) headerNames.nextElement();
+ Enumeration headerValues = pRequest.getHeaders(headerName);
+
+ // Skip the "host" header, as we want something else
+ if (HTTP_REQUEST_HEADER_HOST.equalsIgnoreCase(headerName)) {
+ // Skip this header
+ headerName = null;
+ }
+
+ // Set the the header to the remoteConnection
+ if (headerName != null) {
+ // Convert from multiple line to single line, comma separated, as
+ // there seems to be a shortcoming in the URLConneciton API...
+ StringBuilder headerValue = new StringBuilder();
+ while (headerValues.hasMoreElements()) {
+ String value = (String) headerValues.nextElement();
+ headerValue.append(value);
+ if (headerValues.hasMoreElements()) {
+ headerValue.append(", ");
+ }
+ }
+
+ //System.out.println("client -->>> remote: " + headerName + ": " + headerValue);
+ pRemoteConnection.setRequestProperty(headerName, headerValue.toString());
+ }
+ }
+ }
+}
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/ServletConfigException.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/ServletConfigException.java
index 3d189490..29ea534b 100755
--- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/ServletConfigException.java
+++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/ServletConfigException.java
@@ -1,92 +1,92 @@
-/*
- * Copyright (c) 2008, Harald Kuhr
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name "TwelveMonkeys" nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package com.twelvemonkeys.servlet;
-
-import javax.servlet.ServletException;
-
-/**
- * ServletConfigException.
- *
- * @author Harald Kuhr
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/ServletConfigException.java#2 $
- */
-public class ServletConfigException extends ServletException {
-
- /**
- * Creates a {@code ServletConfigException} with the given message.
- *
- * @param pMessage the exception message
- */
- public ServletConfigException(String pMessage) {
- super(pMessage);
- }
-
- /**
- * Creates a {@code ServletConfigException} with the given message and cause.
- *
- * @param pMessage the exception message
- * @param pCause the exception cause
- */
- public ServletConfigException(String pMessage, Throwable pCause) {
- super(pMessage, pCause);
- if (getCause() == null) {
- initCause(pCause);
- }
- }
-
- /**
- * Creates a {@code ServletConfigException} with the cause.
- *
- * @param pCause the exception cause
- */
- public ServletConfigException(Throwable pCause) {
- super("Erorr in Servlet configuration: " + pCause.getMessage(), pCause);
- if (getCause() == null) {
- initCause(pCause);
- }
- }
-
- /**
- * Gets the cause of this {@code ServletConfigException}.
- *
- * @return the cause, or {@code null} if unknown.
- * @see #getRootCause()
- */
-// public final Throwable getCause() {
-// Throwable cause = super.getCause();
-// return cause != null ? cause : super.getRootCause();
-// }
-
- /**
- * @deprecated Use {@link #getCause()} instead.
- */
-// public final Throwable getRootCause() {
-// return getCause();
-// }
-}
+/*
+ * Copyright (c) 2008, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name "TwelveMonkeys" nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.twelvemonkeys.servlet;
+
+import javax.servlet.ServletException;
+
+/**
+ * ServletConfigException.
+ *
+ * @author Harald Kuhr
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/ServletConfigException.java#2 $
+ */
+public class ServletConfigException extends ServletException {
+
+ /**
+ * Creates a {@code ServletConfigException} with the given message.
+ *
+ * @param pMessage the exception message
+ */
+ public ServletConfigException(String pMessage) {
+ super(pMessage);
+ }
+
+ /**
+ * Creates a {@code ServletConfigException} with the given message and cause.
+ *
+ * @param pMessage the exception message
+ * @param pCause the exception cause
+ */
+ public ServletConfigException(String pMessage, Throwable pCause) {
+ super(pMessage, pCause);
+ if (getCause() == null) {
+ initCause(pCause);
+ }
+ }
+
+ /**
+ * Creates a {@code ServletConfigException} with the cause.
+ *
+ * @param pCause the exception cause
+ */
+ public ServletConfigException(Throwable pCause) {
+ super("Erorr in Servlet configuration: " + pCause.getMessage(), pCause);
+ if (getCause() == null) {
+ initCause(pCause);
+ }
+ }
+
+ /**
+ * Gets the cause of this {@code ServletConfigException}.
+ *
+ * @return the cause, or {@code null} if unknown.
+ * @see #getRootCause()
+ */
+// public final Throwable getCause() {
+// Throwable cause = super.getCause();
+// return cause != null ? cause : super.getRootCause();
+// }
+
+ /**
+ * @deprecated Use {@link #getCause()} instead.
+ */
+// public final Throwable getRootCause() {
+// return getCause();
+// }
+}
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/ServletConfigMapAdapter.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/ServletConfigMapAdapter.java
index d01d611a..db93a47f 100755
--- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/ServletConfigMapAdapter.java
+++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/ServletConfigMapAdapter.java
@@ -1,284 +1,284 @@
-/*
- * Copyright (c) 2008, Harald Kuhr
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name "TwelveMonkeys" nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package com.twelvemonkeys.servlet;
-
-import com.twelvemonkeys.lang.StringUtil;
-
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletConfig;
-import javax.servlet.ServletContext;
-import java.io.Serializable;
-import java.util.*;
-
-/**
- * {@code ServletConfig} or {@code FilterConfig} adapter, that implements
- * the {@code Map} interface for interoperability with collection-based API's.
- *
- * This {@code Map} is not synchronized.
- *
- *
- * @author Harald Kuhr
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/ServletConfigMapAdapter.java#2 $
- */
-class ServletConfigMapAdapter extends AbstractMap
- * requestURI = "/webapp/index.jsp"
- * contextPath = "/webapp"
- *
- * The method will return {@code "/index.jsp"}.
- *
- * @param pRequest the current HTTP request
- * @return the request URI relative to the current context path.
- */
- public static String getContextRelativeURI(HttpServletRequest pRequest) {
- String context = pRequest.getContextPath();
- if (!StringUtil.isEmpty(context)) { // "" for root context
- return pRequest.getRequestURI().substring(context.length());
- }
- return pRequest.getRequestURI();
- }
-
- /**
- * Returns a {@code URL} containing the real path for a given virtual
- * path, on URL form.
- * Note that this mehtod will return {@code null} for all the same reasons
- * as {@code ServletContext.getRealPath(java.lang.String)} does.
- *
- * @param pContext the servlet context
- * @param pPath the virtual path
- * @return a {@code URL} object containing the path, or {@code null}.
- * @throws MalformedURLException if the path refers to a malformed URL
- * @see ServletContext#getRealPath(java.lang.String)
- * @see ServletContext#getResource(java.lang.String)
- */
- public static URL getRealURL(ServletContext pContext, String pPath) throws MalformedURLException {
- String realPath = pContext.getRealPath(pPath);
- if (realPath != null) {
- // NOTE: First convert to URI, as of Java 6 File.toURL is deprecated
- return new File(realPath).toURI().toURL();
- }
- return null;
- }
-
- /**
- * Gets the temp directory for the given {@code ServletContext} (webapp).
- *
- * @param pContext the servlet context
- * @return the temp directory
- */
- public static File getTempDir(ServletContext pContext) {
- return (File) pContext.getAttribute("javax.servlet.context.tempdir");
- }
-
- /**
- * Gets the identificator string containing the unique identifier assigned
- * to this session.
- * The identifier is assigned by the servlet container and is implementation
- * dependent.
- *
- * @param pRequest The HTTP servlet request object.
- * @return the session Id
- */
- public static String getSessionId(HttpServletRequest pRequest) {
- HttpSession session = pRequest.getSession();
-
- return (session != null) ? session.getId() : null;
- }
-
- /**
- * Creates an unmodifiable {@code Map} view of the given
- * {@code ServletConfig}s init-parameters.
- * Note: The returned {@code Map} is optimized for {@code get}
- * operations and iterating over it's {@code keySet}.
- * For other operations it may not perform well.
- *
- * @param pConfig the serlvet configuration
- * @return a {@code Map} view of the config
- * @throws IllegalArgumentException if {@code pConfig} is {@code null}
- */
- public static Map
+ * requestURI = "/webapp/index.jsp"
+ * contextPath = "/webapp"
+ *
+ * The method will return {@code "/index.jsp"}.
+ *
+ * @param pRequest the current HTTP request
+ * @return the request URI relative to the current context path.
+ */
+ public static String getContextRelativeURI(HttpServletRequest pRequest) {
+ String context = pRequest.getContextPath();
+ if (!StringUtil.isEmpty(context)) { // "" for root context
+ return pRequest.getRequestURI().substring(context.length());
+ }
+ return pRequest.getRequestURI();
+ }
+
+ /**
+ * Returns a {@code URL} containing the real path for a given virtual
+ * path, on URL form.
+ * Note that this mehtod will return {@code null} for all the same reasons
+ * as {@code ServletContext.getRealPath(java.lang.String)} does.
+ *
+ * @param pContext the servlet context
+ * @param pPath the virtual path
+ * @return a {@code URL} object containing the path, or {@code null}.
+ * @throws MalformedURLException if the path refers to a malformed URL
+ * @see ServletContext#getRealPath(java.lang.String)
+ * @see ServletContext#getResource(java.lang.String)
+ */
+ public static URL getRealURL(ServletContext pContext, String pPath) throws MalformedURLException {
+ String realPath = pContext.getRealPath(pPath);
+ if (realPath != null) {
+ // NOTE: First convert to URI, as of Java 6 File.toURL is deprecated
+ return new File(realPath).toURI().toURL();
+ }
+ return null;
+ }
+
+ /**
+ * Gets the temp directory for the given {@code ServletContext} (webapp).
+ *
+ * @param pContext the servlet context
+ * @return the temp directory
+ */
+ public static File getTempDir(ServletContext pContext) {
+ return (File) pContext.getAttribute("javax.servlet.context.tempdir");
+ }
+
+ /**
+ * Gets the identificator string containing the unique identifier assigned
+ * to this session.
+ * The identifier is assigned by the servlet container and is implementation
+ * dependent.
+ *
+ * @param pRequest The HTTP servlet request object.
+ * @return the session Id
+ */
+ public static String getSessionId(HttpServletRequest pRequest) {
+ HttpSession session = pRequest.getSession();
+
+ return (session != null) ? session.getId() : null;
+ }
+
+ /**
+ * Creates an unmodifiable {@code Map} view of the given
+ * {@code ServletConfig}s init-parameters.
+ * Note: The returned {@code Map} is optimized for {@code get}
+ * operations and iterating over it's {@code keySet}.
+ * For other operations it may not perform well.
+ *
+ * @param pConfig the serlvet configuration
+ * @return a {@code Map} view of the config
+ * @throws IllegalArgumentException if {@code pConfig} is {@code null}
+ */
+ public static Map
- * The format is {@code <mime-type>=<filename>,
- * <mime-type>=<filename>}.
- *
- * Example: {@code <text/vnd.wap.wmlgt;=</errors/503.wml>,
- * <text/html>=</errors/503.html>}
- *
- * @param pResponseMessages
- */
- public void setResponseMessages(String pResponseMessages) {
- // Split string in type=filename pairs
- String[] mappings = StringUtil.toStringArray(pResponseMessages, ", \r\n\t");
- List types = new ArrayList();
-
- for (int i = 0; i < mappings.length; i++) {
- // Split pairs on '='
- String[] mapping = StringUtil.toStringArray(mappings[i], "= ");
-
- // Test for wrong mapping
- if ((mapping == null) || (mapping.length < 2)) {
- log("Error in init param \"responseMessages\": " + pResponseMessages);
- continue;
- }
- types.add(mapping[0]);
- mResponseMessageNames.put(mapping[0], mapping[1]);
- }
-
- // Create arrays
- mResponseMessageTypes = (String[]) types.toArray(new String[types.size()]);
- }
-
- /**
- * @param pRequest
- * @param pResponse
- * @param pChain
- * @throws IOException
- * @throws ServletException
- */
- protected void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain)
- throws IOException, ServletException {
- try {
- if (beginRequest()) {
- // Continue request
- pChain.doFilter(pRequest, pResponse);
- }
- else {
- // Send error and end request
- // Get HTTP specific versions
- HttpServletRequest request = (HttpServletRequest) pRequest;
- HttpServletResponse response = (HttpServletResponse) pResponse;
-
- // Get content type
- String contentType = getContentType(request);
-
- // Note: This is not the way the spec says you should do it.
- // However, we handle error response this way for preformace reasons.
- // The "correct" way would be to use sendError() and register a servlet
- // that does the content negotiation as errorpage in the web descriptor.
- response.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
- response.setContentType(contentType);
- response.getWriter().println(getMessage(contentType));
-
- // Log warning, as this shouldn't happen too often
- log("Request denied, no more available threads for requestURI=" + request.getRequestURI());
- }
- }
- finally {
- doneRequest();
- }
- }
-
- /**
- * Marks the beginning of a request
- *
- * @return true
if the request should be handled.
- */
- private boolean beginRequest() {
- synchronized (mRunningThreadsLock) {
- mRunningThreads++;
- }
- return (mRunningThreads <= mMaxConcurrentThreadCount);
- }
-
- /**
- * Marks the end of the request
- */
- private void doneRequest() {
- synchronized (mRunningThreadsLock) {
- mRunningThreads--;
- }
- }
-
- /**
- * Gets the content type for the response, suitable for the requesting user agent.
- *
- * @param pRequest
- * @return the content type
- */
- private String getContentType(HttpServletRequest pRequest) {
- if (mResponseMessageTypes != null) {
- String accept = pRequest.getHeader("Accept");
-
- for (int i = 0; i < mResponseMessageTypes.length; i++) {
- String type = mResponseMessageTypes[i];
-
- // Note: This is not 100% correct way of doing content negotiation
- // But we just want a compatible result, quick, so this is okay
- if (StringUtil.contains(accept, type)) {
- return type;
- }
- }
- }
-
- // If none found, return default
- return DEFAULT_TYPE;
- }
-
- /**
- * Gets the response message for the given content type.
- *
- * @param pContentType
- * @return the message
- */
- private String getMessage(String pContentType) {
-
- String fileName = (String) mResponseMessageNames.get(pContentType);
-
- // Get cached value
- CacheEntry entry = (CacheEntry) mResponseCache.get(fileName);
-
- if ((entry == null) || entry.isExpired()) {
-
- // Create and add or replace cached value
- entry = new CacheEntry(readMessage(fileName));
- mResponseCache.put(fileName, entry);
- }
-
- // Return value
- return (entry.getValue() != null)
- ? (String) entry.getValue()
- : DEFUALT_RESPONSE_MESSAGE;
- }
-
- /**
- * Reads the response message from a file in the current web app.
- *
- * @param pFileName
- * @return the message
- */
- private String readMessage(String pFileName) {
- try {
- // Read resource from web app
- InputStream is = getServletContext().getResourceAsStream(pFileName);
-
- if (is != null) {
- return new String(FileUtil.read(is));
- }
- else {
- log("File not found: " + pFileName);
- }
- }
- catch (IOException ioe) {
- log("Error reading file: " + pFileName + " (" + ioe.getMessage() + ")");
- }
- return null;
- }
-
- /**
- * Keeps track of Cached objects
- */
- private static class CacheEntry {
- private Object mValue;
- private long mTimestamp = -1;
-
- CacheEntry(Object pValue) {
- mValue = pValue;
- mTimestamp = System.currentTimeMillis();
- }
-
- Object getValue() {
- return mValue;
- }
-
- boolean isExpired() {
- return (System.currentTimeMillis() - mTimestamp) > 60000; // Cache 1 minute
- }
- }
+/*
+ * Copyright (c) 2008, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name "TwelveMonkeys" nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.twelvemonkeys.servlet;
+
+import com.twelvemonkeys.io.FileUtil;
+import com.twelvemonkeys.lang.StringUtil;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * ThrottleFilter, a filter for easing server during heavy load.
+ *
+ * Intercepts requests, and returns HTTP response code 503
+ * (Service Unavailable), if there are more than a given number of concurrent
+ * requests, to avoid large backlogs. The number of concurrent requests and the
+ * response messages sent to the user agent, is configurable from the web
+ * descriptor.
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haku $
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/ThrottleFilter.java#1 $
+ * @see #setMaxConcurrentThreadCount
+ * @see #setResponseMessages
+ */
+public class ThrottleFilter extends GenericFilter {
+
+ /**
+ * Minimum free thread count, defaults to {@code 10}
+ */
+ protected int mMaxConcurrentThreadCount = 10;
+
+ /**
+ * The number of running request threads
+ */
+ private int mRunningThreads = 0;
+ private final Object mRunningThreadsLock = new Object();
+
+ /**
+ * Default response message sent to user agents, if the request is rejected
+ */
+ protected final static String DEFUALT_RESPONSE_MESSAGE =
+ "Service temporarily unavailable, please try again later.";
+
+ /**
+ * Default response content type
+ */
+ protected static final String DEFAULT_TYPE = "text/html";
+
+ /**
+ * The reposne message sent to user agenta, if the request is rejected
+ */
+ private Map mResponseMessageNames = new HashMap(10);
+
+ /**
+ * The reposne message sent to user agents, if the request is rejected
+ */
+ private String[] mResponseMessageTypes = null;
+
+ /**
+ * Cache for response messages
+ */
+ private Map mResponseCache = new HashMap(10);
+
+
+ /**
+ * Sets the minimum free thread count.
+ *
+ * @param pMaxConcurrentThreadCount
+ */
+ public void setMaxConcurrentThreadCount(String pMaxConcurrentThreadCount) {
+ if (!StringUtil.isEmpty(pMaxConcurrentThreadCount)) {
+ try {
+ mMaxConcurrentThreadCount = Integer.parseInt(pMaxConcurrentThreadCount);
+ }
+ catch (NumberFormatException nfe) {
+ // Use default
+ }
+ }
+ }
+
+ /**
+ * Sets the response message sent to the user agent, if the request is
+ * rejected.
+ *
+ * The format is {@code <mime-type>=<filename>,
+ * <mime-type>=<filename>}.
+ *
+ * Example: {@code <text/vnd.wap.wmlgt;=</errors/503.wml>,
+ * <text/html>=</errors/503.html>}
+ *
+ * @param pResponseMessages
+ */
+ public void setResponseMessages(String pResponseMessages) {
+ // Split string in type=filename pairs
+ String[] mappings = StringUtil.toStringArray(pResponseMessages, ", \r\n\t");
+ List types = new ArrayList();
+
+ for (int i = 0; i < mappings.length; i++) {
+ // Split pairs on '='
+ String[] mapping = StringUtil.toStringArray(mappings[i], "= ");
+
+ // Test for wrong mapping
+ if ((mapping == null) || (mapping.length < 2)) {
+ log("Error in init param \"responseMessages\": " + pResponseMessages);
+ continue;
+ }
+ types.add(mapping[0]);
+ mResponseMessageNames.put(mapping[0], mapping[1]);
+ }
+
+ // Create arrays
+ mResponseMessageTypes = (String[]) types.toArray(new String[types.size()]);
+ }
+
+ /**
+ * @param pRequest
+ * @param pResponse
+ * @param pChain
+ * @throws IOException
+ * @throws ServletException
+ */
+ protected void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain)
+ throws IOException, ServletException {
+ try {
+ if (beginRequest()) {
+ // Continue request
+ pChain.doFilter(pRequest, pResponse);
+ }
+ else {
+ // Send error and end request
+ // Get HTTP specific versions
+ HttpServletRequest request = (HttpServletRequest) pRequest;
+ HttpServletResponse response = (HttpServletResponse) pResponse;
+
+ // Get content type
+ String contentType = getContentType(request);
+
+ // Note: This is not the way the spec says you should do it.
+ // However, we handle error response this way for preformace reasons.
+ // The "correct" way would be to use sendError() and register a servlet
+ // that does the content negotiation as errorpage in the web descriptor.
+ response.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
+ response.setContentType(contentType);
+ response.getWriter().println(getMessage(contentType));
+
+ // Log warning, as this shouldn't happen too often
+ log("Request denied, no more available threads for requestURI=" + request.getRequestURI());
+ }
+ }
+ finally {
+ doneRequest();
+ }
+ }
+
+ /**
+ * Marks the beginning of a request
+ *
+ * @return true
if the request should be handled.
+ */
+ private boolean beginRequest() {
+ synchronized (mRunningThreadsLock) {
+ mRunningThreads++;
+ }
+ return (mRunningThreads <= mMaxConcurrentThreadCount);
+ }
+
+ /**
+ * Marks the end of the request
+ */
+ private void doneRequest() {
+ synchronized (mRunningThreadsLock) {
+ mRunningThreads--;
+ }
+ }
+
+ /**
+ * Gets the content type for the response, suitable for the requesting user agent.
+ *
+ * @param pRequest
+ * @return the content type
+ */
+ private String getContentType(HttpServletRequest pRequest) {
+ if (mResponseMessageTypes != null) {
+ String accept = pRequest.getHeader("Accept");
+
+ for (int i = 0; i < mResponseMessageTypes.length; i++) {
+ String type = mResponseMessageTypes[i];
+
+ // Note: This is not 100% correct way of doing content negotiation
+ // But we just want a compatible result, quick, so this is okay
+ if (StringUtil.contains(accept, type)) {
+ return type;
+ }
+ }
+ }
+
+ // If none found, return default
+ return DEFAULT_TYPE;
+ }
+
+ /**
+ * Gets the response message for the given content type.
+ *
+ * @param pContentType
+ * @return the message
+ */
+ private String getMessage(String pContentType) {
+
+ String fileName = (String) mResponseMessageNames.get(pContentType);
+
+ // Get cached value
+ CacheEntry entry = (CacheEntry) mResponseCache.get(fileName);
+
+ if ((entry == null) || entry.isExpired()) {
+
+ // Create and add or replace cached value
+ entry = new CacheEntry(readMessage(fileName));
+ mResponseCache.put(fileName, entry);
+ }
+
+ // Return value
+ return (entry.getValue() != null)
+ ? (String) entry.getValue()
+ : DEFUALT_RESPONSE_MESSAGE;
+ }
+
+ /**
+ * Reads the response message from a file in the current web app.
+ *
+ * @param pFileName
+ * @return the message
+ */
+ private String readMessage(String pFileName) {
+ try {
+ // Read resource from web app
+ InputStream is = getServletContext().getResourceAsStream(pFileName);
+
+ if (is != null) {
+ return new String(FileUtil.read(is));
+ }
+ else {
+ log("File not found: " + pFileName);
+ }
+ }
+ catch (IOException ioe) {
+ log("Error reading file: " + pFileName + " (" + ioe.getMessage() + ")");
+ }
+ return null;
+ }
+
+ /**
+ * Keeps track of Cached objects
+ */
+ private static class CacheEntry {
+ private Object mValue;
+ private long mTimestamp = -1;
+
+ CacheEntry(Object pValue) {
+ mValue = pValue;
+ mTimestamp = System.currentTimeMillis();
+ }
+
+ Object getValue() {
+ return mValue;
+ }
+
+ boolean isExpired() {
+ return (System.currentTimeMillis() - mTimestamp) > 60000; // Cache 1 minute
+ }
+ }
}
\ No newline at end of file
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/TimingFilter.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/TimingFilter.java
index cad2bdf3..586b1f82 100755
--- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/TimingFilter.java
+++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/TimingFilter.java
@@ -1,113 +1,113 @@
-/*
- * Copyright (c) 2008, Harald Kuhr
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name "TwelveMonkeys" nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package com.twelvemonkeys.servlet;
-
-import javax.servlet.FilterChain;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import java.io.IOException;
-
-/**
- * TimingFilter class description.
- *
- * @author Harald Kuhr
- * @author last modified by $Author: haku $
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/TimingFilter.java#1 $
- */
-public class TimingFilter extends GenericFilter {
-
- private String mAttribUsage = null;
-
- /**
- * Method init
- *
- * @throws ServletException
- */
- public void init() throws ServletException {
- mAttribUsage = getFilterName() + ".timerDelta";
- }
-
- /**
- *
- * @param pRequest
- * @param pResponse
- * @param pChain
- * @throws IOException
- * @throws ServletException
- */
- protected void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain)
- throws IOException, ServletException {
- // Get total usage of earlier filters on same level
- Object usageAttrib = pRequest.getAttribute(mAttribUsage);
- long total = 0;
-
- if (usageAttrib instanceof Long) {
- // If set, get value, and remove attribute for nested resources
- total = ((Long) usageAttrib).longValue();
- pRequest.removeAttribute(mAttribUsage);
- }
-
- // Start timing
- long start = System.currentTimeMillis();
-
- try {
- // Continue chain
- pChain.doFilter(pRequest, pResponse);
- }
- finally {
- // Stop timing
- long end = System.currentTimeMillis();
-
- // Get time usage of included resources, add to total usage
- usageAttrib = pRequest.getAttribute(mAttribUsage);
- long usage = 0;
- if (usageAttrib instanceof Long) {
- usage = ((Long) usageAttrib).longValue();
- }
-
- // Get the name of the included resource
- String resourceURI = ServletUtil.getIncludeRequestURI(pRequest);
-
- // If none, this is probably the parent page itself
- if (resourceURI == null) {
- resourceURI = ((HttpServletRequest) pRequest).getRequestURI();
- }
- long delta = end - start;
-
- log("Request processing time for resource \"" + resourceURI + "\": " +
- (delta - usage) + " ms (accumulated: " + delta + " ms).");
-
- // Store total usage
- total += delta;
- pRequest.setAttribute(mAttribUsage, new Long(total));
- }
- }
+/*
+ * Copyright (c) 2008, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name "TwelveMonkeys" nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.twelvemonkeys.servlet;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+
+/**
+ * TimingFilter class description.
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haku $
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/TimingFilter.java#1 $
+ */
+public class TimingFilter extends GenericFilter {
+
+ private String mAttribUsage = null;
+
+ /**
+ * Method init
+ *
+ * @throws ServletException
+ */
+ public void init() throws ServletException {
+ mAttribUsage = getFilterName() + ".timerDelta";
+ }
+
+ /**
+ *
+ * @param pRequest
+ * @param pResponse
+ * @param pChain
+ * @throws IOException
+ * @throws ServletException
+ */
+ protected void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain)
+ throws IOException, ServletException {
+ // Get total usage of earlier filters on same level
+ Object usageAttrib = pRequest.getAttribute(mAttribUsage);
+ long total = 0;
+
+ if (usageAttrib instanceof Long) {
+ // If set, get value, and remove attribute for nested resources
+ total = ((Long) usageAttrib).longValue();
+ pRequest.removeAttribute(mAttribUsage);
+ }
+
+ // Start timing
+ long start = System.currentTimeMillis();
+
+ try {
+ // Continue chain
+ pChain.doFilter(pRequest, pResponse);
+ }
+ finally {
+ // Stop timing
+ long end = System.currentTimeMillis();
+
+ // Get time usage of included resources, add to total usage
+ usageAttrib = pRequest.getAttribute(mAttribUsage);
+ long usage = 0;
+ if (usageAttrib instanceof Long) {
+ usage = ((Long) usageAttrib).longValue();
+ }
+
+ // Get the name of the included resource
+ String resourceURI = ServletUtil.getIncludeRequestURI(pRequest);
+
+ // If none, this is probably the parent page itself
+ if (resourceURI == null) {
+ resourceURI = ((HttpServletRequest) pRequest).getRequestURI();
+ }
+ long delta = end - start;
+
+ log("Request processing time for resource \"" + resourceURI + "\": " +
+ (delta - usage) + " ms (accumulated: " + delta + " ms).");
+
+ // Store total usage
+ total += delta;
+ pRequest.setAttribute(mAttribUsage, new Long(total));
+ }
+ }
}
\ No newline at end of file
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/TrimWhiteSpaceFilter.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/TrimWhiteSpaceFilter.java
index cc9782b0..935cd31b 100755
--- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/TrimWhiteSpaceFilter.java
+++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/TrimWhiteSpaceFilter.java
@@ -1,238 +1,238 @@
-/*
- * Copyright (c) 2008, Harald Kuhr
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name "TwelveMonkeys" nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package com.twelvemonkeys.servlet;
-
-import javax.servlet.FilterChain;
-import javax.servlet.ServletException;
-import javax.servlet.ServletOutputStream;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.ServletResponseWrapper;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.OutputStream;
-import java.io.FilterOutputStream;
-
-/**
- * Removes extra unneccessary white space from a servlet response.
- * White space is defined as per {@link Character#isWhitespace(char)}.
- *
- * This filter has no understanding of the content in the reponse, and will
- * remove repeated white space anywhere in the stream. It is intended for
- * removing white space from HTML or XML streams, but this limitation makes it
- * less suited for filtering HTML/XHTML with embedded CSS or JavaScript,
- * in case white space should be significant here. It is strongly reccommended
- * you keep CSS and JavaScript in separate files (this will have the added
- * benefit of further reducing the ammount of data communicated between
- * server and client).
- *
- * At the moment this filter has no concept of encoding.
- * This means, that if some multi-byte escape sequence contains one or more
- * bytes that individually is treated as a white space, these bytes
- * may be skipped.
- * As UTF-8
- * guarantees that no bytes are repeated in this way, this filter can safely
- * filter UTF-8.
- * Simple 8 bit character encodings, like the
- * ISO/IEC 8859 standard, or
- *
- * are always safe.
- *
- * Configuration
- * To use {@code TrimWhiteSpaceFilter} in your web-application, you simply need
- * to add it to your web descriptor ({@code web.xml}).
- * If using a servlet container that supports the Servlet 2.4 spec, the new
- * {@code dispatcher} element should be used, and set to
- * {@code REQUEST/FORWARD}, to make sure the filter is invoked only once for
- * requests.
- * If using an older web descriptor, set the {@code init-param}
- * {@code "once-per-request"} to {@code "true"} (this will have the same effect,
- * but might perform slightly worse than the 2.4 version).
- * Please see the examples below.
- *
- * Servlet 2.4 version, filter section:
- *
- * <!-- TrimWS Filter Configuration -->
- * <filter>
- * <filter-name>trimws</filter-name>
- * <filter-class>com.twelvemonkeys.servlet.TrimWhiteSpaceFilter</filter-class>
- * <!-- auto-flush=true is the default, may be omitted -->
- * <init-param>
- * <param-name>auto-flush</param-name>
- * <param-value>true</param-value>
- * </init-param>
- * </filter>
- *
- * Filter-mapping section:
- *
- * <!-- TimWS Filter Mapping -->
- * <filter-mapping>
- * <filter-name>trimws</filter-name>
- * <url-pattern>*.html</url-pattern>
- * <dispatcher>REQUEST</dispatcher>
- * <dispatcher>FORWARD</dispatcher>
- * </filter-mapping>
- * <filter-mapping>
- * <filter-name>trimws</filter-name>
- * <url-pattern>*.jsp</url-pattern>
- * <dispatcher>REQUEST</dispatcher>
- * <dispatcher>FORWARD</dispatcher>
- * </filter-mapping>
- *
- *
- * @author Harald Kuhr
- * @author last modified by $Author: haku $
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/TrimWhiteSpaceFilter.java#2 $
- */
-public class TrimWhiteSpaceFilter extends GenericFilter {
-
- private boolean mAutoFlush = true;
-
- @InitParam
- public void setAutoFlush(final boolean pAutoFlush) {
- mAutoFlush = pAutoFlush;
- }
-
- public void init() throws ServletException {
- super.init();
- log("Automatic flushing is " + (mAutoFlush ? "enabled" : "disabled"));
- }
-
- protected void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain) throws IOException, ServletException {
- ServletResponseWrapper wrapped = new TrimWSServletResponseWrapper(pResponse);
- pChain.doFilter(pRequest, ServletUtil.createWrapper(wrapped));
- if (mAutoFlush) {
- wrapped.flushBuffer();
- }
- }
-
- static final class TrimWSFilterOutputStream extends FilterOutputStream {
- boolean mLastWasWS = true; // Avoids leading WS by init to true
-
- public TrimWSFilterOutputStream(OutputStream pOut) {
- super(pOut);
- }
-
- // Override this, in case the wrapped outputstream overrides...
- public final void write(byte pBytes[]) throws IOException {
- write(pBytes, 0, pBytes.length);
- }
-
- // Override this, in case the wrapped outputstream overrides...
- public final void write(byte pBytes[], int pOff, int pLen) throws IOException {
- if (pBytes == null) {
- throw new NullPointerException("bytes == null");
- }
- else if (pOff < 0 || pLen < 0 || (pOff + pLen > pBytes.length)) {
- throw new IndexOutOfBoundsException("Bytes: " + pBytes.length + " Offset: " + pOff + " Length: " + pLen);
- }
-
- for (int i = 0; i < pLen ; i++) {
- write(pBytes[pOff + i]);
- }
- }
-
- public void write(int pByte) throws IOException {
- // TODO: Is this good enough for multi-byte encodings like UTF-16?
- // Consider writing through a Writer that does that for us, and
- // also buffer whitespace, so we write a linefeed every time there's
- // one in the original...
-
- // According to http://en.wikipedia.org/wiki/UTF-8:
- // "[...] US-ASCII octet values do not appear otherwise in a UTF-8
- // encoded character stream. This provides compatibility with file
- // systems or other software (e.g., the printf() function in
- // C libraries) that parse based on US-ASCII values but are
- // transparent to other values."
-
- if (!Character.isWhitespace((char) pByte)) {
- // If char is not WS, just store
- super.write(pByte);
- mLastWasWS = false;
- }
- else {
- // TODO: Consider writing only 0x0a (LF) and 0x20 (space)
- // Else, if char is WS, store first, skip the rest
- if (!mLastWasWS) {
- if (pByte == 0x0d) { // Convert all CR/LF's to 0x0a
- super.write(0x0a);
- }
- else {
- super.write(pByte);
- }
- }
- mLastWasWS = true;
- }
- }
- }
-
- private static class TrimWSStreamDelegate extends ServletResponseStreamDelegate {
- public TrimWSStreamDelegate(ServletResponse pResponse) {
- super(pResponse);
- }
-
- protected OutputStream createOutputStream() throws IOException {
- return new TrimWSFilterOutputStream(mResponse.getOutputStream());
- }
- }
-
- static class TrimWSServletResponseWrapper extends ServletResponseWrapper {
- private final ServletResponseStreamDelegate mStreamDelegate = new TrimWSStreamDelegate(getResponse());
-
- public TrimWSServletResponseWrapper(ServletResponse pResponse) {
- super(pResponse);
- }
-
- public ServletOutputStream getOutputStream() throws IOException {
- return mStreamDelegate.getOutputStream();
- }
-
- public PrintWriter getWriter() throws IOException {
- return mStreamDelegate.getWriter();
- }
-
- public void setContentLength(int pLength) {
- // Will be changed by filter, so don't set.
- }
-
- @Override
- public void flushBuffer() throws IOException {
- mStreamDelegate.flushBuffer();
- }
-
- @Override
- public void resetBuffer() {
- mStreamDelegate.resetBuffer();
- }
-
- // TODO: Consider picking up content-type/encoding, as we can only
- // filter US-ASCII, UTF-8 and other compatible encodings?
- }
+/*
+ * Copyright (c) 2008, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name "TwelveMonkeys" nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.twelvemonkeys.servlet;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.ServletResponseWrapper;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.OutputStream;
+import java.io.FilterOutputStream;
+
+/**
+ * Removes extra unneccessary white space from a servlet response.
+ * White space is defined as per {@link Character#isWhitespace(char)}.
+ *
+ * This filter has no understanding of the content in the reponse, and will
+ * remove repeated white space anywhere in the stream. It is intended for
+ * removing white space from HTML or XML streams, but this limitation makes it
+ * less suited for filtering HTML/XHTML with embedded CSS or JavaScript,
+ * in case white space should be significant here. It is strongly reccommended
+ * you keep CSS and JavaScript in separate files (this will have the added
+ * benefit of further reducing the ammount of data communicated between
+ * server and client).
+ *
+ * At the moment this filter has no concept of encoding.
+ * This means, that if some multi-byte escape sequence contains one or more
+ * bytes that individually is treated as a white space, these bytes
+ * may be skipped.
+ * As UTF-8
+ * guarantees that no bytes are repeated in this way, this filter can safely
+ * filter UTF-8.
+ * Simple 8 bit character encodings, like the
+ * ISO/IEC 8859 standard, or
+ *
+ * are always safe.
+ *
+ * Configuration
+ * To use {@code TrimWhiteSpaceFilter} in your web-application, you simply need
+ * to add it to your web descriptor ({@code web.xml}).
+ * If using a servlet container that supports the Servlet 2.4 spec, the new
+ * {@code dispatcher} element should be used, and set to
+ * {@code REQUEST/FORWARD}, to make sure the filter is invoked only once for
+ * requests.
+ * If using an older web descriptor, set the {@code init-param}
+ * {@code "once-per-request"} to {@code "true"} (this will have the same effect,
+ * but might perform slightly worse than the 2.4 version).
+ * Please see the examples below.
+ *
+ * Servlet 2.4 version, filter section:
+ *
+ * <!-- TrimWS Filter Configuration -->
+ * <filter>
+ * <filter-name>trimws</filter-name>
+ * <filter-class>com.twelvemonkeys.servlet.TrimWhiteSpaceFilter</filter-class>
+ * <!-- auto-flush=true is the default, may be omitted -->
+ * <init-param>
+ * <param-name>auto-flush</param-name>
+ * <param-value>true</param-value>
+ * </init-param>
+ * </filter>
+ *
+ * Filter-mapping section:
+ *
+ * <!-- TimWS Filter Mapping -->
+ * <filter-mapping>
+ * <filter-name>trimws</filter-name>
+ * <url-pattern>*.html</url-pattern>
+ * <dispatcher>REQUEST</dispatcher>
+ * <dispatcher>FORWARD</dispatcher>
+ * </filter-mapping>
+ * <filter-mapping>
+ * <filter-name>trimws</filter-name>
+ * <url-pattern>*.jsp</url-pattern>
+ * <dispatcher>REQUEST</dispatcher>
+ * <dispatcher>FORWARD</dispatcher>
+ * </filter-mapping>
+ *
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haku $
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/TrimWhiteSpaceFilter.java#2 $
+ */
+public class TrimWhiteSpaceFilter extends GenericFilter {
+
+ private boolean mAutoFlush = true;
+
+ @InitParam
+ public void setAutoFlush(final boolean pAutoFlush) {
+ mAutoFlush = pAutoFlush;
+ }
+
+ public void init() throws ServletException {
+ super.init();
+ log("Automatic flushing is " + (mAutoFlush ? "enabled" : "disabled"));
+ }
+
+ protected void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain) throws IOException, ServletException {
+ ServletResponseWrapper wrapped = new TrimWSServletResponseWrapper(pResponse);
+ pChain.doFilter(pRequest, ServletUtil.createWrapper(wrapped));
+ if (mAutoFlush) {
+ wrapped.flushBuffer();
+ }
+ }
+
+ static final class TrimWSFilterOutputStream extends FilterOutputStream {
+ boolean mLastWasWS = true; // Avoids leading WS by init to true
+
+ public TrimWSFilterOutputStream(OutputStream pOut) {
+ super(pOut);
+ }
+
+ // Override this, in case the wrapped outputstream overrides...
+ public final void write(byte pBytes[]) throws IOException {
+ write(pBytes, 0, pBytes.length);
+ }
+
+ // Override this, in case the wrapped outputstream overrides...
+ public final void write(byte pBytes[], int pOff, int pLen) throws IOException {
+ if (pBytes == null) {
+ throw new NullPointerException("bytes == null");
+ }
+ else if (pOff < 0 || pLen < 0 || (pOff + pLen > pBytes.length)) {
+ throw new IndexOutOfBoundsException("Bytes: " + pBytes.length + " Offset: " + pOff + " Length: " + pLen);
+ }
+
+ for (int i = 0; i < pLen ; i++) {
+ write(pBytes[pOff + i]);
+ }
+ }
+
+ public void write(int pByte) throws IOException {
+ // TODO: Is this good enough for multi-byte encodings like UTF-16?
+ // Consider writing through a Writer that does that for us, and
+ // also buffer whitespace, so we write a linefeed every time there's
+ // one in the original...
+
+ // According to http://en.wikipedia.org/wiki/UTF-8:
+ // "[...] US-ASCII octet values do not appear otherwise in a UTF-8
+ // encoded character stream. This provides compatibility with file
+ // systems or other software (e.g., the printf() function in
+ // C libraries) that parse based on US-ASCII values but are
+ // transparent to other values."
+
+ if (!Character.isWhitespace((char) pByte)) {
+ // If char is not WS, just store
+ super.write(pByte);
+ mLastWasWS = false;
+ }
+ else {
+ // TODO: Consider writing only 0x0a (LF) and 0x20 (space)
+ // Else, if char is WS, store first, skip the rest
+ if (!mLastWasWS) {
+ if (pByte == 0x0d) { // Convert all CR/LF's to 0x0a
+ super.write(0x0a);
+ }
+ else {
+ super.write(pByte);
+ }
+ }
+ mLastWasWS = true;
+ }
+ }
+ }
+
+ private static class TrimWSStreamDelegate extends ServletResponseStreamDelegate {
+ public TrimWSStreamDelegate(ServletResponse pResponse) {
+ super(pResponse);
+ }
+
+ protected OutputStream createOutputStream() throws IOException {
+ return new TrimWSFilterOutputStream(mResponse.getOutputStream());
+ }
+ }
+
+ static class TrimWSServletResponseWrapper extends ServletResponseWrapper {
+ private final ServletResponseStreamDelegate mStreamDelegate = new TrimWSStreamDelegate(getResponse());
+
+ public TrimWSServletResponseWrapper(ServletResponse pResponse) {
+ super(pResponse);
+ }
+
+ public ServletOutputStream getOutputStream() throws IOException {
+ return mStreamDelegate.getOutputStream();
+ }
+
+ public PrintWriter getWriter() throws IOException {
+ return mStreamDelegate.getWriter();
+ }
+
+ public void setContentLength(int pLength) {
+ // Will be changed by filter, so don't set.
+ }
+
+ @Override
+ public void flushBuffer() throws IOException {
+ mStreamDelegate.flushBuffer();
+ }
+
+ @Override
+ public void resetBuffer() {
+ mStreamDelegate.resetBuffer();
+ }
+
+ // TODO: Consider picking up content-type/encoding, as we can only
+ // filter US-ASCII, UTF-8 and other compatible encodings?
+ }
}
\ No newline at end of file
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/CacheFilter.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/CacheFilter.java
index 94eca85c..812bfd8a 100755
--- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/CacheFilter.java
+++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/CacheFilter.java
@@ -1,199 +1,199 @@
-/*
- * Copyright (c) 2008, Harald Kuhr
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name "TwelveMonkeys" nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package com.twelvemonkeys.servlet.cache;
-
-import com.twelvemonkeys.lang.StringUtil;
-import com.twelvemonkeys.servlet.GenericFilter;
-import com.twelvemonkeys.servlet.ServletConfigException;
-import com.twelvemonkeys.servlet.ServletUtil;
-
-import javax.servlet.*;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.File;
-import java.io.IOException;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-/**
- * A Filter that provides response caching, for HTTP {@code GET} requests.
- *
- * Originally based on ideas and code found in the ONJava article
- * Two
- * Servlet Filters Every Web Application Should Have
- * by Jayson Falkner.
- *
- * @author Jayson Falkner
- * @author Harald Kuhr
- * @author last modified by $Author: haku $
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/CacheFilter.java#4 $
- *
- */
-public class CacheFilter extends GenericFilter {
-
- HTTPCache mCache;
-
- /**
- * Initializes the filter
- *
- * @throws javax.servlet.ServletException
- */
- public void init() throws ServletException {
- FilterConfig config = getFilterConfig();
-
- // Default don't delete cache files on exit (peristent cache)
- boolean deleteCacheOnExit = "TRUE".equalsIgnoreCase(config.getInitParameter("deleteCacheOnExit"));
-
- // Default expiry time 10 minutes
- int expiryTime = 10 * 60 * 1000;
-
- String expiryTimeStr = config.getInitParameter("expiryTime");
- if (!StringUtil.isEmpty(expiryTimeStr)) {
- try {
- expiryTime = Integer.parseInt(expiryTimeStr);
- }
- catch (NumberFormatException e) {
- throw new ServletConfigException("Could not parse expiryTime: " + e.toString(), e);
- }
- }
-
- // Default max mem cache size 10 MB
- int memCacheSize = 10;
-
- String memCacheSizeStr = config.getInitParameter("memCacheSize");
- if (!StringUtil.isEmpty(memCacheSizeStr)) {
- try {
- memCacheSize = Integer.parseInt(memCacheSizeStr);
- }
- catch (NumberFormatException e) {
- throw new ServletConfigException("Could not parse memCacheSize: " + e.toString(), e);
- }
- }
-
- int maxCachedEntites = 10000;
-
- try {
- mCache = new HTTPCache(
- getTempFolder(),
- expiryTime,
- memCacheSize * 1024 * 1024,
- maxCachedEntites,
- deleteCacheOnExit,
- new ServletContextLoggerAdapter(getFilterName(), getServletContext())
- ) {
- @Override
- protected File getRealFile(CacheRequest pRequest) {
- String contextRelativeURI = ServletUtil.getContextRelativeURI(((ServletCacheRequest) pRequest).getRequest());
-
- String path = getServletContext().getRealPath(contextRelativeURI);
-
- if (path != null) {
- return new File(path);
- }
-
- return null;
- }
- };
- log("Created cache: " + mCache);
- }
- catch (IllegalArgumentException e) {
- throw new ServletConfigException("Could not create cache: " + e.toString(), e);
- }
- }
-
- private File getTempFolder() {
- File tempRoot = (File) getServletContext().getAttribute("javax.servlet.context.tempdir");
- if (tempRoot == null) {
- throw new IllegalStateException("Missing context attribute \"javax.servlet.context.tempdir\"");
- }
- return new File(tempRoot, getFilterName());
- }
-
- public void destroy() {
- log("Destroying cache: " + mCache);
- mCache = null;
- super.destroy();
- }
-
- protected void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain) throws IOException, ServletException {
- // We can only cache HTTP GET/HEAD requests
- if (!(pRequest instanceof HttpServletRequest
- && pResponse instanceof HttpServletResponse
- && isCachable((HttpServletRequest) pRequest))) {
- pChain.doFilter(pRequest, pResponse); // Continue chain
- }
- else {
- ServletCacheRequest cacheRequest = new ServletCacheRequest((HttpServletRequest) pRequest);
- ServletCacheResponse cacheResponse = new ServletCacheResponse((HttpServletResponse) pResponse);
- ServletResponseResolver resolver = new ServletResponseResolver(cacheRequest, cacheResponse, pChain);
-
- // Render fast
- try {
- mCache.doCached(cacheRequest, cacheResponse, resolver);
- }
- catch (CacheException e) {
- if (e.getCause() instanceof ServletException) {
- throw (ServletException) e.getCause();
- }
- else {
- throw new ServletException(e);
- }
- }
- finally {
- pResponse.flushBuffer();
- }
- }
- }
-
- private boolean isCachable(HttpServletRequest pRequest) {
- // TODO: Get Cache-Control: no-cache/max-age=0 and Pragma: no-cache from REQUEST too?
- return "GET".equals(pRequest.getMethod()) || "HEAD".equals(pRequest.getMethod());
- }
-
- // TODO: Extract, complete and document this class, might be useful in other cases
- // Maybe add it to the ServletUtil class
- static class ServletContextLoggerAdapter extends Logger {
- private final ServletContext mContext;
-
- public ServletContextLoggerAdapter(String pName, ServletContext pContext) {
- super(pName, null);
- mContext = pContext;
- }
-
- @Override
- public void log(Level pLevel, String pMessage) {
- mContext.log(pMessage);
- }
-
- @Override
- public void log(Level pLevel, String pMessage, Throwable pThrowable) {
- mContext.log(pMessage, pThrowable);
- }
- }
+/*
+ * Copyright (c) 2008, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name "TwelveMonkeys" nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.twelvemonkeys.servlet.cache;
+
+import com.twelvemonkeys.lang.StringUtil;
+import com.twelvemonkeys.servlet.GenericFilter;
+import com.twelvemonkeys.servlet.ServletConfigException;
+import com.twelvemonkeys.servlet.ServletUtil;
+
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.File;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * A Filter that provides response caching, for HTTP {@code GET} requests.
+ *
+ * Originally based on ideas and code found in the ONJava article
+ * Two
+ * Servlet Filters Every Web Application Should Have
+ * by Jayson Falkner.
+ *
+ * @author Jayson Falkner
+ * @author Harald Kuhr
+ * @author last modified by $Author: haku $
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/CacheFilter.java#4 $
+ *
+ */
+public class CacheFilter extends GenericFilter {
+
+ HTTPCache mCache;
+
+ /**
+ * Initializes the filter
+ *
+ * @throws javax.servlet.ServletException
+ */
+ public void init() throws ServletException {
+ FilterConfig config = getFilterConfig();
+
+ // Default don't delete cache files on exit (peristent cache)
+ boolean deleteCacheOnExit = "TRUE".equalsIgnoreCase(config.getInitParameter("deleteCacheOnExit"));
+
+ // Default expiry time 10 minutes
+ int expiryTime = 10 * 60 * 1000;
+
+ String expiryTimeStr = config.getInitParameter("expiryTime");
+ if (!StringUtil.isEmpty(expiryTimeStr)) {
+ try {
+ expiryTime = Integer.parseInt(expiryTimeStr);
+ }
+ catch (NumberFormatException e) {
+ throw new ServletConfigException("Could not parse expiryTime: " + e.toString(), e);
+ }
+ }
+
+ // Default max mem cache size 10 MB
+ int memCacheSize = 10;
+
+ String memCacheSizeStr = config.getInitParameter("memCacheSize");
+ if (!StringUtil.isEmpty(memCacheSizeStr)) {
+ try {
+ memCacheSize = Integer.parseInt(memCacheSizeStr);
+ }
+ catch (NumberFormatException e) {
+ throw new ServletConfigException("Could not parse memCacheSize: " + e.toString(), e);
+ }
+ }
+
+ int maxCachedEntites = 10000;
+
+ try {
+ mCache = new HTTPCache(
+ getTempFolder(),
+ expiryTime,
+ memCacheSize * 1024 * 1024,
+ maxCachedEntites,
+ deleteCacheOnExit,
+ new ServletContextLoggerAdapter(getFilterName(), getServletContext())
+ ) {
+ @Override
+ protected File getRealFile(CacheRequest pRequest) {
+ String contextRelativeURI = ServletUtil.getContextRelativeURI(((ServletCacheRequest) pRequest).getRequest());
+
+ String path = getServletContext().getRealPath(contextRelativeURI);
+
+ if (path != null) {
+ return new File(path);
+ }
+
+ return null;
+ }
+ };
+ log("Created cache: " + mCache);
+ }
+ catch (IllegalArgumentException e) {
+ throw new ServletConfigException("Could not create cache: " + e.toString(), e);
+ }
+ }
+
+ private File getTempFolder() {
+ File tempRoot = (File) getServletContext().getAttribute("javax.servlet.context.tempdir");
+ if (tempRoot == null) {
+ throw new IllegalStateException("Missing context attribute \"javax.servlet.context.tempdir\"");
+ }
+ return new File(tempRoot, getFilterName());
+ }
+
+ public void destroy() {
+ log("Destroying cache: " + mCache);
+ mCache = null;
+ super.destroy();
+ }
+
+ protected void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain) throws IOException, ServletException {
+ // We can only cache HTTP GET/HEAD requests
+ if (!(pRequest instanceof HttpServletRequest
+ && pResponse instanceof HttpServletResponse
+ && isCachable((HttpServletRequest) pRequest))) {
+ pChain.doFilter(pRequest, pResponse); // Continue chain
+ }
+ else {
+ ServletCacheRequest cacheRequest = new ServletCacheRequest((HttpServletRequest) pRequest);
+ ServletCacheResponse cacheResponse = new ServletCacheResponse((HttpServletResponse) pResponse);
+ ServletResponseResolver resolver = new ServletResponseResolver(cacheRequest, cacheResponse, pChain);
+
+ // Render fast
+ try {
+ mCache.doCached(cacheRequest, cacheResponse, resolver);
+ }
+ catch (CacheException e) {
+ if (e.getCause() instanceof ServletException) {
+ throw (ServletException) e.getCause();
+ }
+ else {
+ throw new ServletException(e);
+ }
+ }
+ finally {
+ pResponse.flushBuffer();
+ }
+ }
+ }
+
+ private boolean isCachable(HttpServletRequest pRequest) {
+ // TODO: Get Cache-Control: no-cache/max-age=0 and Pragma: no-cache from REQUEST too?
+ return "GET".equals(pRequest.getMethod()) || "HEAD".equals(pRequest.getMethod());
+ }
+
+ // TODO: Extract, complete and document this class, might be useful in other cases
+ // Maybe add it to the ServletUtil class
+ static class ServletContextLoggerAdapter extends Logger {
+ private final ServletContext mContext;
+
+ public ServletContextLoggerAdapter(String pName, ServletContext pContext) {
+ super(pName, null);
+ mContext = pContext;
+ }
+
+ @Override
+ public void log(Level pLevel, String pMessage) {
+ mContext.log(pMessage);
+ }
+
+ @Override
+ public void log(Level pLevel, String pMessage, Throwable pThrowable) {
+ mContext.log(pMessage, pThrowable);
+ }
+ }
}
\ No newline at end of file
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/CacheResponseWrapper.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/CacheResponseWrapper.java
index bd2f051a..04eb74df 100755
--- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/CacheResponseWrapper.java
+++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/CacheResponseWrapper.java
@@ -1,261 +1,261 @@
-/*
- * Copyright (c) 2008, Harald Kuhr
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name "TwelveMonkeys" nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package com.twelvemonkeys.servlet.cache;
-
-import com.twelvemonkeys.lang.StringUtil;
-import com.twelvemonkeys.net.NetUtil;
-import com.twelvemonkeys.servlet.ServletResponseStreamDelegate;
-
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.HttpServletResponseWrapper;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.PrintWriter;
-
-/**
- * CacheResponseWrapper class description.
- *
- * Based on ideas and code found in the ONJava article
- * Two
- * Servlet Filters Every Web Application Should Have
- * by Jayson Falkner.
- *
- * @author Jayson Falkner
- * @author Harald Kuhr
- * @author last modified by $Author: haku $
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/CacheResponseWrapper.java#3 $
- */
-class CacheResponseWrapper extends HttpServletResponseWrapper {
- private ServletResponseStreamDelegate mStreamDelegate;
-
- private CacheResponse mResponse;
- private CachedEntity mCached;
- private WritableCachedResponse mCachedResponse;
-
- private Boolean mCachable;
- private int mStatus;
-
- public CacheResponseWrapper(final ServletCacheResponse pResponse, final CachedEntity pCached) {
- super(pResponse.getResponse());
- mResponse = pResponse;
- mCached = pCached;
- init();
- }
-
- /*
- NOTE: This class defers determining if a response is cachable until the
- output stream is needed.
- This it the reason for the somewhat complicated logic in the add/setHeader
- methods below.
- */
- private void init() {
- mCachable = null;
- mStatus = SC_OK;
- mCachedResponse = mCached.createCachedResponse();
- mStreamDelegate = new ServletResponseStreamDelegate(this) {
- protected OutputStream createOutputStream() throws IOException {
- // Test if this request is really cachable, otherwise,
- // just write through to underlying response, and don't cache
- if (isCachable()) {
- return mCachedResponse.getOutputStream();
- }
- else {
- mCachedResponse.setStatus(mStatus);
- mCachedResponse.writeHeadersTo(CacheResponseWrapper.this.mResponse);
- return super.getOutputStream();
- }
- }
- };
- }
-
- CachedResponse getCachedResponse() {
- return mCachedResponse.getCachedResponse();
- }
-
- public boolean isCachable() {
- // NOTE: Intentionally not synchronized
- if (mCachable == null) {
- mCachable = isCachableImpl();
- }
-
- return mCachable;
- }
-
- private boolean isCachableImpl() {
- if (mStatus != SC_OK) {
- return false;
- }
-
- // Vary: *
- String[] values = mCachedResponse.getHeaderValues(HTTPCache.HEADER_VARY);
- if (values != null) {
- for (String value : values) {
- if ("*".equals(value)) {
- return false;
- }
- }
- }
-
- // Cache-Control: no-cache, no-store, must-revalidate
- values = mCachedResponse.getHeaderValues(HTTPCache.HEADER_CACHE_CONTROL);
- if (values != null) {
- for (String value : values) {
- if (StringUtil.contains(value, "no-cache")
- || StringUtil.contains(value, "no-store")
- || StringUtil.contains(value, "must-revalidate")) {
- return false;
- }
- }
- }
-
- // Pragma: no-cache
- values = mCachedResponse.getHeaderValues(HTTPCache.HEADER_PRAGMA);
- if (values != null) {
- for (String value : values) {
- if (StringUtil.contains(value, "no-cache")) {
- return false;
- }
- }
- }
-
- return true;
- }
-
- public void flushBuffer() throws IOException {
- mStreamDelegate.flushBuffer();
- }
-
- public void resetBuffer() {
- // Servlet 2.3
- mStreamDelegate.resetBuffer();
- }
-
- public void reset() {
- if (Boolean.FALSE.equals(mCachable)) {
- super.reset();
- }
- // No else, might be cachable after all..
- init();
- }
-
- public ServletOutputStream getOutputStream() throws IOException {
- return mStreamDelegate.getOutputStream();
- }
-
- public PrintWriter getWriter() throws IOException {
- return mStreamDelegate.getWriter();
- }
-
- public boolean containsHeader(String name) {
- return mCachedResponse.getHeaderValues(name) != null;
- }
-
- public void sendError(int pStatusCode, String msg) throws IOException {
- // NOT cachable
- mStatus = pStatusCode;
- super.sendError(pStatusCode, msg);
- }
-
- public void sendError(int pStatusCode) throws IOException {
- // NOT cachable
- mStatus = pStatusCode;
- super.sendError(pStatusCode);
- }
-
- public void setStatus(int pStatusCode, String sm) {
- // NOTE: This method is deprecated
- setStatus(pStatusCode);
- }
-
- public void setStatus(int pStatusCode) {
- // NOT cachable unless pStatusCode == 200 (or a FEW others?)
- if (pStatusCode != SC_OK) {
- mStatus = pStatusCode;
- super.setStatus(pStatusCode);
- }
- }
-
- public void sendRedirect(String pLocation) throws IOException {
- // NOT cachable
- mStatus = SC_MOVED_TEMPORARILY;
- super.sendRedirect(pLocation);
- }
-
- public void setDateHeader(String pName, long pValue) {
- // If in write-trough-mode, set headers directly
- if (Boolean.FALSE.equals(mCachable)) {
- super.setDateHeader(pName, pValue);
- }
- mCachedResponse.setHeader(pName, NetUtil.formatHTTPDate(pValue));
- }
-
- public void addDateHeader(String pName, long pValue) {
- // If in write-trough-mode, set headers directly
- if (Boolean.FALSE.equals(mCachable)) {
- super.addDateHeader(pName, pValue);
- }
- mCachedResponse.addHeader(pName, NetUtil.formatHTTPDate(pValue));
- }
-
- public void setHeader(String pName, String pValue) {
- // If in write-trough-mode, set headers directly
- if (Boolean.FALSE.equals(mCachable)) {
- super.setHeader(pName, pValue);
- }
- mCachedResponse.setHeader(pName, pValue);
- }
-
- public void addHeader(String pName, String pValue) {
- // If in write-trough-mode, set headers directly
- if (Boolean.FALSE.equals(mCachable)) {
- super.addHeader(pName, pValue);
- }
- mCachedResponse.addHeader(pName, pValue);
- }
-
- public void setIntHeader(String pName, int pValue) {
- // If in write-trough-mode, set headers directly
- if (Boolean.FALSE.equals(mCachable)) {
- super.setIntHeader(pName, pValue);
- }
- mCachedResponse.setHeader(pName, String.valueOf(pValue));
- }
-
- public void addIntHeader(String pName, int pValue) {
- // If in write-trough-mode, set headers directly
- if (Boolean.FALSE.equals(mCachable)) {
- super.addIntHeader(pName, pValue);
- }
- mCachedResponse.addHeader(pName, String.valueOf(pValue));
- }
-
- public final void setContentType(String type) {
- setHeader(HTTPCache.HEADER_CONTENT_TYPE, type);
- }
+/*
+ * Copyright (c) 2008, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name "TwelveMonkeys" nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.twelvemonkeys.servlet.cache;
+
+import com.twelvemonkeys.lang.StringUtil;
+import com.twelvemonkeys.net.NetUtil;
+import com.twelvemonkeys.servlet.ServletResponseStreamDelegate;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletResponseWrapper;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+
+/**
+ * CacheResponseWrapper class description.
+ *
+ * Based on ideas and code found in the ONJava article
+ * Two
+ * Servlet Filters Every Web Application Should Have
+ * by Jayson Falkner.
+ *
+ * @author Jayson Falkner
+ * @author Harald Kuhr
+ * @author last modified by $Author: haku $
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/CacheResponseWrapper.java#3 $
+ */
+class CacheResponseWrapper extends HttpServletResponseWrapper {
+ private ServletResponseStreamDelegate mStreamDelegate;
+
+ private CacheResponse mResponse;
+ private CachedEntity mCached;
+ private WritableCachedResponse mCachedResponse;
+
+ private Boolean mCachable;
+ private int mStatus;
+
+ public CacheResponseWrapper(final ServletCacheResponse pResponse, final CachedEntity pCached) {
+ super(pResponse.getResponse());
+ mResponse = pResponse;
+ mCached = pCached;
+ init();
+ }
+
+ /*
+ NOTE: This class defers determining if a response is cachable until the
+ output stream is needed.
+ This it the reason for the somewhat complicated logic in the add/setHeader
+ methods below.
+ */
+ private void init() {
+ mCachable = null;
+ mStatus = SC_OK;
+ mCachedResponse = mCached.createCachedResponse();
+ mStreamDelegate = new ServletResponseStreamDelegate(this) {
+ protected OutputStream createOutputStream() throws IOException {
+ // Test if this request is really cachable, otherwise,
+ // just write through to underlying response, and don't cache
+ if (isCachable()) {
+ return mCachedResponse.getOutputStream();
+ }
+ else {
+ mCachedResponse.setStatus(mStatus);
+ mCachedResponse.writeHeadersTo(CacheResponseWrapper.this.mResponse);
+ return super.getOutputStream();
+ }
+ }
+ };
+ }
+
+ CachedResponse getCachedResponse() {
+ return mCachedResponse.getCachedResponse();
+ }
+
+ public boolean isCachable() {
+ // NOTE: Intentionally not synchronized
+ if (mCachable == null) {
+ mCachable = isCachableImpl();
+ }
+
+ return mCachable;
+ }
+
+ private boolean isCachableImpl() {
+ if (mStatus != SC_OK) {
+ return false;
+ }
+
+ // Vary: *
+ String[] values = mCachedResponse.getHeaderValues(HTTPCache.HEADER_VARY);
+ if (values != null) {
+ for (String value : values) {
+ if ("*".equals(value)) {
+ return false;
+ }
+ }
+ }
+
+ // Cache-Control: no-cache, no-store, must-revalidate
+ values = mCachedResponse.getHeaderValues(HTTPCache.HEADER_CACHE_CONTROL);
+ if (values != null) {
+ for (String value : values) {
+ if (StringUtil.contains(value, "no-cache")
+ || StringUtil.contains(value, "no-store")
+ || StringUtil.contains(value, "must-revalidate")) {
+ return false;
+ }
+ }
+ }
+
+ // Pragma: no-cache
+ values = mCachedResponse.getHeaderValues(HTTPCache.HEADER_PRAGMA);
+ if (values != null) {
+ for (String value : values) {
+ if (StringUtil.contains(value, "no-cache")) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ public void flushBuffer() throws IOException {
+ mStreamDelegate.flushBuffer();
+ }
+
+ public void resetBuffer() {
+ // Servlet 2.3
+ mStreamDelegate.resetBuffer();
+ }
+
+ public void reset() {
+ if (Boolean.FALSE.equals(mCachable)) {
+ super.reset();
+ }
+ // No else, might be cachable after all..
+ init();
+ }
+
+ public ServletOutputStream getOutputStream() throws IOException {
+ return mStreamDelegate.getOutputStream();
+ }
+
+ public PrintWriter getWriter() throws IOException {
+ return mStreamDelegate.getWriter();
+ }
+
+ public boolean containsHeader(String name) {
+ return mCachedResponse.getHeaderValues(name) != null;
+ }
+
+ public void sendError(int pStatusCode, String msg) throws IOException {
+ // NOT cachable
+ mStatus = pStatusCode;
+ super.sendError(pStatusCode, msg);
+ }
+
+ public void sendError(int pStatusCode) throws IOException {
+ // NOT cachable
+ mStatus = pStatusCode;
+ super.sendError(pStatusCode);
+ }
+
+ public void setStatus(int pStatusCode, String sm) {
+ // NOTE: This method is deprecated
+ setStatus(pStatusCode);
+ }
+
+ public void setStatus(int pStatusCode) {
+ // NOT cachable unless pStatusCode == 200 (or a FEW others?)
+ if (pStatusCode != SC_OK) {
+ mStatus = pStatusCode;
+ super.setStatus(pStatusCode);
+ }
+ }
+
+ public void sendRedirect(String pLocation) throws IOException {
+ // NOT cachable
+ mStatus = SC_MOVED_TEMPORARILY;
+ super.sendRedirect(pLocation);
+ }
+
+ public void setDateHeader(String pName, long pValue) {
+ // If in write-trough-mode, set headers directly
+ if (Boolean.FALSE.equals(mCachable)) {
+ super.setDateHeader(pName, pValue);
+ }
+ mCachedResponse.setHeader(pName, NetUtil.formatHTTPDate(pValue));
+ }
+
+ public void addDateHeader(String pName, long pValue) {
+ // If in write-trough-mode, set headers directly
+ if (Boolean.FALSE.equals(mCachable)) {
+ super.addDateHeader(pName, pValue);
+ }
+ mCachedResponse.addHeader(pName, NetUtil.formatHTTPDate(pValue));
+ }
+
+ public void setHeader(String pName, String pValue) {
+ // If in write-trough-mode, set headers directly
+ if (Boolean.FALSE.equals(mCachable)) {
+ super.setHeader(pName, pValue);
+ }
+ mCachedResponse.setHeader(pName, pValue);
+ }
+
+ public void addHeader(String pName, String pValue) {
+ // If in write-trough-mode, set headers directly
+ if (Boolean.FALSE.equals(mCachable)) {
+ super.addHeader(pName, pValue);
+ }
+ mCachedResponse.addHeader(pName, pValue);
+ }
+
+ public void setIntHeader(String pName, int pValue) {
+ // If in write-trough-mode, set headers directly
+ if (Boolean.FALSE.equals(mCachable)) {
+ super.setIntHeader(pName, pValue);
+ }
+ mCachedResponse.setHeader(pName, String.valueOf(pValue));
+ }
+
+ public void addIntHeader(String pName, int pValue) {
+ // If in write-trough-mode, set headers directly
+ if (Boolean.FALSE.equals(mCachable)) {
+ super.addIntHeader(pName, pValue);
+ }
+ mCachedResponse.addHeader(pName, String.valueOf(pValue));
+ }
+
+ public final void setContentType(String type) {
+ setHeader(HTTPCache.HEADER_CONTENT_TYPE, type);
+ }
}
\ No newline at end of file
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/CachedEntity.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/CachedEntity.java
index 7e29bdc2..f39c4e14 100755
--- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/CachedEntity.java
+++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/CachedEntity.java
@@ -1,75 +1,75 @@
-/*
- * Copyright (c) 2008, Harald Kuhr
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name "TwelveMonkeys" nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package com.twelvemonkeys.servlet.cache;
-
-import java.io.IOException;
-
-/**
- * CachedEntity
- *
- * @author Harald Kuhr
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/CachedEntity.java#3 $
- */
-interface CachedEntity {
-
- /**
- * Renders the cached entity to the response.
- *
- * @param pRequest the request
- * @param pResponse the response
- * @throws java.io.IOException if an I/O exception occurs
- */
- void render(CacheRequest pRequest, CacheResponse pResponse) throws IOException;
-
- /**
- * Captures (caches) the response for the given request.
- *
- * @param pRequest the request
- * @param pResponse the response
- * @throws java.io.IOException if an I/O exception occurs
- *
- * @see #createCachedResponse()
- */
- void capture(CacheRequest pRequest, CachedResponse pResponse) throws IOException;
-
- /**
- * Tests if the content of this entity is stale for the given request.
- *
- * @param pRequest the request
- * @return {@code true} if content is stale
- */
- boolean isStale(CacheRequest pRequest);
-
- /**
- * Creates a {@code WritableCachedResponse} to use to capture the response.
- *
- * @return a {@code WritableCachedResponse}
- */
- WritableCachedResponse createCachedResponse();
-}
+/*
+ * Copyright (c) 2008, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name "TwelveMonkeys" nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.twelvemonkeys.servlet.cache;
+
+import java.io.IOException;
+
+/**
+ * CachedEntity
+ *
+ * @author Harald Kuhr
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/CachedEntity.java#3 $
+ */
+interface CachedEntity {
+
+ /**
+ * Renders the cached entity to the response.
+ *
+ * @param pRequest the request
+ * @param pResponse the response
+ * @throws java.io.IOException if an I/O exception occurs
+ */
+ void render(CacheRequest pRequest, CacheResponse pResponse) throws IOException;
+
+ /**
+ * Captures (caches) the response for the given request.
+ *
+ * @param pRequest the request
+ * @param pResponse the response
+ * @throws java.io.IOException if an I/O exception occurs
+ *
+ * @see #createCachedResponse()
+ */
+ void capture(CacheRequest pRequest, CachedResponse pResponse) throws IOException;
+
+ /**
+ * Tests if the content of this entity is stale for the given request.
+ *
+ * @param pRequest the request
+ * @return {@code true} if content is stale
+ */
+ boolean isStale(CacheRequest pRequest);
+
+ /**
+ * Creates a {@code WritableCachedResponse} to use to capture the response.
+ *
+ * @return a {@code WritableCachedResponse}
+ */
+ WritableCachedResponse createCachedResponse();
+}
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/CachedEntityImpl.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/CachedEntityImpl.java
index af0c37a7..d7719814 100755
--- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/CachedEntityImpl.java
+++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/CachedEntityImpl.java
@@ -1,172 +1,172 @@
-/*
- * Copyright (c) 2008, Harald Kuhr
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name "TwelveMonkeys" nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package com.twelvemonkeys.servlet.cache;
-
-import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
-import java.util.List;
-
-/**
- * CachedEntity
- *
- * @author Harald Kuhr
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/cache/CachedEntityImpl.java#3 $
- */
-class CachedEntityImpl implements CachedEntity {
- private String mCacheURI;
- private HTTPCache mCache;
-
- CachedEntityImpl(String pCacheURI, HTTPCache pCache) {
- if (pCacheURI == null) {
- throw new IllegalArgumentException("cacheURI == null");
- }
-
- mCacheURI = pCacheURI;
- mCache = pCache;
- }
-
- public void render(CacheRequest pRequest, CacheResponse pResponse) throws IOException {
- // Get cached content
- CachedResponse cached = mCache.getContent(mCacheURI, pRequest);
-
- // Sanity check
- if (cached == null) {
- throw new IllegalStateException("Tried to render non-cached response (cache == null).");
- }
-
- // If the cached entity is not modified since the date of the browsers
- // version, then simply send a "304 Not Modified" response
- // Otherwise send the full response.
-
- // TODO: WHY DID I COMMENT OUT THIS LINE AND REPLACE IT WITH THE ONE BELOW??
- //long lastModified = HTTPCache.getDateHeader(cached.getHeaderValue(HTTPCache.HEADER_LAST_MODIFIED));
- long lastModified = HTTPCache.getDateHeader(cached.getHeaderValue(HTTPCache.HEADER_CACHED_TIME));
-
- // TODO: Consider handling time skews between server "now" and client "now"?
- // NOTE: The If-Modified-Since is probably right according to the server
- // even in a time skew situation, as the client should use either the
- // Date or Last-Modifed dates from the response headers (server generated)
- long ifModifiedSince = -1L;
- try {
- List
- * To use {@code GZIPFilter} in your web-application, you simply need to add it
- * to your web descriptor ({@code web.xml}). If using a servlet container that
- * supports the Servlet 2.4 spec, the new {@code dispatcher} element should be
- * used, and set to {@code REQUEST/FORWARD}, to make sure the filter is invoked
- * only once for requests.
- * If using an older web descriptor, set the {@code init-param}
- * {@code "once-per-request"} to {@code "true"} (this will have the same effect,
- * but might perform slightly worse than the 2.4 version).
- * Please see the examples below.
- * Servlet 2.4 version, filter section:
- *
- * <!-- GZIP Filter Configuration -->
- * <filter>
- * <filter-name>gzip</filter-name>
- * <filter-class>com.twelvemonkeys.servlet.GZIPFilter</filter-class>
- * </filter>
- *
- * Filter-mapping section:
- *
- * <!-- GZIP Filter Mapping -->
- * <filter-mapping>
- * <filter-name>gzip</filter-name>
- * <url-pattern>*.html</url-pattern>
- * <dispatcher>REQUEST</dispatcher>
- * <dispatcher>FORWARD</dispatcher>
- * </filter-mapping>
- * <filter-mapping>
- * <filter-name>gzip</filter-name>
- * <url-pattern>*.jsp< /url-pattern>
- * <dispatcher>REQUEST</dispatcher>
- * <dispatcher>FORWARD</dispatcher>
- * </filter-mapping>
- *
- *
- * Based on ideas and code found in the ONJava article
- * Two
- * Servlet Filters Every Web Application Should Have
- * by Jayson Falkner.
- *
- *
- * @author Jayson Falkner
- * @author Harald Kuhr
- * @author last modified by $Author: haku $
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/gzip/GZIPFilter.java#1 $
- */
-public class GZIPFilter extends GenericFilter {
-
- {
- mOncePerRequest = true;
- }
-
- protected void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain) throws IOException, ServletException {
- // Can only filter HTTP responses
- if (pRequest instanceof HttpServletRequest) {
- HttpServletRequest request = (HttpServletRequest) pRequest;
- HttpServletResponse response = (HttpServletResponse) pResponse;
-
- // If GZIP is supported, use compression
- String accept = request.getHeader("Accept-Encoding");
- if (accept != null && accept.indexOf("gzip") != -1) {
- //System.out.println("GZIP supported, compressing.");
- // TODO: Set Vary: Accept-Encoding ?!
-
- GZIPResponseWrapper wrapped = new GZIPResponseWrapper(response);
- try {
- pChain.doFilter(pRequest, wrapped);
- }
- finally {
- wrapped.flushResponse();
- }
- return;
- }
- }
-
- // Else, contiue chain
- pChain.doFilter(pRequest, pResponse);
- }
-}
+/*
+ * Copyright (c) 2008, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name "TwelveMonkeys" nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.twelvemonkeys.servlet.gzip;
+
+import com.twelvemonkeys.servlet.GenericFilter;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * A filter to reduce the output size of web resources.
+ *
+ * The HTTP protocol supports compression of the content to reduce network
+ * bandwidth. The important headers involved, are the {@code Accept-Encoding}
+ * request header, and the {@code Content-Encoding} response header.
+ * This feature can be used to further reduce the number of bytes transferred
+ * over the network, at the cost of some extra processing time at both endpoints.
+ * Most modern browsers supports compression in GZIP format, which is fairly
+ * efficient in cost/compression ratio.
+ *
+ * The filter tests for the presence of an {@code Accept-Encoding} header with a
+ * value of {@code "gzip"} (several different encoding header values are
+ * possible in one header). If not present, the filter simply passes the
+ * request/response pair through, leaving it untouched. If present, the
+ * {@code Content-Encoding} header is set, with the value {@code "gzip"},
+ * and the response is wrapped.
+ * The response output stream is wrapped in a
+ * {@link java.util.zip.GZIPOutputStream} which performs the GZIP encoding.
+ * For efficiency, the filter does not buffer the response, but writes through
+ * the gzipped output stream.
+ *
+ * Configuration
+ * To use {@code GZIPFilter} in your web-application, you simply need to add it
+ * to your web descriptor ({@code web.xml}). If using a servlet container that
+ * supports the Servlet 2.4 spec, the new {@code dispatcher} element should be
+ * used, and set to {@code REQUEST/FORWARD}, to make sure the filter is invoked
+ * only once for requests.
+ * If using an older web descriptor, set the {@code init-param}
+ * {@code "once-per-request"} to {@code "true"} (this will have the same effect,
+ * but might perform slightly worse than the 2.4 version).
+ * Please see the examples below.
+ * Servlet 2.4 version, filter section:
+ *
+ * <!-- GZIP Filter Configuration -->
+ * <filter>
+ * <filter-name>gzip</filter-name>
+ * <filter-class>com.twelvemonkeys.servlet.GZIPFilter</filter-class>
+ * </filter>
+ *
+ * Filter-mapping section:
+ *
+ * <!-- GZIP Filter Mapping -->
+ * <filter-mapping>
+ * <filter-name>gzip</filter-name>
+ * <url-pattern>*.html</url-pattern>
+ * <dispatcher>REQUEST</dispatcher>
+ * <dispatcher>FORWARD</dispatcher>
+ * </filter-mapping>
+ * <filter-mapping>
+ * <filter-name>gzip</filter-name>
+ * <url-pattern>*.jsp< /url-pattern>
+ * <dispatcher>REQUEST</dispatcher>
+ * <dispatcher>FORWARD</dispatcher>
+ * </filter-mapping>
+ *
+ *
+ * Based on ideas and code found in the ONJava article
+ * Two
+ * Servlet Filters Every Web Application Should Have
+ * by Jayson Falkner.
+ *
+ *
+ * @author Jayson Falkner
+ * @author Harald Kuhr
+ * @author last modified by $Author: haku $
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/gzip/GZIPFilter.java#1 $
+ */
+public class GZIPFilter extends GenericFilter {
+
+ {
+ mOncePerRequest = true;
+ }
+
+ protected void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain) throws IOException, ServletException {
+ // Can only filter HTTP responses
+ if (pRequest instanceof HttpServletRequest) {
+ HttpServletRequest request = (HttpServletRequest) pRequest;
+ HttpServletResponse response = (HttpServletResponse) pResponse;
+
+ // If GZIP is supported, use compression
+ String accept = request.getHeader("Accept-Encoding");
+ if (accept != null && accept.indexOf("gzip") != -1) {
+ //System.out.println("GZIP supported, compressing.");
+ // TODO: Set Vary: Accept-Encoding ?!
+
+ GZIPResponseWrapper wrapped = new GZIPResponseWrapper(response);
+ try {
+ pChain.doFilter(pRequest, wrapped);
+ }
+ finally {
+ wrapped.flushResponse();
+ }
+ return;
+ }
+ }
+
+ // Else, contiue chain
+ pChain.doFilter(pRequest, pResponse);
+ }
+}
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/gzip/GZIPResponseWrapper.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/gzip/GZIPResponseWrapper.java
index 60579c9f..4555d6bd 100755
--- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/gzip/GZIPResponseWrapper.java
+++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/gzip/GZIPResponseWrapper.java
@@ -1,146 +1,146 @@
-/*
- * Copyright (c) 2008, Harald Kuhr
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name "TwelveMonkeys" nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package com.twelvemonkeys.servlet.gzip;
-
-import com.twelvemonkeys.servlet.OutputStreamAdapter;
-
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpServletResponseWrapper;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.util.zip.GZIPOutputStream;
-
-/**
- * GZIPResponseWrapper class description.
- *
- * Based on ideas and code found in the ONJava article
- * Two Servlet Filters Every Web Application Should Have
- * by Jayson Falkner.
- *
- * @author Jayson Falkner
- * @author Harald Kuhr
- * @author last modified by $Author: haku $
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/gzip/GZIPResponseWrapper.java#1 $
- */
-public class GZIPResponseWrapper extends HttpServletResponseWrapper {
- protected ServletOutputStream mOut = null;
- protected PrintWriter mWriter = null;
- protected GZIPOutputStream mGZIPOut = null;
- protected int mContentLength = -1;
-
- public GZIPResponseWrapper(HttpServletResponse response) {
- super(response);
- response.addHeader("Content-Encoding", "gzip");
- }
-
- public ServletOutputStream createOutputStream() throws IOException {
- // FIX: Write directly to servlet output stream, for faster responses.
- // Relies on chunked streams, or buffering in the servlet engine.
- if (mContentLength >= 0) {
- mGZIPOut = new GZIPOutputStream(getResponse().getOutputStream(), mContentLength);
- }
- else {
- mGZIPOut = new GZIPOutputStream(getResponse().getOutputStream());
- }
-
- // Wrap in ServletOutputStream and return
- return new OutputStreamAdapter(mGZIPOut);
- }
-
- // TODO: Move this to flushbuffer or something? Hmmm..
- public void flushResponse() {
- try {
- try {
- // Finish GZIP encodig
- if (mGZIPOut != null) {
- mGZIPOut.finish();
- }
-
- flushBuffer();
- }
- finally {
- // Close stream
- if (mWriter != null) {
- mWriter.close();
- }
- else {
- if (mOut != null) {
- mOut.close();
- }
- }
- }
- }
- catch (IOException e) {
- // TODO: Fix this one...
- e.printStackTrace();
- }
- }
-
- public void flushBuffer() throws IOException {
- if (mWriter != null) {
- mWriter.flush();
- }
- else if (mOut != null) {
- mOut.flush();
- }
- }
-
- public ServletOutputStream getOutputStream() throws IOException {
- if (mWriter != null) {
- throw new IllegalStateException("getWriter() has already been called!");
- }
-
- if (mOut == null) {
- mOut = createOutputStream();
- }
- return (mOut);
- }
-
- public PrintWriter getWriter() throws IOException {
- if (mWriter != null) {
- return (mWriter);
- }
-
- if (mOut != null) {
- throw new IllegalStateException("getOutputStream() has already been called!");
- }
-
- mOut = createOutputStream();
- // TODO: This is wrong. Should use getCharacterEncoding() or "ISO-8859-1" if gCE returns null.
- mWriter = new PrintWriter(new OutputStreamWriter(mOut, "UTF-8"));
- return (mWriter);
- }
-
- public void setContentLength(int pLength) {
- // NOTE: Do not call super, as we will shrink the size.
- mContentLength = pLength;
- }
-}
+/*
+ * Copyright (c) 2008, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name "TwelveMonkeys" nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.twelvemonkeys.servlet.gzip;
+
+import com.twelvemonkeys.servlet.OutputStreamAdapter;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.util.zip.GZIPOutputStream;
+
+/**
+ * GZIPResponseWrapper class description.
+ *
+ * Based on ideas and code found in the ONJava article
+ * Two Servlet Filters Every Web Application Should Have
+ * by Jayson Falkner.
+ *
+ * @author Jayson Falkner
+ * @author Harald Kuhr
+ * @author last modified by $Author: haku $
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/gzip/GZIPResponseWrapper.java#1 $
+ */
+public class GZIPResponseWrapper extends HttpServletResponseWrapper {
+ protected ServletOutputStream mOut = null;
+ protected PrintWriter mWriter = null;
+ protected GZIPOutputStream mGZIPOut = null;
+ protected int mContentLength = -1;
+
+ public GZIPResponseWrapper(HttpServletResponse response) {
+ super(response);
+ response.addHeader("Content-Encoding", "gzip");
+ }
+
+ public ServletOutputStream createOutputStream() throws IOException {
+ // FIX: Write directly to servlet output stream, for faster responses.
+ // Relies on chunked streams, or buffering in the servlet engine.
+ if (mContentLength >= 0) {
+ mGZIPOut = new GZIPOutputStream(getResponse().getOutputStream(), mContentLength);
+ }
+ else {
+ mGZIPOut = new GZIPOutputStream(getResponse().getOutputStream());
+ }
+
+ // Wrap in ServletOutputStream and return
+ return new OutputStreamAdapter(mGZIPOut);
+ }
+
+ // TODO: Move this to flushbuffer or something? Hmmm..
+ public void flushResponse() {
+ try {
+ try {
+ // Finish GZIP encodig
+ if (mGZIPOut != null) {
+ mGZIPOut.finish();
+ }
+
+ flushBuffer();
+ }
+ finally {
+ // Close stream
+ if (mWriter != null) {
+ mWriter.close();
+ }
+ else {
+ if (mOut != null) {
+ mOut.close();
+ }
+ }
+ }
+ }
+ catch (IOException e) {
+ // TODO: Fix this one...
+ e.printStackTrace();
+ }
+ }
+
+ public void flushBuffer() throws IOException {
+ if (mWriter != null) {
+ mWriter.flush();
+ }
+ else if (mOut != null) {
+ mOut.flush();
+ }
+ }
+
+ public ServletOutputStream getOutputStream() throws IOException {
+ if (mWriter != null) {
+ throw new IllegalStateException("getWriter() has already been called!");
+ }
+
+ if (mOut == null) {
+ mOut = createOutputStream();
+ }
+ return (mOut);
+ }
+
+ public PrintWriter getWriter() throws IOException {
+ if (mWriter != null) {
+ return (mWriter);
+ }
+
+ if (mOut != null) {
+ throw new IllegalStateException("getOutputStream() has already been called!");
+ }
+
+ mOut = createOutputStream();
+ // TODO: This is wrong. Should use getCharacterEncoding() or "ISO-8859-1" if gCE returns null.
+ mWriter = new PrintWriter(new OutputStreamWriter(mOut, "UTF-8"));
+ return (mWriter);
+ }
+
+ public void setContentLength(int pLength) {
+ // NOTE: Do not call super, as we will shrink the size.
+ mContentLength = pLength;
+ }
+}
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/AWTImageFilterAdapter.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/AWTImageFilterAdapter.java
index 10164832..045fedcf 100755
--- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/AWTImageFilterAdapter.java
+++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/AWTImageFilterAdapter.java
@@ -1,72 +1,72 @@
-/*
- * Copyright (c) 2008, Harald Kuhr
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name "TwelveMonkeys" nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package com.twelvemonkeys.servlet.image;
-
-import com.twelvemonkeys.image.ImageUtil;
-
-import javax.servlet.ServletRequest;
-import java.awt.*;
-import java.awt.image.BufferedImage;
-import java.awt.image.RenderedImage;
-
-/**
- * AWTImageFilterAdapter
- *
- * @author $Author: haku $
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/AWTImageFilterAdapter.java#1 $
- *
- */
-public class AWTImageFilterAdapter extends ImageFilter {
-
- private java.awt.image.ImageFilter mFilter = null;
-
- public void setImageFilter(String pFilterClass) {
- try {
- Class filterClass = Class.forName(pFilterClass);
- mFilter = (java.awt.image.ImageFilter) filterClass.newInstance();
- }
- catch (ClassNotFoundException e) {
- log("Could not load filter class.", e);
- }
- catch (InstantiationException e) {
- log("Could not instantiate filter.", e);
- }
- catch (IllegalAccessException e) {
- log("Could not access filter class.", e);
- }
- }
-
- protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) {
- // Filter
- Image img = ImageUtil.filter(pImage, mFilter);
-
- // Create BufferedImage & return
- return ImageUtil.toBuffered(img, BufferedImage.TYPE_INT_RGB); // TODO: This is for JPEG only...
- }
-}
+/*
+ * Copyright (c) 2008, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name "TwelveMonkeys" nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.twelvemonkeys.servlet.image;
+
+import com.twelvemonkeys.image.ImageUtil;
+
+import javax.servlet.ServletRequest;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.awt.image.RenderedImage;
+
+/**
+ * AWTImageFilterAdapter
+ *
+ * @author $Author: haku $
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/AWTImageFilterAdapter.java#1 $
+ *
+ */
+public class AWTImageFilterAdapter extends ImageFilter {
+
+ private java.awt.image.ImageFilter mFilter = null;
+
+ public void setImageFilter(String pFilterClass) {
+ try {
+ Class filterClass = Class.forName(pFilterClass);
+ mFilter = (java.awt.image.ImageFilter) filterClass.newInstance();
+ }
+ catch (ClassNotFoundException e) {
+ log("Could not load filter class.", e);
+ }
+ catch (InstantiationException e) {
+ log("Could not instantiate filter.", e);
+ }
+ catch (IllegalAccessException e) {
+ log("Could not access filter class.", e);
+ }
+ }
+
+ protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) {
+ // Filter
+ Image img = ImageUtil.filter(pImage, mFilter);
+
+ // Create BufferedImage & return
+ return ImageUtil.toBuffered(img, BufferedImage.TYPE_INT_RGB); // TODO: This is for JPEG only...
+ }
+}
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/BufferedImageOpAdapter.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/BufferedImageOpAdapter.java
index 68d4ea50..999f17f5 100755
--- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/BufferedImageOpAdapter.java
+++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/BufferedImageOpAdapter.java
@@ -1,67 +1,67 @@
-/*
- * Copyright (c) 2008, Harald Kuhr
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name "TwelveMonkeys" nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package com.twelvemonkeys.servlet.image;
-
-import javax.servlet.ServletRequest;
-import java.awt.image.BufferedImage;
-import java.awt.image.BufferedImageOp;
-import java.awt.image.RenderedImage;
-
-/**
- * BufferedImageOpAdapter
- *
- * @author $Author: haku $
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/BufferedImageOpAdapter.java#1 $
- *
- */
-public class BufferedImageOpAdapter extends ImageFilter {
-
- private BufferedImageOp mFilter = null;
-
- public void setImageFilter(String pFilterClass) {
- try {
- Class filterClass = Class.forName(pFilterClass);
- mFilter = (BufferedImageOp) filterClass.newInstance();
- }
- catch (ClassNotFoundException e) {
- log("Could not instantiate filter class.", e);
- }
- catch (InstantiationException e) {
- log("Could not instantiate filter.", e);
- }
- catch (IllegalAccessException e) {
- log("Could not access filter class.", e);
- }
- }
-
- protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) {
- // Filter & return
- return mFilter.filter(pImage, null);
- }
-}
+/*
+ * Copyright (c) 2008, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name "TwelveMonkeys" nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.twelvemonkeys.servlet.image;
+
+import javax.servlet.ServletRequest;
+import java.awt.image.BufferedImage;
+import java.awt.image.BufferedImageOp;
+import java.awt.image.RenderedImage;
+
+/**
+ * BufferedImageOpAdapter
+ *
+ * @author $Author: haku $
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/BufferedImageOpAdapter.java#1 $
+ *
+ */
+public class BufferedImageOpAdapter extends ImageFilter {
+
+ private BufferedImageOp mFilter = null;
+
+ public void setImageFilter(String pFilterClass) {
+ try {
+ Class filterClass = Class.forName(pFilterClass);
+ mFilter = (BufferedImageOp) filterClass.newInstance();
+ }
+ catch (ClassNotFoundException e) {
+ log("Could not instantiate filter class.", e);
+ }
+ catch (InstantiationException e) {
+ log("Could not instantiate filter.", e);
+ }
+ catch (IllegalAccessException e) {
+ log("Could not access filter class.", e);
+ }
+ }
+
+ protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) {
+ // Filter & return
+ return mFilter.filter(pImage, null);
+ }
+}
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ColorServlet.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ColorServlet.java
index e97833b4..539aca8d 100755
--- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ColorServlet.java
+++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ColorServlet.java
@@ -1,212 +1,212 @@
-/*
- * Copyright (c) 2008, Harald Kuhr
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name "TwelveMonkeys" nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package com.twelvemonkeys.servlet.image;
-
-import com.twelvemonkeys.servlet.GenericServlet;
-
-import javax.servlet.ServletException;
-import javax.servlet.ServletOutputStream;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import java.io.IOException;
-import java.util.zip.CRC32;
-
-/**
- * Creates a minimal 1 x 1 pixel PNG image, in a color specified by the
- * {@code "color"} parameter. The color is HTML-style #RRGGBB, with two
- * digits hex number for red, green and blue (the hash, '#', is optional).
- *
- * The class does only byte manipulation, there is no server-side image
- * processing involving AWT ({@code Toolkit} class) of any kind.
- *
- * @author Harald Kuhr
- * @author last modified by $Author: haku $
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ColorServlet.java#2 $
- */
-public class ColorServlet extends GenericServlet {
- private final static String RGB_PARAME = "color";
-
- // A minimal, one color indexed PNG
- private final static byte[] PNG_IMG = new byte[]{
- (byte) 0x89, (byte) 'P', (byte) 'N', (byte) 'G', // PNG signature (8 bytes)
- 0x0d, 0x0a, 0x1a, 0x0a,
-
- 0x00, 0x00, 0x00, 0x0d, // IHDR length (13)
- (byte) 'I', (byte) 'H', (byte) 'D', (byte) 'R', // Image header
- 0x00, 0x00, 0x00, 0x01, // width
- 0x00, 0x00, 0x00, 0x01, // height
- 0x01, 0x03, 0x00, 0x00, 0x00, // bits, color type, compression, filter, interlace
- 0x25, (byte) 0xdb, 0x56, (byte) 0xca, // IHDR CRC
-
- 0x00, 0x00, 0x00, 0x03, // PLTE length (3)
- (byte) 'P', (byte) 'L', (byte) 'T', (byte) 'E', // Palette
- 0x00, 0x00, (byte) 0xff, // red, green, blue (updated by this servlet)
- (byte) 0x8a, (byte) 0x78, (byte) 0xd2, 0x57, // PLTE CRC
-
- 0x00, 0x00, 0x00, 0x0a, // IDAT length (10)
- (byte) 'I', (byte) 'D', (byte) 'A', (byte) 'T', // Image data
- 0x78, (byte) 0xda, 0x63, 0x60, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01,
- (byte) 0xe5, 0x27, (byte) 0xde, (byte) 0xfc, // IDAT CRC
-
-
- 0x00, 0x00, 0x00, 0x00, // IEND length (0)
- (byte) 'I', (byte) 'E', (byte) 'N', (byte) 'D', // Image end
- (byte) 0xae, (byte) 0x42, (byte) 0x60, (byte) 0x82 // IEND CRC
- };
-
- private final static int PLTE_CHUNK_START = 37; // after chunk length
- private final static int PLTE_CHUNK_LENGTH = 7; // chunk name & data
-
- private final static int RED_IDX = 4;
- private final static int GREEN_IDX = RED_IDX + 1;
- private final static int BLUE_IDX = GREEN_IDX + 1;
-
- private final CRC32 mCRC = new CRC32();
-
- /**
- * Creates a ColorDroplet.
- */
- public ColorServlet() {
- super();
- }
-
- /**
- * Renders the 1 x 1 single color PNG to the response.
- *
- * @see ColorServlet class description
- *
- * @param pRequest the request
- * @param pResponse the response
- *
- * @throws IOException
- * @throws ServletException
- */
- public void service(ServletRequest pRequest, ServletResponse pResponse) throws IOException, ServletException {
-
- int red = 0;
- int green = 0;
- int blue = 0;
-
- // Get color parameter and parse color
- String rgb = pRequest.getParameter(RGB_PARAME);
- if (rgb != null && rgb.length() >= 6 && rgb.length() <= 7) {
- int index = 0;
-
- // If the hash ('#') character is included, skip it.
- if (rgb.length() == 7) {
- index++;
- }
-
- try {
- // Two digit hex for each color
- String r = rgb.substring(index, index += 2);
- red = Integer.parseInt(r, 0x10);
-
- String g = rgb.substring(index, index += 2);
- green = Integer.parseInt(g, 0x10);
-
- String b = rgb.substring(index, index += 2);
- blue = Integer.parseInt(b, 0x10);
- }
- catch (NumberFormatException nfe) {
- log("Wrong color format for ColorDroplet: " + rgb + ". Must be RRGGBB.");
- }
- }
-
- // Set MIME type for PNG
- pResponse.setContentType("image/png");
- ServletOutputStream out = pResponse.getOutputStream();
-
- try {
- // Write header (and palette chunk length)
- out.write(PNG_IMG, 0, PLTE_CHUNK_START);
-
- // Create palette chunk, excl lenght, and write
- byte[] palette = makePalette(red, green, blue);
- out.write(palette);
-
- // Write image data until end
- int pos = PLTE_CHUNK_START + PLTE_CHUNK_LENGTH + 4;
- out.write(PNG_IMG, pos, PNG_IMG.length - pos);
- }
- finally {
- out.flush();
- }
- }
-
- /**
- * Updates the CRC for a byte array. Note that the byte array must be at
- * least {@code pOff + pLen + 4} bytes long, as the CRC is stored in the
- * 4 last bytes.
- *
- * @param pBytes the bytes to create CRC for
- * @param pOff the offset into the byte array to create CRC for
- * @param pLen the length of the byte array to create CRC for
- */
- private void updateCRC(byte[] pBytes, int pOff, int pLen) {
- int value;
-
- synchronized (mCRC) {
- mCRC.reset();
- mCRC.update(pBytes, pOff, pLen);
- value = (int) mCRC.getValue();
- }
-
- pBytes[pOff + pLen ] = (byte) ((value >> 24) & 0xff);
- pBytes[pOff + pLen + 1] = (byte) ((value >> 16) & 0xff);
- pBytes[pOff + pLen + 2] = (byte) ((value >> 8) & 0xff);
- pBytes[pOff + pLen + 3] = (byte) ( value & 0xff);
- }
-
- /**
- * Creates a PNG palette (PLTE) chunk with one color.
- * The palette chunk data is always 3 bytes in length (one byte per color
- * component).
- * The returned byte array is then {@code 4 + 3 + 4 = 11} bytes,
- * including chunk header, data and CRC.
- *
- * @param pRed the red component
- * @param pGreen the reen component
- * @param pBlue the blue component
- *
- * @return the bytes for the PLTE chunk, including CRC (but not length)
- */
- private byte[] makePalette(int pRed, int pGreen, int pBlue) {
- byte[] palette = new byte[PLTE_CHUNK_LENGTH + 4];
- System.arraycopy(PNG_IMG, PLTE_CHUNK_START, palette, 0, PLTE_CHUNK_LENGTH);
-
- palette[RED_IDX] = (byte) pRed;
- palette[GREEN_IDX] = (byte) pGreen;
- palette[BLUE_IDX] = (byte) pBlue;
-
- updateCRC(palette, 0, PLTE_CHUNK_LENGTH);
-
- return palette;
- }
-}
+/*
+ * Copyright (c) 2008, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name "TwelveMonkeys" nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.twelvemonkeys.servlet.image;
+
+import com.twelvemonkeys.servlet.GenericServlet;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import java.io.IOException;
+import java.util.zip.CRC32;
+
+/**
+ * Creates a minimal 1 x 1 pixel PNG image, in a color specified by the
+ * {@code "color"} parameter. The color is HTML-style #RRGGBB, with two
+ * digits hex number for red, green and blue (the hash, '#', is optional).
+ *
+ * The class does only byte manipulation, there is no server-side image
+ * processing involving AWT ({@code Toolkit} class) of any kind.
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haku $
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ColorServlet.java#2 $
+ */
+public class ColorServlet extends GenericServlet {
+ private final static String RGB_PARAME = "color";
+
+ // A minimal, one color indexed PNG
+ private final static byte[] PNG_IMG = new byte[]{
+ (byte) 0x89, (byte) 'P', (byte) 'N', (byte) 'G', // PNG signature (8 bytes)
+ 0x0d, 0x0a, 0x1a, 0x0a,
+
+ 0x00, 0x00, 0x00, 0x0d, // IHDR length (13)
+ (byte) 'I', (byte) 'H', (byte) 'D', (byte) 'R', // Image header
+ 0x00, 0x00, 0x00, 0x01, // width
+ 0x00, 0x00, 0x00, 0x01, // height
+ 0x01, 0x03, 0x00, 0x00, 0x00, // bits, color type, compression, filter, interlace
+ 0x25, (byte) 0xdb, 0x56, (byte) 0xca, // IHDR CRC
+
+ 0x00, 0x00, 0x00, 0x03, // PLTE length (3)
+ (byte) 'P', (byte) 'L', (byte) 'T', (byte) 'E', // Palette
+ 0x00, 0x00, (byte) 0xff, // red, green, blue (updated by this servlet)
+ (byte) 0x8a, (byte) 0x78, (byte) 0xd2, 0x57, // PLTE CRC
+
+ 0x00, 0x00, 0x00, 0x0a, // IDAT length (10)
+ (byte) 'I', (byte) 'D', (byte) 'A', (byte) 'T', // Image data
+ 0x78, (byte) 0xda, 0x63, 0x60, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01,
+ (byte) 0xe5, 0x27, (byte) 0xde, (byte) 0xfc, // IDAT CRC
+
+
+ 0x00, 0x00, 0x00, 0x00, // IEND length (0)
+ (byte) 'I', (byte) 'E', (byte) 'N', (byte) 'D', // Image end
+ (byte) 0xae, (byte) 0x42, (byte) 0x60, (byte) 0x82 // IEND CRC
+ };
+
+ private final static int PLTE_CHUNK_START = 37; // after chunk length
+ private final static int PLTE_CHUNK_LENGTH = 7; // chunk name & data
+
+ private final static int RED_IDX = 4;
+ private final static int GREEN_IDX = RED_IDX + 1;
+ private final static int BLUE_IDX = GREEN_IDX + 1;
+
+ private final CRC32 mCRC = new CRC32();
+
+ /**
+ * Creates a ColorDroplet.
+ */
+ public ColorServlet() {
+ super();
+ }
+
+ /**
+ * Renders the 1 x 1 single color PNG to the response.
+ *
+ * @see ColorServlet class description
+ *
+ * @param pRequest the request
+ * @param pResponse the response
+ *
+ * @throws IOException
+ * @throws ServletException
+ */
+ public void service(ServletRequest pRequest, ServletResponse pResponse) throws IOException, ServletException {
+
+ int red = 0;
+ int green = 0;
+ int blue = 0;
+
+ // Get color parameter and parse color
+ String rgb = pRequest.getParameter(RGB_PARAME);
+ if (rgb != null && rgb.length() >= 6 && rgb.length() <= 7) {
+ int index = 0;
+
+ // If the hash ('#') character is included, skip it.
+ if (rgb.length() == 7) {
+ index++;
+ }
+
+ try {
+ // Two digit hex for each color
+ String r = rgb.substring(index, index += 2);
+ red = Integer.parseInt(r, 0x10);
+
+ String g = rgb.substring(index, index += 2);
+ green = Integer.parseInt(g, 0x10);
+
+ String b = rgb.substring(index, index += 2);
+ blue = Integer.parseInt(b, 0x10);
+ }
+ catch (NumberFormatException nfe) {
+ log("Wrong color format for ColorDroplet: " + rgb + ". Must be RRGGBB.");
+ }
+ }
+
+ // Set MIME type for PNG
+ pResponse.setContentType("image/png");
+ ServletOutputStream out = pResponse.getOutputStream();
+
+ try {
+ // Write header (and palette chunk length)
+ out.write(PNG_IMG, 0, PLTE_CHUNK_START);
+
+ // Create palette chunk, excl lenght, and write
+ byte[] palette = makePalette(red, green, blue);
+ out.write(palette);
+
+ // Write image data until end
+ int pos = PLTE_CHUNK_START + PLTE_CHUNK_LENGTH + 4;
+ out.write(PNG_IMG, pos, PNG_IMG.length - pos);
+ }
+ finally {
+ out.flush();
+ }
+ }
+
+ /**
+ * Updates the CRC for a byte array. Note that the byte array must be at
+ * least {@code pOff + pLen + 4} bytes long, as the CRC is stored in the
+ * 4 last bytes.
+ *
+ * @param pBytes the bytes to create CRC for
+ * @param pOff the offset into the byte array to create CRC for
+ * @param pLen the length of the byte array to create CRC for
+ */
+ private void updateCRC(byte[] pBytes, int pOff, int pLen) {
+ int value;
+
+ synchronized (mCRC) {
+ mCRC.reset();
+ mCRC.update(pBytes, pOff, pLen);
+ value = (int) mCRC.getValue();
+ }
+
+ pBytes[pOff + pLen ] = (byte) ((value >> 24) & 0xff);
+ pBytes[pOff + pLen + 1] = (byte) ((value >> 16) & 0xff);
+ pBytes[pOff + pLen + 2] = (byte) ((value >> 8) & 0xff);
+ pBytes[pOff + pLen + 3] = (byte) ( value & 0xff);
+ }
+
+ /**
+ * Creates a PNG palette (PLTE) chunk with one color.
+ * The palette chunk data is always 3 bytes in length (one byte per color
+ * component).
+ * The returned byte array is then {@code 4 + 3 + 4 = 11} bytes,
+ * including chunk header, data and CRC.
+ *
+ * @param pRed the red component
+ * @param pGreen the reen component
+ * @param pBlue the blue component
+ *
+ * @return the bytes for the PLTE chunk, including CRC (but not length)
+ */
+ private byte[] makePalette(int pRed, int pGreen, int pBlue) {
+ byte[] palette = new byte[PLTE_CHUNK_LENGTH + 4];
+ System.arraycopy(PNG_IMG, PLTE_CHUNK_START, palette, 0, PLTE_CHUNK_LENGTH);
+
+ palette[RED_IDX] = (byte) pRed;
+ palette[GREEN_IDX] = (byte) pGreen;
+ palette[BLUE_IDX] = (byte) pBlue;
+
+ updateCRC(palette, 0, PLTE_CHUNK_LENGTH);
+
+ return palette;
+ }
+}
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ComposeFilter.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ComposeFilter.java
index 0952c366..fc9a67e8 100755
--- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ComposeFilter.java
+++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ComposeFilter.java
@@ -1,59 +1,59 @@
-/*
- * Copyright (c) 2008, Harald Kuhr
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name "TwelveMonkeys" nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package com.twelvemonkeys.servlet.image;
-
-import javax.servlet.ServletRequest;
-import java.awt.image.RenderedImage;
-import java.awt.image.BufferedImage;
-import java.io.IOException;
-
-/**
- * ComposeFilter
- *
- *
- * @author Harald Kuhr
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ComposeFilter.java#1 $
- */
-public class ComposeFilter extends ImageFilter {
- protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) throws IOException {
- // 1. Load different image, locally (using ServletContext.getResource)
- // - Allow loading other filtered sources, or servlets? For example to
- // apply filename or timestamps?
- // - Allow applying text directly? Variables?
- // 2. Apply transformations from config
- // - Relative positioning
- // - Relative scaling
- // - Repeat (fill-pattern)?
- // - Rotation?
- // - Transparency?
- // - Background or foreground (layers)?
- // 3. Apply loaded image to original image (or vice versa?).
- return pImage;
- }
-}
+/*
+ * Copyright (c) 2008, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name "TwelveMonkeys" nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.twelvemonkeys.servlet.image;
+
+import javax.servlet.ServletRequest;
+import java.awt.image.RenderedImage;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+
+/**
+ * ComposeFilter
+ *
+ *
+ * @author Harald Kuhr
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ComposeFilter.java#1 $
+ */
+public class ComposeFilter extends ImageFilter {
+ protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) throws IOException {
+ // 1. Load different image, locally (using ServletContext.getResource)
+ // - Allow loading other filtered sources, or servlets? For example to
+ // apply filename or timestamps?
+ // - Allow applying text directly? Variables?
+ // 2. Apply transformations from config
+ // - Relative positioning
+ // - Relative scaling
+ // - Repeat (fill-pattern)?
+ // - Rotation?
+ // - Transparency?
+ // - Background or foreground (layers)?
+ // 3. Apply loaded image to original image (or vice versa?).
+ return pImage;
+ }
+}
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ContentNegotiationFilter.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ContentNegotiationFilter.java
index 4d2996ee..37fea59f 100755
--- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ContentNegotiationFilter.java
+++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ContentNegotiationFilter.java
@@ -1,436 +1,436 @@
-/*
- * Copyright (c) 2008, Harald Kuhr
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name "TwelveMonkeys" nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package com.twelvemonkeys.servlet.image;
-
-import com.twelvemonkeys.image.ImageUtil;
-import com.twelvemonkeys.lang.StringUtil;
-
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.FilterChain;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.imageio.ImageIO;
-import java.awt.*;
-import java.awt.image.BufferedImage;
-import java.awt.image.IndexColorModel;
-import java.awt.image.RenderedImage;
-import java.io.IOException;
-import java.net.HttpURLConnection;
-import java.util.*;
-
-/**
- * This filter implements server side content negotiation and transcoding for
- * images.
- *
- * @todo Add support for automatic recognition of known browsers, to avoid
- * unneccessary conversion (as IE supports PNG, the latests FireFox supports
- * JPEG and GIF, etc. even though they both don't explicitly list these formats
- * in their Accept headers).
- */
-public class ContentNegotiationFilter extends ImageFilter {
-
- private final static String MIME_TYPE_IMAGE_PREFIX = "image/";
- private static final String MIME_TYPE_IMAGE_ANY = MIME_TYPE_IMAGE_PREFIX + "*";
- private static final String MIME_TYPE_ANY = "*/*";
- private static final String HTTP_HEADER_ACCEPT = "Accept";
- private static final String HTTP_HEADER_VARY = "Vary";
- protected static final String HTTP_HEADER_USER_AGENT = "User-Agent";
-
- private static final String FORMAT_JPEG = "image/jpeg";
- private static final String FORMAT_WBMP = "image/wbmp";
- private static final String FORMAT_GIF = "image/gif";
- private static final String FORMAT_PNG = "image/png";
-
- private final static String[] sKnownFormats = new String[] {
- FORMAT_JPEG, FORMAT_PNG, FORMAT_GIF, FORMAT_WBMP
- };
- private float[] mKnownFormatQuality = new float[] {
- 1f, 1f, 0.99f, 0.5f
- };
-
- private HashMap
- *
- *
- * @example
- * <IMG src="/crop/test.jpg?image=http://www.iconmedialab.com/images/random/home_image_12.jpg&cropWidth=500&cropUniform=true">
- *
- * @example
- * <IMG src="/crop/test.png?cache=false&image=http://www.iconmedialab.com/images/random/home_image_12.jpg&cropWidth=50&cropUnits=PERCENT">
- *
- * @author Harald Kuhr
- * @author last modified by $Author: haku $
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/CropFilter.java#1 $
- */
-public class CropFilter extends ScaleFilter {
- /** {@code cropX}*/
- protected final static String PARAM_CROP_X = "cropX";
- /** {@code cropY}*/
- protected final static String PARAM_CROP_Y = "cropY";
- /** {@code cropWidth}*/
- protected final static String PARAM_CROP_WIDTH = "cropWidth";
- /** {@code cropHeight}*/
- protected final static String PARAM_CROP_HEIGHT = "cropHeight";
- /** {@code cropUniform}*/
- protected final static String PARAM_CROP_UNIFORM = "cropUniform";
- /** {@code cropUnits}*/
- protected final static String PARAM_CROP_UNITS = "cropUnits";
-
- /**
- * Reads the image from the requested URL, scales it, crops it, and returns
- * it in the
- * Servlet stream. See above for details on parameters.
- */
- protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) {
- // Get crop coordinates
- int x = ServletUtil.getIntParameter(pRequest, PARAM_CROP_X, -1);
- int y = ServletUtil.getIntParameter(pRequest, PARAM_CROP_Y, -1);
- int width = ServletUtil.getIntParameter(pRequest, PARAM_CROP_WIDTH, -1);
- int height = ServletUtil.getIntParameter(pRequest, PARAM_CROP_HEIGHT, -1);
-
- boolean uniform =
- ServletUtil.getBooleanParameter(pRequest, PARAM_CROP_UNIFORM, false);
-
- int units = getUnits(ServletUtil.getParameter(pRequest, PARAM_CROP_UNITS, null));
-
- // Get crop bounds
- Rectangle bounds =
- getBounds(x, y, width, height, units, uniform, pImage);
-
- // Return cropped version
- return pImage.getSubimage((int) bounds.getX(), (int) bounds.getY(),
- (int) bounds.getWidth(),
- (int) bounds.getHeight());
- //return scaled.getSubimage(x, y, width, height);
- }
-
- protected Rectangle getBounds(int pX, int pY, int pWidth, int pHeight,
- int pUnits, boolean pUniform,
- BufferedImage pImg) {
- // Algoritm:
- // Try to get x and y (default 0,0).
- // Try to get width and height (default width-x, height-y)
- //
- // If percent, get ratio
- //
- // If uniform
- //
-
- int oldWidth = pImg.getWidth();
- int oldHeight = pImg.getHeight();
- float ratio;
-
- if (pUnits == UNITS_PERCENT) {
- if (pWidth >= 0 && pHeight >= 0) {
- // Non-uniform
- pWidth = (int) ((float) oldWidth * (float) pWidth / 100f);
- pHeight = (int) ((float) oldHeight * (float) pHeight / 100f);
- }
- else if (pWidth >= 0) {
- // Find ratio from pWidth
- ratio = (float) pWidth / 100f;
- pWidth = (int) ((float) oldWidth * ratio);
- pHeight = (int) ((float) oldHeight * ratio);
-
- }
- else if (pHeight >= 0) {
- // Find ratio from pHeight
- ratio = (float) pHeight / 100f;
- pWidth = (int) ((float) oldWidth * ratio);
- pHeight = (int) ((float) oldHeight * ratio);
- }
- // Else: No crop
- }
- //else if (UNITS_PIXELS.equalsIgnoreCase(pUnits)) {
- else if (pUnits == UNITS_PIXELS) {
- // Uniform
- if (pUniform) {
- if (pWidth >= 0 && pHeight >= 0) {
- // Compute both ratios
- ratio = (float) pWidth / (float) oldWidth;
- float heightRatio = (float) pHeight / (float) oldHeight;
-
- // Find the largest ratio, and use that for both
- if (heightRatio < ratio) {
- ratio = heightRatio;
- pWidth = (int) ((float) oldWidth * ratio);
- }
- else {
- pHeight = (int) ((float) oldHeight * ratio);
- }
-
- }
- else if (pWidth >= 0) {
- // Find ratio from pWidth
- ratio = (float) pWidth / (float) oldWidth;
- pHeight = (int) ((float) oldHeight * ratio);
- }
- else if (pHeight >= 0) {
- // Find ratio from pHeight
- ratio = (float) pHeight / (float) oldHeight;
- pWidth = (int) ((float) oldWidth * ratio);
- }
- // Else: No crop
- }
- }
- // Else: No crop
-
- // Not specified, or outside bounds: Use original dimensions
- if (pWidth < 0 || (pX < 0 && pWidth > oldWidth)
- || (pX >= 0 && (pX + pWidth) > oldWidth)) {
- pWidth = (pX >= 0 ? oldWidth - pX : oldWidth);
- }
- if (pHeight < 0 || (pY < 0 && pHeight > oldHeight)
- || (pY >= 0 && (pY + pHeight) > oldHeight)) {
- pHeight = (pY >= 0 ? oldHeight - pY : oldHeight);
- }
-
- // Center
- if (pX < 0) {
- pX = (pImg.getWidth() - pWidth) / 2;
- }
- if (pY < 0) {
- pY = (pImg.getHeight() - pHeight) / 2;
- }
-
- //System.out.println("x: " + pX + " y: " + pY
- // + " w: " + pWidth + " h " + pHeight);
-
- return new Rectangle(pX, pY, pWidth, pHeight);
- }
+/*
+ * Copyright (c) 2008, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name "TwelveMonkeys" nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.twelvemonkeys.servlet.image;
+
+import com.twelvemonkeys.servlet.ServletUtil;
+
+import javax.servlet.ServletRequest;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.awt.image.RenderedImage;
+
+/**
+ * This Servlet is able to render a cropped part of an image.
+ *
+ *
+ *
+ *
+ * @example
+ * <IMG src="/crop/test.jpg?image=http://www.iconmedialab.com/images/random/home_image_12.jpg&cropWidth=500&cropUniform=true">
+ *
+ * @example
+ * <IMG src="/crop/test.png?cache=false&image=http://www.iconmedialab.com/images/random/home_image_12.jpg&cropWidth=50&cropUnits=PERCENT">
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haku $
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/CropFilter.java#1 $
+ */
+public class CropFilter extends ScaleFilter {
+ /** {@code cropX}*/
+ protected final static String PARAM_CROP_X = "cropX";
+ /** {@code cropY}*/
+ protected final static String PARAM_CROP_Y = "cropY";
+ /** {@code cropWidth}*/
+ protected final static String PARAM_CROP_WIDTH = "cropWidth";
+ /** {@code cropHeight}*/
+ protected final static String PARAM_CROP_HEIGHT = "cropHeight";
+ /** {@code cropUniform}*/
+ protected final static String PARAM_CROP_UNIFORM = "cropUniform";
+ /** {@code cropUnits}*/
+ protected final static String PARAM_CROP_UNITS = "cropUnits";
+
+ /**
+ * Reads the image from the requested URL, scales it, crops it, and returns
+ * it in the
+ * Servlet stream. See above for details on parameters.
+ */
+ protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) {
+ // Get crop coordinates
+ int x = ServletUtil.getIntParameter(pRequest, PARAM_CROP_X, -1);
+ int y = ServletUtil.getIntParameter(pRequest, PARAM_CROP_Y, -1);
+ int width = ServletUtil.getIntParameter(pRequest, PARAM_CROP_WIDTH, -1);
+ int height = ServletUtil.getIntParameter(pRequest, PARAM_CROP_HEIGHT, -1);
+
+ boolean uniform =
+ ServletUtil.getBooleanParameter(pRequest, PARAM_CROP_UNIFORM, false);
+
+ int units = getUnits(ServletUtil.getParameter(pRequest, PARAM_CROP_UNITS, null));
+
+ // Get crop bounds
+ Rectangle bounds =
+ getBounds(x, y, width, height, units, uniform, pImage);
+
+ // Return cropped version
+ return pImage.getSubimage((int) bounds.getX(), (int) bounds.getY(),
+ (int) bounds.getWidth(),
+ (int) bounds.getHeight());
+ //return scaled.getSubimage(x, y, width, height);
+ }
+
+ protected Rectangle getBounds(int pX, int pY, int pWidth, int pHeight,
+ int pUnits, boolean pUniform,
+ BufferedImage pImg) {
+ // Algoritm:
+ // Try to get x and y (default 0,0).
+ // Try to get width and height (default width-x, height-y)
+ //
+ // If percent, get ratio
+ //
+ // If uniform
+ //
+
+ int oldWidth = pImg.getWidth();
+ int oldHeight = pImg.getHeight();
+ float ratio;
+
+ if (pUnits == UNITS_PERCENT) {
+ if (pWidth >= 0 && pHeight >= 0) {
+ // Non-uniform
+ pWidth = (int) ((float) oldWidth * (float) pWidth / 100f);
+ pHeight = (int) ((float) oldHeight * (float) pHeight / 100f);
+ }
+ else if (pWidth >= 0) {
+ // Find ratio from pWidth
+ ratio = (float) pWidth / 100f;
+ pWidth = (int) ((float) oldWidth * ratio);
+ pHeight = (int) ((float) oldHeight * ratio);
+
+ }
+ else if (pHeight >= 0) {
+ // Find ratio from pHeight
+ ratio = (float) pHeight / 100f;
+ pWidth = (int) ((float) oldWidth * ratio);
+ pHeight = (int) ((float) oldHeight * ratio);
+ }
+ // Else: No crop
+ }
+ //else if (UNITS_PIXELS.equalsIgnoreCase(pUnits)) {
+ else if (pUnits == UNITS_PIXELS) {
+ // Uniform
+ if (pUniform) {
+ if (pWidth >= 0 && pHeight >= 0) {
+ // Compute both ratios
+ ratio = (float) pWidth / (float) oldWidth;
+ float heightRatio = (float) pHeight / (float) oldHeight;
+
+ // Find the largest ratio, and use that for both
+ if (heightRatio < ratio) {
+ ratio = heightRatio;
+ pWidth = (int) ((float) oldWidth * ratio);
+ }
+ else {
+ pHeight = (int) ((float) oldHeight * ratio);
+ }
+
+ }
+ else if (pWidth >= 0) {
+ // Find ratio from pWidth
+ ratio = (float) pWidth / (float) oldWidth;
+ pHeight = (int) ((float) oldHeight * ratio);
+ }
+ else if (pHeight >= 0) {
+ // Find ratio from pHeight
+ ratio = (float) pHeight / (float) oldHeight;
+ pWidth = (int) ((float) oldWidth * ratio);
+ }
+ // Else: No crop
+ }
+ }
+ // Else: No crop
+
+ // Not specified, or outside bounds: Use original dimensions
+ if (pWidth < 0 || (pX < 0 && pWidth > oldWidth)
+ || (pX >= 0 && (pX + pWidth) > oldWidth)) {
+ pWidth = (pX >= 0 ? oldWidth - pX : oldWidth);
+ }
+ if (pHeight < 0 || (pY < 0 && pHeight > oldHeight)
+ || (pY >= 0 && (pY + pHeight) > oldHeight)) {
+ pHeight = (pY >= 0 ? oldHeight - pY : oldHeight);
+ }
+
+ // Center
+ if (pX < 0) {
+ pX = (pImg.getWidth() - pWidth) / 2;
+ }
+ if (pY < 0) {
+ pY = (pImg.getHeight() - pHeight) / 2;
+ }
+
+ //System.out.println("x: " + pX + " y: " + pY
+ // + " w: " + pWidth + " h " + pHeight);
+
+ return new Rectangle(pX, pY, pWidth, pHeight);
+ }
}
\ No newline at end of file
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageFilter.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageFilter.java
index 04978301..7b716c9e 100755
--- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageFilter.java
+++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageFilter.java
@@ -1,199 +1,199 @@
-/*
- * Copyright (c) 2008, Harald Kuhr
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name "TwelveMonkeys" nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package com.twelvemonkeys.servlet.image;
-
-import com.twelvemonkeys.image.ImageUtil;
-import com.twelvemonkeys.lang.StringUtil;
-import com.twelvemonkeys.servlet.GenericFilter;
-
-import javax.servlet.*;
-import java.awt.image.BufferedImage;
-import java.awt.image.RenderedImage;
-import java.io.IOException;
-
-/**
- * Abstract base class for image filters. Automatically decoding and encoding of
- * the image is handled in the {@code doFilterImpl} method.
- *
- * @see #doFilter(java.awt.image.BufferedImage,javax.servlet.ServletRequest,ImageServletResponse)
- *
- * @author Harald Kuhr
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageFilter.java#2 $
- *
- */
-public abstract class ImageFilter extends GenericFilter {
-
- protected String[] mTriggerParams = null;
-
- /**
- * The {@code doFilterImpl} method is called once, or each time a
- * request/response pair is passed through the chain, depending on the
- * {@link #mOncePerRequest} member variable.
- *
- * @see #mOncePerRequest
- * @see com.twelvemonkeys.servlet.GenericFilter#doFilterImpl doFilter
- * @see Filter#doFilter Filter.doFilter
- *
- * @param pRequest the servlet request
- * @param pResponse the servlet response
- * @param pChain the filter chain
- *
- * @throws IOException
- * @throws ServletException
- */
- protected void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain)
- throws IOException, ServletException {
-
- //System.out.println("Starting filtering...");
- // Test for trigger params
- if (!trigger(pRequest)) {
- //System.out.println("Passing request on to next in chain (skipping " + getFilterName() + ")...");
- // Pass the request on
- pChain.doFilter(pRequest, pResponse);
- }
- else {
- // For images, we do post filtering only and need to wrap the response
- ImageServletResponse imageResponse;
- boolean encode;
- if (pResponse instanceof ImageServletResponse) {
- //System.out.println("Allready ImageServletResponse");
- imageResponse = (ImageServletResponse) pResponse;
- encode = false; // Allready wrapped, will be encoded later in the chain
- }
- else {
- //System.out.println("Wrapping in ImageServletResponse");
- imageResponse = new ImageServletResponseImpl(pRequest, pResponse, getServletContext());
- encode = true; // This is first filter in chain, must encode when done
- }
-
- //System.out.println("Passing request on to next in chain...");
- // Pass the request on
- pChain.doFilter(pRequest, imageResponse);
-
- //System.out.println("Post filtering...");
-
- // Get image
- //System.out.println("Getting image from ImageServletResponse...");
- // Get the image from the wrapped response
- RenderedImage image = imageResponse.getImage();
- //System.out.println("Got image: " + image);
-
- // Note: Image will be null if this is a HEAD request, the
- // If-Modified-Since header is present, or similar.
- if (image != null) {
- // Do the image filtering
- //System.out.println("Filtering image (" + getFilterName() + ")...");
- image = doFilter(ImageUtil.toBuffered(image), pRequest, imageResponse);
- //System.out.println("Done filtering.");
-
- //System.out.println("Making image available...");
- // Make image available to other filters (avoid unnecessary
- // serializing/deserializing)
- imageResponse.setImage(image);
- //System.out.println("Done.");
-
- if (encode) {
- //System.out.println("Encoding image...");
- // Encode image to original repsonse
- if (image != null) {
- // TODO: Be smarter than this...
- // TODO: Make sure ETag is same, if image content is the same...
- // Use ETag of original response (or derived from)
- // Use last modified of original response? Or keep original resource's, don't set at all?
- // TODO: Why weak ETag?
- String etag = "W/\"" + Integer.toHexString(hashCode()) + "-" + Integer.toHexString(image.hashCode()) + "\"";
- ((ImageServletResponseImpl) imageResponse).setHeader("ETag", etag);
- ((ImageServletResponseImpl) imageResponse).setDateHeader("Last-Modified", (System.currentTimeMillis() / 1000) * 1000);
- imageResponse.flush();
- }
- //System.out.println("Done encoding.");
- }
- }
- }
- //System.out.println("Filtering done.");
- }
-
- /**
- * Tests if the filter should do image filtering/processing.
- *
- * This default implementation uses {@link #mTriggerParams} to test if:
- *
- *
- *
- *
- * @param pRequest the servlet request
- * @return {@code true} if the filter should do image filtering
- */
- protected boolean trigger(ServletRequest pRequest) {
- // If triggerParams not set, assume always trigger
- if (mTriggerParams == null) {
- return true;
- }
-
- // Trigger only for certain request parameters
- for (String triggerParam : mTriggerParams) {
- if (pRequest.getParameter(triggerParam) != null) {
- return true;
- }
- }
-
- // Didn't trigger
- return false;
- }
-
- /**
- * Sets the trigger parameters.
- * The parameter is supposed to be a comma-separated string of parameter
- * names.
- *
- * @param pTriggerParams a comma-separated string of parameter names.
- */
- public void setTriggerParams(String pTriggerParams) {
- mTriggerParams = StringUtil.toStringArray(pTriggerParams);
- }
-
- /**
- * Filters the image for this request.
- *
- * @param pImage the image to filter
- * @param pRequest the servlet request
- * @param pResponse the servlet response
- *
- * @return the filtered image
- * @throws java.io.IOException if an I/O error occurs during filtering
- */
- protected abstract RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) throws IOException;
-}
+/*
+ * Copyright (c) 2008, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name "TwelveMonkeys" nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.twelvemonkeys.servlet.image;
+
+import com.twelvemonkeys.image.ImageUtil;
+import com.twelvemonkeys.lang.StringUtil;
+import com.twelvemonkeys.servlet.GenericFilter;
+
+import javax.servlet.*;
+import java.awt.image.BufferedImage;
+import java.awt.image.RenderedImage;
+import java.io.IOException;
+
+/**
+ * Abstract base class for image filters. Automatically decoding and encoding of
+ * the image is handled in the {@code doFilterImpl} method.
+ *
+ * @see #doFilter(java.awt.image.BufferedImage,javax.servlet.ServletRequest,ImageServletResponse)
+ *
+ * @author Harald Kuhr
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageFilter.java#2 $
+ *
+ */
+public abstract class ImageFilter extends GenericFilter {
+
+ protected String[] mTriggerParams = null;
+
+ /**
+ * The {@code doFilterImpl} method is called once, or each time a
+ * request/response pair is passed through the chain, depending on the
+ * {@link #mOncePerRequest} member variable.
+ *
+ * @see #mOncePerRequest
+ * @see com.twelvemonkeys.servlet.GenericFilter#doFilterImpl doFilter
+ * @see Filter#doFilter Filter.doFilter
+ *
+ * @param pRequest the servlet request
+ * @param pResponse the servlet response
+ * @param pChain the filter chain
+ *
+ * @throws IOException
+ * @throws ServletException
+ */
+ protected void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain)
+ throws IOException, ServletException {
+
+ //System.out.println("Starting filtering...");
+ // Test for trigger params
+ if (!trigger(pRequest)) {
+ //System.out.println("Passing request on to next in chain (skipping " + getFilterName() + ")...");
+ // Pass the request on
+ pChain.doFilter(pRequest, pResponse);
+ }
+ else {
+ // For images, we do post filtering only and need to wrap the response
+ ImageServletResponse imageResponse;
+ boolean encode;
+ if (pResponse instanceof ImageServletResponse) {
+ //System.out.println("Allready ImageServletResponse");
+ imageResponse = (ImageServletResponse) pResponse;
+ encode = false; // Allready wrapped, will be encoded later in the chain
+ }
+ else {
+ //System.out.println("Wrapping in ImageServletResponse");
+ imageResponse = new ImageServletResponseImpl(pRequest, pResponse, getServletContext());
+ encode = true; // This is first filter in chain, must encode when done
+ }
+
+ //System.out.println("Passing request on to next in chain...");
+ // Pass the request on
+ pChain.doFilter(pRequest, imageResponse);
+
+ //System.out.println("Post filtering...");
+
+ // Get image
+ //System.out.println("Getting image from ImageServletResponse...");
+ // Get the image from the wrapped response
+ RenderedImage image = imageResponse.getImage();
+ //System.out.println("Got image: " + image);
+
+ // Note: Image will be null if this is a HEAD request, the
+ // If-Modified-Since header is present, or similar.
+ if (image != null) {
+ // Do the image filtering
+ //System.out.println("Filtering image (" + getFilterName() + ")...");
+ image = doFilter(ImageUtil.toBuffered(image), pRequest, imageResponse);
+ //System.out.println("Done filtering.");
+
+ //System.out.println("Making image available...");
+ // Make image available to other filters (avoid unnecessary
+ // serializing/deserializing)
+ imageResponse.setImage(image);
+ //System.out.println("Done.");
+
+ if (encode) {
+ //System.out.println("Encoding image...");
+ // Encode image to original repsonse
+ if (image != null) {
+ // TODO: Be smarter than this...
+ // TODO: Make sure ETag is same, if image content is the same...
+ // Use ETag of original response (or derived from)
+ // Use last modified of original response? Or keep original resource's, don't set at all?
+ // TODO: Why weak ETag?
+ String etag = "W/\"" + Integer.toHexString(hashCode()) + "-" + Integer.toHexString(image.hashCode()) + "\"";
+ ((ImageServletResponseImpl) imageResponse).setHeader("ETag", etag);
+ ((ImageServletResponseImpl) imageResponse).setDateHeader("Last-Modified", (System.currentTimeMillis() / 1000) * 1000);
+ imageResponse.flush();
+ }
+ //System.out.println("Done encoding.");
+ }
+ }
+ }
+ //System.out.println("Filtering done.");
+ }
+
+ /**
+ * Tests if the filter should do image filtering/processing.
+ *
+ * This default implementation uses {@link #mTriggerParams} to test if:
+ *
+ *
+ *
+ *
+ * @param pRequest the servlet request
+ * @return {@code true} if the filter should do image filtering
+ */
+ protected boolean trigger(ServletRequest pRequest) {
+ // If triggerParams not set, assume always trigger
+ if (mTriggerParams == null) {
+ return true;
+ }
+
+ // Trigger only for certain request parameters
+ for (String triggerParam : mTriggerParams) {
+ if (pRequest.getParameter(triggerParam) != null) {
+ return true;
+ }
+ }
+
+ // Didn't trigger
+ return false;
+ }
+
+ /**
+ * Sets the trigger parameters.
+ * The parameter is supposed to be a comma-separated string of parameter
+ * names.
+ *
+ * @param pTriggerParams a comma-separated string of parameter names.
+ */
+ public void setTriggerParams(String pTriggerParams) {
+ mTriggerParams = StringUtil.toStringArray(pTriggerParams);
+ }
+
+ /**
+ * Filters the image for this request.
+ *
+ * @param pImage the image to filter
+ * @param pRequest the servlet request
+ * @param pResponse the servlet response
+ *
+ * @return the filtered image
+ * @throws java.io.IOException if an I/O error occurs during filtering
+ */
+ protected abstract RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) throws IOException;
+}
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageServletException.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageServletException.java
index 6e4de253..6587c8cb 100755
--- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageServletException.java
+++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageServletException.java
@@ -1,55 +1,55 @@
-/*
- * Copyright (c) 2008, Harald Kuhr
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name "TwelveMonkeys" nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package com.twelvemonkeys.servlet.image;
-
-import javax.servlet.*;
-
-/**
- * This excpetion is a subclass of ServletException, and acts just as a marker
- * for excpetions thrown by the ImageServlet API.
- *
- * @author Harald Kuhr
- * @author last modified by $Author: haku $
- *
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageServletException.java#2 $
- */
-public class ImageServletException extends ServletException {
-
- public ImageServletException(String pMessage) {
- super(pMessage);
- }
-
- public ImageServletException(Throwable pThrowable) {
- super(pThrowable);
- }
-
- public ImageServletException(String pMessage, Throwable pThrowable) {
- super(pMessage, pThrowable);
- }
-}
+/*
+ * Copyright (c) 2008, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name "TwelveMonkeys" nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.twelvemonkeys.servlet.image;
+
+import javax.servlet.*;
+
+/**
+ * This excpetion is a subclass of ServletException, and acts just as a marker
+ * for excpetions thrown by the ImageServlet API.
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haku $
+ *
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageServletException.java#2 $
+ */
+public class ImageServletException extends ServletException {
+
+ public ImageServletException(String pMessage) {
+ super(pMessage);
+ }
+
+ public ImageServletException(Throwable pThrowable) {
+ super(pThrowable);
+ }
+
+ public ImageServletException(String pMessage, Throwable pThrowable) {
+ super(pMessage, pThrowable);
+ }
+}
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageServletResponse.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageServletResponse.java
index 772bd548..557c07cd 100755
--- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageServletResponse.java
+++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageServletResponse.java
@@ -1,193 +1,193 @@
-/*
- * Copyright (c) 2008, Harald Kuhr
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name "TwelveMonkeys" nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package com.twelvemonkeys.servlet.image;
-
-import javax.servlet.ServletResponse;
-import java.io.IOException;
-import java.awt.image.RenderedImage;
-import java.awt.image.BufferedImage;
-
-/**
- * ImageServletResponse.
- *
- * The request attributes regarding image size and source region (AOI) are used
- * in the decoding process, and must be set before the first invocation of
- * {@link #getImage()} to have any effect.
- *
- * @author Harald Kuhr
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageServletResponse.java#4 $
- */
-public interface ImageServletResponse extends ServletResponse {
- /**
- * Request attribute of type {@link java.awt.Dimension} controlling image
- * size.
- * If either {@code width} or {@code height} is negative, the size is
- * computed, using uniform scaling.
- * Else, if {@code SIZE_UNIFORM} is {@code true}, the size will be
- * computed to the largest possible area (with correct aspect ratio)
- * fitting inside the target area.
- * Otherwise, the image is scaled to the given size, with no regard to
- * aspect ratio.
- *
- * Defaults to {@code null} (original image size).
- */
- String ATTRIB_SIZE = "com.twelvemonkeys.servlet.image.ImageServletResponse.SIZE";
-
- /**
- * Request attribute of type {@link Boolean} controlling image sizing.
- *
- * Defaults to {@code Boolean.TRUE}.
- */
- String ATTRIB_SIZE_UNIFORM = "com.twelvemonkeys.servlet.image.ImageServletResponse.SIZE_UNIFORM";
-
- /**
- * Request attribute of type {@link Boolean} controlling image sizing.
- *
- * Defaults to {@code Boolean.FALSE}.
- */
- String ATTRIB_SIZE_PERCENT = "com.twelvemonkeys.servlet.image.ImageServletResponse.SIZE_PERCENT";
-
- /**
- * Request attribute of type {@link java.awt.Rectangle} controlling image
- * source region (area of interest).
- *
- * Defaults to {@code null} (the entire image).
- */
- String ATTRIB_AOI = "com.twelvemonkeys.servlet.image.ImageServletResponse.AOI";
-
- /**
- * Request attribute of type {@link Boolean} controlling image AOI.
- *
- * Defaults to {@code Boolean.FALSE}.
- */
- String ATTRIB_AOI_UNIFORM = "com.twelvemonkeys.servlet.image.ImageServletResponse.AOI_UNIFORM";
-
- /**
- * Request attribute of type {@link Boolean} controlling image AOI.
- *
- * Defaults to {@code Boolean.FALSE}.
- */
- String ATTRIB_AOI_PERCENT = "com.twelvemonkeys.servlet.image.ImageServletResponse.AOI_PERCENT";
-
- /**
- * Request attribute of type {@link java.awt.Color} controlling background
- * color for any transparent/translucent areas of the image.
- *
- * Defaults to {@code null} (keeps the transparent areas transparent).
- */
- String ATTRIB_BG_COLOR = "com.twelvemonkeys.servlet.image.ImageServletResponse.BG_COLOR";
-
- /**
- * Request attribute of type {@link Float} controlling image output compression/quality.
- * Used for formats that accepts compression or quality settings,
- * like JPEG (quality), PNG (compression only) etc.
- *
- * Defaults to {@code 0.8f} for JPEG.
- */
- String ATTRIB_OUTPUT_QUALITY = "com.twelvemonkeys.servlet.image.ImageServletResponse.OUTPUT_QUALITY";
-
- /**
- * Request attribute of type {@link Double} controlling image read
- * subsampling factor. Controls the maximum sample pixels in each direction,
- * that is read per pixel in the output image, if the result will be
- * downscaled.
- * Larger values will result in better quality, at the expense of higher
- * memory consumption and CPU usage.
- * However, using values above {@code 3.0} will usually not improve image
- * quality.
- * Legal values are in the range {@code [1.0 .. positive infinity>}.
- *
- * Defaults to {@code 2.0}.
- */
- String ATTRIB_READ_SUBSAMPLING_FACTOR = "com.twelvemonkeys.servlet.image.ImageServletResponse.READ_SUBSAMPLING_FACTOR";
-
- /**
- * Request attribute of type {@link Integer} controlling image resample
- * algorithm.
- * Legal values are {@link java.awt.Image#SCALE_DEFAULT SCALE_DEFAULT},
- * {@link java.awt.Image#SCALE_FAST SCALE_FAST} or
- * {@link java.awt.Image#SCALE_SMOOTH SCALE_SMOOTH}.
- *
- * Note: When using a value of {@code SCALE_FAST}, you should also use a
- * subsampling factor of {@code 1.0}, for fast read/scale.
- * Otherwise, use a subsampling factor of {@code 2.0} for better quality.
- *
- * Defaults to {@code SCALE_DEFAULT}.
- */
- String ATTRIB_IMAGE_RESAMPLE_ALGORITHM = "com.twelvemonkeys.servlet.image.ImageServletResponse.IMAGE_RESAMPLE_ALGORITHM";
-
- /**
- * Gets the image format for this response, such as "image/gif" or "image/jpeg".
- * If not set, the default format is that of the original image.
- *
- * @return the image format for this response.
- * @see #setOutputContentType(String)
- */
- String getOutputContentType();
-
- /**
- * Sets the image format for this response, such as "image/gif" or "image/jpeg".
- *
- * 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.
- *
- * If not set, the default format is that of the original image.
- *
- * @param pImageFormat the image format for this response.
- */
- void setOutputContentType(String pImageFormat);
-
- //TODO: ?? void setCompressionQuality(float pQualityFactor);
- //TODO: ?? float getCompressionQuality();
-
- /**
- * Writes the image to the original {@code ServletOutputStream}.
- * If no format is {@linkplain #setOutputContentType(String) set} in this response,
- * the image is encoded in the same format as the original image.
- *
- * @throws java.io.IOException if an I/O exception occurs during writing
- */
- void flush() throws IOException;
-
- /**
- * Gets the decoded image from the response.
- *
- * @return a {@code BufferedImage} or {@code null} if the image could not be read.
- *
- * @throws java.io.IOException if an I/O exception occurs during reading
- */
- BufferedImage getImage() throws IOException;
-
- /**
- * Sets the image for this response.
- *
- * @param pImage the new response image.
- */
- void setImage(RenderedImage pImage);
-}
+/*
+ * Copyright (c) 2008, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name "TwelveMonkeys" nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.twelvemonkeys.servlet.image;
+
+import javax.servlet.ServletResponse;
+import java.io.IOException;
+import java.awt.image.RenderedImage;
+import java.awt.image.BufferedImage;
+
+/**
+ * ImageServletResponse.
+ *
+ * The request attributes regarding image size and source region (AOI) are used
+ * in the decoding process, and must be set before the first invocation of
+ * {@link #getImage()} to have any effect.
+ *
+ * @author Harald Kuhr
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageServletResponse.java#4 $
+ */
+public interface ImageServletResponse extends ServletResponse {
+ /**
+ * Request attribute of type {@link java.awt.Dimension} controlling image
+ * size.
+ * If either {@code width} or {@code height} is negative, the size is
+ * computed, using uniform scaling.
+ * Else, if {@code SIZE_UNIFORM} is {@code true}, the size will be
+ * computed to the largest possible area (with correct aspect ratio)
+ * fitting inside the target area.
+ * Otherwise, the image is scaled to the given size, with no regard to
+ * aspect ratio.
+ *
+ * Defaults to {@code null} (original image size).
+ */
+ String ATTRIB_SIZE = "com.twelvemonkeys.servlet.image.ImageServletResponse.SIZE";
+
+ /**
+ * Request attribute of type {@link Boolean} controlling image sizing.
+ *
+ * Defaults to {@code Boolean.TRUE}.
+ */
+ String ATTRIB_SIZE_UNIFORM = "com.twelvemonkeys.servlet.image.ImageServletResponse.SIZE_UNIFORM";
+
+ /**
+ * Request attribute of type {@link Boolean} controlling image sizing.
+ *
+ * Defaults to {@code Boolean.FALSE}.
+ */
+ String ATTRIB_SIZE_PERCENT = "com.twelvemonkeys.servlet.image.ImageServletResponse.SIZE_PERCENT";
+
+ /**
+ * Request attribute of type {@link java.awt.Rectangle} controlling image
+ * source region (area of interest).
+ *
+ * Defaults to {@code null} (the entire image).
+ */
+ String ATTRIB_AOI = "com.twelvemonkeys.servlet.image.ImageServletResponse.AOI";
+
+ /**
+ * Request attribute of type {@link Boolean} controlling image AOI.
+ *
+ * Defaults to {@code Boolean.FALSE}.
+ */
+ String ATTRIB_AOI_UNIFORM = "com.twelvemonkeys.servlet.image.ImageServletResponse.AOI_UNIFORM";
+
+ /**
+ * Request attribute of type {@link Boolean} controlling image AOI.
+ *
+ * Defaults to {@code Boolean.FALSE}.
+ */
+ String ATTRIB_AOI_PERCENT = "com.twelvemonkeys.servlet.image.ImageServletResponse.AOI_PERCENT";
+
+ /**
+ * Request attribute of type {@link java.awt.Color} controlling background
+ * color for any transparent/translucent areas of the image.
+ *
+ * Defaults to {@code null} (keeps the transparent areas transparent).
+ */
+ String ATTRIB_BG_COLOR = "com.twelvemonkeys.servlet.image.ImageServletResponse.BG_COLOR";
+
+ /**
+ * Request attribute of type {@link Float} controlling image output compression/quality.
+ * Used for formats that accepts compression or quality settings,
+ * like JPEG (quality), PNG (compression only) etc.
+ *
+ * Defaults to {@code 0.8f} for JPEG.
+ */
+ String ATTRIB_OUTPUT_QUALITY = "com.twelvemonkeys.servlet.image.ImageServletResponse.OUTPUT_QUALITY";
+
+ /**
+ * Request attribute of type {@link Double} controlling image read
+ * subsampling factor. Controls the maximum sample pixels in each direction,
+ * that is read per pixel in the output image, if the result will be
+ * downscaled.
+ * Larger values will result in better quality, at the expense of higher
+ * memory consumption and CPU usage.
+ * However, using values above {@code 3.0} will usually not improve image
+ * quality.
+ * Legal values are in the range {@code [1.0 .. positive infinity>}.
+ *
+ * Defaults to {@code 2.0}.
+ */
+ String ATTRIB_READ_SUBSAMPLING_FACTOR = "com.twelvemonkeys.servlet.image.ImageServletResponse.READ_SUBSAMPLING_FACTOR";
+
+ /**
+ * Request attribute of type {@link Integer} controlling image resample
+ * algorithm.
+ * Legal values are {@link java.awt.Image#SCALE_DEFAULT SCALE_DEFAULT},
+ * {@link java.awt.Image#SCALE_FAST SCALE_FAST} or
+ * {@link java.awt.Image#SCALE_SMOOTH SCALE_SMOOTH}.
+ *
+ * Note: When using a value of {@code SCALE_FAST}, you should also use a
+ * subsampling factor of {@code 1.0}, for fast read/scale.
+ * Otherwise, use a subsampling factor of {@code 2.0} for better quality.
+ *
+ * Defaults to {@code SCALE_DEFAULT}.
+ */
+ String ATTRIB_IMAGE_RESAMPLE_ALGORITHM = "com.twelvemonkeys.servlet.image.ImageServletResponse.IMAGE_RESAMPLE_ALGORITHM";
+
+ /**
+ * Gets the image format for this response, such as "image/gif" or "image/jpeg".
+ * If not set, the default format is that of the original image.
+ *
+ * @return the image format for this response.
+ * @see #setOutputContentType(String)
+ */
+ String getOutputContentType();
+
+ /**
+ * Sets the image format for this response, such as "image/gif" or "image/jpeg".
+ *
+ * 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.
+ *
+ * If not set, the default format is that of the original image.
+ *
+ * @param pImageFormat the image format for this response.
+ */
+ void setOutputContentType(String pImageFormat);
+
+ //TODO: ?? void setCompressionQuality(float pQualityFactor);
+ //TODO: ?? float getCompressionQuality();
+
+ /**
+ * Writes the image to the original {@code ServletOutputStream}.
+ * If no format is {@linkplain #setOutputContentType(String) set} in this response,
+ * the image is encoded in the same format as the original image.
+ *
+ * @throws java.io.IOException if an I/O exception occurs during writing
+ */
+ void flush() throws IOException;
+
+ /**
+ * Gets the decoded image from the response.
+ *
+ * @return a {@code BufferedImage} or {@code null} if the image could not be read.
+ *
+ * @throws java.io.IOException if an I/O exception occurs during reading
+ */
+ BufferedImage getImage() throws IOException;
+
+ /**
+ * Sets the image for this response.
+ *
+ * @param pImage the new response image.
+ */
+ void setImage(RenderedImage pImage);
+}
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageServletResponseImpl.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageServletResponseImpl.java
index 3a023428..bc3b3f04 100755
--- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageServletResponseImpl.java
+++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageServletResponseImpl.java
@@ -1,735 +1,735 @@
-/*
- * Copyright (c) 2008, Harald Kuhr
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name "TwelveMonkeys" nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package com.twelvemonkeys.servlet.image;
-
-import com.twelvemonkeys.image.ImageUtil;
-import com.twelvemonkeys.io.FastByteArrayOutputStream;
-import com.twelvemonkeys.lang.StringUtil;
-import com.twelvemonkeys.servlet.ServletResponseStreamDelegate;
-import com.twelvemonkeys.servlet.ServletUtil;
-
-import javax.imageio.*;
-import javax.imageio.stream.ImageInputStream;
-import javax.imageio.stream.ImageOutputStream;
-import javax.servlet.ServletContext;
-import javax.servlet.ServletOutputStream;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpServletResponseWrapper;
-import java.awt.*;
-import java.awt.image.BufferedImage;
-import java.awt.image.IndexColorModel;
-import java.awt.image.RenderedImage;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.PrintWriter;
-import java.lang.reflect.Method;
-import java.net.URL;
-import java.util.Iterator;
-
-/**
- * This {@link ImageServletResponse} implementation can be used with image
- * requests, to have the image immediately decoded to a {@code BufferedImage}.
- * The image may be optionally subsampled, scaled and/or cropped.
- * The response also automtically handles writing the image back to the underlying response stream
- * in the preferred format, when the response is flushed.
- *
- *
- *
- * @example
- * JPEG:
- * <IMG src="/scale/test.jpg?image=http://www.iconmedialab.com/images/random/home_image_12.jpg&width=500&uniform=true">
- *
- * PNG:
- * <IMG src="/scale/test.png?cache=false&image=http://www.iconmedialab.com/images/random/home_image_12.jpg&width=50&units=PERCENT">
- *
- * @todo Correct rounding errors, resulting in black borders when rotating 90
- * degrees, and one of width or height is odd length...
- *
- * @author Harald Kuhr
- * @author last modified by $Author: haku $
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/RotateFilter.java#1 $
- */
-
-public class RotateFilter extends ImageFilter {
- /** {@code angle}*/
- protected final static String PARAM_ANGLE = "angle";
- /** {@code angleUnits (RADIANS|DEGREES)}*/
- protected final static String PARAM_ANGLE_UNITS = "angleUnits";
- /** {@code crop}*/
- protected final static String PARAM_CROP = "rotateCrop";
- /** {@code bgcolor}*/
- protected final static String PARAM_BGCOLOR = "rotateBgcolor";
-
- /** {@code degrees}*/
- private final static String ANGLE_DEGREES = "degrees";
- /** {@code radians}*/
- //private final static String ANGLE_RADIANS = "radians";
-
- /**
- * Reads the image from the requested URL, rotates it, and returns
- * it in the
- * Servlet stream. See above for details on parameters.
- */
-
- protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) {
- // Get angle
- double ang = getAngle(pRequest);
-
- // Get bounds
- Rectangle2D rect = getBounds(pRequest, pImage, ang);
- int width = (int) rect.getWidth();
- int height = (int) rect.getHeight();
-
- // Create result image
- BufferedImage res = ImageUtil.createTransparent(width, height, BufferedImage.TYPE_INT_ARGB);
- Graphics2D g = res.createGraphics();
-
- // Get background color and clear
- String str = pRequest.getParameter(PARAM_BGCOLOR);
- if (!StringUtil.isEmpty(str)) {
- Color bgcolor = StringUtil.toColor(str);
- g.setBackground(bgcolor);
- g.clearRect(0, 0, width, height);
- }
-
- // Set mHints (why do I always get jagged edgdes?)
- 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_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON));
- hints.add(new RenderingHints(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC));
-
- g.setRenderingHints(hints);
-
- // Rotate around center
- AffineTransform at = AffineTransform
- .getRotateInstance(ang, width / 2.0, height / 2.0);
-
- // Move to center
- at.translate(width / 2.0 - pImage.getWidth() / 2.0,
- height / 2.0 - pImage.getHeight() / 2.0);
-
- // Draw it, centered
- g.drawImage(pImage, at, null);
-
- return res;
- }
-
- /**
- * Gets the angle of rotation.
- */
-
- private double getAngle(ServletRequest pReq) {
- double angle = 0.0;
- String str = pReq.getParameter(PARAM_ANGLE);
- if (!StringUtil.isEmpty(str)) {
- angle = Double.parseDouble(str);
-
- // Convert to radians, if needed
- str = pReq.getParameter(PARAM_ANGLE_UNITS);
- if (!StringUtil.isEmpty(str)
- && ANGLE_DEGREES.equalsIgnoreCase(str)) {
- angle = MathUtil.toRadians(angle);
- }
- }
-
- return angle;
- }
-
- /**
- * Get the bounding rectangle of the rotated image.
- */
-
- private Rectangle2D getBounds(ServletRequest pReq, BufferedImage pImage,
- double pAng) {
- // Get dimensions of original image
- int width = pImage.getWidth(); // loads the image
- int height = pImage.getHeight();
-
- // Test if we want to crop image (default)
- // if true
- // - find the largest bounding box INSIDE the rotated image,
- // that matches the original proportions (nearest 90deg)
- // (scale up to fit dimensions?)
- // else
- // - find the smallest bounding box OUTSIDE the rotated image.
- // - that matches the original proportions (nearest 90deg) ?
- // (scale down to fit dimensions?)
- AffineTransform at =
- AffineTransform.getRotateInstance(pAng, width / 2.0, height / 2.0);
-
- Rectangle2D orig = new Rectangle(width, height);
- Shape rotated = at.createTransformedShape(orig);
-
- if (ServletUtil.getBooleanParameter(pReq, PARAM_CROP, false)) {
- // TODO: Inside box
- return rotated.getBounds2D();
- }
- else {
- return rotated.getBounds2D();
- }
- }
-}
-
+/*
+ * Copyright (c) 2008, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name "TwelveMonkeys" nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.twelvemonkeys.servlet.image;
+
+import com.twelvemonkeys.image.ImageUtil;
+import com.twelvemonkeys.lang.MathUtil;
+import com.twelvemonkeys.lang.StringUtil;
+import com.twelvemonkeys.servlet.ServletUtil;
+
+import javax.servlet.ServletRequest;
+import java.awt.*;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+import java.awt.image.RenderedImage;
+
+/**
+ * This Servlet is able to render a cropped part of an image.
+ *
+ *
+ *
+ *
+ * @example
+ * JPEG:
+ * <IMG src="/scale/test.jpg?image=http://www.iconmedialab.com/images/random/home_image_12.jpg&width=500&uniform=true">
+ *
+ * PNG:
+ * <IMG src="/scale/test.png?cache=false&image=http://www.iconmedialab.com/images/random/home_image_12.jpg&width=50&units=PERCENT">
+ *
+ * @todo Correct rounding errors, resulting in black borders when rotating 90
+ * degrees, and one of width or height is odd length...
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haku $
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/RotateFilter.java#1 $
+ */
+
+public class RotateFilter extends ImageFilter {
+ /** {@code angle}*/
+ protected final static String PARAM_ANGLE = "angle";
+ /** {@code angleUnits (RADIANS|DEGREES)}*/
+ protected final static String PARAM_ANGLE_UNITS = "angleUnits";
+ /** {@code crop}*/
+ protected final static String PARAM_CROP = "rotateCrop";
+ /** {@code bgcolor}*/
+ protected final static String PARAM_BGCOLOR = "rotateBgcolor";
+
+ /** {@code degrees}*/
+ private final static String ANGLE_DEGREES = "degrees";
+ /** {@code radians}*/
+ //private final static String ANGLE_RADIANS = "radians";
+
+ /**
+ * Reads the image from the requested URL, rotates it, and returns
+ * it in the
+ * Servlet stream. See above for details on parameters.
+ */
+
+ protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) {
+ // Get angle
+ double ang = getAngle(pRequest);
+
+ // Get bounds
+ Rectangle2D rect = getBounds(pRequest, pImage, ang);
+ int width = (int) rect.getWidth();
+ int height = (int) rect.getHeight();
+
+ // Create result image
+ BufferedImage res = ImageUtil.createTransparent(width, height, BufferedImage.TYPE_INT_ARGB);
+ Graphics2D g = res.createGraphics();
+
+ // Get background color and clear
+ String str = pRequest.getParameter(PARAM_BGCOLOR);
+ if (!StringUtil.isEmpty(str)) {
+ Color bgcolor = StringUtil.toColor(str);
+ g.setBackground(bgcolor);
+ g.clearRect(0, 0, width, height);
+ }
+
+ // Set mHints (why do I always get jagged edgdes?)
+ 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_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON));
+ hints.add(new RenderingHints(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC));
+
+ g.setRenderingHints(hints);
+
+ // Rotate around center
+ AffineTransform at = AffineTransform
+ .getRotateInstance(ang, width / 2.0, height / 2.0);
+
+ // Move to center
+ at.translate(width / 2.0 - pImage.getWidth() / 2.0,
+ height / 2.0 - pImage.getHeight() / 2.0);
+
+ // Draw it, centered
+ g.drawImage(pImage, at, null);
+
+ return res;
+ }
+
+ /**
+ * Gets the angle of rotation.
+ */
+
+ private double getAngle(ServletRequest pReq) {
+ double angle = 0.0;
+ String str = pReq.getParameter(PARAM_ANGLE);
+ if (!StringUtil.isEmpty(str)) {
+ angle = Double.parseDouble(str);
+
+ // Convert to radians, if needed
+ str = pReq.getParameter(PARAM_ANGLE_UNITS);
+ if (!StringUtil.isEmpty(str)
+ && ANGLE_DEGREES.equalsIgnoreCase(str)) {
+ angle = MathUtil.toRadians(angle);
+ }
+ }
+
+ return angle;
+ }
+
+ /**
+ * Get the bounding rectangle of the rotated image.
+ */
+
+ private Rectangle2D getBounds(ServletRequest pReq, BufferedImage pImage,
+ double pAng) {
+ // Get dimensions of original image
+ int width = pImage.getWidth(); // loads the image
+ int height = pImage.getHeight();
+
+ // Test if we want to crop image (default)
+ // if true
+ // - find the largest bounding box INSIDE the rotated image,
+ // that matches the original proportions (nearest 90deg)
+ // (scale up to fit dimensions?)
+ // else
+ // - find the smallest bounding box OUTSIDE the rotated image.
+ // - that matches the original proportions (nearest 90deg) ?
+ // (scale down to fit dimensions?)
+ AffineTransform at =
+ AffineTransform.getRotateInstance(pAng, width / 2.0, height / 2.0);
+
+ Rectangle2D orig = new Rectangle(width, height);
+ Shape rotated = at.createTransformedShape(orig);
+
+ if (ServletUtil.getBooleanParameter(pReq, PARAM_CROP, false)) {
+ // TODO: Inside box
+ return rotated.getBounds2D();
+ }
+ else {
+ return rotated.getBounds2D();
+ }
+ }
+}
+
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ScaleFilter.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ScaleFilter.java
index 1d7a11fa..8bd9ca07 100755
--- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ScaleFilter.java
+++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ScaleFilter.java
@@ -1,322 +1,322 @@
-/*
- * Copyright (c) 2008, Harald Kuhr
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name "TwelveMonkeys" nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package com.twelvemonkeys.servlet.image;
-
-import com.twelvemonkeys.image.ImageUtil;
-import com.twelvemonkeys.lang.StringUtil;
-import com.twelvemonkeys.servlet.ServletUtil;
-
-import javax.servlet.ServletRequest;
-import java.awt.*;
-import java.awt.image.BufferedImage;
-import java.awt.image.RenderedImage;
-import java.lang.reflect.Field;
-
-
-/**
- * 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
- * or similar.
- *
- *
- *
- *
- * @author Harald Kuhr
- * @author last modified by $Author: haku $
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ScaleFilter.java#1 $
- *
- * @example <IMG src="/scale/test.jpg?scaleX=500&scaleUniform=false">
- * @example <IMG src="/scale/test.png?scaleY=50&scaleUnits=PERCENT">
- */
-public class ScaleFilter extends ImageFilter {
-
- /**
- * Width and height are absolute pixels. The default.
- */
- public static final int UNITS_PIXELS = 1;
- /**
- * Width and height are percentage of original width and height.
- */
- public static final int UNITS_PERCENT = 5;
- /**
- * Ahh, good choice!
- */
- //private static final int UNITS_METRIC = 42;
- /**
- * The root of all evil...
- */
- //private static final int UNITS_INCHES = 666;
- /**
- * Unknown units.
- */
- public static final int UNITS_UNKNOWN = 0;
-
- /**
- * {@code scaleQuality}
- */
- protected final static String PARAM_SCALE_QUALITY = "scaleQuality";
- /**
- * {@code scaleUnits}
- */
- protected final static String PARAM_SCALE_UNITS = "scaleUnits";
- /**
- * {@code scaleUniform}
- */
- protected final static String PARAM_SCALE_UNIFORM = "scaleUniform";
- /**
- * {@code scaleX}
- */
- protected final static String PARAM_SCALE_X = "scaleX";
- /**
- * {@code scaleY}
- */
- protected final static String PARAM_SCALE_Y = "scaleY";
- /**
- * {@code image}
- */
- protected final static String PARAM_IMAGE = "image";
-
- /** */
- protected int mDefaultScaleQuality = Image.SCALE_DEFAULT;
-
- /**
- * Reads the image from the requested URL, scales it, and returns it in the
- * Servlet stream. See above for details on parameters.
- */
- protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) {
-
- // Get quality setting
- // SMOOTH | FAST | REPLICATE | DEFAULT | AREA_AVERAGING
- // See Image (mHints)
- int quality = getQuality(pRequest.getParameter(PARAM_SCALE_QUALITY));
-
- // Get units, default is pixels
- // PIXELS | PERCENT | METRIC | INCHES
- int units = getUnits(pRequest.getParameter(PARAM_SCALE_UNITS));
- if (units == UNITS_UNKNOWN) {
- log("Unknown units for scale, returning original.");
- return pImage;
- }
-
- // Use uniform scaling? Default is true
- boolean uniformScale = ServletUtil.getBooleanParameter(pRequest, PARAM_SCALE_UNIFORM, true);
-
- // Get dimensions
- int width = ServletUtil.getIntParameter(pRequest, PARAM_SCALE_X, -1);
- int height = ServletUtil.getIntParameter(pRequest, PARAM_SCALE_Y, -1);
-
- // Get dimensions for scaled image
- Dimension dim = getDimensions(pImage, width, height, units, uniformScale);
-
- width = (int) dim.getWidth();
- height = (int) dim.getHeight();
-
- // Return scaled instance directly
- return ImageUtil.createScaled(pImage, width, height, quality);
- }
-
- /**
- * Gets the quality constant for the scaling, from the string argument.
- *
- * @param pQualityStr The string representation of the scale quality
- * constant.
- * @return The matching quality constant, or the default quality if none
- * was found.
- * @see java.awt.Image
- * @see java.awt.Image#getScaledInstance(int,int,int)
- */
- protected int getQuality(String pQualityStr) {
- if (!StringUtil.isEmpty(pQualityStr)) {
- try {
- // Get quality constant from Image using reflection
- Class cl = Image.class;
- Field field = cl.getField(pQualityStr.toUpperCase());
-
- return field.getInt(null);
- }
- catch (IllegalAccessException ia) {
- log("Unable to get quality.", ia);
- }
- catch (NoSuchFieldException nsf) {
- log("Unable to get quality.", nsf);
- }
- }
-
- return mDefaultScaleQuality;
- }
-
- public void setDefaultScaleQuality(String pDefaultScaleQuality) {
- mDefaultScaleQuality = getQuality(pDefaultScaleQuality);
- }
-
- /**
- * Gets the units constant for the width and height arguments, from the
- * given string argument.
- *
- * @param pUnitStr The string representation of the units constant,
- * can be one of "PIXELS" or "PERCENT".
- * @return The mathcing units constant, or UNITS_UNKNOWN if none was found.
- */
- protected int getUnits(String pUnitStr) {
- if (StringUtil.isEmpty(pUnitStr)
- || pUnitStr.equalsIgnoreCase("PIXELS")) {
- return UNITS_PIXELS;
- }
- else if (pUnitStr.equalsIgnoreCase("PERCENT")) {
- return UNITS_PERCENT;
- }
- else {
- return UNITS_UNKNOWN;
- }
- }
-
- /**
- * Gets the dimensions (height and width) of the scaled image. The
- * dimensions are computed based on the old image's dimensions, the units
- * used for specifying new dimensions and whether or not uniform scaling
- * should be used (se algorithm below).
- *
- * @param pImage the image to be scaled
- * @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 pUnits the constant specifying units for width and height
- * parameter (UNITS_PIXELS or UNITS_PERCENT)
- * @param pUniformScale boolean specifying uniform scale or not
- * @return a Dimension object, with the correct width and heigth
- * in pixels, for the scaled version of the image.
- */
- protected Dimension getDimensions(Image pImage, int pWidth, int pHeight,
- int pUnits, boolean pUniformScale) {
-
- // If uniform, make sure width and height are scaled the same ammount
- // (use ONLY height or ONLY width).
- //
- // Algoritm:
- // if uniform
- // if newHeight not set
- // find ratio newWidth / oldWidth
- // oldHeight *= ratio
- // else if newWidth not set
- // find ratio newWidth / oldWidth
- // oldHeight *= ratio
- // else
- // find both ratios and use the smallest one
- // (this will be the largest version of the image that fits
- // inside the rectangle given)
- // (if PERCENT, just use smallest percentage).
- //
- // If units is percent, we only need old height and width
-
- int oldWidth = ImageUtil.getWidth(pImage);
- int oldHeight = ImageUtil.getHeight(pImage);
- float ratio;
-
- if (pUnits == UNITS_PERCENT) {
- if (pWidth >= 0 && pHeight >= 0) {
- // Non-uniform
- pWidth = (int) ((float) oldWidth * (float) pWidth / 100f);
- pHeight = (int) ((float) oldHeight * (float) pHeight / 100f);
- }
- else if (pWidth >= 0) {
- // Find ratio from pWidth
- ratio = (float) pWidth / 100f;
- pWidth = (int) ((float) oldWidth * ratio);
- pHeight = (int) ((float) oldHeight * ratio);
- }
- else if (pHeight >= 0) {
- // Find ratio from pHeight
- ratio = (float) pHeight / 100f;
- pWidth = (int) ((float) oldWidth * ratio);
- pHeight = (int) ((float) oldHeight * ratio);
- }
- // Else: No scale
- }
- else if (pUnits == UNITS_PIXELS) {
- if (pUniformScale) {
- if (pWidth >= 0 && pHeight >= 0) {
- // Compute both ratios
- ratio = (float) pWidth / (float) oldWidth;
- float heightRatio = (float) pHeight / (float) oldHeight;
-
- // Find the largest ratio, and use that for both
- if (heightRatio < ratio) {
- ratio = heightRatio;
- pWidth = (int) ((float) oldWidth * ratio);
- }
- else {
- pHeight = (int) ((float) oldHeight * ratio);
- }
-
- }
- else if (pWidth >= 0) {
- // Find ratio from pWidth
- ratio = (float) pWidth / (float) oldWidth;
- pHeight = (int) ((float) oldHeight * ratio);
- }
- else if (pHeight >= 0) {
- // Find ratio from pHeight
- ratio = (float) pHeight / (float) oldHeight;
- pWidth = (int) ((float) oldWidth * ratio);
- }
- // Else: No scale
- }
- }
-
- // Default is no scale, just work as a proxy
- if (pWidth < 0) {
- pWidth = oldWidth;
- }
- if (pHeight < 0) {
- pHeight = oldHeight;
- }
-
- // Create new Dimension object and return
- return new Dimension(pWidth, pHeight);
- }
-}
+/*
+ * Copyright (c) 2008, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name "TwelveMonkeys" nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.twelvemonkeys.servlet.image;
+
+import com.twelvemonkeys.image.ImageUtil;
+import com.twelvemonkeys.lang.StringUtil;
+import com.twelvemonkeys.servlet.ServletUtil;
+
+import javax.servlet.ServletRequest;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.awt.image.RenderedImage;
+import java.lang.reflect.Field;
+
+
+/**
+ * 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
+ * or similar.
+ *
+ *
+ *
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haku $
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/ScaleFilter.java#1 $
+ *
+ * @example <IMG src="/scale/test.jpg?scaleX=500&scaleUniform=false">
+ * @example <IMG src="/scale/test.png?scaleY=50&scaleUnits=PERCENT">
+ */
+public class ScaleFilter extends ImageFilter {
+
+ /**
+ * Width and height are absolute pixels. The default.
+ */
+ public static final int UNITS_PIXELS = 1;
+ /**
+ * Width and height are percentage of original width and height.
+ */
+ public static final int UNITS_PERCENT = 5;
+ /**
+ * Ahh, good choice!
+ */
+ //private static final int UNITS_METRIC = 42;
+ /**
+ * The root of all evil...
+ */
+ //private static final int UNITS_INCHES = 666;
+ /**
+ * Unknown units.
+ */
+ public static final int UNITS_UNKNOWN = 0;
+
+ /**
+ * {@code scaleQuality}
+ */
+ protected final static String PARAM_SCALE_QUALITY = "scaleQuality";
+ /**
+ * {@code scaleUnits}
+ */
+ protected final static String PARAM_SCALE_UNITS = "scaleUnits";
+ /**
+ * {@code scaleUniform}
+ */
+ protected final static String PARAM_SCALE_UNIFORM = "scaleUniform";
+ /**
+ * {@code scaleX}
+ */
+ protected final static String PARAM_SCALE_X = "scaleX";
+ /**
+ * {@code scaleY}
+ */
+ protected final static String PARAM_SCALE_Y = "scaleY";
+ /**
+ * {@code image}
+ */
+ protected final static String PARAM_IMAGE = "image";
+
+ /** */
+ protected int mDefaultScaleQuality = Image.SCALE_DEFAULT;
+
+ /**
+ * Reads the image from the requested URL, scales it, and returns it in the
+ * Servlet stream. See above for details on parameters.
+ */
+ protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) {
+
+ // Get quality setting
+ // SMOOTH | FAST | REPLICATE | DEFAULT | AREA_AVERAGING
+ // See Image (mHints)
+ int quality = getQuality(pRequest.getParameter(PARAM_SCALE_QUALITY));
+
+ // Get units, default is pixels
+ // PIXELS | PERCENT | METRIC | INCHES
+ int units = getUnits(pRequest.getParameter(PARAM_SCALE_UNITS));
+ if (units == UNITS_UNKNOWN) {
+ log("Unknown units for scale, returning original.");
+ return pImage;
+ }
+
+ // Use uniform scaling? Default is true
+ boolean uniformScale = ServletUtil.getBooleanParameter(pRequest, PARAM_SCALE_UNIFORM, true);
+
+ // Get dimensions
+ int width = ServletUtil.getIntParameter(pRequest, PARAM_SCALE_X, -1);
+ int height = ServletUtil.getIntParameter(pRequest, PARAM_SCALE_Y, -1);
+
+ // Get dimensions for scaled image
+ Dimension dim = getDimensions(pImage, width, height, units, uniformScale);
+
+ width = (int) dim.getWidth();
+ height = (int) dim.getHeight();
+
+ // Return scaled instance directly
+ return ImageUtil.createScaled(pImage, width, height, quality);
+ }
+
+ /**
+ * Gets the quality constant for the scaling, from the string argument.
+ *
+ * @param pQualityStr The string representation of the scale quality
+ * constant.
+ * @return The matching quality constant, or the default quality if none
+ * was found.
+ * @see java.awt.Image
+ * @see java.awt.Image#getScaledInstance(int,int,int)
+ */
+ protected int getQuality(String pQualityStr) {
+ if (!StringUtil.isEmpty(pQualityStr)) {
+ try {
+ // Get quality constant from Image using reflection
+ Class cl = Image.class;
+ Field field = cl.getField(pQualityStr.toUpperCase());
+
+ return field.getInt(null);
+ }
+ catch (IllegalAccessException ia) {
+ log("Unable to get quality.", ia);
+ }
+ catch (NoSuchFieldException nsf) {
+ log("Unable to get quality.", nsf);
+ }
+ }
+
+ return mDefaultScaleQuality;
+ }
+
+ public void setDefaultScaleQuality(String pDefaultScaleQuality) {
+ mDefaultScaleQuality = getQuality(pDefaultScaleQuality);
+ }
+
+ /**
+ * Gets the units constant for the width and height arguments, from the
+ * given string argument.
+ *
+ * @param pUnitStr The string representation of the units constant,
+ * can be one of "PIXELS" or "PERCENT".
+ * @return The mathcing units constant, or UNITS_UNKNOWN if none was found.
+ */
+ protected int getUnits(String pUnitStr) {
+ if (StringUtil.isEmpty(pUnitStr)
+ || pUnitStr.equalsIgnoreCase("PIXELS")) {
+ return UNITS_PIXELS;
+ }
+ else if (pUnitStr.equalsIgnoreCase("PERCENT")) {
+ return UNITS_PERCENT;
+ }
+ else {
+ return UNITS_UNKNOWN;
+ }
+ }
+
+ /**
+ * Gets the dimensions (height and width) of the scaled image. The
+ * dimensions are computed based on the old image's dimensions, the units
+ * used for specifying new dimensions and whether or not uniform scaling
+ * should be used (se algorithm below).
+ *
+ * @param pImage the image to be scaled
+ * @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 pUnits the constant specifying units for width and height
+ * parameter (UNITS_PIXELS or UNITS_PERCENT)
+ * @param pUniformScale boolean specifying uniform scale or not
+ * @return a Dimension object, with the correct width and heigth
+ * in pixels, for the scaled version of the image.
+ */
+ protected Dimension getDimensions(Image pImage, int pWidth, int pHeight,
+ int pUnits, boolean pUniformScale) {
+
+ // If uniform, make sure width and height are scaled the same ammount
+ // (use ONLY height or ONLY width).
+ //
+ // Algoritm:
+ // if uniform
+ // if newHeight not set
+ // find ratio newWidth / oldWidth
+ // oldHeight *= ratio
+ // else if newWidth not set
+ // find ratio newWidth / oldWidth
+ // oldHeight *= ratio
+ // else
+ // find both ratios and use the smallest one
+ // (this will be the largest version of the image that fits
+ // inside the rectangle given)
+ // (if PERCENT, just use smallest percentage).
+ //
+ // If units is percent, we only need old height and width
+
+ int oldWidth = ImageUtil.getWidth(pImage);
+ int oldHeight = ImageUtil.getHeight(pImage);
+ float ratio;
+
+ if (pUnits == UNITS_PERCENT) {
+ if (pWidth >= 0 && pHeight >= 0) {
+ // Non-uniform
+ pWidth = (int) ((float) oldWidth * (float) pWidth / 100f);
+ pHeight = (int) ((float) oldHeight * (float) pHeight / 100f);
+ }
+ else if (pWidth >= 0) {
+ // Find ratio from pWidth
+ ratio = (float) pWidth / 100f;
+ pWidth = (int) ((float) oldWidth * ratio);
+ pHeight = (int) ((float) oldHeight * ratio);
+ }
+ else if (pHeight >= 0) {
+ // Find ratio from pHeight
+ ratio = (float) pHeight / 100f;
+ pWidth = (int) ((float) oldWidth * ratio);
+ pHeight = (int) ((float) oldHeight * ratio);
+ }
+ // Else: No scale
+ }
+ else if (pUnits == UNITS_PIXELS) {
+ if (pUniformScale) {
+ if (pWidth >= 0 && pHeight >= 0) {
+ // Compute both ratios
+ ratio = (float) pWidth / (float) oldWidth;
+ float heightRatio = (float) pHeight / (float) oldHeight;
+
+ // Find the largest ratio, and use that for both
+ if (heightRatio < ratio) {
+ ratio = heightRatio;
+ pWidth = (int) ((float) oldWidth * ratio);
+ }
+ else {
+ pHeight = (int) ((float) oldHeight * ratio);
+ }
+
+ }
+ else if (pWidth >= 0) {
+ // Find ratio from pWidth
+ ratio = (float) pWidth / (float) oldWidth;
+ pHeight = (int) ((float) oldHeight * ratio);
+ }
+ else if (pHeight >= 0) {
+ // Find ratio from pHeight
+ ratio = (float) pHeight / (float) oldHeight;
+ pWidth = (int) ((float) oldWidth * ratio);
+ }
+ // Else: No scale
+ }
+ }
+
+ // Default is no scale, just work as a proxy
+ if (pWidth < 0) {
+ pWidth = oldWidth;
+ }
+ if (pHeight < 0) {
+ pHeight = oldHeight;
+ }
+
+ // Create new Dimension object and return
+ return new Dimension(pWidth, pHeight);
+ }
+}
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/SourceRenderFilter.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/SourceRenderFilter.java
index 37873803..1fb6d497 100755
--- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/SourceRenderFilter.java
+++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/SourceRenderFilter.java
@@ -1,154 +1,154 @@
-package com.twelvemonkeys.servlet.image;
-
-import com.twelvemonkeys.servlet.ServletUtil;
-
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.FilterChain;
-import javax.servlet.ServletException;
-import java.awt.image.RenderedImage;
-import java.awt.image.BufferedImage;
-import java.awt.*;
-import java.io.IOException;
-
-/**
- * A {@link javax.servlet.Filter} that extracts request parameters, and sets the
- * corresponding request attributes from {@link ImageServletResponse}.
- * Only affects how the image is decoded, and must be applied before any
- * other image filters in the chain.
- *
- * @see ImageServletResponse#ATTRIB_SIZE
- * @see ImageServletResponse#ATTRIB_AOI
- *
- * @author Harald Kuhr
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/SourceRenderFilter.java#1 $
- */
-public class SourceRenderFilter extends ImageFilter {
- private String mSizeWidthParam = "size.w";
- private String mSizeHeightParam = "size.h";
- private String mSizePercentParam = "size.percent";
- private String mSizeUniformParam = "size.uniform";
-
- private String mRegionWidthParam = "aoi.w";
- private String mRegionHeightParam = "aoi.h";
- private String mRegionLeftParam = "aoi.x";
- private String mRegionTopParam = "aoi.y";
- private String mRegionPercentParam = "aoi.percent";
- private String mRegionUniformParam = "aoi.uniform";
-
- public void setRegionHeightParam(String pRegionHeightParam) {
- mRegionHeightParam = pRegionHeightParam;
- }
-
- public void setRegionWidthParam(String pRegionWidthParam) {
- mRegionWidthParam = pRegionWidthParam;
- }
-
- public void setRegionLeftParam(String pRegionLeftParam) {
- mRegionLeftParam = pRegionLeftParam;
- }
-
- public void setRegionTopParam(String pRegionTopParam) {
- mRegionTopParam = pRegionTopParam;
- }
-
- public void setSizeHeightParam(String pSizeHeightParam) {
- mSizeHeightParam = pSizeHeightParam;
- }
-
- public void setSizeWidthParam(String pSizeWidthParam) {
- mSizeWidthParam = pSizeWidthParam;
- }
-
- public void setRegionPercentParam(String pRegionPercentParam) {
- mRegionPercentParam = pRegionPercentParam;
- }
-
- public void setRegionUniformParam(String pRegionUniformParam) {
- mRegionUniformParam = pRegionUniformParam;
- }
-
- public void setSizePercentParam(String pSizePercentParam) {
- mSizePercentParam = pSizePercentParam;
- }
-
- public void setSizeUniformParam(String pSizeUniformParam) {
- mSizeUniformParam = pSizeUniformParam;
- }
-
- public void init() throws ServletException {
- if (mTriggerParams == null) {
- // Add all params as triggers
- mTriggerParams = new String[]{mSizeWidthParam, mSizeHeightParam,
- mSizeUniformParam, mSizePercentParam,
- mRegionLeftParam, mRegionTopParam,
- mRegionWidthParam, mRegionHeightParam,
- mRegionUniformParam, mRegionPercentParam};
- }
- }
-
- /**
- * Extracts request parameters, and sets the corresponding request
- * attributes if specified.
- *
- * @param pRequest
- * @param pResponse
- * @param pChain
- * @throws IOException
- * @throws ServletException
- */
- protected void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain) throws IOException, ServletException {
- // TODO: Max size configuration, to avoid DOS attacks? OutOfMemory
-
- // Size parameters
- int width = ServletUtil.getIntParameter(pRequest, mSizeWidthParam, -1);
- int height = ServletUtil.getIntParameter(pRequest, mSizeHeightParam, -1);
- if (width > 0 || height > 0) {
- pRequest.setAttribute(ImageServletResponse.ATTRIB_SIZE, new Dimension(width, height));
- }
-
- // Size uniform/percent
- boolean uniform = ServletUtil.getBooleanParameter(pRequest, mSizeUniformParam, true);
- if (!uniform) {
- pRequest.setAttribute(ImageServletResponse.ATTRIB_SIZE_UNIFORM, Boolean.FALSE);
- }
- boolean percent = ServletUtil.getBooleanParameter(pRequest, mSizePercentParam, false);
- if (percent) {
- pRequest.setAttribute(ImageServletResponse.ATTRIB_SIZE_PERCENT, Boolean.TRUE);
- }
-
- // Area of interest parameters
- int x = ServletUtil.getIntParameter(pRequest, mRegionLeftParam, -1); // Default is center
- int y = ServletUtil.getIntParameter(pRequest, mRegionTopParam, -1); // Default is center
- width = ServletUtil.getIntParameter(pRequest, mRegionWidthParam, -1);
- height = ServletUtil.getIntParameter(pRequest, mRegionHeightParam, -1);
- if (width > 0 || height > 0) {
- pRequest.setAttribute(ImageServletResponse.ATTRIB_AOI, new Rectangle(x, y, width, height));
- }
-
- // AOI uniform/percent
- uniform = ServletUtil.getBooleanParameter(pRequest, mRegionUniformParam, false);
- if (uniform) {
- pRequest.setAttribute(ImageServletResponse.ATTRIB_SIZE_UNIFORM, Boolean.TRUE);
- }
- percent = ServletUtil.getBooleanParameter(pRequest, mRegionPercentParam, false);
- if (percent) {
- pRequest.setAttribute(ImageServletResponse.ATTRIB_SIZE_PERCENT, Boolean.TRUE);
- }
-
- super.doFilterImpl(pRequest, pResponse, pChain);
- }
-
- /**
- * This implementation does no filtering, and simply returns the image
- * passed in.
- *
- * @param pImage
- * @param pRequest
- * @param pResponse
- * @return {@code pImage}
- */
- protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) {
- return pImage;
- }
+package com.twelvemonkeys.servlet.image;
+
+import com.twelvemonkeys.servlet.ServletUtil;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import java.awt.image.RenderedImage;
+import java.awt.image.BufferedImage;
+import java.awt.*;
+import java.io.IOException;
+
+/**
+ * A {@link javax.servlet.Filter} that extracts request parameters, and sets the
+ * corresponding request attributes from {@link ImageServletResponse}.
+ * Only affects how the image is decoded, and must be applied before any
+ * other image filters in the chain.
+ *
+ * @see ImageServletResponse#ATTRIB_SIZE
+ * @see ImageServletResponse#ATTRIB_AOI
+ *
+ * @author Harald Kuhr
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/SourceRenderFilter.java#1 $
+ */
+public class SourceRenderFilter extends ImageFilter {
+ private String mSizeWidthParam = "size.w";
+ private String mSizeHeightParam = "size.h";
+ private String mSizePercentParam = "size.percent";
+ private String mSizeUniformParam = "size.uniform";
+
+ private String mRegionWidthParam = "aoi.w";
+ private String mRegionHeightParam = "aoi.h";
+ private String mRegionLeftParam = "aoi.x";
+ private String mRegionTopParam = "aoi.y";
+ private String mRegionPercentParam = "aoi.percent";
+ private String mRegionUniformParam = "aoi.uniform";
+
+ public void setRegionHeightParam(String pRegionHeightParam) {
+ mRegionHeightParam = pRegionHeightParam;
+ }
+
+ public void setRegionWidthParam(String pRegionWidthParam) {
+ mRegionWidthParam = pRegionWidthParam;
+ }
+
+ public void setRegionLeftParam(String pRegionLeftParam) {
+ mRegionLeftParam = pRegionLeftParam;
+ }
+
+ public void setRegionTopParam(String pRegionTopParam) {
+ mRegionTopParam = pRegionTopParam;
+ }
+
+ public void setSizeHeightParam(String pSizeHeightParam) {
+ mSizeHeightParam = pSizeHeightParam;
+ }
+
+ public void setSizeWidthParam(String pSizeWidthParam) {
+ mSizeWidthParam = pSizeWidthParam;
+ }
+
+ public void setRegionPercentParam(String pRegionPercentParam) {
+ mRegionPercentParam = pRegionPercentParam;
+ }
+
+ public void setRegionUniformParam(String pRegionUniformParam) {
+ mRegionUniformParam = pRegionUniformParam;
+ }
+
+ public void setSizePercentParam(String pSizePercentParam) {
+ mSizePercentParam = pSizePercentParam;
+ }
+
+ public void setSizeUniformParam(String pSizeUniformParam) {
+ mSizeUniformParam = pSizeUniformParam;
+ }
+
+ public void init() throws ServletException {
+ if (mTriggerParams == null) {
+ // Add all params as triggers
+ mTriggerParams = new String[]{mSizeWidthParam, mSizeHeightParam,
+ mSizeUniformParam, mSizePercentParam,
+ mRegionLeftParam, mRegionTopParam,
+ mRegionWidthParam, mRegionHeightParam,
+ mRegionUniformParam, mRegionPercentParam};
+ }
+ }
+
+ /**
+ * Extracts request parameters, and sets the corresponding request
+ * attributes if specified.
+ *
+ * @param pRequest
+ * @param pResponse
+ * @param pChain
+ * @throws IOException
+ * @throws ServletException
+ */
+ protected void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain) throws IOException, ServletException {
+ // TODO: Max size configuration, to avoid DOS attacks? OutOfMemory
+
+ // Size parameters
+ int width = ServletUtil.getIntParameter(pRequest, mSizeWidthParam, -1);
+ int height = ServletUtil.getIntParameter(pRequest, mSizeHeightParam, -1);
+ if (width > 0 || height > 0) {
+ pRequest.setAttribute(ImageServletResponse.ATTRIB_SIZE, new Dimension(width, height));
+ }
+
+ // Size uniform/percent
+ boolean uniform = ServletUtil.getBooleanParameter(pRequest, mSizeUniformParam, true);
+ if (!uniform) {
+ pRequest.setAttribute(ImageServletResponse.ATTRIB_SIZE_UNIFORM, Boolean.FALSE);
+ }
+ boolean percent = ServletUtil.getBooleanParameter(pRequest, mSizePercentParam, false);
+ if (percent) {
+ pRequest.setAttribute(ImageServletResponse.ATTRIB_SIZE_PERCENT, Boolean.TRUE);
+ }
+
+ // Area of interest parameters
+ int x = ServletUtil.getIntParameter(pRequest, mRegionLeftParam, -1); // Default is center
+ int y = ServletUtil.getIntParameter(pRequest, mRegionTopParam, -1); // Default is center
+ width = ServletUtil.getIntParameter(pRequest, mRegionWidthParam, -1);
+ height = ServletUtil.getIntParameter(pRequest, mRegionHeightParam, -1);
+ if (width > 0 || height > 0) {
+ pRequest.setAttribute(ImageServletResponse.ATTRIB_AOI, new Rectangle(x, y, width, height));
+ }
+
+ // AOI uniform/percent
+ uniform = ServletUtil.getBooleanParameter(pRequest, mRegionUniformParam, false);
+ if (uniform) {
+ pRequest.setAttribute(ImageServletResponse.ATTRIB_SIZE_UNIFORM, Boolean.TRUE);
+ }
+ percent = ServletUtil.getBooleanParameter(pRequest, mRegionPercentParam, false);
+ if (percent) {
+ pRequest.setAttribute(ImageServletResponse.ATTRIB_SIZE_PERCENT, Boolean.TRUE);
+ }
+
+ super.doFilterImpl(pRequest, pResponse, pChain);
+ }
+
+ /**
+ * This implementation does no filtering, and simply returns the image
+ * passed in.
+ *
+ * @param pImage
+ * @param pRequest
+ * @param pResponse
+ * @return {@code pImage}
+ */
+ protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) {
+ return pImage;
+ }
}
\ No newline at end of file
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/TextRenderer.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/TextRenderer.java
index b4380bf3..ebe41d97 100755
--- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/TextRenderer.java
+++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/TextRenderer.java
@@ -1,348 +1,348 @@
-/*
- * Copyright (c) 2008, Harald Kuhr
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name "TwelveMonkeys" nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package com.twelvemonkeys.servlet.image;
-
-import com.twelvemonkeys.lang.MathUtil;
-import com.twelvemonkeys.lang.StringUtil;
-import com.twelvemonkeys.servlet.ServletUtil;
-
-import javax.servlet.ServletRequest;
-import javax.servlet.http.HttpServletRequest;
-import java.awt.*;
-import java.awt.geom.Rectangle2D;
-
-/**
- * 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,
- * style or color, into an image, and output it as a GIF, JPEG or PNG image,
- * with optional caching of the rendered image files.
- *
- *
- *
- *
- * @example
- * <IMG src="/text/test.gif?height=40&width=600
- * &fontFamily=TimesRoman&fontSize=30&fontStyle=italic&fgcolor=%23990033
- * &bgcolor=%23cccccc&text=the%20quick%20brown%20fox%20jumps%20over%20the
- * %20lazy%20dog&cache=false" />
- *
- * @example
- * <IMG src="/text/test.jpg?height=40&width=600
- * &fontFamily=TimesRoman&fontSize=30&fontStyle=italic&fgcolor=black
- * &bgcolor=%23cccccc&text=the%20quick%20brown%20fox%20jumps%20over%20the
- * %20lazy%20dog&compression=3&cache=false" />
- *
- * @example
- * <IMG src="/text/test.png?height=40&width=600
- * &fontFamily=TimesRoman&fontSize=30&fontStyle=italic&fgcolor=%23336699
- * &bgcolor=%23cccccc&text=the%20quick%20brown%20fox%20jumps%20over%20the
- * %20lazy%20dog&cache=true" />
- *
- * @author Harald Kuhr
- * @author last modified by $Author: haku $
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/TextRenderer.java#2 $
- */
-
-class TextRenderer /*extends ImageServlet implements ImagePainterServlet*/ {
- // TODO: Create something useable out of this piece of old junk.. ;-)
- // It just needs a graphics object to write onto
- // Alternatively, defer, and compute the size needed
- // Or, make it a filter...
-
- /** {@code "italic"} */
- public final static String FONT_STYLE_ITALIC = "italic";
- /** {@code "plain"} */
- public final static String FONT_STYLE_PLAIN = "plain";
- /** {@code "bold"} */
- public final static String FONT_STYLE_BOLD = "bold";
-
- /** {@code text} */
- public final static String PARAM_TEXT = "text";
- /** {@code marginLeft} */
- public final static String PARAM_MARGIN_LEFT = "marginLeft";
- /** {@code marginTop} */
- public final static String PARAM_MARGIN_TOP = "marginTop";
- /** {@code fontFamily} */
- public final static String PARAM_FONT_FAMILY = "fontFamily";
- /** {@code fontSize} */
- public final static String PARAM_FONT_SIZE = "fontSize";
- /** {@code fontStyle} */
- public final static String PARAM_FONT_STYLE = "fontStyle";
- /** {@code textRotation} */
- public final static String PARAM_TEXT_ROTATION = "textRotation";
- /** {@code textRotation} */
- public final static String PARAM_TEXT_ROTATION_UNITS = "textRotationUnits";
-
- /** {@code bgcolor} */
- public final static String PARAM_BGCOLOR = "bgcolor";
- /** {@code fgcolor} */
- public final static String PARAM_FGCOLOR = "fgcolor";
-
- protected final static String ROTATION_DEGREES = "DEGREES";
- protected final static String ROTATION_RADIANS = "RADIANS";
-
- /**
- * Creates the TextRender servlet.
- */
-
- public TextRenderer() {
- }
-
- /**
- * Renders the text string for this servlet request.
- */
- private void paint(ServletRequest pReq, Graphics2D pRes,
- int pWidth, int pHeight)
- throws ImageServletException {
-
- // Get parameters
- String text = pReq.getParameter(PARAM_TEXT);
- String[] lines = StringUtil.toStringArray(text, "\n\r");
-
- String fontFamily = pReq.getParameter(PARAM_FONT_FAMILY);
- String fontSize = pReq.getParameter(PARAM_FONT_SIZE);
- String fontStyle = pReq.getParameter(PARAM_FONT_STYLE);
-
- String bgcolor = pReq.getParameter(PARAM_BGCOLOR);
- String fgcolor = pReq.getParameter(PARAM_FGCOLOR);
-
- // TODO: Make them static..
- 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_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY));
- // pRes.addRenderingHints(new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON));
-
- //System.out.println(pRes.getBackground());
-
- // Clear area with bgcolor
- if (!StringUtil.isEmpty(bgcolor)) {
- pRes.setBackground(StringUtil.toColor(bgcolor));
- pRes.clearRect(0, 0, pWidth, pHeight);
-
- //System.out.println(pRes.getBackground());
- }
-
- // Create and set font
- Font font = new Font((fontFamily != null ? fontFamily : "Helvetica"),
- getFontStyle(fontStyle),
- (fontSize != null ? Integer.parseInt(fontSize)
- : 12));
- pRes.setFont(font);
-
- // Set rotation
- double angle = getAngle(pReq);
- pRes.rotate(angle, pWidth / 2.0, pHeight / 2.0);
-
- // Draw string in fgcolor
- pRes.setColor(fgcolor != null ? StringUtil.toColor(fgcolor)
- : Color.black);
-
- float x = ServletUtil.getFloatParameter(pReq, PARAM_MARGIN_LEFT,
- Float.MIN_VALUE);
- Rectangle2D[] bounds = new Rectangle2D[lines.length];
- if (x <= Float.MIN_VALUE) {
- // Center
- float longest = 0f;
- for (int i = 0; i < lines.length; i++) {
- bounds[i] = font.getStringBounds(lines[i],
- pRes.getFontRenderContext());
- if (bounds[i].getWidth() > longest) {
- longest = (float) bounds[i].getWidth();
- }
- }
-
- //x = (float) ((pWidth - bounds.getWidth()) / 2f);
- x = (float) ((pWidth - longest) / 2f);
-
- //System.out.println("marginLeft: " + x);
- }
- //else {
- //System.out.println("marginLeft (from param): " + x);
- //}
-
- float y = ServletUtil.getFloatParameter(pReq, PARAM_MARGIN_TOP,
- Float.MIN_VALUE);
- float lineHeight = (float) (bounds[0] != null ? bounds[0].getHeight() :
- font.getStringBounds(lines[0],
- pRes.getFontRenderContext()).getHeight());
-
- if (y <= Float.MIN_VALUE) {
- // Center
- y = (float) ((pHeight - lineHeight) / 2f)
- - (lineHeight * (lines.length - 2.5f) / 2f);
-
- //System.out.println("marginTop: " + y);
- }
- else {
- // Todo: Correct for font height?
- y += font.getSize2D();
- //System.out.println("marginTop (from param):" + y);
-
- }
-
- //System.out.println("Font size: " + font.getSize2D());
- //System.out.println("Line height: " + lineHeight);
-
- // Draw
- for (int i = 0; i < lines.length; i++) {
- pRes.drawString(lines[i], x, y + lineHeight * i);
- }
- }
-
- /**
- * Returns the font style constant.
- *
- * @param pStyle a string containing either the word {@code "plain"} or one
- * or more of {@code "bold"} and {@code italic}.
- * @return the font style constant as defined in {@link Font}.
- *
- * @see Font#PLAIN
- * @see Font#BOLD
- * @see Font#ITALIC
- */
- private int getFontStyle(String pStyle) {
- if (pStyle == null
- || StringUtil.containsIgnoreCase(pStyle, FONT_STYLE_PLAIN)) {
- return Font.PLAIN;
- }
-
- // Try to find bold/italic
- int style = Font.PLAIN;
- if (StringUtil.containsIgnoreCase(pStyle, FONT_STYLE_BOLD)) {
- style |= Font.BOLD;
- }
- if (StringUtil.containsIgnoreCase(pStyle, FONT_STYLE_ITALIC)) {
- style |= Font.ITALIC;
- }
-
- return style;
- }
-
- /**
- * Gets the angle of rotation from the request.
- *
- * @param pRequest the servlet request to get parameters from
- * @return the angle in radians.
- */
- private double getAngle(ServletRequest pRequest) {
- // Get angle
- double angle =
- ServletUtil.getDoubleParameter(pRequest, PARAM_TEXT_ROTATION, 0.0);
-
- // Convert to radians, if needed
- String units = pRequest.getParameter(PARAM_TEXT_ROTATION_UNITS);
- if (!StringUtil.isEmpty(units)
- && ROTATION_DEGREES.equalsIgnoreCase(units)) {
- angle = MathUtil.toRadians(angle);
- }
-
- return angle;
- }
-
-}
-
-
+/*
+ * Copyright (c) 2008, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name "TwelveMonkeys" nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.twelvemonkeys.servlet.image;
+
+import com.twelvemonkeys.lang.MathUtil;
+import com.twelvemonkeys.lang.StringUtil;
+import com.twelvemonkeys.servlet.ServletUtil;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.http.HttpServletRequest;
+import java.awt.*;
+import java.awt.geom.Rectangle2D;
+
+/**
+ * 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,
+ * style or color, into an image, and output it as a GIF, JPEG or PNG image,
+ * with optional caching of the rendered image files.
+ *
+ *
+ *
+ *
+ * @example
+ * <IMG src="/text/test.gif?height=40&width=600
+ * &fontFamily=TimesRoman&fontSize=30&fontStyle=italic&fgcolor=%23990033
+ * &bgcolor=%23cccccc&text=the%20quick%20brown%20fox%20jumps%20over%20the
+ * %20lazy%20dog&cache=false" />
+ *
+ * @example
+ * <IMG src="/text/test.jpg?height=40&width=600
+ * &fontFamily=TimesRoman&fontSize=30&fontStyle=italic&fgcolor=black
+ * &bgcolor=%23cccccc&text=the%20quick%20brown%20fox%20jumps%20over%20the
+ * %20lazy%20dog&compression=3&cache=false" />
+ *
+ * @example
+ * <IMG src="/text/test.png?height=40&width=600
+ * &fontFamily=TimesRoman&fontSize=30&fontStyle=italic&fgcolor=%23336699
+ * &bgcolor=%23cccccc&text=the%20quick%20brown%20fox%20jumps%20over%20the
+ * %20lazy%20dog&cache=true" />
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haku $
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/TextRenderer.java#2 $
+ */
+
+class TextRenderer /*extends ImageServlet implements ImagePainterServlet*/ {
+ // TODO: Create something useable out of this piece of old junk.. ;-)
+ // It just needs a graphics object to write onto
+ // Alternatively, defer, and compute the size needed
+ // Or, make it a filter...
+
+ /** {@code "italic"} */
+ public final static String FONT_STYLE_ITALIC = "italic";
+ /** {@code "plain"} */
+ public final static String FONT_STYLE_PLAIN = "plain";
+ /** {@code "bold"} */
+ public final static String FONT_STYLE_BOLD = "bold";
+
+ /** {@code text} */
+ public final static String PARAM_TEXT = "text";
+ /** {@code marginLeft} */
+ public final static String PARAM_MARGIN_LEFT = "marginLeft";
+ /** {@code marginTop} */
+ public final static String PARAM_MARGIN_TOP = "marginTop";
+ /** {@code fontFamily} */
+ public final static String PARAM_FONT_FAMILY = "fontFamily";
+ /** {@code fontSize} */
+ public final static String PARAM_FONT_SIZE = "fontSize";
+ /** {@code fontStyle} */
+ public final static String PARAM_FONT_STYLE = "fontStyle";
+ /** {@code textRotation} */
+ public final static String PARAM_TEXT_ROTATION = "textRotation";
+ /** {@code textRotation} */
+ public final static String PARAM_TEXT_ROTATION_UNITS = "textRotationUnits";
+
+ /** {@code bgcolor} */
+ public final static String PARAM_BGCOLOR = "bgcolor";
+ /** {@code fgcolor} */
+ public final static String PARAM_FGCOLOR = "fgcolor";
+
+ protected final static String ROTATION_DEGREES = "DEGREES";
+ protected final static String ROTATION_RADIANS = "RADIANS";
+
+ /**
+ * Creates the TextRender servlet.
+ */
+
+ public TextRenderer() {
+ }
+
+ /**
+ * Renders the text string for this servlet request.
+ */
+ private void paint(ServletRequest pReq, Graphics2D pRes,
+ int pWidth, int pHeight)
+ throws ImageServletException {
+
+ // Get parameters
+ String text = pReq.getParameter(PARAM_TEXT);
+ String[] lines = StringUtil.toStringArray(text, "\n\r");
+
+ String fontFamily = pReq.getParameter(PARAM_FONT_FAMILY);
+ String fontSize = pReq.getParameter(PARAM_FONT_SIZE);
+ String fontStyle = pReq.getParameter(PARAM_FONT_STYLE);
+
+ String bgcolor = pReq.getParameter(PARAM_BGCOLOR);
+ String fgcolor = pReq.getParameter(PARAM_FGCOLOR);
+
+ // TODO: Make them static..
+ 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_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY));
+ // pRes.addRenderingHints(new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON));
+
+ //System.out.println(pRes.getBackground());
+
+ // Clear area with bgcolor
+ if (!StringUtil.isEmpty(bgcolor)) {
+ pRes.setBackground(StringUtil.toColor(bgcolor));
+ pRes.clearRect(0, 0, pWidth, pHeight);
+
+ //System.out.println(pRes.getBackground());
+ }
+
+ // Create and set font
+ Font font = new Font((fontFamily != null ? fontFamily : "Helvetica"),
+ getFontStyle(fontStyle),
+ (fontSize != null ? Integer.parseInt(fontSize)
+ : 12));
+ pRes.setFont(font);
+
+ // Set rotation
+ double angle = getAngle(pReq);
+ pRes.rotate(angle, pWidth / 2.0, pHeight / 2.0);
+
+ // Draw string in fgcolor
+ pRes.setColor(fgcolor != null ? StringUtil.toColor(fgcolor)
+ : Color.black);
+
+ float x = ServletUtil.getFloatParameter(pReq, PARAM_MARGIN_LEFT,
+ Float.MIN_VALUE);
+ Rectangle2D[] bounds = new Rectangle2D[lines.length];
+ if (x <= Float.MIN_VALUE) {
+ // Center
+ float longest = 0f;
+ for (int i = 0; i < lines.length; i++) {
+ bounds[i] = font.getStringBounds(lines[i],
+ pRes.getFontRenderContext());
+ if (bounds[i].getWidth() > longest) {
+ longest = (float) bounds[i].getWidth();
+ }
+ }
+
+ //x = (float) ((pWidth - bounds.getWidth()) / 2f);
+ x = (float) ((pWidth - longest) / 2f);
+
+ //System.out.println("marginLeft: " + x);
+ }
+ //else {
+ //System.out.println("marginLeft (from param): " + x);
+ //}
+
+ float y = ServletUtil.getFloatParameter(pReq, PARAM_MARGIN_TOP,
+ Float.MIN_VALUE);
+ float lineHeight = (float) (bounds[0] != null ? bounds[0].getHeight() :
+ font.getStringBounds(lines[0],
+ pRes.getFontRenderContext()).getHeight());
+
+ if (y <= Float.MIN_VALUE) {
+ // Center
+ y = (float) ((pHeight - lineHeight) / 2f)
+ - (lineHeight * (lines.length - 2.5f) / 2f);
+
+ //System.out.println("marginTop: " + y);
+ }
+ else {
+ // Todo: Correct for font height?
+ y += font.getSize2D();
+ //System.out.println("marginTop (from param):" + y);
+
+ }
+
+ //System.out.println("Font size: " + font.getSize2D());
+ //System.out.println("Line height: " + lineHeight);
+
+ // Draw
+ for (int i = 0; i < lines.length; i++) {
+ pRes.drawString(lines[i], x, y + lineHeight * i);
+ }
+ }
+
+ /**
+ * Returns the font style constant.
+ *
+ * @param pStyle a string containing either the word {@code "plain"} or one
+ * or more of {@code "bold"} and {@code italic}.
+ * @return the font style constant as defined in {@link Font}.
+ *
+ * @see Font#PLAIN
+ * @see Font#BOLD
+ * @see Font#ITALIC
+ */
+ private int getFontStyle(String pStyle) {
+ if (pStyle == null
+ || StringUtil.containsIgnoreCase(pStyle, FONT_STYLE_PLAIN)) {
+ return Font.PLAIN;
+ }
+
+ // Try to find bold/italic
+ int style = Font.PLAIN;
+ if (StringUtil.containsIgnoreCase(pStyle, FONT_STYLE_BOLD)) {
+ style |= Font.BOLD;
+ }
+ if (StringUtil.containsIgnoreCase(pStyle, FONT_STYLE_ITALIC)) {
+ style |= Font.ITALIC;
+ }
+
+ return style;
+ }
+
+ /**
+ * Gets the angle of rotation from the request.
+ *
+ * @param pRequest the servlet request to get parameters from
+ * @return the angle in radians.
+ */
+ private double getAngle(ServletRequest pRequest) {
+ // Get angle
+ double angle =
+ ServletUtil.getDoubleParameter(pRequest, PARAM_TEXT_ROTATION, 0.0);
+
+ // Convert to radians, if needed
+ String units = pRequest.getParameter(PARAM_TEXT_ROTATION_UNITS);
+ if (!StringUtil.isEmpty(units)
+ && ROTATION_DEGREES.equalsIgnoreCase(units)) {
+ angle = MathUtil.toRadians(angle);
+ }
+
+ return angle;
+ }
+
+}
+
+
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/package_info.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/package_info.java
index 006395c7..e12ed172 100755
--- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/package_info.java
+++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/image/package_info.java
@@ -1,34 +1,34 @@
-/**
- * Contains various image-outputting servlets, that should run under any servlet engine. To create your own image servlet, simply subclass the servlet
- * {@code ImageServlet}. Optionally implement the interface
- * {@code ImagePainterServlet}, if you want to do painting.
- *
- *
- * See the document
- * AWT Enhancements and bugtraq report
- * 4281163 for more information on this issue.
- *
- *
- *
+ *
+ * See the document
+ * AWT Enhancements and bugtraq report
+ * 4281163 for more information on this issue.
+ *
+ *
+ *
- * PageContext.REQUEST_SCOPE scope.
- */
-
- public void setValue(String pValue) {
- mParameterValue = new Param(pValue);
- }
-
- /**
- * 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.
- *
- * @return If this tag is enclosed within an {@code IncludeTag}, then
- * the default return value from this method is the {@code
- * TagSupport.SKIP_BODY} value.
- * @exception JspException
- */
-
- public int doStartTag() throws JspException {
- //checkEnclosedInIncludeTag();
-
- addParameter();
-
- return SKIP_BODY;
- }
-
- /**
- * This is the method responsible for actually testing that the tag
- * implemented by this class is enclosed within an {@code IncludeTag}.
- *
- * @exception JspException
- */
- /*
- protected void checkEnclosedInIncludeTag() throws JspException {
- Tag parentTag = getParent();
-
- if ((parentTag != null) && (parentTag instanceof IncludeTag)) {
- return;
- }
-
- String msg = "A class that extends EnclosedIncludeBodyReaderTag " +
- "is not enclosed within an IncludeTag.";
- log(msg);
- throw new JspException(msg);
- }
- */
-
- /**
- * This method adds the parameter whose name and value were passed to this
- * object via the tag attributes to the parent {@code Include} tag.
- */
-
- private void addParameter() {
- IncludeTag includeTag = (IncludeTag) getParent();
-
- includeTag.addParameter(mParameterName, mParameterValue);
- }
-
- /**
- * 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
- * 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
- * life cycle just in case a JspException was thrown during the tag
- * execution.
- */
-
- protected void clearServiceState() {
- mParameterName = null;
- mParameterValue = null;
- }
-}
+/*
+ * Copyright (c) 2002 TwelveMonkeys.
+ * All rights reserved.
+ *
+ * $Log: ParamTag.java,v $
+ * Revision 1.2 2003/10/06 14:26:00 WMHAKUR
+ * Code clean-up only.
+ *
+ * Revision 1.1 2002/10/18 14:03:09 WMHAKUR
+ * Moved to com.twelvemonkeys.servlet.jsp.droplet.taglib
+ *
+ *
+ */
+
+package com.twelvemonkeys.servlet.jsp.droplet.taglib;
+
+import java.io.IOException;
+
+import javax.servlet.*;
+import javax.servlet.jsp.*;
+import javax.servlet.jsp.tagext.*;
+
+import com.twelvemonkeys.servlet.jsp.droplet.*;
+import com.twelvemonkeys.servlet.jsp.taglib.*;
+
+/**
+ * Parameter tag that emulates ATG Dynamo JHTML behaviour for JSP.
+ *
+ * @author Thomas Purcell (CSC Australia)
+ * @author Harald Kuhr
+ * @author last modified by $Author: haku $
+ *
+ * @version $Revision: #1 $, ($Date: 2008/05/05 $)
+ *
+ */
+
+public class ParamTag extends ExTagSupport {
+
+ /**
+ * This is the name of the parameter to be inserted into the {@code
+ * PageContext.REQUEST_SCOPE} scope.
+ */
+
+ private String mParameterName;
+
+ /**
+ * This is the value for the parameter to be inserted into the {@code
+ * PageContext.REQUEST_SCOPE} scope.
+ */
+
+ private Object mParameterValue;
+
+ /**
+ * This method allows the JSP page to set the name for the parameter by
+ * using the {@code name} tag attribute.
+ *
+ * @param pName The name for the parameter to insert into the {@code
+ * PageContext.REQUEST_SCOPE} scope.
+ */
+
+ public void setName(String pName) {
+ mParameterName = pName;
+ }
+
+ /**
+ * This method allows the JSP page to set the value for hte parameter by
+ * using the {@code value} tag attribute.
+ *
+ * @param pValue The value for the parameter to insert into the
+ * PageContext.REQUEST_SCOPE scope.
+ */
+
+ public void setValue(String pValue) {
+ mParameterValue = new Param(pValue);
+ }
+
+ /**
+ * 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.
+ *
+ * @return If this tag is enclosed within an {@code IncludeTag}, then
+ * the default return value from this method is the {@code
+ * TagSupport.SKIP_BODY} value.
+ * @exception JspException
+ */
+
+ public int doStartTag() throws JspException {
+ //checkEnclosedInIncludeTag();
+
+ addParameter();
+
+ return SKIP_BODY;
+ }
+
+ /**
+ * This is the method responsible for actually testing that the tag
+ * implemented by this class is enclosed within an {@code IncludeTag}.
+ *
+ * @exception JspException
+ */
+ /*
+ protected void checkEnclosedInIncludeTag() throws JspException {
+ Tag parentTag = getParent();
+
+ if ((parentTag != null) && (parentTag instanceof IncludeTag)) {
+ return;
+ }
+
+ String msg = "A class that extends EnclosedIncludeBodyReaderTag " +
+ "is not enclosed within an IncludeTag.";
+ log(msg);
+ throw new JspException(msg);
+ }
+ */
+
+ /**
+ * This method adds the parameter whose name and value were passed to this
+ * object via the tag attributes to the parent {@code Include} tag.
+ */
+
+ private void addParameter() {
+ IncludeTag includeTag = (IncludeTag) getParent();
+
+ includeTag.addParameter(mParameterName, mParameterValue);
+ }
+
+ /**
+ * 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
+ * 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
+ * life cycle just in case a JspException was thrown during the tag
+ * execution.
+ */
+
+ protected void clearServiceState() {
+ mParameterName = null;
+ mParameterValue = null;
+ }
+}
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/ValueOfTEI.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/ValueOfTEI.java
index 25aae40d..9ecafd8d 100755
--- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/ValueOfTEI.java
+++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/ValueOfTEI.java
@@ -1,50 +1,50 @@
-/*
- * Copyright (c) 2002 TwelveMonkeys.
- * All rights reserved.
- *
- * $Log: ValueOfTEI.java,v $
- * Revision 1.3 2003/10/06 14:26:07 WMHAKUR
- * Code clean-up only.
- *
- * Revision 1.2 2002/10/18 14:28:07 WMHAKUR
- * Fixed package error.
- *
- * Revision 1.1 2002/10/18 14:03:52 WMHAKUR
- * Moved to com.twelvemonkeys.servlet.jsp.droplet.taglib
- *
- *
- */
-
-package com.twelvemonkeys.servlet.jsp.droplet.taglib;
-
-import java.io.IOException;
-
-import javax.servlet.jsp.*;
-import javax.servlet.jsp.tagext.*;
-
-/**
- * TagExtraInfo for ValueOf.
- * @todo More meaningful response to the user.
- *
- * @author Harald Kuhr
- * @author last modified by $Author: haku $
- *
- * @version $Revision: #1 $, ($Date: 2008/05/05 $)
- *
- */
-
-public class ValueOfTEI extends TagExtraInfo {
-
- public boolean isValid(TagData pTagData) {
- Object nameAttr = pTagData.getAttribute("name");
- Object paramAttr = pTagData.getAttribute("param");
-
- if ((nameAttr != null && paramAttr == null) ||
- (nameAttr == null && paramAttr != null)) {
- return true; // Exactly one of name or param set
- }
-
- // Either both or none,
- return false;
- }
-}
+/*
+ * Copyright (c) 2002 TwelveMonkeys.
+ * All rights reserved.
+ *
+ * $Log: ValueOfTEI.java,v $
+ * Revision 1.3 2003/10/06 14:26:07 WMHAKUR
+ * Code clean-up only.
+ *
+ * Revision 1.2 2002/10/18 14:28:07 WMHAKUR
+ * Fixed package error.
+ *
+ * Revision 1.1 2002/10/18 14:03:52 WMHAKUR
+ * Moved to com.twelvemonkeys.servlet.jsp.droplet.taglib
+ *
+ *
+ */
+
+package com.twelvemonkeys.servlet.jsp.droplet.taglib;
+
+import java.io.IOException;
+
+import javax.servlet.jsp.*;
+import javax.servlet.jsp.tagext.*;
+
+/**
+ * TagExtraInfo for ValueOf.
+ * @todo More meaningful response to the user.
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haku $
+ *
+ * @version $Revision: #1 $, ($Date: 2008/05/05 $)
+ *
+ */
+
+public class ValueOfTEI extends TagExtraInfo {
+
+ public boolean isValid(TagData pTagData) {
+ Object nameAttr = pTagData.getAttribute("name");
+ Object paramAttr = pTagData.getAttribute("param");
+
+ if ((nameAttr != null && paramAttr == null) ||
+ (nameAttr == null && paramAttr != null)) {
+ return true; // Exactly one of name or param set
+ }
+
+ // Either both or none,
+ return false;
+ }
+}
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/ValueOfTag.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/ValueOfTag.java
index b3171222..180abe75 100755
--- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/ValueOfTag.java
+++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/ValueOfTag.java
@@ -1,147 +1,147 @@
-/*
- * Copyright (c) 2002 TwelveMonkeys.
- * All rights reserved.
- *
- * $Log: ValueOfTag.java,v $
- * Revision 1.2 2003/10/06 14:26:14 WMHAKUR
- * Code clean-up only.
- *
- * Revision 1.1 2002/10/18 14:03:52 WMHAKUR
- * Moved to com.twelvemonkeys.servlet.jsp.droplet.taglib
- *
- *
- */
-
-package com.twelvemonkeys.servlet.jsp.droplet.taglib;
-
-import java.io.*;
-
-import javax.servlet.*;
-import javax.servlet.jsp.*;
-
-import com.twelvemonkeys.servlet.jsp.droplet.*;
-import com.twelvemonkeys.servlet.jsp.taglib.*;
-
-/**
- * ValueOf tag that emulates ATG Dynamo JHTML behaviour for JSP.
- *
- * @author Thomas Purcell (CSC Australia)
- * @author Harald Kuhr
- * @author last modified by $Author: haku $
- *
- * @version $Revision: #1 $, ($Date: 2008/05/05 $)
- */
-public class ValueOfTag extends ExTagSupport {
-
- /**
- * 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}
- * attribute.
- */
- private String mParameterName;
-
- /**
- * This is the value of the parameter read from the {@code
- * PageContext.REQUEST_SCOPE} scope. If the parameter doesn't exist,
- * then this will be null.
- */
- private Object mParameterValue;
-
- /**
- * 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
- * PageContext.REQUEST_SCOPE} scope.
- *
- * @param pName The name of the parameter to be read from the {@code
- * PageContext.REQUEST_SCOPE} scope.
- */
- public void setName(String pName) {
- mParameterName = pName;
- }
-
- /**
- * 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
- * PageContext.REQUEST_SCOPE} scope. This is just a synonym for
- * setName, to be more like ATG Dynamo.
- *
- * @param pName The name of the parameter to be read from the {@code
- * PageContext.REQUEST_SCOPE} scope.
- */
- public void setParam(String pName) {
- mParameterName = pName;
- }
-
- /**
- * This method looks in the session scope for the session-scoped attribute
- * 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
- * session-scoped attribute. If it fails to find the session-scoped
- * attribute, it displays the body for this tag.
- *
- * @return If the session-scoped attribute is found, then this method will
- * return {@code TagSupport.SKIP_BODY}, otherwise it will return
- * {@code TagSupport.EVAL_BODY_INCLUDE}.
- * @exception JspException
- *
- */
- public int doStartTag() throws JspException {
- try {
- if (parameterExists()) {
- if (mParameterValue instanceof JspFragment) {
- // OPARAM or PARAM
- ((JspFragment) mParameterValue).service(pageContext);
- /*
- log("Service subpage " + pageContext.getServletContext().getRealPath(((Oparam) mParameterValue).getName()));
-
- pageContext.include(((Oparam) mParameterValue).getName());
- */
- }
- else {
- // Normal JSP parameter value
- JspWriter writer = pageContext.getOut();
- writer.print(mParameterValue);
- }
-
- return SKIP_BODY;
- }
- else {
- return EVAL_BODY_INCLUDE;
- }
- }
- catch (ServletException se) {
- log(se.getMessage(), se);
- throw new JspException(se);
- }
- catch (IOException ioe) {
- String msg = "Caught an IOException in ValueOfTag.doStartTag()\n"
- + ioe.toString();
- log(msg, ioe);
- throw new JspException(msg);
- }
- }
-
- /**
- * This method is used to determine whether the parameter whose name is
- * stored in {@code mParameterName} exists within the {@code
- * PageContext.REQUEST_SCOPE} scope. If the parameter does exist,
- * then this method will return {@code true}, otherwise it returns
- * {@code false}. This method has the side affect of loading the
- * parameter value into {@code mParameterValue} if the parameter
- * does exist.
- *
- * @return {@code true} if the parameter whose name is in {@code
- * mParameterName} exists in the {@code PageContext.REQUEST_SCOPE
- * } scope, {@code false} otherwise.
- */
- private boolean parameterExists() {
- mParameterValue = pageContext.getAttribute(mParameterName, PageContext.REQUEST_SCOPE);
-
- // -- Harald K 20020726
- if (mParameterValue == null) {
- mParameterValue = pageContext.getRequest().getParameter(mParameterName);
- }
-
- return (mParameterValue != null);
- }
-}
+/*
+ * Copyright (c) 2002 TwelveMonkeys.
+ * All rights reserved.
+ *
+ * $Log: ValueOfTag.java,v $
+ * Revision 1.2 2003/10/06 14:26:14 WMHAKUR
+ * Code clean-up only.
+ *
+ * Revision 1.1 2002/10/18 14:03:52 WMHAKUR
+ * Moved to com.twelvemonkeys.servlet.jsp.droplet.taglib
+ *
+ *
+ */
+
+package com.twelvemonkeys.servlet.jsp.droplet.taglib;
+
+import java.io.*;
+
+import javax.servlet.*;
+import javax.servlet.jsp.*;
+
+import com.twelvemonkeys.servlet.jsp.droplet.*;
+import com.twelvemonkeys.servlet.jsp.taglib.*;
+
+/**
+ * ValueOf tag that emulates ATG Dynamo JHTML behaviour for JSP.
+ *
+ * @author Thomas Purcell (CSC Australia)
+ * @author Harald Kuhr
+ * @author last modified by $Author: haku $
+ *
+ * @version $Revision: #1 $, ($Date: 2008/05/05 $)
+ */
+public class ValueOfTag extends ExTagSupport {
+
+ /**
+ * 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}
+ * attribute.
+ */
+ private String mParameterName;
+
+ /**
+ * This is the value of the parameter read from the {@code
+ * PageContext.REQUEST_SCOPE} scope. If the parameter doesn't exist,
+ * then this will be null.
+ */
+ private Object mParameterValue;
+
+ /**
+ * 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
+ * PageContext.REQUEST_SCOPE} scope.
+ *
+ * @param pName The name of the parameter to be read from the {@code
+ * PageContext.REQUEST_SCOPE} scope.
+ */
+ public void setName(String pName) {
+ mParameterName = pName;
+ }
+
+ /**
+ * 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
+ * PageContext.REQUEST_SCOPE} scope. This is just a synonym for
+ * setName, to be more like ATG Dynamo.
+ *
+ * @param pName The name of the parameter to be read from the {@code
+ * PageContext.REQUEST_SCOPE} scope.
+ */
+ public void setParam(String pName) {
+ mParameterName = pName;
+ }
+
+ /**
+ * This method looks in the session scope for the session-scoped attribute
+ * 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
+ * session-scoped attribute. If it fails to find the session-scoped
+ * attribute, it displays the body for this tag.
+ *
+ * @return If the session-scoped attribute is found, then this method will
+ * return {@code TagSupport.SKIP_BODY}, otherwise it will return
+ * {@code TagSupport.EVAL_BODY_INCLUDE}.
+ * @exception JspException
+ *
+ */
+ public int doStartTag() throws JspException {
+ try {
+ if (parameterExists()) {
+ if (mParameterValue instanceof JspFragment) {
+ // OPARAM or PARAM
+ ((JspFragment) mParameterValue).service(pageContext);
+ /*
+ log("Service subpage " + pageContext.getServletContext().getRealPath(((Oparam) mParameterValue).getName()));
+
+ pageContext.include(((Oparam) mParameterValue).getName());
+ */
+ }
+ else {
+ // Normal JSP parameter value
+ JspWriter writer = pageContext.getOut();
+ writer.print(mParameterValue);
+ }
+
+ return SKIP_BODY;
+ }
+ else {
+ return EVAL_BODY_INCLUDE;
+ }
+ }
+ catch (ServletException se) {
+ log(se.getMessage(), se);
+ throw new JspException(se);
+ }
+ catch (IOException ioe) {
+ String msg = "Caught an IOException in ValueOfTag.doStartTag()\n"
+ + ioe.toString();
+ log(msg, ioe);
+ throw new JspException(msg);
+ }
+ }
+
+ /**
+ * This method is used to determine whether the parameter whose name is
+ * stored in {@code mParameterName} exists within the {@code
+ * PageContext.REQUEST_SCOPE} scope. If the parameter does exist,
+ * then this method will return {@code true}, otherwise it returns
+ * {@code false}. This method has the side affect of loading the
+ * parameter value into {@code mParameterValue} if the parameter
+ * does exist.
+ *
+ * @return {@code true} if the parameter whose name is in {@code
+ * mParameterName} exists in the {@code PageContext.REQUEST_SCOPE
+ * } scope, {@code false} otherwise.
+ */
+ private boolean parameterExists() {
+ mParameterValue = pageContext.getAttribute(mParameterName, PageContext.REQUEST_SCOPE);
+
+ // -- Harald K 20020726
+ if (mParameterValue == null) {
+ mParameterValue = pageContext.getRequest().getParameter(mParameterName);
+ }
+
+ return (mParameterValue != null);
+ }
+}
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/package.html b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/package.html
index 8aa9a145..2df9add1 100755
--- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/package.html
+++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/package.html
@@ -1,10 +1,10 @@
-
-
-
-The TwelveMonkeys droplet TagLib.
-
-TODO: Insert taglib-descriptor here?
-
-
-
-
+
+
+
+The TwelveMonkeys droplet TagLib.
+
+TODO: Insert taglib-descriptor here?
+
+
+
+
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/package_info.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/package_info.java
index b4d9a8dc..6681cfbc 100755
--- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/package_info.java
+++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/package_info.java
@@ -1,4 +1,4 @@
-/**
- * JSP support.
- */
+/**
+ * JSP support.
+ */
package com.twelvemonkeys.servlet.jsp;
\ No newline at end of file
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/BodyReaderTag.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/BodyReaderTag.java
index a7b2ae27..36d1f926 100755
--- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/BodyReaderTag.java
+++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/BodyReaderTag.java
@@ -1,43 +1,43 @@
-
-package com.twelvemonkeys.servlet.jsp.taglib;
-
-import javax.servlet.jsp.JspException;
-
-/**
- *
- *
- * @author Thomas Purcell (CSC Australia)
- *
- * @version 1.0
- */
-
-public abstract class BodyReaderTag extends ExBodyTagSupport {
- /**
- * 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
- * method takes the content as a string and passes it to the {@code
- * processBody} method.
- *
- * @return This method returns the {@code BodyTagSupport.SKIP_BODY}
- * constant. This means that the body of the tag will only be
- * processed the one time.
- * @exception JspException
- */
-
- public int doAfterBody() throws JspException {
- processBody(bodyContent.getString());
- return SKIP_BODY;
- }
-
- /**
- * 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
- * the tag will have been interpreted to a String by the JSP engine before
- * this method is called.
- *
- * @param pContent The body for the custom tag converted to a String.
- * @exception JscException
- */
-
- protected abstract void processBody(String pContent) throws JspException;
-}
+
+package com.twelvemonkeys.servlet.jsp.taglib;
+
+import javax.servlet.jsp.JspException;
+
+/**
+ *
+ *
+ * @author Thomas Purcell (CSC Australia)
+ *
+ * @version 1.0
+ */
+
+public abstract class BodyReaderTag extends ExBodyTagSupport {
+ /**
+ * 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
+ * method takes the content as a string and passes it to the {@code
+ * processBody} method.
+ *
+ * @return This method returns the {@code BodyTagSupport.SKIP_BODY}
+ * constant. This means that the body of the tag will only be
+ * processed the one time.
+ * @exception JspException
+ */
+
+ public int doAfterBody() throws JspException {
+ processBody(bodyContent.getString());
+ return SKIP_BODY;
+ }
+
+ /**
+ * 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
+ * the tag will have been interpreted to a String by the JSP engine before
+ * this method is called.
+ *
+ * @param pContent The body for the custom tag converted to a String.
+ * @exception JscException
+ */
+
+ protected abstract void processBody(String pContent) throws JspException;
+}
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/CSVToTableTag.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/CSVToTableTag.java
index cd27b166..bbc0003a 100755
--- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/CSVToTableTag.java
+++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/CSVToTableTag.java
@@ -1,240 +1,240 @@
-/*
- * Copyright (c) 2002 TwelveMonkeys.
- * All rights reserved.
- *
- * $Log: CSVToTableTag.java,v $
- * Revision 1.3 2003/10/06 14:24:50 WMHAKUR
- * Code clean-up only.
- *
- * Revision 1.2 2002/11/26 17:33:49 WMHAKUR
- * Added documentation & removed System.out.println()s.
- *
- * Revision 1.1 2002/11/19 10:50:10 WMHAKUR
- * Renamed from CSVToTable, to follow naming conventions.
- *
- * Revision 1.1 2002/11/18 22:11:16 WMHAKUR
- * Tag to convert CSV to HTML table.
- * Can be further transformed, using XSLT.
- *
- */
-
-package com.twelvemonkeys.servlet.jsp.taglib;
-
-import java.util.*;
-import java.io.*;
-
-import javax.servlet.jsp.*;
-import javax.servlet.jsp.tagext.*;
-
-/**
- * Creates a table from a string of "comma-separated values" (CSV).
- * The delimiter character can be any character (or combination of characters).
- * The default delimiter is TAB ({@code \t}).
- *
- *
- *
- *
- *
- * The input may look like this:
- *
- * <c:totable firstRowIsHeader="true" delimiter=";">
- * header A;header B
- * data 1A; data 1B
- * data 2A; data 2B
- * </c:totable>
- *
- *
- * The output (source) will look like this:
- *
- * <TABLE>
- * <TR>
- * <TH>header A</TH><TH>header B</TH>
- * </TR>
- * <TR>
- * <TD>data 1A</TD><TD>data 1B</TD>
- * </TR>
- * <TR>
- * <TD>data 2A</TD><TD>data 2B</TD>
- * </TR>
- * </TABLE>
- *
- * You wil probably want to use XSLT to make the final output look nicer. :-)
- *
- * @see StringTokenizer
- * @see XSLT spec
- *
- * @author Harald Kuhr
- *
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/CSVToTableTag.java#1 $
- */
-
-public class CSVToTableTag extends ExBodyTagSupport {
-
- public final static String TAB = "\t";
-
- protected String mDelimiter = null;
- protected boolean mFirstRowIsHeader = false;
- protected boolean mFirstColIsHeader = false;
-
- public void setDelimiter(String pDelimiter) {
- mDelimiter = pDelimiter;
- }
-
- public String getDelimiter() {
- return mDelimiter != null ? mDelimiter : TAB;
- }
-
- public void setFirstRowIsHeader(String pBoolean) {
- mFirstRowIsHeader = Boolean.valueOf(pBoolean).booleanValue();
- }
-
- public void setFirstColIsHeader(String pBoolean) {
- mFirstColIsHeader = Boolean.valueOf(pBoolean).booleanValue();
- }
-
-
- public int doEndTag() throws JspException {
- BodyContent content = getBodyContent();
-
- try {
- Table table =
- Table.parseContent(content.getReader(), getDelimiter());
-
- JspWriter out = pageContext.getOut();
-
- //System.out.println("CSVToTable: " + table.getRows() + " rows, "
- // + table.getCols() + " cols.");
-
- if (table.getRows() > 0) {
- out.println("");
- // Loop over rows
- for (int row = 0; row < table.getRows(); row++) {
- out.println("
");
- }
- }
- catch (IOException ioe) {
- throw new JspException(ioe);
- }
-
- return super.doEndTag();
- }
-
- static class Table {
- List mRows = null;
- int mCols = 0;
-
- private Table(List pRows, int pCols) {
- mRows = pRows;
- mCols = pCols;
- }
-
- int getRows() {
- return mRows != null ? mRows.size() : 0;
- }
-
- int getCols() {
- return mCols;
- }
-
- List getTableRows() {
- return mRows;
- }
-
- List getTableRow(int pRow) {
- return mRows != null
- ? (List) mRows.get(pRow)
- : Collections.EMPTY_LIST;
- }
-
- String get(int pRow, int pCol) {
- List row = getTableRow(pRow);
- // Rows may contain unequal number of cols
- return (row.size() > pCol) ? (String) row.get(pCol) : "";
- }
-
- /**
- * Parses a BodyContent to a table.
- *
- */
-
- static Table parseContent(Reader pContent, String pDelim)
- throws IOException {
- ArrayList tableRows = new ArrayList();
- int tdsPerTR = 0;
-
- // Loop through TRs
- BufferedReader reader = new BufferedReader(pContent);
- String tr = null;
- while ((tr = reader.readLine()) != null) {
- // Discard blank lines
- if (tr != null
- && tr.trim().length() <= 0 && tr.indexOf(pDelim) < 0) {
- continue;
- }
-
- //System.out.println("CSVToTable: read LINE=\"" + tr + "\"");
-
- ArrayList tableDatas = new ArrayList();
- StringTokenizer tableRow = new StringTokenizer(tr, pDelim,
- true);
-
- boolean lastWasDelim = false;
- while (tableRow.hasMoreTokens()) {
- String td = tableRow.nextToken();
-
- //System.out.println("CSVToTable: read data=\"" + td + "\"");
-
- // Test if we have empty TD
- if (td.equals(pDelim)) {
- if (lastWasDelim) {
- // Add empty TD
- tableDatas.add("");
- }
-
- // We just read a delimitter
- lastWasDelim = true;
- }
- else {
- // No tab, normal data
- lastWasDelim = false;
-
- // Add normal TD
- tableDatas.add(td);
- }
- } // end while (tableRow.hasNext())
-
- // Store max TD count
- if (tableDatas.size() > tdsPerTR) {
- tdsPerTR = tableDatas.size();
- }
-
- // Add a table row
- tableRows.add(tableDatas);
- }
-
- // Return TABLE
- return new Table(tableRows, tdsPerTR);
- }
- }
-
-
-}
+/*
+ * Copyright (c) 2002 TwelveMonkeys.
+ * All rights reserved.
+ *
+ * $Log: CSVToTableTag.java,v $
+ * Revision 1.3 2003/10/06 14:24:50 WMHAKUR
+ * Code clean-up only.
+ *
+ * Revision 1.2 2002/11/26 17:33:49 WMHAKUR
+ * Added documentation & removed System.out.println()s.
+ *
+ * Revision 1.1 2002/11/19 10:50:10 WMHAKUR
+ * Renamed from CSVToTable, to follow naming conventions.
+ *
+ * Revision 1.1 2002/11/18 22:11:16 WMHAKUR
+ * Tag to convert CSV to HTML table.
+ * Can be further transformed, using XSLT.
+ *
+ */
+
+package com.twelvemonkeys.servlet.jsp.taglib;
+
+import java.util.*;
+import java.io.*;
+
+import javax.servlet.jsp.*;
+import javax.servlet.jsp.tagext.*;
+
+/**
+ * Creates a table from a string of "comma-separated values" (CSV).
+ * The delimiter character can be any character (or combination of characters).
+ * The default delimiter is TAB ({@code \t}).
+ *
+ *
+ * ");
-
- // Loop over cells in each row
- for (int col = 0; col < table.getCols(); col++) {
- // Test if we are using headers, else normal cell
- if (mFirstRowIsHeader && row == 0
- || mFirstColIsHeader && col == 0) {
- out.println(" ");
-
- }
- out.println("" + table.get(row, col)
- + " ");
- }
- else {
- out.println("" + table.get(row, col)
- + " ");
- }
- }
-
- out.println("
+ *
+ *
+ * The input may look like this:
+ *
+ * <c:totable firstRowIsHeader="true" delimiter=";">
+ * header A;header B
+ * data 1A; data 1B
+ * data 2A; data 2B
+ * </c:totable>
+ *
+ *
+ * The output (source) will look like this:
+ *
+ * <TABLE>
+ * <TR>
+ * <TH>header A</TH><TH>header B</TH>
+ * </TR>
+ * <TR>
+ * <TD>data 1A</TD><TD>data 1B</TD>
+ * </TR>
+ * <TR>
+ * <TD>data 2A</TD><TD>data 2B</TD>
+ * </TR>
+ * </TABLE>
+ *
+ * You wil probably want to use XSLT to make the final output look nicer. :-)
+ *
+ * @see StringTokenizer
+ * @see XSLT spec
+ *
+ * @author Harald Kuhr
+ *
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/CSVToTableTag.java#1 $
+ */
+
+public class CSVToTableTag extends ExBodyTagSupport {
+
+ public final static String TAB = "\t";
+
+ protected String mDelimiter = null;
+ protected boolean mFirstRowIsHeader = false;
+ protected boolean mFirstColIsHeader = false;
+
+ public void setDelimiter(String pDelimiter) {
+ mDelimiter = pDelimiter;
+ }
+
+ public String getDelimiter() {
+ return mDelimiter != null ? mDelimiter : TAB;
+ }
+
+ public void setFirstRowIsHeader(String pBoolean) {
+ mFirstRowIsHeader = Boolean.valueOf(pBoolean).booleanValue();
+ }
+
+ public void setFirstColIsHeader(String pBoolean) {
+ mFirstColIsHeader = Boolean.valueOf(pBoolean).booleanValue();
+ }
+
+
+ public int doEndTag() throws JspException {
+ BodyContent content = getBodyContent();
+
+ try {
+ Table table =
+ Table.parseContent(content.getReader(), getDelimiter());
+
+ JspWriter out = pageContext.getOut();
+
+ //System.out.println("CSVToTable: " + table.getRows() + " rows, "
+ // + table.getCols() + " cols.");
+
+ if (table.getRows() > 0) {
+ out.println("");
+ // Loop over rows
+ for (int row = 0; row < table.getRows(); row++) {
+ out.println("
");
+ }
+ }
+ catch (IOException ioe) {
+ throw new JspException(ioe);
+ }
+
+ return super.doEndTag();
+ }
+
+ static class Table {
+ List mRows = null;
+ int mCols = 0;
+
+ private Table(List pRows, int pCols) {
+ mRows = pRows;
+ mCols = pCols;
+ }
+
+ int getRows() {
+ return mRows != null ? mRows.size() : 0;
+ }
+
+ int getCols() {
+ return mCols;
+ }
+
+ List getTableRows() {
+ return mRows;
+ }
+
+ List getTableRow(int pRow) {
+ return mRows != null
+ ? (List) mRows.get(pRow)
+ : Collections.EMPTY_LIST;
+ }
+
+ String get(int pRow, int pCol) {
+ List row = getTableRow(pRow);
+ // Rows may contain unequal number of cols
+ return (row.size() > pCol) ? (String) row.get(pCol) : "";
+ }
+
+ /**
+ * Parses a BodyContent to a table.
+ *
+ */
+
+ static Table parseContent(Reader pContent, String pDelim)
+ throws IOException {
+ ArrayList tableRows = new ArrayList();
+ int tdsPerTR = 0;
+
+ // Loop through TRs
+ BufferedReader reader = new BufferedReader(pContent);
+ String tr = null;
+ while ((tr = reader.readLine()) != null) {
+ // Discard blank lines
+ if (tr != null
+ && tr.trim().length() <= 0 && tr.indexOf(pDelim) < 0) {
+ continue;
+ }
+
+ //System.out.println("CSVToTable: read LINE=\"" + tr + "\"");
+
+ ArrayList tableDatas = new ArrayList();
+ StringTokenizer tableRow = new StringTokenizer(tr, pDelim,
+ true);
+
+ boolean lastWasDelim = false;
+ while (tableRow.hasMoreTokens()) {
+ String td = tableRow.nextToken();
+
+ //System.out.println("CSVToTable: read data=\"" + td + "\"");
+
+ // Test if we have empty TD
+ if (td.equals(pDelim)) {
+ if (lastWasDelim) {
+ // Add empty TD
+ tableDatas.add("");
+ }
+
+ // We just read a delimitter
+ lastWasDelim = true;
+ }
+ else {
+ // No tab, normal data
+ lastWasDelim = false;
+
+ // Add normal TD
+ tableDatas.add(td);
+ }
+ } // end while (tableRow.hasNext())
+
+ // Store max TD count
+ if (tableDatas.size() > tdsPerTR) {
+ tdsPerTR = tableDatas.size();
+ }
+
+ // Add a table row
+ tableRows.add(tableDatas);
+ }
+
+ // Return TABLE
+ return new Table(tableRows, tdsPerTR);
+ }
+ }
+
+
+}
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/ExBodyTagSupport.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/ExBodyTagSupport.java
index c24ab33a..45e215c5 100755
--- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/ExBodyTagSupport.java
+++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/ExBodyTagSupport.java
@@ -1,286 +1,286 @@
-/*
- * Copyright (c) 2002 TwelveMonkeys.
- * All rights reserved.
- *
- * $Log: ExBodyTagSupport.java,v $
- * Revision 1.3 2003/10/06 14:24:57 WMHAKUR
- * Code clean-up only.
- *
- * Revision 1.2 2002/11/18 22:10:27 WMHAKUR
- * *** empty log message ***
- *
- *
- */
-
-package com.twelvemonkeys.servlet.jsp.taglib;
-
-import java.io.*;
-import java.net.*;
-import java.util.*;
-
-import javax.servlet.*;
-import javax.servlet.http.*;
-import javax.servlet.jsp.*;
-import javax.servlet.jsp.tagext.*;
-
-/**
- * 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.
- *
- * @author Thomas Purcell (CSC Australia)
- * @author Harald Kuhr
- *
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/ExBodyTagSupport.java#1 $
- */
-
-public class ExBodyTagSupport extends BodyTagSupport implements ExTag {
- /**
- * writeHtml ensures that the text being outputted appears as it was
- * entered. This prevents users from hacking the system by entering
- * html or jsp code into an entry form where that value will be displayed
- * later in the site.
- *
- * @param pOut The JspWriter to write the output to.
- * @param pHtml The original html to filter and output to the user.
- * @throws IOException If the user clicks Stop in the browser, or their
- * browser crashes, then the JspWriter will throw an IOException when
- * the jsp tries to write to it.
- */
-
- public void writeHtml(JspWriter pOut, String pHtml) throws IOException {
- StringTokenizer parser = new StringTokenizer(pHtml, "<>&", true);
-
- while (parser.hasMoreTokens()) {
- String token = parser.nextToken();
-
- if (token.equals("<")) {
- pOut.print("<");
- }
- else if (token.equals(">")) {
- pOut.print(">");
- }
- else if (token.equals("&")) {
- pOut.print("&");
- }
- else {
- pOut.print(token);
- }
- }
- }
-
- /**
- * Log a message to the servlet context.
- *
- * @param pMsg The error message to log.
- */
-
- public void log(String pMsg) {
- getServletContext().log(pMsg);
- }
-
- /**
- * Log a message to the servlet context and include the exception that is
- * passed in as the second parameter.
- *
- * @param pMsg The error message to log.
- * @param pException The exception that caused this error message to be
- * logged.
- */
-
- public void log(String pMsg, Throwable pException) {
- getServletContext().log(pMsg, pException);
- }
-
- /**
- * Retrieves the ServletContext object associated with the current
- * PageContext object.
- *
- * @return The ServletContext object associated with the current
- * PageContext object.
- */
-
- public ServletContext getServletContext() {
- return pageContext.getServletContext();
- }
-
- /**
- * 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
- * called in the {@code clearServiceState()} method call.
- *
- * @exception JspException
- */
-
- public int doEndTag() throws JspException {
- clearServiceState();
- return super.doEndTag();
- }
-
- /**
- * Called when a tag's role in the current JSP page is finished. After
- * the {@code clearProperties()} method is called, the custom tag
- * should be in an identical state as when it was first created. The
- * {@code clearServiceState()} method is called here just in case an
- * exception was thrown in the custom tag. If an exception was thrown,
- * then the {@code doEndTag()} method will not have been called and
- * the tag might not have been cleaned up properly.
- */
-
- public void release() {
- clearServiceState();
-
- clearProperties();
- super.release();
- }
-
- /**
- * The default implementation for the {@code clearProperties()}. 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
- * super.clearProperties()}. So, if the class extends a different
- * 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
- * tag has finished for the current page and should be returned to it's
- * initial state.
- */
-
- protected void clearProperties() {
- }
-
- /**
- * The default implementation for the {@code clearServiceState()}.
- * 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
- * super.clearServiceState()}. So, if the class extends a different
- * 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
- * within the page, but may be called upon again in this same JSP page.
- */
-
- protected void clearServiceState() {
- }
-
- /**
- * Returns the initialisation parameter from the {@code
- * PageContext.APPLICATION_SCOPE} scope. These initialisation
- * parameters are defined in the {@code web.xml} configuration file.
- *
- * @param pName The name of the initialisation parameter to return the
- * value for.
- * @return The value for the parameter whose name was passed in as a
- * parameter. If the parameter does not exist, then {@code null}
- * will be returned.
- */
-
- public String getInitParameter(String pName) {
- return getInitParameter(pName, PageContext.APPLICATION_SCOPE);
- }
-
- /**
- * Returns an Enumeration containing all the names for all the
- * initialisation parametes defined in the {@code
- * PageContext.APPLICATION_SCOPE} scope.
- *
- * @return An {@code Enumeration} containing all the names for all the
- * initialisation parameters.
- */
-
- public Enumeration getInitParameterNames() {
- return getInitParameterNames(PageContext.APPLICATION_SCOPE);
- }
-
- /**
- * Returns the initialisation parameter from the scope specified with the
- * name specified.
- *
- * @param pName The name of the initialisation parameter to return the
- * value for.
- * @param pScope The scope to search for the initialisation parameter
- * within.
- * @return The value of the parameter found. If no parameter with the
- * name specified is found in the scope specified, then {@code null
- * } is returned.
- */
-
- public String getInitParameter(String pName, int pScope) {
- switch (pScope) {
- case PageContext.PAGE_SCOPE:
- return getServletConfig().getInitParameter(pName);
- case PageContext.APPLICATION_SCOPE:
- return getServletContext().getInitParameter(pName);
- default:
- throw new IllegalArgumentException("Illegal scope.");
- }
- }
-
- /**
- * Returns an enumeration containing all the parameters defined in the
- * scope specified by the parameter.
- *
- * @param pScope The scope to return the names of all the parameters
- * defined within.
- * @return An {@code Enumeration} containing all the names for all the
- * parameters defined in the scope passed in as a parameter.
- */
-
- public Enumeration getInitParameterNames(int pScope) {
- switch (pScope) {
- case PageContext.PAGE_SCOPE:
- return getServletConfig().getInitParameterNames();
- case PageContext.APPLICATION_SCOPE:
- return getServletContext().getInitParameterNames();
- default:
- throw new IllegalArgumentException("Illegal scope");
- }
- }
-
- /**
- * Returns the servlet config associated with the current JSP page request.
- *
- * @return The {@code ServletConfig} associated with the current
- * request.
- */
-
- public ServletConfig getServletConfig() {
- return pageContext.getServletConfig();
- }
-
- /**
- * Gets the context path associated with the current JSP page request.
- * If the request is not a HttpServletRequest, this method will
- * return "/".
- *
- * @return a path relative to the current context's root, or
- * {@code "/"} if this is not a HTTP request.
- */
-
- public String getContextPath() {
- ServletRequest request = pageContext.getRequest();
- if (request instanceof HttpServletRequest) {
- return ((HttpServletRequest) request).getContextPath();
- }
- return "/";
- }
-
- /**
- * Gets the resource associated with the given relative path for the
- * current JSP page request.
- * The path may be absolute, or relative to the current context root.
- *
- * @param pPath the path
- *
- * @return a path relative to the current context root
- */
-
- public InputStream getResourceAsStream(String pPath) {
- // throws MalformedURLException {
- String path = pPath;
-
- if (pPath != null && !pPath.startsWith("/")) {
- path = getContextPath() + pPath;
- }
-
- return pageContext.getServletContext().getResourceAsStream(path);
- }
-
-}
+/*
+ * Copyright (c) 2002 TwelveMonkeys.
+ * All rights reserved.
+ *
+ * $Log: ExBodyTagSupport.java,v $
+ * Revision 1.3 2003/10/06 14:24:57 WMHAKUR
+ * Code clean-up only.
+ *
+ * Revision 1.2 2002/11/18 22:10:27 WMHAKUR
+ * *** empty log message ***
+ *
+ *
+ */
+
+package com.twelvemonkeys.servlet.jsp.taglib;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+import javax.servlet.*;
+import javax.servlet.http.*;
+import javax.servlet.jsp.*;
+import javax.servlet.jsp.tagext.*;
+
+/**
+ * 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.
+ *
+ * @author Thomas Purcell (CSC Australia)
+ * @author Harald Kuhr
+ *
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/ExBodyTagSupport.java#1 $
+ */
+
+public class ExBodyTagSupport extends BodyTagSupport implements ExTag {
+ /**
+ * writeHtml ensures that the text being outputted appears as it was
+ * entered. This prevents users from hacking the system by entering
+ * html or jsp code into an entry form where that value will be displayed
+ * later in the site.
+ *
+ * @param pOut The JspWriter to write the output to.
+ * @param pHtml The original html to filter and output to the user.
+ * @throws IOException If the user clicks Stop in the browser, or their
+ * browser crashes, then the JspWriter will throw an IOException when
+ * the jsp tries to write to it.
+ */
+
+ public void writeHtml(JspWriter pOut, String pHtml) throws IOException {
+ StringTokenizer parser = new StringTokenizer(pHtml, "<>&", true);
+
+ while (parser.hasMoreTokens()) {
+ String token = parser.nextToken();
+
+ if (token.equals("<")) {
+ pOut.print("<");
+ }
+ else if (token.equals(">")) {
+ pOut.print(">");
+ }
+ else if (token.equals("&")) {
+ pOut.print("&");
+ }
+ else {
+ pOut.print(token);
+ }
+ }
+ }
+
+ /**
+ * Log a message to the servlet context.
+ *
+ * @param pMsg The error message to log.
+ */
+
+ public void log(String pMsg) {
+ getServletContext().log(pMsg);
+ }
+
+ /**
+ * Log a message to the servlet context and include the exception that is
+ * passed in as the second parameter.
+ *
+ * @param pMsg The error message to log.
+ * @param pException The exception that caused this error message to be
+ * logged.
+ */
+
+ public void log(String pMsg, Throwable pException) {
+ getServletContext().log(pMsg, pException);
+ }
+
+ /**
+ * Retrieves the ServletContext object associated with the current
+ * PageContext object.
+ *
+ * @return The ServletContext object associated with the current
+ * PageContext object.
+ */
+
+ public ServletContext getServletContext() {
+ return pageContext.getServletContext();
+ }
+
+ /**
+ * 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
+ * called in the {@code clearServiceState()} method call.
+ *
+ * @exception JspException
+ */
+
+ public int doEndTag() throws JspException {
+ clearServiceState();
+ return super.doEndTag();
+ }
+
+ /**
+ * Called when a tag's role in the current JSP page is finished. After
+ * the {@code clearProperties()} method is called, the custom tag
+ * should be in an identical state as when it was first created. The
+ * {@code clearServiceState()} method is called here just in case an
+ * exception was thrown in the custom tag. If an exception was thrown,
+ * then the {@code doEndTag()} method will not have been called and
+ * the tag might not have been cleaned up properly.
+ */
+
+ public void release() {
+ clearServiceState();
+
+ clearProperties();
+ super.release();
+ }
+
+ /**
+ * The default implementation for the {@code clearProperties()}. 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
+ * super.clearProperties()}. So, if the class extends a different
+ * 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
+ * tag has finished for the current page and should be returned to it's
+ * initial state.
+ */
+
+ protected void clearProperties() {
+ }
+
+ /**
+ * The default implementation for the {@code clearServiceState()}.
+ * 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
+ * super.clearServiceState()}. So, if the class extends a different
+ * 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
+ * within the page, but may be called upon again in this same JSP page.
+ */
+
+ protected void clearServiceState() {
+ }
+
+ /**
+ * Returns the initialisation parameter from the {@code
+ * PageContext.APPLICATION_SCOPE} scope. These initialisation
+ * parameters are defined in the {@code web.xml} configuration file.
+ *
+ * @param pName The name of the initialisation parameter to return the
+ * value for.
+ * @return The value for the parameter whose name was passed in as a
+ * parameter. If the parameter does not exist, then {@code null}
+ * will be returned.
+ */
+
+ public String getInitParameter(String pName) {
+ return getInitParameter(pName, PageContext.APPLICATION_SCOPE);
+ }
+
+ /**
+ * Returns an Enumeration containing all the names for all the
+ * initialisation parametes defined in the {@code
+ * PageContext.APPLICATION_SCOPE} scope.
+ *
+ * @return An {@code Enumeration} containing all the names for all the
+ * initialisation parameters.
+ */
+
+ public Enumeration getInitParameterNames() {
+ return getInitParameterNames(PageContext.APPLICATION_SCOPE);
+ }
+
+ /**
+ * Returns the initialisation parameter from the scope specified with the
+ * name specified.
+ *
+ * @param pName The name of the initialisation parameter to return the
+ * value for.
+ * @param pScope The scope to search for the initialisation parameter
+ * within.
+ * @return The value of the parameter found. If no parameter with the
+ * name specified is found in the scope specified, then {@code null
+ * } is returned.
+ */
+
+ public String getInitParameter(String pName, int pScope) {
+ switch (pScope) {
+ case PageContext.PAGE_SCOPE:
+ return getServletConfig().getInitParameter(pName);
+ case PageContext.APPLICATION_SCOPE:
+ return getServletContext().getInitParameter(pName);
+ default:
+ throw new IllegalArgumentException("Illegal scope.");
+ }
+ }
+
+ /**
+ * Returns an enumeration containing all the parameters defined in the
+ * scope specified by the parameter.
+ *
+ * @param pScope The scope to return the names of all the parameters
+ * defined within.
+ * @return An {@code Enumeration} containing all the names for all the
+ * parameters defined in the scope passed in as a parameter.
+ */
+
+ public Enumeration getInitParameterNames(int pScope) {
+ switch (pScope) {
+ case PageContext.PAGE_SCOPE:
+ return getServletConfig().getInitParameterNames();
+ case PageContext.APPLICATION_SCOPE:
+ return getServletContext().getInitParameterNames();
+ default:
+ throw new IllegalArgumentException("Illegal scope");
+ }
+ }
+
+ /**
+ * Returns the servlet config associated with the current JSP page request.
+ *
+ * @return The {@code ServletConfig} associated with the current
+ * request.
+ */
+
+ public ServletConfig getServletConfig() {
+ return pageContext.getServletConfig();
+ }
+
+ /**
+ * Gets the context path associated with the current JSP page request.
+ * If the request is not a HttpServletRequest, this method will
+ * return "/".
+ *
+ * @return a path relative to the current context's root, or
+ * {@code "/"} if this is not a HTTP request.
+ */
+
+ public String getContextPath() {
+ ServletRequest request = pageContext.getRequest();
+ if (request instanceof HttpServletRequest) {
+ return ((HttpServletRequest) request).getContextPath();
+ }
+ return "/";
+ }
+
+ /**
+ * Gets the resource associated with the given relative path for the
+ * current JSP page request.
+ * The path may be absolute, or relative to the current context root.
+ *
+ * @param pPath the path
+ *
+ * @return a path relative to the current context root
+ */
+
+ public InputStream getResourceAsStream(String pPath) {
+ // throws MalformedURLException {
+ String path = pPath;
+
+ if (pPath != null && !pPath.startsWith("/")) {
+ path = getContextPath() + pPath;
+ }
+
+ return pageContext.getServletContext().getResourceAsStream(path);
+ }
+
+}
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/ExTag.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/ExTag.java
index 58e21648..690819ec 100755
--- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/ExTag.java
+++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/ExTag.java
@@ -1,162 +1,162 @@
-/*
- * Copyright (c) 2002 TwelveMonkeys.
- * All rights reserved.
- *
- * $Log: ExTag.java,v $
- * Revision 1.2 2003/10/06 14:25:05 WMHAKUR
- * Code clean-up only.
- *
- * Revision 1.1 2002/11/18 22:10:27 WMHAKUR
- * *** empty log message ***
- *
- *
- */
-
-package com.twelvemonkeys.servlet.jsp.taglib;
-
-
-import java.io.*;
-import java.util.*;
-
-import javax.servlet.*;
-import javax.servlet.jsp.*;
-import javax.servlet.jsp.tagext.*;
-
-/**
- * This interface contains a lot of helper methods for simplifying common
- * taglib related tasks.
- *
- * @author Harald Kuhr
- *
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/ExTag.java#1 $
- */
-
-public interface ExTag extends Tag {
-
- /**
- * writeHtml ensures that the text being outputted appears as it was
- * entered. This prevents users from hacking the system by entering
- * html or jsp code into an entry form where that value will be displayed
- * later in the site.
- *
- * @param pOut The JspWriter to write the output to.
- * @param pHtml The original html to filter and output to the user.
- * @throws IOException If the user clicks Stop in the browser, or their
- * browser crashes, then the JspWriter will throw an IOException when
- * the jsp tries to write to it.
- */
-
- public void writeHtml(JspWriter pOut, String pHtml) throws IOException;
-
- /**
- * Log a message to the servlet context.
- *
- * @param pMsg The error message to log.
- */
-
- public void log(String pMsg);
-
- /**
- * Logs a message to the servlet context and include the exception that is
- * passed in as the second parameter.
- *
- * @param pMsg The error message to log.
- * @param pException The exception that caused this error message to be
- * logged.
- */
-
- public void log(String pMsg, Throwable pException);
-
- /**
- * Retrieves the ServletContext object associated with the current
- * PageContext object.
- *
- * @return The ServletContext object associated with the current
- * PageContext object.
- */
-
- public ServletContext getServletContext();
-
- /**
- * Returns the initialisation parameter from the {@code
- * PageContext.APPLICATION_SCOPE} scope. These initialisation
- * parameters are defined in the {@code web.xml} configuration file.
- *
- * @param pName The name of the initialisation parameter to return the
- * value for.
- * @return The value for the parameter whose name was passed in as a
- * parameter. If the parameter does not exist, then {@code null}
- * will be returned.
- */
-
- public String getInitParameter(String pName);
-
- /**
- * Returns an Enumeration containing all the names for all the
- * initialisation parametes defined in the {@code
- * PageContext.APPLICATION_SCOPE} scope.
- *
- * @return An {@code Enumeration} containing all the names for all the
- * initialisation parameters.
- */
-
- public Enumeration getInitParameterNames();
-
- /**
- * Returns the initialisation parameter from the scope specified with the
- * name specified.
- *
- * @param pName The name of the initialisation parameter to return the
- * value for.
- * @param pScope The scope to search for the initialisation parameter
- * within.
- * @return The value of the parameter found. If no parameter with the
- * name specified is found in the scope specified, then {@code null
- * } is returned.
- */
-
- public String getInitParameter(String pName, int pScope);
-
- /**
- * Returns an enumeration containing all the parameters defined in the
- * scope specified by the parameter.
- *
- * @param pScope The scope to return the names of all the parameters
- * defined within.
- * @return An {@code Enumeration} containing all the names for all the
- * parameters defined in the scope passed in as a parameter.
- */
-
- public Enumeration getInitParameterNames(int pScope);
-
- /**
- * Returns the servlet config associated with the current JSP page request.
- *
- * @return The {@code ServletConfig} associated with the current
- * request.
- */
-
- public ServletConfig getServletConfig();
-
- /**
- * Gets the context path associated with the current JSP page request.
- *
- * @return a path relative to the current context's root.
- */
-
- public String getContextPath();
-
-
- /**
- * Gets the resource associated with the given relative path for the
- * current JSP page request.
- * The path may be absolute, or relative to the current context root.
- *
- * @param pPath the path
- *
- * @return a path relative to the current context root
- */
-
- public InputStream getResourceAsStream(String pPath);
-
-}
+/*
+ * Copyright (c) 2002 TwelveMonkeys.
+ * All rights reserved.
+ *
+ * $Log: ExTag.java,v $
+ * Revision 1.2 2003/10/06 14:25:05 WMHAKUR
+ * Code clean-up only.
+ *
+ * Revision 1.1 2002/11/18 22:10:27 WMHAKUR
+ * *** empty log message ***
+ *
+ *
+ */
+
+package com.twelvemonkeys.servlet.jsp.taglib;
+
+
+import java.io.*;
+import java.util.*;
+
+import javax.servlet.*;
+import javax.servlet.jsp.*;
+import javax.servlet.jsp.tagext.*;
+
+/**
+ * This interface contains a lot of helper methods for simplifying common
+ * taglib related tasks.
+ *
+ * @author Harald Kuhr
+ *
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/ExTag.java#1 $
+ */
+
+public interface ExTag extends Tag {
+
+ /**
+ * writeHtml ensures that the text being outputted appears as it was
+ * entered. This prevents users from hacking the system by entering
+ * html or jsp code into an entry form where that value will be displayed
+ * later in the site.
+ *
+ * @param pOut The JspWriter to write the output to.
+ * @param pHtml The original html to filter and output to the user.
+ * @throws IOException If the user clicks Stop in the browser, or their
+ * browser crashes, then the JspWriter will throw an IOException when
+ * the jsp tries to write to it.
+ */
+
+ public void writeHtml(JspWriter pOut, String pHtml) throws IOException;
+
+ /**
+ * Log a message to the servlet context.
+ *
+ * @param pMsg The error message to log.
+ */
+
+ public void log(String pMsg);
+
+ /**
+ * Logs a message to the servlet context and include the exception that is
+ * passed in as the second parameter.
+ *
+ * @param pMsg The error message to log.
+ * @param pException The exception that caused this error message to be
+ * logged.
+ */
+
+ public void log(String pMsg, Throwable pException);
+
+ /**
+ * Retrieves the ServletContext object associated with the current
+ * PageContext object.
+ *
+ * @return The ServletContext object associated with the current
+ * PageContext object.
+ */
+
+ public ServletContext getServletContext();
+
+ /**
+ * Returns the initialisation parameter from the {@code
+ * PageContext.APPLICATION_SCOPE} scope. These initialisation
+ * parameters are defined in the {@code web.xml} configuration file.
+ *
+ * @param pName The name of the initialisation parameter to return the
+ * value for.
+ * @return The value for the parameter whose name was passed in as a
+ * parameter. If the parameter does not exist, then {@code null}
+ * will be returned.
+ */
+
+ public String getInitParameter(String pName);
+
+ /**
+ * Returns an Enumeration containing all the names for all the
+ * initialisation parametes defined in the {@code
+ * PageContext.APPLICATION_SCOPE} scope.
+ *
+ * @return An {@code Enumeration} containing all the names for all the
+ * initialisation parameters.
+ */
+
+ public Enumeration getInitParameterNames();
+
+ /**
+ * Returns the initialisation parameter from the scope specified with the
+ * name specified.
+ *
+ * @param pName The name of the initialisation parameter to return the
+ * value for.
+ * @param pScope The scope to search for the initialisation parameter
+ * within.
+ * @return The value of the parameter found. If no parameter with the
+ * name specified is found in the scope specified, then {@code null
+ * } is returned.
+ */
+
+ public String getInitParameter(String pName, int pScope);
+
+ /**
+ * Returns an enumeration containing all the parameters defined in the
+ * scope specified by the parameter.
+ *
+ * @param pScope The scope to return the names of all the parameters
+ * defined within.
+ * @return An {@code Enumeration} containing all the names for all the
+ * parameters defined in the scope passed in as a parameter.
+ */
+
+ public Enumeration getInitParameterNames(int pScope);
+
+ /**
+ * Returns the servlet config associated with the current JSP page request.
+ *
+ * @return The {@code ServletConfig} associated with the current
+ * request.
+ */
+
+ public ServletConfig getServletConfig();
+
+ /**
+ * Gets the context path associated with the current JSP page request.
+ *
+ * @return a path relative to the current context's root.
+ */
+
+ public String getContextPath();
+
+
+ /**
+ * Gets the resource associated with the given relative path for the
+ * current JSP page request.
+ * The path may be absolute, or relative to the current context root.
+ *
+ * @param pPath the path
+ *
+ * @return a path relative to the current context root
+ */
+
+ public InputStream getResourceAsStream(String pPath);
+
+}
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/ExTagSupport.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/ExTagSupport.java
index 4cd688ce..0798976b 100755
--- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/ExTagSupport.java
+++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/ExTagSupport.java
@@ -1,289 +1,289 @@
-/*
- * Copyright (c) 2002 TwelveMonkeys.
- * All rights reserved.
- *
- * $Log: ExTagSupport.java,v $
- * Revision 1.3 2003/10/06 14:25:11 WMHAKUR
- * Code clean-up only.
- *
- * Revision 1.2 2002/11/18 22:10:27 WMHAKUR
- * *** empty log message ***
- *
- *
- */
-
-package com.twelvemonkeys.servlet.jsp.taglib;
-
-
-import java.io.*;
-import java.net.*;
-import java.util.*;
-
-import javax.servlet.*;
-import javax.servlet.http.*;
-import javax.servlet.jsp.*;
-import javax.servlet.jsp.tagext.*;
-
-/**
- * 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
- * tasks.
- *
- * @author Thomas Purcell (CSC Australia)
- * @author Harald Kuhr
- *
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/ExTagSupport.java#1 $
- */
-
-public class ExTagSupport extends TagSupport implements ExTag {
- /**
- * writeHtml ensures that the text being outputted appears as it was
- * entered. This prevents users from hacking the system by entering
- * html or jsp code into an entry form where that value will be displayed
- * later in the site.
- *
- * @param pOut The JspWriter to write the output to.
- * @param pHtml The original html to filter and output to the user.
- * @throws IOException If the user clicks Stop in the browser, or their
- * browser crashes, then the JspWriter will throw an IOException when
- * the jsp tries to write to it.
- */
-
- public void writeHtml(JspWriter pOut, String pHtml) throws IOException {
- StringTokenizer parser = new StringTokenizer(pHtml, "<>&", true);
-
- while (parser.hasMoreTokens()) {
- String token = parser.nextToken();
-
- if (token.equals("<")) {
- pOut.print("<");
- }
- else if (token.equals(">")) {
- pOut.print(">");
- }
- else if (token.equals("&")) {
- pOut.print("&");
- }
- else {
- pOut.print(token);
- }
- }
- }
-
- /**
- * Log a message to the servlet context.
- *
- * @param pMsg The error message to log.
- */
-
- public void log(String pMsg) {
- getServletContext().log(pMsg);
- }
-
- /**
- * Log a message to the servlet context and include the exception that is
- * passed in as the second parameter.
- *
- * @param pMsg The error message to log.
- * @param pException The exception that caused this error message to be
- * logged.
- */
-
- public void log(String pMsg, Throwable pException) {
- getServletContext().log(pMsg, pException);
- }
-
- /**
- * Retrieves the ServletContext object associated with the current
- * PageContext object.
- *
- * @return The ServletContext object associated with the current
- * PageContext object.
- */
-
- public ServletContext getServletContext() {
- return pageContext.getServletContext();
- }
-
- /**
- * 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
- * called in the {@code clearServiceState()} method call.
- *
- * @exception JspException
- */
-
- public int doEndTag() throws JspException {
- clearServiceState();
- return super.doEndTag();
- }
-
- /**
- * Called when a tag's role in the current JSP page is finished. After
- * the {@code clearProperties()} method is called, the custom tag
- * should be in an identical state as when it was first created. The
- * {@code clearServiceState()} method is called here just in case an
- * exception was thrown in the custom tag. If an exception was thrown,
- * then the {@code doEndTag()} method will not have been called and
- * the tag might not have been cleaned up properly.
- */
-
- public void release() {
- clearServiceState();
-
- clearProperties();
- super.release();
- }
-
- /**
- * The default implementation for the {@code clearProperties()}. 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
- * super.clearProperties()}. So, if the class extends a different
- * 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
- * tag has finished for the current page and should be returned to it's
- * initial state.
- */
-
- protected void clearProperties() {
- }
-
- /**
- * The default implementation for the {@code clearServiceState()}.
- * 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
- * super.clearServiceState()}. So, if the class extends a different
- * 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
- * within the page, but may be called upon again in this same JSP page.
- */
-
- protected void clearServiceState() {
- }
-
- /**
- * Returns the initialisation parameter from the {@code
- * PageContext.APPLICATION_SCOPE} scope. These initialisation
- * parameters are defined in the {@code web.xml} configuration file.
- *
- * @param pName The name of the initialisation parameter to return the
- * value for.
- * @return The value for the parameter whose name was passed in as a
- * parameter. If the parameter does not exist, then {@code null}
- * will be returned.
- */
-
- public String getInitParameter(String pName) {
- return getInitParameter(pName, PageContext.APPLICATION_SCOPE);
- }
-
- /**
- * Returns an Enumeration containing all the names for all the
- * initialisation parametes defined in the {@code
- * PageContext.APPLICATION_SCOPE} scope.
- *
- * @return An {@code Enumeration} containing all the names for all the
- * initialisation parameters.
- */
-
- public Enumeration getInitParameterNames() {
- return getInitParameterNames(PageContext.APPLICATION_SCOPE);
- }
-
- /**
- * Returns the initialisation parameter from the scope specified with the
- * name specified.
- *
- * @param pName The name of the initialisation parameter to return the
- * value for.
- * @param pScope The scope to search for the initialisation parameter
- * within.
- * @return The value of the parameter found. If no parameter with the
- * name specified is found in the scope specified, then {@code null
- * } is returned.
- */
-
- public String getInitParameter(String pName, int pScope) {
- switch (pScope) {
- case PageContext.PAGE_SCOPE:
- return getServletConfig().getInitParameter(pName);
- case PageContext.APPLICATION_SCOPE:
- return getServletContext().getInitParameter(pName);
- default:
- throw new IllegalArgumentException("Illegal scope.");
- }
- }
-
- /**
- * Returns an enumeration containing all the parameters defined in the
- * scope specified by the parameter.
- *
- * @param pScope The scope to return the names of all the parameters
- * defined within.
- * @return An {@code Enumeration} containing all the names for all the
- * parameters defined in the scope passed in as a parameter.
- */
-
- public Enumeration getInitParameterNames(int pScope) {
- switch (pScope) {
- case PageContext.PAGE_SCOPE:
- return getServletConfig().getInitParameterNames();
- case PageContext.APPLICATION_SCOPE:
- return getServletContext().getInitParameterNames();
- default:
- throw new IllegalArgumentException("Illegal scope");
- }
- }
-
- /**
- * Returns the servlet config associated with the current JSP page request.
- *
- * @return The {@code ServletConfig} associated with the current
- * request.
- */
-
- public ServletConfig getServletConfig() {
- return pageContext.getServletConfig();
- }
-
- /**
- * Gets the context path associated with the current JSP page request.
- * If the request is not a HttpServletRequest, this method will
- * return "/".
- *
- * @return a path relative to the current context's root, or
- * {@code "/"} if this is not a HTTP request.
- */
-
- public String getContextPath() {
- ServletRequest request = pageContext.getRequest();
- if (request instanceof HttpServletRequest) {
- return ((HttpServletRequest) request).getContextPath();
- }
- return "/";
- }
-
- /**
- * Gets the resource associated with the given relative path for the
- * current JSP page request.
- * The path may be absolute, or relative to the current context root.
- *
- * @param pPath the path
- *
- * @return a path relative to the current context root
- */
-
- public InputStream getResourceAsStream(String pPath) {
- //throws MalformedURLException {
- String path = pPath;
-
- if (pPath != null && !pPath.startsWith("/")) {
- path = getContextPath() + pPath;
- }
-
- return pageContext.getServletContext().getResourceAsStream(path);
- }
-
-
-}
+/*
+ * Copyright (c) 2002 TwelveMonkeys.
+ * All rights reserved.
+ *
+ * $Log: ExTagSupport.java,v $
+ * Revision 1.3 2003/10/06 14:25:11 WMHAKUR
+ * Code clean-up only.
+ *
+ * Revision 1.2 2002/11/18 22:10:27 WMHAKUR
+ * *** empty log message ***
+ *
+ *
+ */
+
+package com.twelvemonkeys.servlet.jsp.taglib;
+
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+import javax.servlet.*;
+import javax.servlet.http.*;
+import javax.servlet.jsp.*;
+import javax.servlet.jsp.tagext.*;
+
+/**
+ * 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
+ * tasks.
+ *
+ * @author Thomas Purcell (CSC Australia)
+ * @author Harald Kuhr
+ *
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/ExTagSupport.java#1 $
+ */
+
+public class ExTagSupport extends TagSupport implements ExTag {
+ /**
+ * writeHtml ensures that the text being outputted appears as it was
+ * entered. This prevents users from hacking the system by entering
+ * html or jsp code into an entry form where that value will be displayed
+ * later in the site.
+ *
+ * @param pOut The JspWriter to write the output to.
+ * @param pHtml The original html to filter and output to the user.
+ * @throws IOException If the user clicks Stop in the browser, or their
+ * browser crashes, then the JspWriter will throw an IOException when
+ * the jsp tries to write to it.
+ */
+
+ public void writeHtml(JspWriter pOut, String pHtml) throws IOException {
+ StringTokenizer parser = new StringTokenizer(pHtml, "<>&", true);
+
+ while (parser.hasMoreTokens()) {
+ String token = parser.nextToken();
+
+ if (token.equals("<")) {
+ pOut.print("<");
+ }
+ else if (token.equals(">")) {
+ pOut.print(">");
+ }
+ else if (token.equals("&")) {
+ pOut.print("&");
+ }
+ else {
+ pOut.print(token);
+ }
+ }
+ }
+
+ /**
+ * Log a message to the servlet context.
+ *
+ * @param pMsg The error message to log.
+ */
+
+ public void log(String pMsg) {
+ getServletContext().log(pMsg);
+ }
+
+ /**
+ * Log a message to the servlet context and include the exception that is
+ * passed in as the second parameter.
+ *
+ * @param pMsg The error message to log.
+ * @param pException The exception that caused this error message to be
+ * logged.
+ */
+
+ public void log(String pMsg, Throwable pException) {
+ getServletContext().log(pMsg, pException);
+ }
+
+ /**
+ * Retrieves the ServletContext object associated with the current
+ * PageContext object.
+ *
+ * @return The ServletContext object associated with the current
+ * PageContext object.
+ */
+
+ public ServletContext getServletContext() {
+ return pageContext.getServletContext();
+ }
+
+ /**
+ * 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
+ * called in the {@code clearServiceState()} method call.
+ *
+ * @exception JspException
+ */
+
+ public int doEndTag() throws JspException {
+ clearServiceState();
+ return super.doEndTag();
+ }
+
+ /**
+ * Called when a tag's role in the current JSP page is finished. After
+ * the {@code clearProperties()} method is called, the custom tag
+ * should be in an identical state as when it was first created. The
+ * {@code clearServiceState()} method is called here just in case an
+ * exception was thrown in the custom tag. If an exception was thrown,
+ * then the {@code doEndTag()} method will not have been called and
+ * the tag might not have been cleaned up properly.
+ */
+
+ public void release() {
+ clearServiceState();
+
+ clearProperties();
+ super.release();
+ }
+
+ /**
+ * The default implementation for the {@code clearProperties()}. 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
+ * super.clearProperties()}. So, if the class extends a different
+ * 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
+ * tag has finished for the current page and should be returned to it's
+ * initial state.
+ */
+
+ protected void clearProperties() {
+ }
+
+ /**
+ * The default implementation for the {@code clearServiceState()}.
+ * 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
+ * super.clearServiceState()}. So, if the class extends a different
+ * 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
+ * within the page, but may be called upon again in this same JSP page.
+ */
+
+ protected void clearServiceState() {
+ }
+
+ /**
+ * Returns the initialisation parameter from the {@code
+ * PageContext.APPLICATION_SCOPE} scope. These initialisation
+ * parameters are defined in the {@code web.xml} configuration file.
+ *
+ * @param pName The name of the initialisation parameter to return the
+ * value for.
+ * @return The value for the parameter whose name was passed in as a
+ * parameter. If the parameter does not exist, then {@code null}
+ * will be returned.
+ */
+
+ public String getInitParameter(String pName) {
+ return getInitParameter(pName, PageContext.APPLICATION_SCOPE);
+ }
+
+ /**
+ * Returns an Enumeration containing all the names for all the
+ * initialisation parametes defined in the {@code
+ * PageContext.APPLICATION_SCOPE} scope.
+ *
+ * @return An {@code Enumeration} containing all the names for all the
+ * initialisation parameters.
+ */
+
+ public Enumeration getInitParameterNames() {
+ return getInitParameterNames(PageContext.APPLICATION_SCOPE);
+ }
+
+ /**
+ * Returns the initialisation parameter from the scope specified with the
+ * name specified.
+ *
+ * @param pName The name of the initialisation parameter to return the
+ * value for.
+ * @param pScope The scope to search for the initialisation parameter
+ * within.
+ * @return The value of the parameter found. If no parameter with the
+ * name specified is found in the scope specified, then {@code null
+ * } is returned.
+ */
+
+ public String getInitParameter(String pName, int pScope) {
+ switch (pScope) {
+ case PageContext.PAGE_SCOPE:
+ return getServletConfig().getInitParameter(pName);
+ case PageContext.APPLICATION_SCOPE:
+ return getServletContext().getInitParameter(pName);
+ default:
+ throw new IllegalArgumentException("Illegal scope.");
+ }
+ }
+
+ /**
+ * Returns an enumeration containing all the parameters defined in the
+ * scope specified by the parameter.
+ *
+ * @param pScope The scope to return the names of all the parameters
+ * defined within.
+ * @return An {@code Enumeration} containing all the names for all the
+ * parameters defined in the scope passed in as a parameter.
+ */
+
+ public Enumeration getInitParameterNames(int pScope) {
+ switch (pScope) {
+ case PageContext.PAGE_SCOPE:
+ return getServletConfig().getInitParameterNames();
+ case PageContext.APPLICATION_SCOPE:
+ return getServletContext().getInitParameterNames();
+ default:
+ throw new IllegalArgumentException("Illegal scope");
+ }
+ }
+
+ /**
+ * Returns the servlet config associated with the current JSP page request.
+ *
+ * @return The {@code ServletConfig} associated with the current
+ * request.
+ */
+
+ public ServletConfig getServletConfig() {
+ return pageContext.getServletConfig();
+ }
+
+ /**
+ * Gets the context path associated with the current JSP page request.
+ * If the request is not a HttpServletRequest, this method will
+ * return "/".
+ *
+ * @return a path relative to the current context's root, or
+ * {@code "/"} if this is not a HTTP request.
+ */
+
+ public String getContextPath() {
+ ServletRequest request = pageContext.getRequest();
+ if (request instanceof HttpServletRequest) {
+ return ((HttpServletRequest) request).getContextPath();
+ }
+ return "/";
+ }
+
+ /**
+ * Gets the resource associated with the given relative path for the
+ * current JSP page request.
+ * The path may be absolute, or relative to the current context root.
+ *
+ * @param pPath the path
+ *
+ * @return a path relative to the current context root
+ */
+
+ public InputStream getResourceAsStream(String pPath) {
+ //throws MalformedURLException {
+ String path = pPath;
+
+ if (pPath != null && !pPath.startsWith("/")) {
+ path = getContextPath() + pPath;
+ }
+
+ return pageContext.getServletContext().getResourceAsStream(path);
+ }
+
+
+}
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/LastModifiedTEI.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/LastModifiedTEI.java
index e4d3deb0..8dc90dcc 100755
--- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/LastModifiedTEI.java
+++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/LastModifiedTEI.java
@@ -1,21 +1,21 @@
-
-package com.twelvemonkeys.servlet.jsp.taglib;
-
-import javax.servlet.jsp.*;
-import javax.servlet.jsp.tagext.*;
-
-/**
- * TagExtraInfo for LastModifiedTag
- *
- * @author Harald Kuhr
- *
- * @version 1.1
- */
-
-public class LastModifiedTEI extends TagExtraInfo {
- public VariableInfo[] getVariableInfo(TagData pData) {
- return new VariableInfo[]{
- new VariableInfo("lastModified", "java.lang.String", true, VariableInfo.NESTED),
- };
- }
-}
+
+package com.twelvemonkeys.servlet.jsp.taglib;
+
+import javax.servlet.jsp.*;
+import javax.servlet.jsp.tagext.*;
+
+/**
+ * TagExtraInfo for LastModifiedTag
+ *
+ * @author Harald Kuhr
+ *
+ * @version 1.1
+ */
+
+public class LastModifiedTEI extends TagExtraInfo {
+ public VariableInfo[] getVariableInfo(TagData pData) {
+ return new VariableInfo[]{
+ new VariableInfo("lastModified", "java.lang.String", true, VariableInfo.NESTED),
+ };
+ }
+}
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/LastModifiedTag.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/LastModifiedTag.java
index 8f823389..e5daa6d5 100755
--- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/LastModifiedTag.java
+++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/LastModifiedTag.java
@@ -1,54 +1,54 @@
-
-package com.twelvemonkeys.servlet.jsp.taglib;
-
-import java.io.File;
-import java.util.Date;
-
-import javax.servlet.http.*;
-import javax.servlet.jsp.*;
-import javax.servlet.jsp.tagext.*;
-
-import com.twelvemonkeys.util.convert.*;
-
-/**
- * Prints the last modified
- */
-
-public class LastModifiedTag extends TagSupport {
- private String mFileName = null;
- private String mFormat = null;
-
- public void setFile(String pFileName) {
- mFileName = pFileName;
- }
-
- public void setFormat(String pFormat) {
- mFormat = pFormat;
- }
-
- public int doStartTag() throws JspException {
- File file = null;
-
- if (mFileName != null) {
- file = new File(pageContext.getServletContext()
- .getRealPath(mFileName));
- }
- else {
- HttpServletRequest request =
- (HttpServletRequest) pageContext.getRequest();
-
- // Get the file containing the servlet
- file = new File(pageContext.getServletContext()
- .getRealPath(request.getServletPath()));
- }
-
- Date lastModified = new Date(file.lastModified());
- Converter conv = Converter.getInstance();
-
- // Set the last modified value back
- pageContext.setAttribute("lastModified",
- conv.toString(lastModified, mFormat));
-
- return Tag.EVAL_BODY_INCLUDE;
- }
-}
+
+package com.twelvemonkeys.servlet.jsp.taglib;
+
+import java.io.File;
+import java.util.Date;
+
+import javax.servlet.http.*;
+import javax.servlet.jsp.*;
+import javax.servlet.jsp.tagext.*;
+
+import com.twelvemonkeys.util.convert.*;
+
+/**
+ * Prints the last modified
+ */
+
+public class LastModifiedTag extends TagSupport {
+ private String mFileName = null;
+ private String mFormat = null;
+
+ public void setFile(String pFileName) {
+ mFileName = pFileName;
+ }
+
+ public void setFormat(String pFormat) {
+ mFormat = pFormat;
+ }
+
+ public int doStartTag() throws JspException {
+ File file = null;
+
+ if (mFileName != null) {
+ file = new File(pageContext.getServletContext()
+ .getRealPath(mFileName));
+ }
+ else {
+ HttpServletRequest request =
+ (HttpServletRequest) pageContext.getRequest();
+
+ // Get the file containing the servlet
+ file = new File(pageContext.getServletContext()
+ .getRealPath(request.getServletPath()));
+ }
+
+ Date lastModified = new Date(file.lastModified());
+ Converter conv = Converter.getInstance();
+
+ // Set the last modified value back
+ pageContext.setAttribute("lastModified",
+ conv.toString(lastModified, mFormat));
+
+ return Tag.EVAL_BODY_INCLUDE;
+ }
+}
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/TrimWhiteSpaceTag.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/TrimWhiteSpaceTag.java
index 6ce1fcbb..8b376c00 100755
--- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/TrimWhiteSpaceTag.java
+++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/TrimWhiteSpaceTag.java
@@ -1,89 +1,89 @@
-
-package com.twelvemonkeys.servlet.jsp.taglib;
-
-import java.io.IOException;
-
-import javax.servlet.jsp.JspException;
-import javax.servlet.jsp.tagext.BodyTag;
-
-/**
- * This tag truncates all consecutive whitespace in sequence inside its body,
- * 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).
- *
- * @author Harald Kuhr
- *
- * @version 1.0
- */
-
-public class TrimWhiteSpaceTag extends ExBodyTagSupport {
-
- /**
- * doStartTag implementation, simply returns
- * {@code BodyTag.EVAL_BODY_BUFFERED}.
- *
- * @return {@code BodyTag.EVAL_BODY_BUFFERED}
- */
-
- public int doStartTag() throws JspException {
- return BodyTag.EVAL_BODY_BUFFERED;
- }
-
- /**
- * doEndTag implementation, truncates all whitespace.
- *
- * @return {@code super.doEndTag()}
- */
-
- public int doEndTag() throws JspException {
- // Trim
- String trimmed = truncateWS(bodyContent.getString());
- try {
- // Print trimmed content
- //pageContext.getOut().print("\n");
- pageContext.getOut().print(trimmed);
- //pageContext.getOut().print("\n");
- }
- catch (IOException ioe) {
- throw new JspException(ioe);
- }
-
- return super.doEndTag();
- }
-
- /**
- * Truncates whitespace from the given string.
- *
- * @todo Candidate for StringUtil?
- */
-
- private static String truncateWS(String pStr) {
- char[] chars = pStr.toCharArray();
-
- int count = 0;
- boolean lastWasWS = true; // Avoids leading WS
- for (int i = 0; i < chars.length; i++) {
- if (!Character.isWhitespace(chars[i])) {
- // if char is not WS, just store
- chars[count++] = chars[i];
- lastWasWS = false;
- }
- else {
- // else, if char is WS, store first, skip the rest
- if (!lastWasWS) {
- if (chars[i] == 0x0d) {
- chars[count++] = 0x0a; //Always new line
- }
- else {
- chars[count++] = chars[i];
- }
- }
- lastWasWS = true;
- }
- }
-
- // Return the trucated string
- return new String(chars, 0, count);
- }
-
-}
+
+package com.twelvemonkeys.servlet.jsp.taglib;
+
+import java.io.IOException;
+
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.tagext.BodyTag;
+
+/**
+ * This tag truncates all consecutive whitespace in sequence inside its body,
+ * 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).
+ *
+ * @author Harald Kuhr
+ *
+ * @version 1.0
+ */
+
+public class TrimWhiteSpaceTag extends ExBodyTagSupport {
+
+ /**
+ * doStartTag implementation, simply returns
+ * {@code BodyTag.EVAL_BODY_BUFFERED}.
+ *
+ * @return {@code BodyTag.EVAL_BODY_BUFFERED}
+ */
+
+ public int doStartTag() throws JspException {
+ return BodyTag.EVAL_BODY_BUFFERED;
+ }
+
+ /**
+ * doEndTag implementation, truncates all whitespace.
+ *
+ * @return {@code super.doEndTag()}
+ */
+
+ public int doEndTag() throws JspException {
+ // Trim
+ String trimmed = truncateWS(bodyContent.getString());
+ try {
+ // Print trimmed content
+ //pageContext.getOut().print("\n");
+ pageContext.getOut().print(trimmed);
+ //pageContext.getOut().print("\n");
+ }
+ catch (IOException ioe) {
+ throw new JspException(ioe);
+ }
+
+ return super.doEndTag();
+ }
+
+ /**
+ * Truncates whitespace from the given string.
+ *
+ * @todo Candidate for StringUtil?
+ */
+
+ private static String truncateWS(String pStr) {
+ char[] chars = pStr.toCharArray();
+
+ int count = 0;
+ boolean lastWasWS = true; // Avoids leading WS
+ for (int i = 0; i < chars.length; i++) {
+ if (!Character.isWhitespace(chars[i])) {
+ // if char is not WS, just store
+ chars[count++] = chars[i];
+ lastWasWS = false;
+ }
+ else {
+ // else, if char is WS, store first, skip the rest
+ if (!lastWasWS) {
+ if (chars[i] == 0x0d) {
+ chars[count++] = 0x0a; //Always new line
+ }
+ else {
+ chars[count++] = chars[i];
+ }
+ }
+ lastWasWS = true;
+ }
+ }
+
+ // Return the trucated string
+ return new String(chars, 0, count);
+ }
+
+}
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/XMLTransformTag.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/XMLTransformTag.java
index 4e92a463..66c2d612 100755
--- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/XMLTransformTag.java
+++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/XMLTransformTag.java
@@ -1,162 +1,162 @@
-/*
- * Copyright (c) 2002 TwelveMonkeys.
- * All rights reserved.
- *
- * $Log: XMLTransformTag.java,v $
- * Revision 1.2 2003/10/06 14:25:43 WMHAKUR
- * Code clean-up only.
- *
- * Revision 1.1 2002/11/19 10:50:41 WMHAKUR
- * *** empty log message ***
- *
- */
-
-package com.twelvemonkeys.servlet.jsp.taglib;
-
-import java.io.*;
-import java.net.*;
-
-import javax.servlet.*;
-import javax.servlet.jsp.*;
-import javax.servlet.jsp.tagext.*;
-import javax.xml.transform.*;
-import javax.xml.transform.stream.*;
-
-import com.twelvemonkeys.servlet.jsp.*;
-
-/**
- * This tag performs XSL Transformations (XSLT) on a given XML document or its
- * body content.
- *
- * @author Harald Kuhr
- *
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/XMLTransformTag.java#1 $
- */
-
-public class XMLTransformTag extends ExBodyTagSupport {
- private String mDocumentURI = null;
- private String mStylesheetURI = null;
-
- /**
- * Sets the document attribute for this tag.
- */
-
- public void setDocumentURI(String pDocumentURI) {
- mDocumentURI = pDocumentURI;
- }
-
- /**
- * Sets the stylesheet attribute for this tag.
- */
-
- public void setStylesheetURI(String pStylesheetURI) {
- mStylesheetURI = pStylesheetURI;
- }
-
-
- /**
- * doStartTag implementation, that performs XML Transformation on the
- * given document, if any.
- * If the documentURI attribute is set, then the transformation is
- * performed on the document at that location, and
- * {@code Tag.SKIP_BODY} is returned.
- * Otherwise, this method simply returns
- * {@code BodyTag.EVAL_BODY_BUFFERED} and leaves the transformation to
- * the doEndTag.
- *
- * @return {@code Tag.SKIP_BODY} if {@code documentURI} is not
- * {@code null}, otherwise
- * {@code BodyTag.EVAL_BODY_BUFFERED}.
- *
- * @todo Is it really a good idea to allow "inline" XML in a JSP?
- */
-
- public int doStartTag() throws JspException {
- //log("XML: " + mDocumentURI + " XSL: " + mStylesheetURI);
-
- if (mDocumentURI != null) {
- // If document given, transform and skip body...
- try {
- transform(getSource(mDocumentURI));
- }
- catch (MalformedURLException murle) {
- throw new JspException(murle.getMessage(), murle);
- }
- catch (IOException ioe) {
- throw new JspException(ioe.getMessage(), ioe);
- }
-
- return Tag.SKIP_BODY;
- }
-
- // ...else process the body
- return BodyTag.EVAL_BODY_BUFFERED;
- }
-
- /**
- * doEndTag implementation, that will perform XML Transformation on the
- * body content.
- *
- * @return super.doEndTag()
- */
-
- public int doEndTag() throws JspException {
- // Get body content (trim is CRUCIAL, as some XML parsers are picky...)
- String body = bodyContent.getString().trim();
-
- // Do transformation
- transform(new StreamSource(new ByteArrayInputStream(body.getBytes())));
-
- return super.doEndTag();
- }
-
- /**
- * Performs the transformation and writes the result to the JSP writer.
- *
- * @param in the source document to transform.
- */
-
- public void transform(Source pIn) throws JspException {
- try {
- // Create transformer
- Transformer transformer = TransformerFactory.newInstance()
- .newTransformer(getSource(mStylesheetURI));
-
- // Store temporary output in a bytearray, as the transformer will
- // usually try to flush the stream (illegal operation from a custom
- // tag).
- ByteArrayOutputStream os = new ByteArrayOutputStream();
- StreamResult out = new StreamResult(os);
-
- // Perform the transformation
- transformer.transform(pIn, out);
-
- // Write the result back to the JSP writer
- pageContext.getOut().print(os.toString());
- }
- catch (MalformedURLException murle) {
- throw new JspException(murle.getMessage(), murle);
- }
- catch (IOException ioe) {
- throw new JspException(ioe.getMessage(), ioe);
- }
- catch (TransformerException te) {
- throw new JspException("XSLT Trandformation failed: " + te.getMessage(), te);
- }
- }
-
- /**
- * Returns a StreamSource object, for the given URI
- */
-
- private StreamSource getSource(String pURI)
- throws IOException, MalformedURLException {
- if (pURI != null && pURI.indexOf("://") < 0) {
- // If local, get as stream
- return new StreamSource(getResourceAsStream(pURI));
- }
-
- // ...else, create from URI string
- return new StreamSource(pURI);
- }
-}
+/*
+ * Copyright (c) 2002 TwelveMonkeys.
+ * All rights reserved.
+ *
+ * $Log: XMLTransformTag.java,v $
+ * Revision 1.2 2003/10/06 14:25:43 WMHAKUR
+ * Code clean-up only.
+ *
+ * Revision 1.1 2002/11/19 10:50:41 WMHAKUR
+ * *** empty log message ***
+ *
+ */
+
+package com.twelvemonkeys.servlet.jsp.taglib;
+
+import java.io.*;
+import java.net.*;
+
+import javax.servlet.*;
+import javax.servlet.jsp.*;
+import javax.servlet.jsp.tagext.*;
+import javax.xml.transform.*;
+import javax.xml.transform.stream.*;
+
+import com.twelvemonkeys.servlet.jsp.*;
+
+/**
+ * This tag performs XSL Transformations (XSLT) on a given XML document or its
+ * body content.
+ *
+ * @author Harald Kuhr
+ *
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/XMLTransformTag.java#1 $
+ */
+
+public class XMLTransformTag extends ExBodyTagSupport {
+ private String mDocumentURI = null;
+ private String mStylesheetURI = null;
+
+ /**
+ * Sets the document attribute for this tag.
+ */
+
+ public void setDocumentURI(String pDocumentURI) {
+ mDocumentURI = pDocumentURI;
+ }
+
+ /**
+ * Sets the stylesheet attribute for this tag.
+ */
+
+ public void setStylesheetURI(String pStylesheetURI) {
+ mStylesheetURI = pStylesheetURI;
+ }
+
+
+ /**
+ * doStartTag implementation, that performs XML Transformation on the
+ * given document, if any.
+ * If the documentURI attribute is set, then the transformation is
+ * performed on the document at that location, and
+ * {@code Tag.SKIP_BODY} is returned.
+ * Otherwise, this method simply returns
+ * {@code BodyTag.EVAL_BODY_BUFFERED} and leaves the transformation to
+ * the doEndTag.
+ *
+ * @return {@code Tag.SKIP_BODY} if {@code documentURI} is not
+ * {@code null}, otherwise
+ * {@code BodyTag.EVAL_BODY_BUFFERED}.
+ *
+ * @todo Is it really a good idea to allow "inline" XML in a JSP?
+ */
+
+ public int doStartTag() throws JspException {
+ //log("XML: " + mDocumentURI + " XSL: " + mStylesheetURI);
+
+ if (mDocumentURI != null) {
+ // If document given, transform and skip body...
+ try {
+ transform(getSource(mDocumentURI));
+ }
+ catch (MalformedURLException murle) {
+ throw new JspException(murle.getMessage(), murle);
+ }
+ catch (IOException ioe) {
+ throw new JspException(ioe.getMessage(), ioe);
+ }
+
+ return Tag.SKIP_BODY;
+ }
+
+ // ...else process the body
+ return BodyTag.EVAL_BODY_BUFFERED;
+ }
+
+ /**
+ * doEndTag implementation, that will perform XML Transformation on the
+ * body content.
+ *
+ * @return super.doEndTag()
+ */
+
+ public int doEndTag() throws JspException {
+ // Get body content (trim is CRUCIAL, as some XML parsers are picky...)
+ String body = bodyContent.getString().trim();
+
+ // Do transformation
+ transform(new StreamSource(new ByteArrayInputStream(body.getBytes())));
+
+ return super.doEndTag();
+ }
+
+ /**
+ * Performs the transformation and writes the result to the JSP writer.
+ *
+ * @param in the source document to transform.
+ */
+
+ public void transform(Source pIn) throws JspException {
+ try {
+ // Create transformer
+ Transformer transformer = TransformerFactory.newInstance()
+ .newTransformer(getSource(mStylesheetURI));
+
+ // Store temporary output in a bytearray, as the transformer will
+ // usually try to flush the stream (illegal operation from a custom
+ // tag).
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ StreamResult out = new StreamResult(os);
+
+ // Perform the transformation
+ transformer.transform(pIn, out);
+
+ // Write the result back to the JSP writer
+ pageContext.getOut().print(os.toString());
+ }
+ catch (MalformedURLException murle) {
+ throw new JspException(murle.getMessage(), murle);
+ }
+ catch (IOException ioe) {
+ throw new JspException(ioe.getMessage(), ioe);
+ }
+ catch (TransformerException te) {
+ throw new JspException("XSLT Trandformation failed: " + te.getMessage(), te);
+ }
+ }
+
+ /**
+ * Returns a StreamSource object, for the given URI
+ */
+
+ private StreamSource getSource(String pURI)
+ throws IOException, MalformedURLException {
+ if (pURI != null && pURI.indexOf("://") < 0) {
+ // If local, get as stream
+ return new StreamSource(getResourceAsStream(pURI));
+ }
+
+ // ...else, create from URI string
+ return new StreamSource(pURI);
+ }
+}
diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/ConditionalTagBase.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/ConditionalTagBase.java
index 1ecc0ed7..65e97915 100755
--- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/ConditionalTagBase.java
+++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/ConditionalTagBase.java
@@ -1,140 +1,140 @@
-/****************************************************
- * *
- * (c) 2000-2003 TwelveMonkeys *
- * All rights reserved *
- * http://www.twelvemonkeys.no *
- * *
- * $RCSfile: ConditionalTagBase.java,v $
- * @version $Revision: #1 $
- * $Date: 2008/05/05 $
- * *
- * @author Last modified by: $Author: haku $
- * *
- ****************************************************/
-
-
-
-/*
- * Produced (p) 2002 TwelveMonkeys
- * Address : Svovelstikka 1, Box 6432 Etterstad, 0605 Oslo, Norway.
- * Phone : +47 22 57 70 00
- * Fax : +47 22 57 70 70
- */
-package com.twelvemonkeys.servlet.jsp.taglib.logic;
-
-
-import java.lang.*;
-
-import javax.servlet.jsp.JspException;
-import javax.servlet.jsp.tagext.TagSupport;
-
-
-/**
- * ");
+
+ // Loop over cells in each row
+ for (int col = 0; col < table.getCols(); col++) {
+ // Test if we are using headers, else normal cell
+ if (mFirstRowIsHeader && row == 0
+ || mFirstColIsHeader && col == 0) {
+ out.println(" ");
+
+ }
+ out.println("" + table.get(row, col)
+ + " ");
+ }
+ else {
+ out.println("" + table.get(row, col)
+ + " ");
+ }
+ }
+
+ out.println("
- *
- *
equal | - *Availability: 1.0 | - *||||
Tag for testing if an attribute is equal to a given value. |
- * |||||
Tag Body | - *JSP | - *- * | - * | - * | - * |
Restrictions | - *None |
- * ||||
Attributes | - *Name | - *Required | - *Runtime Expression Evaluation | - *Availability | - *|
- * | name | - *Yes | - *Yes | - *1.0 | - *|
- * | The attribute name |
- * ||||
- * | value | - *No | - *Yes | - *1.0 | - *|
- * | The value for equality testing |
- * ||||
Variables | - *None | - *||||
Examples | - *
- * - *<%@ taglib prefix="twelvemonkeys" uri="twelvemonkeys-logic" %> - *<bean:cookie id="logonUsernameCookie" - * name="<%= com.strutscommand.Constants.LOGON_USERNAME_COOKIE_NAME %>" - * value="no_username_set" /> - *<twelvemonkeys:equal name="logonUsernameCookie" value="no_username_set"> - * <html:text property="username" /> - *</twelvemonkeys:equal> - *- * |
- *
- * NB! If the object value property ({@code value} -> {@code mObjectValue}) is empty than {@code true} will be returned. - *
- * - * @return {@code true} if and only if all conditions are met. - */ - protected boolean condition() throws JspException { - - if (StringUtil.isEmpty(mObjectName)) { - return false; - } - - if (StringUtil.isEmpty(mObjectValue)) { - return true; - } - - Object pageScopedAttribute = pageContext.getAttribute(mObjectName); - if (pageScopedAttribute == null) { - return false; - } - - String pageScopedStringAttribute; - - // String - if (pageScopedAttribute instanceof String) { - pageScopedStringAttribute = (String) pageScopedAttribute; - - // Cookie - } - else if (pageScopedAttribute instanceof Cookie) { - pageScopedStringAttribute = ((Cookie) pageScopedAttribute).getValue(); - - // Type not yet supported... - } - else { - return false; - } - - return (pageScopedStringAttribute.equals(mObjectValue)); - } - -} +/* + * Produced (p) 2002 TwelveMonkeys + * Address : Svovelstikka 1, Box 6432 Etterstad, 0605 Oslo, Norway. + * Phone : +47 22 57 70 00 + * Fax : +47 22 57 70 70 + */ + +package com.twelvemonkeys.servlet.jsp.taglib.logic; + + +import java.lang.*; + +import javax.servlet.http.Cookie; +import javax.servlet.jsp.JspException; + +import com.twelvemonkeys.lang.StringUtil; + + +/** + *+ * Custom tag for testing equality of an attribute against a given value. + * The attribute types supported so far is: + *
equal | + *Availability: 1.0 | + *||||
Tag for testing if an attribute is equal to a given value. |
+ * |||||
Tag Body | + *JSP | + *+ * | + * | + * | + * |
Restrictions | + *None |
+ * ||||
Attributes | + *Name | + *Required | + *Runtime Expression Evaluation | + *Availability | + *|
+ * | name | + *Yes | + *Yes | + *1.0 | + *|
+ * | The attribute name |
+ * ||||
+ * | value | + *No | + *Yes | + *1.0 | + *|
+ * | The value for equality testing |
+ * ||||
Variables | + *None | + *||||
Examples | + *
+ * + *<%@ taglib prefix="twelvemonkeys" uri="twelvemonkeys-logic" %> + *<bean:cookie id="logonUsernameCookie" + * name="<%= com.strutscommand.Constants.LOGON_USERNAME_COOKIE_NAME %>" + * value="no_username_set" /> + *<twelvemonkeys:equal name="logonUsernameCookie" value="no_username_set"> + * <html:text property="username" /> + *</twelvemonkeys:equal> + *+ * |
+ *
+ * NB! If the object value property ({@code value} -> {@code mObjectValue}) is empty than {@code true} will be returned. + *
+ * + * @return {@code true} if and only if all conditions are met. + */ + protected boolean condition() throws JspException { + + if (StringUtil.isEmpty(mObjectName)) { + return false; + } + + if (StringUtil.isEmpty(mObjectValue)) { + return true; + } + + Object pageScopedAttribute = pageContext.getAttribute(mObjectName); + if (pageScopedAttribute == null) { + return false; + } + + String pageScopedStringAttribute; + + // String + if (pageScopedAttribute instanceof String) { + pageScopedStringAttribute = (String) pageScopedAttribute; + + // Cookie + } + else if (pageScopedAttribute instanceof Cookie) { + pageScopedStringAttribute = ((Cookie) pageScopedAttribute).getValue(); + + // Type not yet supported... + } + else { + return false; + } + + return (pageScopedStringAttribute.equals(mObjectValue)); + } + +} diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/IteratorProviderTEI.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/IteratorProviderTEI.java index e0610cec..91ed1fb5 100755 --- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/IteratorProviderTEI.java +++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/IteratorProviderTEI.java @@ -1,41 +1,41 @@ - -package com.twelvemonkeys.servlet.jsp.taglib.logic; - -import javax.servlet.jsp.tagext.*; - -/** - * TagExtraInfo class for IteratorProvider tags. - * - * @author Harald Kuhr - * @version $id: $ - */ -public class IteratorProviderTEI extends TagExtraInfo { - /** - * Gets the variable info for IteratorProvider tags. The attribute with the - * name defined by the "id" attribute and type defined by the "type" - * attribute is declared with scope {@code VariableInfo.AT_END}. - * - * @param pData TagData instance provided by container - * @return an VariableInfo array of lenght 1, containing the attribute - * defined by the id parameter, declared, and with scope - * {@code VariableInfo.AT_END}. - */ - public VariableInfo[] getVariableInfo(TagData pData) { - // Get attribute name - String attributeName = pData.getId(); - if (attributeName == null) { - attributeName = IteratorProviderTag.getDefaultIteratorName(); - } - - // Get type - String type = pData.getAttributeString(IteratorProviderTag.ATTRIBUTE_TYPE); - if (type == null) { - type = IteratorProviderTag.getDefaultIteratorType(); - } - - // Return the variable info - return new VariableInfo[]{ - new VariableInfo(attributeName, type, true, VariableInfo.AT_END), - }; - } -} + +package com.twelvemonkeys.servlet.jsp.taglib.logic; + +import javax.servlet.jsp.tagext.*; + +/** + * TagExtraInfo class for IteratorProvider tags. + * + * @author Harald Kuhr + * @version $id: $ + */ +public class IteratorProviderTEI extends TagExtraInfo { + /** + * Gets the variable info for IteratorProvider tags. The attribute with the + * name defined by the "id" attribute and type defined by the "type" + * attribute is declared with scope {@code VariableInfo.AT_END}. + * + * @param pData TagData instance provided by container + * @return an VariableInfo array of lenght 1, containing the attribute + * defined by the id parameter, declared, and with scope + * {@code VariableInfo.AT_END}. + */ + public VariableInfo[] getVariableInfo(TagData pData) { + // Get attribute name + String attributeName = pData.getId(); + if (attributeName == null) { + attributeName = IteratorProviderTag.getDefaultIteratorName(); + } + + // Get type + String type = pData.getAttributeString(IteratorProviderTag.ATTRIBUTE_TYPE); + if (type == null) { + type = IteratorProviderTag.getDefaultIteratorType(); + } + + // Return the variable info + return new VariableInfo[]{ + new VariableInfo(attributeName, type, true, VariableInfo.AT_END), + }; + } +} diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/IteratorProviderTag.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/IteratorProviderTag.java index f4ae2b1c..7c3e1da6 100755 --- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/IteratorProviderTag.java +++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/IteratorProviderTag.java @@ -1,87 +1,87 @@ - -package com.twelvemonkeys.servlet.jsp.taglib.logic; - -import java.util.Iterator; - -import javax.servlet.jsp.JspException; -import javax.servlet.jsp.tagext.*; - -/** - * Abstract base class for adding iterators to a page. - * - * @todo Possible to use same strategy for all types of objects? Rename class - * to ObjectProviderTag? Hmmm... Might work. - * - * @author Harald Kuhr - * @version $id: $ - */ -public abstract class IteratorProviderTag extends TagSupport { - /** {@code iterator} */ - protected final static String DEFAULT_ITERATOR_NAME = "iterator"; - /** {@code java.util.iterator} */ - protected final static String DEFAULT_ITERATOR_TYPE = "java.util.Iterator"; - /** {@code type} */ - public final static String ATTRIBUTE_TYPE = "type"; - - /** */ - private String mType = null; - - /** - * Gets the type. - * - * @return the type (class name) - */ - public String getType() { - return mType; - } - - /** - * Sets the type. - * - * @param pType - */ - - public void setType(String pType) { - mType = pType; - } - - /** - * doEndTag implementation. - * - * @return {@code Tag.EVAL_PAGE} - * @throws JspException - */ - - public int doEndTag() throws JspException { - // Set the iterator - pageContext.setAttribute(getId(), getIterator()); - - return Tag.EVAL_PAGE; - } - - /** - * Gets the iterator for this tag. - * - * @return an {@link java.util.Iterator} - */ - protected abstract Iterator getIterator(); - - /** - * Gets the default iterator name. - * - * @return {@link #DEFAULT_ITERATOR_NAME} - */ - protected static String getDefaultIteratorName() { - return DEFAULT_ITERATOR_NAME; - } - - /** - * Gets the default iterator type. - * - * @return {@link #DEFAULT_ITERATOR_TYPE} - */ - protected static String getDefaultIteratorType() { - return DEFAULT_ITERATOR_TYPE; - } - -} + +package com.twelvemonkeys.servlet.jsp.taglib.logic; + +import java.util.Iterator; + +import javax.servlet.jsp.JspException; +import javax.servlet.jsp.tagext.*; + +/** + * Abstract base class for adding iterators to a page. + * + * @todo Possible to use same strategy for all types of objects? Rename class + * to ObjectProviderTag? Hmmm... Might work. + * + * @author Harald Kuhr + * @version $id: $ + */ +public abstract class IteratorProviderTag extends TagSupport { + /** {@code iterator} */ + protected final static String DEFAULT_ITERATOR_NAME = "iterator"; + /** {@code java.util.iterator} */ + protected final static String DEFAULT_ITERATOR_TYPE = "java.util.Iterator"; + /** {@code type} */ + public final static String ATTRIBUTE_TYPE = "type"; + + /** */ + private String mType = null; + + /** + * Gets the type. + * + * @return the type (class name) + */ + public String getType() { + return mType; + } + + /** + * Sets the type. + * + * @param pType + */ + + public void setType(String pType) { + mType = pType; + } + + /** + * doEndTag implementation. + * + * @return {@code Tag.EVAL_PAGE} + * @throws JspException + */ + + public int doEndTag() throws JspException { + // Set the iterator + pageContext.setAttribute(getId(), getIterator()); + + return Tag.EVAL_PAGE; + } + + /** + * Gets the iterator for this tag. + * + * @return an {@link java.util.Iterator} + */ + protected abstract Iterator getIterator(); + + /** + * Gets the default iterator name. + * + * @return {@link #DEFAULT_ITERATOR_NAME} + */ + protected static String getDefaultIteratorName() { + return DEFAULT_ITERATOR_NAME; + } + + /** + * Gets the default iterator type. + * + * @return {@link #DEFAULT_ITERATOR_TYPE} + */ + protected static String getDefaultIteratorType() { + return DEFAULT_ITERATOR_TYPE; + } + +} diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/NotEqualTag.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/NotEqualTag.java index 47f59cee..05cf228f 100755 --- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/NotEqualTag.java +++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/NotEqualTag.java @@ -1,168 +1,168 @@ -/* - * Produced (p) 2002 TwelveMonkeys - * Address : Svovelstikka 1, Box 6432 Etterstad, 0605 Oslo, Norway. - * Phone : +47 22 57 70 00 - * Fax : +47 22 57 70 70 - */ - -package com.twelvemonkeys.servlet.jsp.taglib.logic; - - -import com.twelvemonkeys.lang.StringUtil; - -import javax.servlet.http.Cookie; -import javax.servlet.jsp.JspException; - - -/** - *- * Custom tag for testing non-equality of an attribute against a given value. - * The attribute types supported so far is: - *
notEqual | - *Availability: 1.0 | - *||||
Tag for testing if an attribute is NOT equal to a given value. |
- * |||||
Tag Body | - *JSP | - *- * | - * | - * | - * |
Restrictions | - *None |
- * ||||
Attributes | - *Name | - *Required | - *Runtime Expression Evaluation | - *Availability | - *|
- * | name | - *Yes | - *Yes | - *1.0 | - *|
- * | The attribute name |
- * ||||
- * | value | - *No | - *Yes | - *1.0 | - *|
- * | The value for equality testing |
- * ||||
Variables | - *None | - *||||
Examples | - *
- * - *<%@ taglib prefix="twelvemonkeys" uri="twelvemonkeys-logic" %> - *<bean:cookie id="logonUsernameCookie" - * name="<%= com.strutscommand.Constants.LOGON_USERNAME_COOKIE_NAME %>" - * value="no_username_set" /> - *<twelvemonkeys:notEqual name="logonUsernameCookie" value="no_username_set"> - * <html:text property="username" value="<%= logonUsernameCookie.getValue() %>" /> - *</twelvemonkeys:notEqual> - *- * |
- *
- * NB! If the object value property ({@code value} -> {@code mObjectValue}) is empty than {@code true} will be returned. - *
- * - * @return {@code true} if and only if all conditions are met. - */ - protected boolean condition() throws JspException { - - if (StringUtil.isEmpty(mObjectName)) { - return false; - } - - if (StringUtil.isEmpty(mObjectValue)) { - return true; - } - - Object pageScopedAttribute = pageContext.getAttribute(mObjectName); - if (pageScopedAttribute == null) { - return false; - } - - String pageScopedStringAttribute; - - // String - if (pageScopedAttribute instanceof String) { - pageScopedStringAttribute = (String) pageScopedAttribute; - - // Cookie - } - else if (pageScopedAttribute instanceof Cookie) { - pageScopedStringAttribute = ((Cookie) pageScopedAttribute).getValue(); - - // Type not yet supported... - } - else { - return false; - } - - return (!(pageScopedStringAttribute.equals(mObjectValue))); - } - -} +/* + * Produced (p) 2002 TwelveMonkeys + * Address : Svovelstikka 1, Box 6432 Etterstad, 0605 Oslo, Norway. + * Phone : +47 22 57 70 00 + * Fax : +47 22 57 70 70 + */ + +package com.twelvemonkeys.servlet.jsp.taglib.logic; + + +import com.twelvemonkeys.lang.StringUtil; + +import javax.servlet.http.Cookie; +import javax.servlet.jsp.JspException; + + +/** + *+ * Custom tag for testing non-equality of an attribute against a given value. + * The attribute types supported so far is: + *
notEqual | + *Availability: 1.0 | + *||||
Tag for testing if an attribute is NOT equal to a given value. |
+ * |||||
Tag Body | + *JSP | + *+ * | + * | + * | + * |
Restrictions | + *None |
+ * ||||
Attributes | + *Name | + *Required | + *Runtime Expression Evaluation | + *Availability | + *|
+ * | name | + *Yes | + *Yes | + *1.0 | + *|
+ * | The attribute name |
+ * ||||
+ * | value | + *No | + *Yes | + *1.0 | + *|
+ * | The value for equality testing |
+ * ||||
Variables | + *None | + *||||
Examples | + *
+ * + *<%@ taglib prefix="twelvemonkeys" uri="twelvemonkeys-logic" %> + *<bean:cookie id="logonUsernameCookie" + * name="<%= com.strutscommand.Constants.LOGON_USERNAME_COOKIE_NAME %>" + * value="no_username_set" /> + *<twelvemonkeys:notEqual name="logonUsernameCookie" value="no_username_set"> + * <html:text property="username" value="<%= logonUsernameCookie.getValue() %>" /> + *</twelvemonkeys:notEqual> + *+ * |
+ *
+ * NB! If the object value property ({@code value} -> {@code mObjectValue}) is empty than {@code true} will be returned. + *
+ * + * @return {@code true} if and only if all conditions are met. + */ + protected boolean condition() throws JspException { + + if (StringUtil.isEmpty(mObjectName)) { + return false; + } + + if (StringUtil.isEmpty(mObjectValue)) { + return true; + } + + Object pageScopedAttribute = pageContext.getAttribute(mObjectName); + if (pageScopedAttribute == null) { + return false; + } + + String pageScopedStringAttribute; + + // String + if (pageScopedAttribute instanceof String) { + pageScopedStringAttribute = (String) pageScopedAttribute; + + // Cookie + } + else if (pageScopedAttribute instanceof Cookie) { + pageScopedStringAttribute = ((Cookie) pageScopedAttribute).getValue(); + + // Type not yet supported... + } + else { + return false; + } + + return (!(pageScopedStringAttribute.equals(mObjectValue))); + } + +} diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/package_info.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/package_info.java index 266046fd..750d7abf 100755 --- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/package_info.java +++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/package_info.java @@ -1,4 +1,4 @@ -/** - * The TwelveMonkeys common TagLib. - */ -package com.twelvemonkeys.servlet.jsp.taglib; +/** + * The TwelveMonkeys common TagLib. + */ +package com.twelvemonkeys.servlet.jsp.taglib; diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/log4j/Log4JContextWrapper.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/log4j/Log4JContextWrapper.java index a629c438..49e7eb79 100755 --- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/log4j/Log4JContextWrapper.java +++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/log4j/Log4JContextWrapper.java @@ -1,183 +1,183 @@ -package com.twelvemonkeys.servlet.log4j; - -import org.apache.log4j.Logger; - -import java.util.Enumeration; -import java.util.Set; -import java.net.URL; -import java.net.MalformedURLException; -import java.io.InputStream; -import java.lang.reflect.Proxy; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; - -import javax.servlet.ServletContext; -import javax.servlet.RequestDispatcher; -import javax.servlet.Servlet; -import javax.servlet.ServletException; - -/** - * Log4JContextWrapper - * - * - * @author Harald Kuhr - * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/log4j/Log4JContextWrapper.java#1 $ - */ -final class Log4JContextWrapper implements ServletContext { - - // TODO: This solution sucks... - // How about starting to create some kind of pluggable decorator system, - // something along the lines of AOP mixins/interceptor pattern.. - // Probably using a dynamic Proxy, delegating to the mixins and or the - // wrapped object based on configuration. - // This way we could simply call ServletUtil.decorate(ServletContext):ServletContext - // And the context would be decorated with all configured mixins at once, - // requiring less bolierplate delegation code, and less layers of wrapping - // (alternatively we could decorate the Servlet/FilterConfig objects). - // See the ServletUtil.createWrapper methods for some hints.. - - - // Something like this: - public static ServletContext wrap(final ServletContext pContext, final Object[] pDelegates, final ClassLoader pLoader) { - ClassLoader cl = pLoader != null ? pLoader : Thread.currentThread().getContextClassLoader(); - - // TODO: Create a "static" mapping between methods in the ServletContext - // and the corresponding delegate - - // TODO: Resolve super-invokations, to delegate to next delegate in - // chain, and finally invoke pContext - - return (ServletContext) Proxy.newProxyInstance(cl, new Class[] {ServletContext.class}, new InvocationHandler() { - public Object invoke(Object pProxy, Method pMethod, Object[] pArgs) throws Throwable { - // TODO: Test if any of the delegates should receive, if so invoke - - // Else, invoke on original object - return pMethod.invoke(pContext, pArgs); - } - }); - } - - private final ServletContext mContext; - - private final Logger mLogger; - - Log4JContextWrapper(ServletContext pContext) { - mContext = pContext; - - // TODO: We want a logger per servlet, not per servlet context, right? - mLogger = Logger.getLogger(pContext.getServletContextName()); - - // TODO: Automatic init/config of Log4J using context parameter for log4j.xml? - // See Log4JInit.java - - // TODO: Automatic config of properties in the context wrapper? - } - - public final void log(final Exception pException, final String pMessage) { - log(pMessage, pException); - } - - // TODO: Add more logging methods to interface info/warn/error? - // TODO: Implement these mehtods in GenericFilter/GenericServlet? - - public void log(String pMessage) { - // TODO: Get logger for caller.. - // Should be possible using some stack peek hack, but that's slow... - // Find a good way... - // Maybe just pass it into the constuctor, and have one wrapper per servlet - mLogger.info(pMessage); - } - - public void log(String pMessage, Throwable pCause) { - // TODO: Get logger for caller.. - - mLogger.error(pMessage, pCause); - } - - public Object getAttribute(String pMessage) { - return mContext.getAttribute(pMessage); - } - - public Enumeration getAttributeNames() { - return mContext.getAttributeNames(); - } - - public ServletContext getContext(String pMessage) { - return mContext.getContext(pMessage); - } - - public String getInitParameter(String pMessage) { - return mContext.getInitParameter(pMessage); - } - - public Enumeration getInitParameterNames() { - return mContext.getInitParameterNames(); - } - - public int getMajorVersion() { - return mContext.getMajorVersion(); - } - - public String getMimeType(String pMessage) { - return mContext.getMimeType(pMessage); - } - - public int getMinorVersion() { - return mContext.getMinorVersion(); - } - - public RequestDispatcher getNamedDispatcher(String pMessage) { - return mContext.getNamedDispatcher(pMessage); - } - - public String getRealPath(String pMessage) { - return mContext.getRealPath(pMessage); - } - - public RequestDispatcher getRequestDispatcher(String pMessage) { - return mContext.getRequestDispatcher(pMessage); - } - - public URL getResource(String pMessage) throws MalformedURLException { - return mContext.getResource(pMessage); - } - - public InputStream getResourceAsStream(String pMessage) { - return mContext.getResourceAsStream(pMessage); - } - - public Set getResourcePaths(String pMessage) { - return mContext.getResourcePaths(pMessage); - } - - public String getServerInfo() { - return mContext.getServerInfo(); - } - - public Servlet getServlet(String pMessage) throws ServletException { - //noinspection deprecation - return mContext.getServlet(pMessage); - } - - public String getServletContextName() { - return mContext.getServletContextName(); - } - - public Enumeration getServletNames() { - //noinspection deprecation - return mContext.getServletNames(); - } - - public Enumeration getServlets() { - //noinspection deprecation - return mContext.getServlets(); - } - - public void removeAttribute(String pMessage) { - mContext.removeAttribute(pMessage); - } - - public void setAttribute(String pMessage, Object pExtension) { - mContext.setAttribute(pMessage, pExtension); - } -} +package com.twelvemonkeys.servlet.log4j; + +import org.apache.log4j.Logger; + +import java.util.Enumeration; +import java.util.Set; +import java.net.URL; +import java.net.MalformedURLException; +import java.io.InputStream; +import java.lang.reflect.Proxy; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; + +import javax.servlet.ServletContext; +import javax.servlet.RequestDispatcher; +import javax.servlet.Servlet; +import javax.servlet.ServletException; + +/** + * Log4JContextWrapper + * + * + * @author Harald Kuhr + * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/log4j/Log4JContextWrapper.java#1 $ + */ +final class Log4JContextWrapper implements ServletContext { + + // TODO: This solution sucks... + // How about starting to create some kind of pluggable decorator system, + // something along the lines of AOP mixins/interceptor pattern.. + // Probably using a dynamic Proxy, delegating to the mixins and or the + // wrapped object based on configuration. + // This way we could simply call ServletUtil.decorate(ServletContext):ServletContext + // And the context would be decorated with all configured mixins at once, + // requiring less bolierplate delegation code, and less layers of wrapping + // (alternatively we could decorate the Servlet/FilterConfig objects). + // See the ServletUtil.createWrapper methods for some hints.. + + + // Something like this: + public static ServletContext wrap(final ServletContext pContext, final Object[] pDelegates, final ClassLoader pLoader) { + ClassLoader cl = pLoader != null ? pLoader : Thread.currentThread().getContextClassLoader(); + + // TODO: Create a "static" mapping between methods in the ServletContext + // and the corresponding delegate + + // TODO: Resolve super-invokations, to delegate to next delegate in + // chain, and finally invoke pContext + + return (ServletContext) Proxy.newProxyInstance(cl, new Class[] {ServletContext.class}, new InvocationHandler() { + public Object invoke(Object pProxy, Method pMethod, Object[] pArgs) throws Throwable { + // TODO: Test if any of the delegates should receive, if so invoke + + // Else, invoke on original object + return pMethod.invoke(pContext, pArgs); + } + }); + } + + private final ServletContext mContext; + + private final Logger mLogger; + + Log4JContextWrapper(ServletContext pContext) { + mContext = pContext; + + // TODO: We want a logger per servlet, not per servlet context, right? + mLogger = Logger.getLogger(pContext.getServletContextName()); + + // TODO: Automatic init/config of Log4J using context parameter for log4j.xml? + // See Log4JInit.java + + // TODO: Automatic config of properties in the context wrapper? + } + + public final void log(final Exception pException, final String pMessage) { + log(pMessage, pException); + } + + // TODO: Add more logging methods to interface info/warn/error? + // TODO: Implement these mehtods in GenericFilter/GenericServlet? + + public void log(String pMessage) { + // TODO: Get logger for caller.. + // Should be possible using some stack peek hack, but that's slow... + // Find a good way... + // Maybe just pass it into the constuctor, and have one wrapper per servlet + mLogger.info(pMessage); + } + + public void log(String pMessage, Throwable pCause) { + // TODO: Get logger for caller.. + + mLogger.error(pMessage, pCause); + } + + public Object getAttribute(String pMessage) { + return mContext.getAttribute(pMessage); + } + + public Enumeration getAttributeNames() { + return mContext.getAttributeNames(); + } + + public ServletContext getContext(String pMessage) { + return mContext.getContext(pMessage); + } + + public String getInitParameter(String pMessage) { + return mContext.getInitParameter(pMessage); + } + + public Enumeration getInitParameterNames() { + return mContext.getInitParameterNames(); + } + + public int getMajorVersion() { + return mContext.getMajorVersion(); + } + + public String getMimeType(String pMessage) { + return mContext.getMimeType(pMessage); + } + + public int getMinorVersion() { + return mContext.getMinorVersion(); + } + + public RequestDispatcher getNamedDispatcher(String pMessage) { + return mContext.getNamedDispatcher(pMessage); + } + + public String getRealPath(String pMessage) { + return mContext.getRealPath(pMessage); + } + + public RequestDispatcher getRequestDispatcher(String pMessage) { + return mContext.getRequestDispatcher(pMessage); + } + + public URL getResource(String pMessage) throws MalformedURLException { + return mContext.getResource(pMessage); + } + + public InputStream getResourceAsStream(String pMessage) { + return mContext.getResourceAsStream(pMessage); + } + + public Set getResourcePaths(String pMessage) { + return mContext.getResourcePaths(pMessage); + } + + public String getServerInfo() { + return mContext.getServerInfo(); + } + + public Servlet getServlet(String pMessage) throws ServletException { + //noinspection deprecation + return mContext.getServlet(pMessage); + } + + public String getServletContextName() { + return mContext.getServletContextName(); + } + + public Enumeration getServletNames() { + //noinspection deprecation + return mContext.getServletNames(); + } + + public Enumeration getServlets() { + //noinspection deprecation + return mContext.getServlets(); + } + + public void removeAttribute(String pMessage) { + mContext.removeAttribute(pMessage); + } + + public void setAttribute(String pMessage, Object pExtension) { + mContext.setAttribute(pMessage, pExtension); + } +} diff --git a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/package_info.java b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/package_info.java index 09d358cb..c14599d4 100755 --- a/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/package_info.java +++ b/twelvemonkeys-servlet/src/main/java/com/twelvemonkeys/servlet/package_info.java @@ -1,4 +1,4 @@ -/** - * Contains servlet support classes. - */ -package com.twelvemonkeys.servlet; +/** + * Contains servlet support classes. + */ +package com.twelvemonkeys.servlet; diff --git a/twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/FilterAbstractTestCase.java b/twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/FilterAbstractTestCase.java index 45a4ea6c..30dd6711 100755 --- a/twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/FilterAbstractTestCase.java +++ b/twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/FilterAbstractTestCase.java @@ -1,438 +1,438 @@ -package com.twelvemonkeys.servlet; - -import com.twelvemonkeys.lang.ObjectAbstractTestCase; - -import java.util.*; -import java.net.URL; -import java.net.MalformedURLException; -import java.io.*; - -import javax.servlet.*; - -/** - * FilterAbstractTestCase - * - * - * @author Harald Kuhr - * @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 $ - */ -public abstract class FilterAbstractTestCase extends ObjectAbstractTestCase { - protected Object makeObject() { - return makeFilter(); - } - - protected abstract Filter makeFilter(); - - // TODO: Is it a good thing to have an API like this? - protected FilterConfig makeFilterConfig() { - return makeFilterConfig(new HashMap()); - } - - protected FilterConfig makeFilterConfig(Map pParams) { - return new MockFilterConfig(pParams); - } - - protected ServletRequest makeRequest() { - return new MockServletRequest(); - } - - protected ServletResponse makeResponse() { - return new MockServletResponse(); - } - - protected FilterChain makeFilterChain() { - return new MockFilterChain(); - } - - public void testInitNull() { - Filter filter = makeFilter(); - - // The spec seems to be a little unclear on this issue, but anyway, - // the container should never invoke init(null)... - try { - filter.init(null); - fail("Should throw Exception on init(null)"); - } - catch (IllegalArgumentException e) { - // Good - } - catch (NullPointerException e) { - // Bad (but not unreasonable) - } - catch (ServletException e) { - // Hmmm.. The jury is still out. - } - } - - public void testInit() { - Filter filter = makeFilter(); - - try { - filter.init(makeFilterConfig()); - } - catch (ServletException e) { - assertNotNull(e.getMessage()); - } - finally { - filter.destroy(); - } - } - - public void testLifeCycle() throws ServletException { - Filter filter = makeFilter(); - - try { - filter.init(makeFilterConfig()); - } - finally { - filter.destroy(); - } - } - - public void testFilterBasic() throws ServletException, IOException { - Filter filter = makeFilter(); - - try { - filter.init(makeFilterConfig()); - - filter.doFilter(makeRequest(), makeResponse(), makeFilterChain()); - } - finally { - filter.destroy(); - } - } - - public void testDestroy() { - // TODO: Implement - } - - static class MockFilterConfig implements FilterConfig { - private final Map mParams; - - MockFilterConfig() { - this(new HashMap()); - } - - MockFilterConfig(Map pParams) { - if (pParams == null) { - throw new IllegalArgumentException("params == null"); - } - mParams = pParams; - } - - public String getFilterName() { - return "mock-filter"; - } - - public String getInitParameter(String pName) { - return (String) mParams.get(pName); - } - - public Enumeration getInitParameterNames() { - return Collections.enumeration(mParams.keySet()); - } - - public ServletContext getServletContext() { - return new MockServletContext(); - } - - private static class MockServletContext implements ServletContext { - private final Map mAttributes; - private final Map mParams; - - MockServletContext() { - mAttributes = new HashMap(); - mParams = new HashMap(); - } - - public Object getAttribute(String s) { - return mAttributes.get(s); - } - - public Enumeration getAttributeNames() { - return Collections.enumeration(mAttributes.keySet()); - } - - public ServletContext getContext(String s) { - return null; // TODO: Implement - } - - public String getInitParameter(String s) { - return (String) mParams.get(s); - } - - public Enumeration getInitParameterNames() { - return Collections.enumeration(mParams.keySet()); - } - - public int getMajorVersion() { - return 0; // TODO: Implement - } - - public String getMimeType(String s) { - return null; // TODO: Implement - } - - public int getMinorVersion() { - return 0; // TODO: Implement - } - - public RequestDispatcher getNamedDispatcher(String s) { - return null; // TODO: Implement - } - - public String getRealPath(String s) { - return null; // TODO: Implement - } - - public RequestDispatcher getRequestDispatcher(String s) { - return null; // TODO: Implement - } - - public URL getResource(String s) throws MalformedURLException { - return null; // TODO: Implement - } - - public InputStream getResourceAsStream(String s) { - return null; // TODO: Implement - } - - public Set getResourcePaths(String s) { - return null; // TODO: Implement - } - - public String getServerInfo() { - return null; // TODO: Implement - } - - public Servlet getServlet(String s) throws ServletException { - return null; // TODO: Implement - } - - public String getServletContextName() { - return "mock"; - } - - public Enumeration getServletNames() { - return null; // TODO: Implement - } - - public Enumeration getServlets() { - return null; // TODO: Implement - } - - public void log(Exception exception, String s) { - // TODO: Implement - } - - public void log(String s) { - // TODO: Implement - } - - public void log(String s, Throwable throwable) { - // TODO: Implement - } - - public void removeAttribute(String s) { - mAttributes.remove(s); - } - - public void setAttribute(String s, Object obj) { - mAttributes.put(s, obj); - } - } - } - - static class MockServletRequest implements ServletRequest { - final private Map mAttributes; - - public MockServletRequest() { - mAttributes = new HashMap(); - } - - public Object getAttribute(String pKey) { - return mAttributes.get(pKey); - } - - public Enumeration getAttributeNames() { - return Collections.enumeration(mAttributes.keySet()); - } - - public String getCharacterEncoding() { - return null; // TODO: Implement - } - - public void setCharacterEncoding(String pMessage) throws UnsupportedEncodingException { - // TODO: Implement - } - - public int getContentLength() { - return 0; // TODO: Implement - } - - public String getContentType() { - return null; // TODO: Implement - } - - public ServletInputStream getInputStream() throws IOException { - return null; // TODO: Implement - } - - public String getParameter(String pMessage) { - return null; // TODO: Implement - } - - public Enumeration getParameterNames() { - return null; // TODO: Implement - } - - public String[] getParameterValues(String pMessage) { - return new String[0]; // TODO: Implement - } - - public Map getParameterMap() { - return null; // TODO: Implement - } - - public String getProtocol() { - return null; // TODO: Implement - } - - public String getScheme() { - return null; // TODO: Implement - } - - public String getServerName() { - return null; // TODO: Implement - } - - public int getServerPort() { - return 0; // TODO: Implement - } - - public BufferedReader getReader() throws IOException { - return null; // TODO: Implement - } - - public String getRemoteAddr() { - return null; // TODO: Implement - } - - public String getRemoteHost() { - return null; // TODO: Implement - } - - public void setAttribute(String pKey, Object pValue) { - mAttributes.put(pKey, pValue); - } - - public void removeAttribute(String pKey) { - mAttributes.remove(pKey); - } - - public Locale getLocale() { - return null; // TODO: Implement - } - - public Enumeration getLocales() { - return null; // TODO: Implement - } - - public boolean isSecure() { - return false; // TODO: Implement - } - - public RequestDispatcher getRequestDispatcher(String pMessage) { - return null; // TODO: Implement - } - - public String getRealPath(String pMessage) { - return null; // TODO: Implement - } - - public int getRemotePort() { - throw new UnsupportedOperationException("Method getRemotePort not implemented");// TODO: Implement - } - - public String getLocalName() { - throw new UnsupportedOperationException("Method getLocalName not implemented");// TODO: Implement - } - - public String getLocalAddr() { - throw new UnsupportedOperationException("Method getLocalAddr not implemented");// TODO: Implement - } - - public int getLocalPort() { - throw new UnsupportedOperationException("Method getLocalPort not implemented");// TODO: Implement - } - } - - static class MockServletResponse implements ServletResponse { - public void flushBuffer() throws IOException { - // TODO: Implement - } - - public int getBufferSize() { - return 0; // TODO: Implement - } - - public String getCharacterEncoding() { - return null; // TODO: Implement - } - - public String getContentType() { - throw new UnsupportedOperationException("Method getContentType not implemented");// TODO: Implement - } - - public Locale getLocale() { - return null; // TODO: Implement - } - - public ServletOutputStream getOutputStream() throws IOException { - return null; // TODO: Implement - } - - public PrintWriter getWriter() throws IOException { - return null; // TODO: Implement - } - - public void setCharacterEncoding(String charset) { - throw new UnsupportedOperationException("Method setCharacterEncoding not implemented");// TODO: Implement - } - - public boolean isCommitted() { - return false; // TODO: Implement - } - - public void reset() { - // TODO: Implement - } - - public void resetBuffer() { - // TODO: Implement - } - - public void setBufferSize(int pLength) { - // TODO: Implement - } - - public void setContentLength(int pLength) { - // TODO: Implement - } - - public void setContentType(String pMessage) { - // TODO: Implement - } - - public void setLocale(Locale pLocale) { - // TODO: Implement - } - } - - static class MockFilterChain implements FilterChain { - public void doFilter(ServletRequest pRequest, ServletResponse pResponse) throws IOException, ServletException { - // TODO: Implement - } - } -} +package com.twelvemonkeys.servlet; + +import com.twelvemonkeys.lang.ObjectAbstractTestCase; + +import java.util.*; +import java.net.URL; +import java.net.MalformedURLException; +import java.io.*; + +import javax.servlet.*; + +/** + * FilterAbstractTestCase + * + * + * @author Harald Kuhr + * @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 $ + */ +public abstract class FilterAbstractTestCase extends ObjectAbstractTestCase { + protected Object makeObject() { + return makeFilter(); + } + + protected abstract Filter makeFilter(); + + // TODO: Is it a good thing to have an API like this? + protected FilterConfig makeFilterConfig() { + return makeFilterConfig(new HashMap()); + } + + protected FilterConfig makeFilterConfig(Map pParams) { + return new MockFilterConfig(pParams); + } + + protected ServletRequest makeRequest() { + return new MockServletRequest(); + } + + protected ServletResponse makeResponse() { + return new MockServletResponse(); + } + + protected FilterChain makeFilterChain() { + return new MockFilterChain(); + } + + public void testInitNull() { + Filter filter = makeFilter(); + + // The spec seems to be a little unclear on this issue, but anyway, + // the container should never invoke init(null)... + try { + filter.init(null); + fail("Should throw Exception on init(null)"); + } + catch (IllegalArgumentException e) { + // Good + } + catch (NullPointerException e) { + // Bad (but not unreasonable) + } + catch (ServletException e) { + // Hmmm.. The jury is still out. + } + } + + public void testInit() { + Filter filter = makeFilter(); + + try { + filter.init(makeFilterConfig()); + } + catch (ServletException e) { + assertNotNull(e.getMessage()); + } + finally { + filter.destroy(); + } + } + + public void testLifeCycle() throws ServletException { + Filter filter = makeFilter(); + + try { + filter.init(makeFilterConfig()); + } + finally { + filter.destroy(); + } + } + + public void testFilterBasic() throws ServletException, IOException { + Filter filter = makeFilter(); + + try { + filter.init(makeFilterConfig()); + + filter.doFilter(makeRequest(), makeResponse(), makeFilterChain()); + } + finally { + filter.destroy(); + } + } + + public void testDestroy() { + // TODO: Implement + } + + static class MockFilterConfig implements FilterConfig { + private final Map mParams; + + MockFilterConfig() { + this(new HashMap()); + } + + MockFilterConfig(Map pParams) { + if (pParams == null) { + throw new IllegalArgumentException("params == null"); + } + mParams = pParams; + } + + public String getFilterName() { + return "mock-filter"; + } + + public String getInitParameter(String pName) { + return (String) mParams.get(pName); + } + + public Enumeration getInitParameterNames() { + return Collections.enumeration(mParams.keySet()); + } + + public ServletContext getServletContext() { + return new MockServletContext(); + } + + private static class MockServletContext implements ServletContext { + private final Map mAttributes; + private final Map mParams; + + MockServletContext() { + mAttributes = new HashMap(); + mParams = new HashMap(); + } + + public Object getAttribute(String s) { + return mAttributes.get(s); + } + + public Enumeration getAttributeNames() { + return Collections.enumeration(mAttributes.keySet()); + } + + public ServletContext getContext(String s) { + return null; // TODO: Implement + } + + public String getInitParameter(String s) { + return (String) mParams.get(s); + } + + public Enumeration getInitParameterNames() { + return Collections.enumeration(mParams.keySet()); + } + + public int getMajorVersion() { + return 0; // TODO: Implement + } + + public String getMimeType(String s) { + return null; // TODO: Implement + } + + public int getMinorVersion() { + return 0; // TODO: Implement + } + + public RequestDispatcher getNamedDispatcher(String s) { + return null; // TODO: Implement + } + + public String getRealPath(String s) { + return null; // TODO: Implement + } + + public RequestDispatcher getRequestDispatcher(String s) { + return null; // TODO: Implement + } + + public URL getResource(String s) throws MalformedURLException { + return null; // TODO: Implement + } + + public InputStream getResourceAsStream(String s) { + return null; // TODO: Implement + } + + public Set getResourcePaths(String s) { + return null; // TODO: Implement + } + + public String getServerInfo() { + return null; // TODO: Implement + } + + public Servlet getServlet(String s) throws ServletException { + return null; // TODO: Implement + } + + public String getServletContextName() { + return "mock"; + } + + public Enumeration getServletNames() { + return null; // TODO: Implement + } + + public Enumeration getServlets() { + return null; // TODO: Implement + } + + public void log(Exception exception, String s) { + // TODO: Implement + } + + public void log(String s) { + // TODO: Implement + } + + public void log(String s, Throwable throwable) { + // TODO: Implement + } + + public void removeAttribute(String s) { + mAttributes.remove(s); + } + + public void setAttribute(String s, Object obj) { + mAttributes.put(s, obj); + } + } + } + + static class MockServletRequest implements ServletRequest { + final private Map mAttributes; + + public MockServletRequest() { + mAttributes = new HashMap(); + } + + public Object getAttribute(String pKey) { + return mAttributes.get(pKey); + } + + public Enumeration getAttributeNames() { + return Collections.enumeration(mAttributes.keySet()); + } + + public String getCharacterEncoding() { + return null; // TODO: Implement + } + + public void setCharacterEncoding(String pMessage) throws UnsupportedEncodingException { + // TODO: Implement + } + + public int getContentLength() { + return 0; // TODO: Implement + } + + public String getContentType() { + return null; // TODO: Implement + } + + public ServletInputStream getInputStream() throws IOException { + return null; // TODO: Implement + } + + public String getParameter(String pMessage) { + return null; // TODO: Implement + } + + public Enumeration getParameterNames() { + return null; // TODO: Implement + } + + public String[] getParameterValues(String pMessage) { + return new String[0]; // TODO: Implement + } + + public Map getParameterMap() { + return null; // TODO: Implement + } + + public String getProtocol() { + return null; // TODO: Implement + } + + public String getScheme() { + return null; // TODO: Implement + } + + public String getServerName() { + return null; // TODO: Implement + } + + public int getServerPort() { + return 0; // TODO: Implement + } + + public BufferedReader getReader() throws IOException { + return null; // TODO: Implement + } + + public String getRemoteAddr() { + return null; // TODO: Implement + } + + public String getRemoteHost() { + return null; // TODO: Implement + } + + public void setAttribute(String pKey, Object pValue) { + mAttributes.put(pKey, pValue); + } + + public void removeAttribute(String pKey) { + mAttributes.remove(pKey); + } + + public Locale getLocale() { + return null; // TODO: Implement + } + + public Enumeration getLocales() { + return null; // TODO: Implement + } + + public boolean isSecure() { + return false; // TODO: Implement + } + + public RequestDispatcher getRequestDispatcher(String pMessage) { + return null; // TODO: Implement + } + + public String getRealPath(String pMessage) { + return null; // TODO: Implement + } + + public int getRemotePort() { + throw new UnsupportedOperationException("Method getRemotePort not implemented");// TODO: Implement + } + + public String getLocalName() { + throw new UnsupportedOperationException("Method getLocalName not implemented");// TODO: Implement + } + + public String getLocalAddr() { + throw new UnsupportedOperationException("Method getLocalAddr not implemented");// TODO: Implement + } + + public int getLocalPort() { + throw new UnsupportedOperationException("Method getLocalPort not implemented");// TODO: Implement + } + } + + static class MockServletResponse implements ServletResponse { + public void flushBuffer() throws IOException { + // TODO: Implement + } + + public int getBufferSize() { + return 0; // TODO: Implement + } + + public String getCharacterEncoding() { + return null; // TODO: Implement + } + + public String getContentType() { + throw new UnsupportedOperationException("Method getContentType not implemented");// TODO: Implement + } + + public Locale getLocale() { + return null; // TODO: Implement + } + + public ServletOutputStream getOutputStream() throws IOException { + return null; // TODO: Implement + } + + public PrintWriter getWriter() throws IOException { + return null; // TODO: Implement + } + + public void setCharacterEncoding(String charset) { + throw new UnsupportedOperationException("Method setCharacterEncoding not implemented");// TODO: Implement + } + + public boolean isCommitted() { + return false; // TODO: Implement + } + + public void reset() { + // TODO: Implement + } + + public void resetBuffer() { + // TODO: Implement + } + + public void setBufferSize(int pLength) { + // TODO: Implement + } + + public void setContentLength(int pLength) { + // TODO: Implement + } + + public void setContentType(String pMessage) { + // TODO: Implement + } + + public void setLocale(Locale pLocale) { + // TODO: Implement + } + } + + static class MockFilterChain implements FilterChain { + public void doFilter(ServletRequest pRequest, ServletResponse pResponse) throws IOException, ServletException { + // TODO: Implement + } + } +} diff --git a/twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/GenericFilterTestCase.java b/twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/GenericFilterTestCase.java index 75170b8d..fd81745f 100755 --- a/twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/GenericFilterTestCase.java +++ b/twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/GenericFilterTestCase.java @@ -1,151 +1,151 @@ -package com.twelvemonkeys.servlet; - -import java.io.IOException; -import java.util.Map; -import java.util.HashMap; - -import javax.servlet.*; - -/** - * GenericFilterTestCase - * - * - * @author Harald Kuhr - * @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 $ - */ -public final class GenericFilterTestCase extends FilterAbstractTestCase { - protected Filter makeFilter() { - return new GenericFilterImpl(); - } - - public void testInitOncePerRequest() { - // Default FALSE - GenericFilter filter = new GenericFilterImpl(); - - try { - filter.init(makeFilterConfig()); - } - catch (ServletException e) { - fail(e.getMessage()); - } - - assertFalse("OncePerRequest should default to false", filter.mOncePerRequest); - filter.destroy(); - - // TRUE - filter = new GenericFilterImpl(); - Map params = new HashMap(); - params.put("once-per-request", "true"); - - try { - filter.init(makeFilterConfig(params)); - } - catch (ServletException e) { - fail(e.getMessage()); - } - - assertTrue("oncePerRequest should be true", filter.mOncePerRequest); - filter.destroy(); - - // TRUE - filter = new GenericFilterImpl(); - params = new HashMap(); - params.put("oncePerRequest", "true"); - - try { - filter.init(makeFilterConfig(params)); - } - catch (ServletException e) { - fail(e.getMessage()); - } - - assertTrue("oncePerRequest should be true", filter.mOncePerRequest); - filter.destroy(); - } - - public void testFilterOnlyOnce() { - final GenericFilterImpl filter = new GenericFilterImpl(); - filter.setOncePerRequest(true); - - try { - filter.init(makeFilterConfig()); - } - catch (ServletException e) { - fail(e.getMessage()); - } - - FilterChain chain = new MyFilterChain(new Filter[] {filter, filter, filter}); - - try { - chain.doFilter(makeRequest(), makeResponse()); - } - catch (IOException e) { - fail(e.getMessage()); - } - catch (ServletException e) { - fail(e.getMessage()); - } - - assertEquals("Filter was invoked more than once!", 1, filter.invocationCount); - - filter.destroy(); - } - - public void testFilterMultiple() { - final GenericFilterImpl filter = new GenericFilterImpl(); - - try { - filter.init(makeFilterConfig()); - } - catch (ServletException e) { - fail(e.getMessage()); - } - - FilterChain chain = new MyFilterChain(new Filter[] { - filter, filter, filter, filter, filter - }); - - try { - chain.doFilter(makeRequest(), makeResponse()); - } - catch (IOException e) { - fail(e.getMessage()); - } - catch (ServletException e) { - fail(e.getMessage()); - } - - assertEquals("Filter was invoked not invoked five times!", 5, filter.invocationCount); - - filter.destroy(); - } - - private static class GenericFilterImpl extends GenericFilter { - int invocationCount; - protected void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain) throws IOException, ServletException { - invocationCount++; - pChain.doFilter(pRequest, pResponse); - } - } - - private static class MyFilterChain implements FilterChain { - - Filter[] mFilters; - int mCurrentFilter; - - public MyFilterChain(Filter[] pFilters) { - if (pFilters == null) { - throw new IllegalArgumentException("filters == null"); - } - mFilters = pFilters; - mCurrentFilter = 0; - } - - public void doFilter(ServletRequest pRequest, ServletResponse pResponse) throws IOException, ServletException { - if (mCurrentFilter < mFilters.length) { - mFilters[mCurrentFilter++].doFilter(pRequest, pResponse, this); - } - } - } -} +package com.twelvemonkeys.servlet; + +import java.io.IOException; +import java.util.Map; +import java.util.HashMap; + +import javax.servlet.*; + +/** + * GenericFilterTestCase + * + * + * @author Harald Kuhr + * @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 $ + */ +public final class GenericFilterTestCase extends FilterAbstractTestCase { + protected Filter makeFilter() { + return new GenericFilterImpl(); + } + + public void testInitOncePerRequest() { + // Default FALSE + GenericFilter filter = new GenericFilterImpl(); + + try { + filter.init(makeFilterConfig()); + } + catch (ServletException e) { + fail(e.getMessage()); + } + + assertFalse("OncePerRequest should default to false", filter.mOncePerRequest); + filter.destroy(); + + // TRUE + filter = new GenericFilterImpl(); + Map params = new HashMap(); + params.put("once-per-request", "true"); + + try { + filter.init(makeFilterConfig(params)); + } + catch (ServletException e) { + fail(e.getMessage()); + } + + assertTrue("oncePerRequest should be true", filter.mOncePerRequest); + filter.destroy(); + + // TRUE + filter = new GenericFilterImpl(); + params = new HashMap(); + params.put("oncePerRequest", "true"); + + try { + filter.init(makeFilterConfig(params)); + } + catch (ServletException e) { + fail(e.getMessage()); + } + + assertTrue("oncePerRequest should be true", filter.mOncePerRequest); + filter.destroy(); + } + + public void testFilterOnlyOnce() { + final GenericFilterImpl filter = new GenericFilterImpl(); + filter.setOncePerRequest(true); + + try { + filter.init(makeFilterConfig()); + } + catch (ServletException e) { + fail(e.getMessage()); + } + + FilterChain chain = new MyFilterChain(new Filter[] {filter, filter, filter}); + + try { + chain.doFilter(makeRequest(), makeResponse()); + } + catch (IOException e) { + fail(e.getMessage()); + } + catch (ServletException e) { + fail(e.getMessage()); + } + + assertEquals("Filter was invoked more than once!", 1, filter.invocationCount); + + filter.destroy(); + } + + public void testFilterMultiple() { + final GenericFilterImpl filter = new GenericFilterImpl(); + + try { + filter.init(makeFilterConfig()); + } + catch (ServletException e) { + fail(e.getMessage()); + } + + FilterChain chain = new MyFilterChain(new Filter[] { + filter, filter, filter, filter, filter + }); + + try { + chain.doFilter(makeRequest(), makeResponse()); + } + catch (IOException e) { + fail(e.getMessage()); + } + catch (ServletException e) { + fail(e.getMessage()); + } + + assertEquals("Filter was invoked not invoked five times!", 5, filter.invocationCount); + + filter.destroy(); + } + + private static class GenericFilterImpl extends GenericFilter { + int invocationCount; + protected void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain) throws IOException, ServletException { + invocationCount++; + pChain.doFilter(pRequest, pResponse); + } + } + + private static class MyFilterChain implements FilterChain { + + Filter[] mFilters; + int mCurrentFilter; + + public MyFilterChain(Filter[] pFilters) { + if (pFilters == null) { + throw new IllegalArgumentException("filters == null"); + } + mFilters = pFilters; + mCurrentFilter = 0; + } + + public void doFilter(ServletRequest pRequest, ServletResponse pResponse) throws IOException, ServletException { + if (mCurrentFilter < mFilters.length) { + mFilters[mCurrentFilter++].doFilter(pRequest, pResponse, this); + } + } + } +} diff --git a/twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/ServletConfigMapAdapterTestCase.java b/twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/ServletConfigMapAdapterTestCase.java index 7ba56d50..976e506f 100755 --- a/twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/ServletConfigMapAdapterTestCase.java +++ b/twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/ServletConfigMapAdapterTestCase.java @@ -1,192 +1,192 @@ -package com.twelvemonkeys.servlet; - -import com.twelvemonkeys.util.MapAbstractTestCase; - -import javax.servlet.*; -import java.util.*; -import java.io.Serializable; -import java.io.InputStream; -import java.net.URL; -import java.net.MalformedURLException; - -/** - * ServletConfigMapAdapterTestCase - * - * - * @author Harald Kuhr - * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/ServletConfigMapAdapterTestCase.java#3 $ - */ -public abstract class ServletConfigMapAdapterTestCase extends MapAbstractTestCase { - - public boolean isPutAddSupported() { - return false; - } - - public boolean isPutChangeSupported() { - return false; - } - - public boolean isRemoveSupported() { - return false; - } - - public boolean isSetValueSupported() { - return false; - } - - private static class TestConfig implements ServletConfig, FilterConfig, ServletContext, Serializable, Cloneable { - Map mMap = new HashMap(); - - public String getServletName() { - return "dummy"; // Not needed for this test - } - - public String getFilterName() { - return getServletName(); - } - - public String getServletContextName() { - return getServletName(); - } - - - public ServletContext getServletContext() { - throw new UnsupportedOperationException("Method getSerlvetContext not implemented"); - } - - public String getInitParameter(String s) { - return (String) mMap.get(s); - } - - public Enumeration getInitParameterNames() { - //noinspection unchecked - return Collections.enumeration(mMap.keySet()); - } - - public ServletContext getContext(String uripath) { - throw new UnsupportedOperationException("Method getContext not implemented"); - } - - public int getMajorVersion() { - throw new UnsupportedOperationException("Method getMajorVersion not implemented"); - } - - public int getMinorVersion() { - throw new UnsupportedOperationException("Method getMinorVersion not implemented"); - } - - public String getMimeType(String file) { - throw new UnsupportedOperationException("Method getMimeType not implemented"); - } - - public Set getResourcePaths(String path) { - throw new UnsupportedOperationException("Method getResourcePaths not implemented"); - } - - public URL getResource(String path) throws MalformedURLException { - throw new UnsupportedOperationException("Method getResource not implemented"); - } - - public InputStream getResourceAsStream(String path) { - throw new UnsupportedOperationException("Method getResourceAsStream not implemented"); - } - - public RequestDispatcher getRequestDispatcher(String path) { - throw new UnsupportedOperationException("Method getRequestDispatcher not implemented"); - } - - public RequestDispatcher getNamedDispatcher(String name) { - throw new UnsupportedOperationException("Method getNamedDispatcher not implemented"); - } - - public Servlet getServlet(String name) throws ServletException { - throw new UnsupportedOperationException("Method getServlet not implemented"); - } - - public Enumeration getServlets() { - throw new UnsupportedOperationException("Method getServlets not implemented"); - } - - public Enumeration getServletNames() { - throw new UnsupportedOperationException("Method getServletNames not implemented"); - } - - public void log(String msg) { - throw new UnsupportedOperationException("Method log not implemented"); - } - - public void log(Exception exception, String msg) { - throw new UnsupportedOperationException("Method log not implemented"); - } - - public void log(String message, Throwable throwable) { - throw new UnsupportedOperationException("Method log not implemented"); - } - - public String getRealPath(String path) { - throw new UnsupportedOperationException("Method getRealPath not implemented"); - } - - public String getServerInfo() { - throw new UnsupportedOperationException("Method getServerInfo not implemented"); - } - - public Object getAttribute(String name) { - throw new UnsupportedOperationException("Method getAttribute not implemented"); - } - - public Enumeration getAttributeNames() { - throw new UnsupportedOperationException("Method getAttributeNames not implemented"); - } - - public void setAttribute(String name, Object object) { - throw new UnsupportedOperationException("Method setAttribute not implemented"); - } - - public void removeAttribute(String name) { - throw new UnsupportedOperationException("Method removeAttribute not implemented"); - } - } - - public static final class ServletConfigMapTestCase extends ServletConfigMapAdapterTestCase { - - public Map makeEmptyMap() { - ServletConfig config = new TestConfig(); - return new ServletConfigMapAdapter(config); - } - - public Map makeFullMap() { - ServletConfig config = new TestConfig(); - addSampleMappings(((TestConfig) config).mMap); - return new ServletConfigMapAdapter(config); - } - } - - public static final class FilterConfigMapTestCase extends ServletConfigMapAdapterTestCase { - - public Map makeEmptyMap() { - FilterConfig config = new TestConfig(); - return new ServletConfigMapAdapter(config); - } - - public Map makeFullMap() { - FilterConfig config = new TestConfig(); - addSampleMappings(((TestConfig) config).mMap); - return new ServletConfigMapAdapter(config); - } - } - - public static final class ServletContextMapTestCase extends ServletConfigMapAdapterTestCase { - - public Map makeEmptyMap() { - ServletContext config = new TestConfig(); - return new ServletConfigMapAdapter(config); - } - - public Map makeFullMap() { - FilterConfig config = new TestConfig(); - addSampleMappings(((TestConfig) config).mMap); - return new ServletConfigMapAdapter(config); - } - } -} +package com.twelvemonkeys.servlet; + +import com.twelvemonkeys.util.MapAbstractTestCase; + +import javax.servlet.*; +import java.util.*; +import java.io.Serializable; +import java.io.InputStream; +import java.net.URL; +import java.net.MalformedURLException; + +/** + * ServletConfigMapAdapterTestCase + * + * + * @author Harald Kuhr + * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/ServletConfigMapAdapterTestCase.java#3 $ + */ +public abstract class ServletConfigMapAdapterTestCase extends MapAbstractTestCase { + + public boolean isPutAddSupported() { + return false; + } + + public boolean isPutChangeSupported() { + return false; + } + + public boolean isRemoveSupported() { + return false; + } + + public boolean isSetValueSupported() { + return false; + } + + private static class TestConfig implements ServletConfig, FilterConfig, ServletContext, Serializable, Cloneable { + Map mMap = new HashMap(); + + public String getServletName() { + return "dummy"; // Not needed for this test + } + + public String getFilterName() { + return getServletName(); + } + + public String getServletContextName() { + return getServletName(); + } + + + public ServletContext getServletContext() { + throw new UnsupportedOperationException("Method getSerlvetContext not implemented"); + } + + public String getInitParameter(String s) { + return (String) mMap.get(s); + } + + public Enumeration getInitParameterNames() { + //noinspection unchecked + return Collections.enumeration(mMap.keySet()); + } + + public ServletContext getContext(String uripath) { + throw new UnsupportedOperationException("Method getContext not implemented"); + } + + public int getMajorVersion() { + throw new UnsupportedOperationException("Method getMajorVersion not implemented"); + } + + public int getMinorVersion() { + throw new UnsupportedOperationException("Method getMinorVersion not implemented"); + } + + public String getMimeType(String file) { + throw new UnsupportedOperationException("Method getMimeType not implemented"); + } + + public Set getResourcePaths(String path) { + throw new UnsupportedOperationException("Method getResourcePaths not implemented"); + } + + public URL getResource(String path) throws MalformedURLException { + throw new UnsupportedOperationException("Method getResource not implemented"); + } + + public InputStream getResourceAsStream(String path) { + throw new UnsupportedOperationException("Method getResourceAsStream not implemented"); + } + + public RequestDispatcher getRequestDispatcher(String path) { + throw new UnsupportedOperationException("Method getRequestDispatcher not implemented"); + } + + public RequestDispatcher getNamedDispatcher(String name) { + throw new UnsupportedOperationException("Method getNamedDispatcher not implemented"); + } + + public Servlet getServlet(String name) throws ServletException { + throw new UnsupportedOperationException("Method getServlet not implemented"); + } + + public Enumeration getServlets() { + throw new UnsupportedOperationException("Method getServlets not implemented"); + } + + public Enumeration getServletNames() { + throw new UnsupportedOperationException("Method getServletNames not implemented"); + } + + public void log(String msg) { + throw new UnsupportedOperationException("Method log not implemented"); + } + + public void log(Exception exception, String msg) { + throw new UnsupportedOperationException("Method log not implemented"); + } + + public void log(String message, Throwable throwable) { + throw new UnsupportedOperationException("Method log not implemented"); + } + + public String getRealPath(String path) { + throw new UnsupportedOperationException("Method getRealPath not implemented"); + } + + public String getServerInfo() { + throw new UnsupportedOperationException("Method getServerInfo not implemented"); + } + + public Object getAttribute(String name) { + throw new UnsupportedOperationException("Method getAttribute not implemented"); + } + + public Enumeration getAttributeNames() { + throw new UnsupportedOperationException("Method getAttributeNames not implemented"); + } + + public void setAttribute(String name, Object object) { + throw new UnsupportedOperationException("Method setAttribute not implemented"); + } + + public void removeAttribute(String name) { + throw new UnsupportedOperationException("Method removeAttribute not implemented"); + } + } + + public static final class ServletConfigMapTestCase extends ServletConfigMapAdapterTestCase { + + public Map makeEmptyMap() { + ServletConfig config = new TestConfig(); + return new ServletConfigMapAdapter(config); + } + + public Map makeFullMap() { + ServletConfig config = new TestConfig(); + addSampleMappings(((TestConfig) config).mMap); + return new ServletConfigMapAdapter(config); + } + } + + public static final class FilterConfigMapTestCase extends ServletConfigMapAdapterTestCase { + + public Map makeEmptyMap() { + FilterConfig config = new TestConfig(); + return new ServletConfigMapAdapter(config); + } + + public Map makeFullMap() { + FilterConfig config = new TestConfig(); + addSampleMappings(((TestConfig) config).mMap); + return new ServletConfigMapAdapter(config); + } + } + + public static final class ServletContextMapTestCase extends ServletConfigMapAdapterTestCase { + + public Map makeEmptyMap() { + ServletContext config = new TestConfig(); + return new ServletConfigMapAdapter(config); + } + + public Map makeFullMap() { + FilterConfig config = new TestConfig(); + addSampleMappings(((TestConfig) config).mMap); + return new ServletConfigMapAdapter(config); + } + } +} diff --git a/twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/ServletHeadersMapAdapterTestCase.java b/twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/ServletHeadersMapAdapterTestCase.java index cd078d9c..6f0b180a 100755 --- a/twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/ServletHeadersMapAdapterTestCase.java +++ b/twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/ServletHeadersMapAdapterTestCase.java @@ -1,103 +1,103 @@ -package com.twelvemonkeys.servlet; - -import com.twelvemonkeys.util.MapAbstractTestCase; -import org.jmock.Mock; -import org.jmock.core.Invocation; -import org.jmock.core.Stub; -import org.jmock.core.stub.CustomStub; - -import javax.servlet.http.HttpServletRequest; -import java.util.*; - -/** - * ServletConfigMapAdapterTestCase - * - * - * @author Harald Kuhr - * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-servlet/src/test/java/com/twelvemonkeys/servlet/ServletHeadersMapAdapterTestCase.java#1 $ - */ -public class ServletHeadersMapAdapterTestCase extends MapAbstractTestCase { - private static final List
- * The string masks provided are treated as case sensitive.
- * Null-valued string masks as well as null valued strings to be parsed, will lead to rejection.
- *
- *
- *
- * This task is performed based on regular expression techniques.
- * 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.
- * The '*' corresponds to ([Union of all characters in the alphabet])*
- * The '?' corresponds to ([Union of all characters in the alphabet])
- * These expressions are not suited for textual representation at all, I must say. Is there any math tags included in HTML?
- *
- *
- * - * This class uses the Regexp package from Apache's Jakarta Project, links below. - * - *
- *
- * Examples of usage:
- * This example will return "Accepted!".
- *
- * REWildcardStringParser parser = new REWildcardStringParser("*_28????.jp*"); - * if (parser.parseString("gupu_280915.jpg")) { - * System.out.println("Accepted!"); - * } else { - * System.out.println("Not accepted!"); - * } - *- * - *
- * - * @author Eirik Torske - * @see Jakarta Regexp - * @see {@code org.apache.regexp.RE} - * @see com.twelvemonkeys.util.regex.WildcardStringParser - * - * @todo Rewrite to use this regex package, and not Jakarta directly! - */ -public class REWildcardStringParser /*extends EntityObject*/ { - - // Constants - - /** Field 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', - '\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', '.', '_', '-' - }; - - /** Field FREE_RANGE_CHARACTER */ - public static final char FREE_RANGE_CHARACTER = '*'; - - /** Field FREE_PASS_CHARACTER */ - public static final char FREE_PASS_CHARACTER = '?'; - - // Members - Pattern mRegexpParser; - String mStringMask; - boolean mInitialized = false; - int mTotalNumberOfStringsParsed; - boolean mDebugging; - PrintStream out; - - // Properties - // Constructors - - /** - * Creates a wildcard string parser. - *
- * @param pStringMask the wildcard string mask. - */ - public REWildcardStringParser(final String pStringMask) { - this(pStringMask, false); - } - - /** - * Creates a wildcard string parser. - *
- * @param pStringMask the wildcard string mask. - * @param pDebugging {@code true} will cause debug messages to be emitted to {@code System.out}. - */ - public REWildcardStringParser(final String pStringMask, final boolean pDebugging) { - this(pStringMask, pDebugging, System.out); - } - - /** - * Creates a wildcard string parser. - *
- * @param pStringMask the wildcard string mask. - * @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. - */ - public REWildcardStringParser(final String pStringMask, final boolean pDebugging, final PrintStream pDebuggingPrintStream) { - - this.mStringMask = pStringMask; - this.mDebugging = pDebugging; - this.out = pDebuggingPrintStream; - mInitialized = buildRegexpParser(); - } - - // Methods - - /** - * 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... - * @return the corresponding regular expression or {@code null} if an error occurred. - */ - private String convertWildcardExpressionToRegularExpression(final String pWildcardExpression) { - - if (pWildcardExpression == null) { - if (mDebugging) { - out.println(DebugUtil.getPrefixDebugMessage(this) + "wildcard expression is null - also returning null as regexp!"); - } - return null; - } - StringBuilder regexpBuffer = new StringBuilder(); - boolean convertingError = false; - - for (int i = 0; i < pWildcardExpression.length(); i++) { - if (convertingError) { - return null; - } - - // Free-range character '*' - char stringMaskChar = pWildcardExpression.charAt(i); - - if (isFreeRangeCharacter(stringMaskChar)) { - regexpBuffer.append("(([a-�A-�0-9]|.|_|-)*)"); - } - - // Free-pass character '?' - else if (isFreePassCharacter(stringMaskChar)) { - regexpBuffer.append("([a-�A_�0-9]|.|_|-)"); - } - - // Valid characters - else if (isInAlphabet(stringMaskChar)) { - regexpBuffer.append(stringMaskChar); - } - - // Invalid character - aborting - else { - if (mDebugging) { - out.println(DebugUtil.getPrefixDebugMessage(this) - + "one or more characters in string mask are not legal characters - returning null as regexp!"); - } - convertingError = true; - } - } - return regexpBuffer.toString(); - } - - /** - * Builds the regexp parser. - */ - private boolean buildRegexpParser() { - - // Convert wildcard string mask to regular expression - String regexp = convertWildcardExpressionToRegularExpression(mStringMask); - - if (regexp == null) { - out.println(DebugUtil.getPrefixErrorMessage(this) - + "irregularity in regexp conversion - now not able to parse any strings, all strings will be rejected!"); - return false; - } - - // Instantiate a regular expression parser - try { - mRegexpParser = Pattern.compile(regexp); - } - catch (PatternSyntaxException e) { - if (mDebugging) { - out.println(DebugUtil.getPrefixErrorMessage(this) + "RESyntaxException \"" + e.getMessage() - + "\" caught - now not able to parse any strings, all strings will be rejected!"); - } - if (mDebugging) { - e.printStackTrace(System.err); - } - return false; - } - if (mDebugging) { - out.println(DebugUtil.getPrefixDebugMessage(this) + "regular expression parser from regular expression " + regexp - + " extracted from wildcard string mask " + mStringMask + "."); - } - return true; - } - - /** - * Simple check of the string to be parsed. - */ - private boolean checkStringToBeParsed(final String pStringToBeParsed) { - - // Check for nullness - if (pStringToBeParsed == null) { - if (mDebugging) { - out.println(DebugUtil.getPrefixDebugMessage(this) + "string to be parsed is null - rejection!"); - } - return false; - } - - // Check if valid character (element in alphabet) - for (int i = 0; i < pStringToBeParsed.length(); i++) { - if (!isInAlphabet(pStringToBeParsed.charAt(i))) { - if (mDebugging) { - out.println(DebugUtil.getPrefixDebugMessage(this) - + "one or more characters in string to be parsed are not legal characters - rejection!"); - } - return false; - } - } - return true; - } - - /** - * 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) { - - for (int i = 0; i < ALPHABET.length; i++) { - if (pCharToCheck == ALPHABET[i]) { - return true; - } - } - return false; - } - - /** - * Tests if a certain character is the designated "free-range" character ('*'). - */ - public static boolean isFreeRangeCharacter(final char pCharToCheck) { - return pCharToCheck == FREE_RANGE_CHARACTER; - } - - /** - * Tests if a certain character is the designated "free-pass" character ('?'). - */ - public static boolean isFreePassCharacter(final char pCharToCheck) { - return pCharToCheck == FREE_PASS_CHARACTER; - } - - /** - * Tests if a certain character is a wildcard character ('*' or '?'). - */ - public static boolean isWildcardCharacter(final char pCharToCheck) { - return ((isFreeRangeCharacter(pCharToCheck)) || (isFreePassCharacter(pCharToCheck))); - } - - /** - * Gets the string mask that was used when building the parser atomaton. - *
- * @return the string mask used for building the parser automaton. - */ - public String getStringMask() { - return mStringMask; - } - - /** - * Parses a string. - *
- * - * @param pStringToBeParsed - * @return {@code true} if and only if the string are accepted by the parser. - */ - public boolean parseString(final String pStringToBeParsed) { - - if (mDebugging) { - out.println(); - } - if (mDebugging) { - out.println(DebugUtil.getPrefixDebugMessage(this) + "parsing \"" + pStringToBeParsed + "\"..."); - } - - // Update statistics - mTotalNumberOfStringsParsed++; - - // Check string to be parsed - if (!checkStringToBeParsed(pStringToBeParsed)) { - return false; - } - - // Perform parsing and return accetance/rejection flag - if (mInitialized) { - return mRegexpParser.matcher(pStringToBeParsed).matches(); - } else { - out.println(DebugUtil.getPrefixErrorMessage(this) + "trying to use non-initialized parser - string rejected!"); - } - return false; - } - - /* - * Overriding mandatory methods from EntityObject's. - */ - - /** - * Method toString - * - * - * @return - * - */ - public String toString() { - - StringBuilder buffer = new StringBuilder(); - - buffer.append(DebugUtil.getClassName(this)); - buffer.append(": String mask "); - buffer.append(mStringMask); - buffer.append("\n"); - return buffer.toString(); - } - - // Just taking the lazy, easy and dangerous way out - - /** - * Method equals - * - * - * @param pObject - * - * @return - * - */ - public boolean equals(Object pObject) { - - if (pObject instanceof REWildcardStringParser) { - REWildcardStringParser externalParser = (REWildcardStringParser) pObject; - - return (externalParser.mStringMask == this.mStringMask); - } - return ((Object) this).equals(pObject); - } - - // Just taking the lazy, easy and dangerous way out - - /** - * Method hashCode - * - * - * @return - * - */ - public int hashCode() { - return ((Object) this).hashCode(); - } - - protected Object clone() throws CloneNotSupportedException { - return new REWildcardStringParser(mStringMask); - } - - // Just taking the lazy, easy and dangerous way out - protected void finalize() throws Throwable {} -} - - -/*--- Formatted in Sun Java Convention Style on ma, des 1, '03 ---*/ - - -/*------ Formatted by Jindent 3.23 Basic 1.0 --- http://www.jindent.de ------*/ +/* + * Copyright (c) 2008, Harald Kuhr + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name "TwelveMonkeys" nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.twelvemonkeys.util.regex; + +import com.twelvemonkeys.util.DebugUtil; + +import java.io.PrintStream; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +/** + * This class parses arbitrary strings against a wildcard string mask provided. + * The wildcard characters are '*' and '?'. + *
+ * The string masks provided are treated as case sensitive.
+ * Null-valued string masks as well as null valued strings to be parsed, will lead to rejection.
+ *
+ *
+ *
+ * This task is performed based on regular expression techniques.
+ * 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.
+ * The '*' corresponds to ([Union of all characters in the alphabet])*
+ * The '?' corresponds to ([Union of all characters in the alphabet])
+ * These expressions are not suited for textual representation at all, I must say. Is there any math tags included in HTML?
+ *
+ *
+ * + * This class uses the Regexp package from Apache's Jakarta Project, links below. + * + *
+ *
+ * Examples of usage:
+ * This example will return "Accepted!".
+ *
+ * REWildcardStringParser parser = new REWildcardStringParser("*_28????.jp*"); + * if (parser.parseString("gupu_280915.jpg")) { + * System.out.println("Accepted!"); + * } else { + * System.out.println("Not accepted!"); + * } + *+ * + *
+ * + * @author Eirik Torske + * @see Jakarta Regexp + * @see {@code org.apache.regexp.RE} + * @see com.twelvemonkeys.util.regex.WildcardStringParser + * + * @todo Rewrite to use this regex package, and not Jakarta directly! + */ +public class REWildcardStringParser /*extends EntityObject*/ { + + // Constants + + /** Field 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', + '\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', '.', '_', '-' + }; + + /** Field FREE_RANGE_CHARACTER */ + public static final char FREE_RANGE_CHARACTER = '*'; + + /** Field FREE_PASS_CHARACTER */ + public static final char FREE_PASS_CHARACTER = '?'; + + // Members + Pattern mRegexpParser; + String mStringMask; + boolean mInitialized = false; + int mTotalNumberOfStringsParsed; + boolean mDebugging; + PrintStream out; + + // Properties + // Constructors + + /** + * Creates a wildcard string parser. + *
+ * @param pStringMask the wildcard string mask. + */ + public REWildcardStringParser(final String pStringMask) { + this(pStringMask, false); + } + + /** + * Creates a wildcard string parser. + *
+ * @param pStringMask the wildcard string mask. + * @param pDebugging {@code true} will cause debug messages to be emitted to {@code System.out}. + */ + public REWildcardStringParser(final String pStringMask, final boolean pDebugging) { + this(pStringMask, pDebugging, System.out); + } + + /** + * Creates a wildcard string parser. + *
+ * @param pStringMask the wildcard string mask. + * @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. + */ + public REWildcardStringParser(final String pStringMask, final boolean pDebugging, final PrintStream pDebuggingPrintStream) { + + this.mStringMask = pStringMask; + this.mDebugging = pDebugging; + this.out = pDebuggingPrintStream; + mInitialized = buildRegexpParser(); + } + + // Methods + + /** + * 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... + * @return the corresponding regular expression or {@code null} if an error occurred. + */ + private String convertWildcardExpressionToRegularExpression(final String pWildcardExpression) { + + if (pWildcardExpression == null) { + if (mDebugging) { + out.println(DebugUtil.getPrefixDebugMessage(this) + "wildcard expression is null - also returning null as regexp!"); + } + return null; + } + StringBuilder regexpBuffer = new StringBuilder(); + boolean convertingError = false; + + for (int i = 0; i < pWildcardExpression.length(); i++) { + if (convertingError) { + return null; + } + + // Free-range character '*' + char stringMaskChar = pWildcardExpression.charAt(i); + + if (isFreeRangeCharacter(stringMaskChar)) { + regexpBuffer.append("(([a-�A-�0-9]|.|_|-)*)"); + } + + // Free-pass character '?' + else if (isFreePassCharacter(stringMaskChar)) { + regexpBuffer.append("([a-�A_�0-9]|.|_|-)"); + } + + // Valid characters + else if (isInAlphabet(stringMaskChar)) { + regexpBuffer.append(stringMaskChar); + } + + // Invalid character - aborting + else { + if (mDebugging) { + out.println(DebugUtil.getPrefixDebugMessage(this) + + "one or more characters in string mask are not legal characters - returning null as regexp!"); + } + convertingError = true; + } + } + return regexpBuffer.toString(); + } + + /** + * Builds the regexp parser. + */ + private boolean buildRegexpParser() { + + // Convert wildcard string mask to regular expression + String regexp = convertWildcardExpressionToRegularExpression(mStringMask); + + if (regexp == null) { + out.println(DebugUtil.getPrefixErrorMessage(this) + + "irregularity in regexp conversion - now not able to parse any strings, all strings will be rejected!"); + return false; + } + + // Instantiate a regular expression parser + try { + mRegexpParser = Pattern.compile(regexp); + } + catch (PatternSyntaxException e) { + if (mDebugging) { + out.println(DebugUtil.getPrefixErrorMessage(this) + "RESyntaxException \"" + e.getMessage() + + "\" caught - now not able to parse any strings, all strings will be rejected!"); + } + if (mDebugging) { + e.printStackTrace(System.err); + } + return false; + } + if (mDebugging) { + out.println(DebugUtil.getPrefixDebugMessage(this) + "regular expression parser from regular expression " + regexp + + " extracted from wildcard string mask " + mStringMask + "."); + } + return true; + } + + /** + * Simple check of the string to be parsed. + */ + private boolean checkStringToBeParsed(final String pStringToBeParsed) { + + // Check for nullness + if (pStringToBeParsed == null) { + if (mDebugging) { + out.println(DebugUtil.getPrefixDebugMessage(this) + "string to be parsed is null - rejection!"); + } + return false; + } + + // Check if valid character (element in alphabet) + for (int i = 0; i < pStringToBeParsed.length(); i++) { + if (!isInAlphabet(pStringToBeParsed.charAt(i))) { + if (mDebugging) { + out.println(DebugUtil.getPrefixDebugMessage(this) + + "one or more characters in string to be parsed are not legal characters - rejection!"); + } + return false; + } + } + return true; + } + + /** + * 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) { + + for (int i = 0; i < ALPHABET.length; i++) { + if (pCharToCheck == ALPHABET[i]) { + return true; + } + } + return false; + } + + /** + * Tests if a certain character is the designated "free-range" character ('*'). + */ + public static boolean isFreeRangeCharacter(final char pCharToCheck) { + return pCharToCheck == FREE_RANGE_CHARACTER; + } + + /** + * Tests if a certain character is the designated "free-pass" character ('?'). + */ + public static boolean isFreePassCharacter(final char pCharToCheck) { + return pCharToCheck == FREE_PASS_CHARACTER; + } + + /** + * Tests if a certain character is a wildcard character ('*' or '?'). + */ + public static boolean isWildcardCharacter(final char pCharToCheck) { + return ((isFreeRangeCharacter(pCharToCheck)) || (isFreePassCharacter(pCharToCheck))); + } + + /** + * Gets the string mask that was used when building the parser atomaton. + *
+ * @return the string mask used for building the parser automaton. + */ + public String getStringMask() { + return mStringMask; + } + + /** + * Parses a string. + *
+ * + * @param pStringToBeParsed + * @return {@code true} if and only if the string are accepted by the parser. + */ + public boolean parseString(final String pStringToBeParsed) { + + if (mDebugging) { + out.println(); + } + if (mDebugging) { + out.println(DebugUtil.getPrefixDebugMessage(this) + "parsing \"" + pStringToBeParsed + "\"..."); + } + + // Update statistics + mTotalNumberOfStringsParsed++; + + // Check string to be parsed + if (!checkStringToBeParsed(pStringToBeParsed)) { + return false; + } + + // Perform parsing and return accetance/rejection flag + if (mInitialized) { + return mRegexpParser.matcher(pStringToBeParsed).matches(); + } else { + out.println(DebugUtil.getPrefixErrorMessage(this) + "trying to use non-initialized parser - string rejected!"); + } + return false; + } + + /* + * Overriding mandatory methods from EntityObject's. + */ + + /** + * Method toString + * + * + * @return + * + */ + public String toString() { + + StringBuilder buffer = new StringBuilder(); + + buffer.append(DebugUtil.getClassName(this)); + buffer.append(": String mask "); + buffer.append(mStringMask); + buffer.append("\n"); + return buffer.toString(); + } + + // Just taking the lazy, easy and dangerous way out + + /** + * Method equals + * + * + * @param pObject + * + * @return + * + */ + public boolean equals(Object pObject) { + + if (pObject instanceof REWildcardStringParser) { + REWildcardStringParser externalParser = (REWildcardStringParser) pObject; + + return (externalParser.mStringMask == this.mStringMask); + } + return ((Object) this).equals(pObject); + } + + // Just taking the lazy, easy and dangerous way out + + /** + * Method hashCode + * + * + * @return + * + */ + public int hashCode() { + return ((Object) this).hashCode(); + } + + protected Object clone() throws CloneNotSupportedException { + return new REWildcardStringParser(mStringMask); + } + + // Just taking the lazy, easy and dangerous way out + protected void finalize() throws Throwable {} +} + + +/*--- Formatted in Sun Java Convention Style on ma, des 1, '03 ---*/ + + +/*------ Formatted by Jindent 3.23 Basic 1.0 --- http://www.jindent.de ------*/ diff --git a/sandbox/sandbox-servlet/src/main/java/com/twelvemonkeys/servlet/image/TextRenderer.java b/sandbox/sandbox-servlet/src/main/java/com/twelvemonkeys/servlet/image/TextRenderer.java index 1bfda5ff..69530349 100755 --- a/sandbox/sandbox-servlet/src/main/java/com/twelvemonkeys/servlet/image/TextRenderer.java +++ b/sandbox/sandbox-servlet/src/main/java/com/twelvemonkeys/servlet/image/TextRenderer.java @@ -1,336 +1,336 @@ -/* - * Copyright (c) 2008, Harald Kuhr - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name "TwelveMonkeys" nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package com.twelvemonkeys.servlet.image; - -import com.twelvemonkeys.lang.StringUtil; -import com.twelvemonkeys.servlet.ServletUtil; - -import javax.servlet.ServletRequest; -import javax.servlet.http.HttpServletRequest; -import java.awt.*; -import java.awt.geom.Rectangle2D; - -/** - * 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, - * style or color, into an image, and output it as a GIF, JPEG or PNG image, - * with optional caching of the rendered image files. - * - *
- * Based on NestingValidator.java,
- * taken from More Servlets and JavaServer Pages
- * from Prentice Hall and Sun Microsystems Press,
- * http://www.moreservlets.com/.
- * © 2002 Marty Hall; may be freely used or adapted.
- *
- * @author Harald Kuhr
- * @author last modified by $Author: haku $
- *
- * @version $Revision: #1 $, ($Date: 2008/05/05 $)
- *
- */
-public class NestingValidator extends TagLibraryValidator {
-
- private List
+ * Based on NestingValidator.java,
+ * taken from More Servlets and JavaServer Pages
+ * from Prentice Hall and Sun Microsystems Press,
+ * http://www.moreservlets.com/.
+ * © 2002 Marty Hall; may be freely used or adapted.
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haku $
+ *
+ * @version $Revision: #1 $, ($Date: 2008/05/05 $)
+ *
+ */
+public class NestingValidator extends TagLibraryValidator {
+
+ private List An abstract base class for tags with some kind of conditional presentation of the tag body. Perform the test required for this particular tag, and either evaluate or skip the body of this tag. Evaluate the remainder of the current page as normal. Release all allocated resources. The condition that must be met in order to display the body of this tag. An abstract base class for tags with some kind of conditional presentation of the tag body. Perform the test required for this particular tag, and either evaluate or skip the body of this tag. Evaluate the remainder of the current page as normal. Release all allocated resources. The condition that must be met in order to display the body of this tag.
- * Custom tag for testing equality of an attribute against a given value.
- * The attribute types supported so far is:
- *
- * PageContext.REQUEST_SCOPE scope.
- */
- public void setValue(String pValue) {
- parameterValue = new Param(pValue);
- }
-
- /**
- * 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.
- *
- * @return If this tag is enclosed within an {@code IncludeTag}, then
- * the default return value from this method is the {@code
- * TagSupport.SKIP_BODY} value.
- * @exception JspException
- */
- public int doStartTag() throws JspException {
- //checkEnclosedInIncludeTag();
-
- addParameter();
-
- return SKIP_BODY;
- }
-
- /**
- * This is the method responsible for actually testing that the tag
- * implemented by this class is enclosed within an {@code IncludeTag}.
- *
- * @exception JspException
- */
- /*
- protected void checkEnclosedInIncludeTag() throws JspException {
- Tag parentTag = getParent();
-
- if ((parentTag != null) && (parentTag instanceof IncludeTag)) {
- return;
- }
-
- String msg = "A class that extends EnclosedIncludeBodyReaderTag " +
- "is not enclosed within an IncludeTag.";
- log(msg);
- throw new JspException(msg);
- }
- */
-
- /**
- * This method adds the parameter whose name and value were passed to this
- * object via the tag attributes to the parent {@code Include} tag.
- */
- private void addParameter() {
- IncludeTag includeTag = (IncludeTag) getParent();
-
- includeTag.addParameter(parameterName, parameterValue);
- }
-
- /**
- * 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
- * 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
- * life cycle just in case a JspException was thrown during the tag
- * execution.
- */
- protected void clearServiceState() {
- parameterName = null;
- parameterValue = null;
- }
-}
+/*
+ * Copyright (c) 2002 TwelveMonkeys.
+ * All rights reserved.
+ *
+ * $Log: ParamTag.java,v $
+ * Revision 1.2 2003/10/06 14:26:00 WMHAKUR
+ * Code clean-up only.
+ *
+ * Revision 1.1 2002/10/18 14:03:09 WMHAKUR
+ * Moved to 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.taglib.ExTagSupport;
+
+import javax.servlet.jsp.JspException;
+
+/**
+ * Parameter tag that emulates ATG Dynamo JHTML behaviour for JSP.
+ *
+ * @author Thomas Purcell (CSC Australia)
+ * @author Harald Kuhr
+ * @author last modified by $Author: haku $
+ *
+ * @version $Revision: #1 $, ($Date: 2008/05/05 $)
+ *
+ */
+public class ParamTag extends ExTagSupport {
+
+ /**
+ * This is the name of the parameter to be inserted into the {@code
+ * PageContext.REQUEST_SCOPE} scope.
+ */
+ private String parameterName;
+
+ /**
+ * This is the value for the parameter to be inserted into the {@code
+ * PageContext.REQUEST_SCOPE} scope.
+ */
+ private Object parameterValue;
+
+ /**
+ * This method allows the JSP page to set the name for the parameter by
+ * using the {@code name} tag attribute.
+ *
+ * @param pName The name for the parameter to insert into the {@code
+ * PageContext.REQUEST_SCOPE} scope.
+ */
+ public void setName(String pName) {
+ parameterName = pName;
+ }
+
+ /**
+ * This method allows the JSP page to set the value for hte parameter by
+ * using the {@code value} tag attribute.
+ *
+ * @param pValue The value for the parameter to insert into the
+ * PageContext.REQUEST_SCOPE scope.
+ */
+ public void setValue(String pValue) {
+ parameterValue = new Param(pValue);
+ }
+
+ /**
+ * 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.
+ *
+ * @return If this tag is enclosed within an {@code IncludeTag}, then
+ * the default return value from this method is the {@code
+ * TagSupport.SKIP_BODY} value.
+ * @exception JspException
+ */
+ public int doStartTag() throws JspException {
+ //checkEnclosedInIncludeTag();
+
+ addParameter();
+
+ return SKIP_BODY;
+ }
+
+ /**
+ * This is the method responsible for actually testing that the tag
+ * implemented by this class is enclosed within an {@code IncludeTag}.
+ *
+ * @exception JspException
+ */
+ /*
+ protected void checkEnclosedInIncludeTag() throws JspException {
+ Tag parentTag = getParent();
+
+ if ((parentTag != null) && (parentTag instanceof IncludeTag)) {
+ return;
+ }
+
+ String msg = "A class that extends EnclosedIncludeBodyReaderTag " +
+ "is not enclosed within an IncludeTag.";
+ log(msg);
+ throw new JspException(msg);
+ }
+ */
+
+ /**
+ * This method adds the parameter whose name and value were passed to this
+ * object via the tag attributes to the parent {@code Include} tag.
+ */
+ private void addParameter() {
+ IncludeTag includeTag = (IncludeTag) getParent();
+
+ includeTag.addParameter(parameterName, parameterValue);
+ }
+
+ /**
+ * 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
+ * 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
+ * life cycle just in case a JspException was thrown during the tag
+ * execution.
+ */
+ protected void clearServiceState() {
+ parameterName = null;
+ parameterValue = null;
+ }
+}
diff --git a/sandbox/sandbox-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/ValueOfTEI.java b/sandbox/sandbox-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/ValueOfTEI.java
index d7a658fe..692001bc 100755
--- a/sandbox/sandbox-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/ValueOfTEI.java
+++ b/sandbox/sandbox-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/ValueOfTEI.java
@@ -1,47 +1,47 @@
-/*
- * Copyright (c) 2002 TwelveMonkeys.
- * All rights reserved.
- *
- * $Log: ValueOfTEI.java,v $
- * Revision 1.3 2003/10/06 14:26:07 WMHAKUR
- * Code clean-up only.
- *
- * Revision 1.2 2002/10/18 14:28:07 WMHAKUR
- * Fixed package error.
- *
- * Revision 1.1 2002/10/18 14:03:52 WMHAKUR
- * Moved to com.twelvemonkeys.servlet.jsp.droplet.taglib
- *
- *
- */
-
-package com.twelvemonkeys.servlet.jsp.droplet.taglib;
-
-import javax.servlet.jsp.tagext.TagData;
-import javax.servlet.jsp.tagext.TagExtraInfo;
-
-/**
- * TagExtraInfo for ValueOf.
- * @todo More meaningful response to the user.
- *
- * @author Harald Kuhr
- * @author last modified by $Author: haku $
- *
- * @version $Revision: #1 $, ($Date: 2008/05/05 $)
- *
- */
-
-public class ValueOfTEI extends TagExtraInfo {
-
- public boolean isValid(TagData pTagData) {
- Object nameAttr = pTagData.getAttribute("name");
- Object paramAttr = pTagData.getAttribute("param");
-
- if ((nameAttr != null && paramAttr == null) || (nameAttr == null && paramAttr != null)) {
- return true; // Exactly one of name or param set
- }
-
- // Either both or none,
- return false;
- }
-}
+/*
+ * Copyright (c) 2002 TwelveMonkeys.
+ * All rights reserved.
+ *
+ * $Log: ValueOfTEI.java,v $
+ * Revision 1.3 2003/10/06 14:26:07 WMHAKUR
+ * Code clean-up only.
+ *
+ * Revision 1.2 2002/10/18 14:28:07 WMHAKUR
+ * Fixed package error.
+ *
+ * Revision 1.1 2002/10/18 14:03:52 WMHAKUR
+ * Moved to com.twelvemonkeys.servlet.jsp.droplet.taglib
+ *
+ *
+ */
+
+package com.twelvemonkeys.servlet.jsp.droplet.taglib;
+
+import javax.servlet.jsp.tagext.TagData;
+import javax.servlet.jsp.tagext.TagExtraInfo;
+
+/**
+ * TagExtraInfo for ValueOf.
+ * @todo More meaningful response to the user.
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haku $
+ *
+ * @version $Revision: #1 $, ($Date: 2008/05/05 $)
+ *
+ */
+
+public class ValueOfTEI extends TagExtraInfo {
+
+ public boolean isValid(TagData pTagData) {
+ Object nameAttr = pTagData.getAttribute("name");
+ Object paramAttr = pTagData.getAttribute("param");
+
+ if ((nameAttr != null && paramAttr == null) || (nameAttr == null && paramAttr != null)) {
+ return true; // Exactly one of name or param set
+ }
+
+ // Either both or none,
+ return false;
+ }
+}
diff --git a/sandbox/sandbox-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/ValueOfTag.java b/sandbox/sandbox-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/ValueOfTag.java
index 7989b082..a7e2a866 100755
--- a/sandbox/sandbox-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/ValueOfTag.java
+++ b/sandbox/sandbox-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/droplet/taglib/ValueOfTag.java
@@ -1,148 +1,148 @@
-/*
- * Copyright (c) 2002 TwelveMonkeys.
- * All rights reserved.
- *
- * $Log: ValueOfTag.java,v $
- * Revision 1.2 2003/10/06 14:26:14 WMHAKUR
- * Code clean-up only.
- *
- * Revision 1.1 2002/10/18 14:03:52 WMHAKUR
- * Moved to 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.taglib.ExTagSupport;
-
-import javax.servlet.ServletException;
-import javax.servlet.jsp.JspException;
-import javax.servlet.jsp.JspWriter;
-import javax.servlet.jsp.PageContext;
-import java.io.IOException;
-
-/**
- * ValueOf tag that emulates ATG Dynamo JHTML behaviour for JSP.
- *
- * @author Thomas Purcell (CSC Australia)
- * @author Harald Kuhr
- * @author last modified by $Author: haku $
- *
- * @version $Revision: #1 $, ($Date: 2008/05/05 $)
- */
-public class ValueOfTag extends ExTagSupport {
-
- /**
- * 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}
- * attribute.
- */
- private String parameterName;
-
- /**
- * This is the value of the parameter read from the {@code
- * PageContext.REQUEST_SCOPE} scope. If the parameter doesn't exist,
- * then this will be null.
- */
- private Object parameterValue;
-
- /**
- * 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
- * PageContext.REQUEST_SCOPE} scope.
- *
- * @param pName The name of the parameter to be read from the {@code
- * PageContext.REQUEST_SCOPE} scope.
- */
- public void setName(String pName) {
- parameterName = pName;
- }
-
- /**
- * 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
- * PageContext.REQUEST_SCOPE} scope. This is just a synonym for
- * setName, to be more like ATG Dynamo.
- *
- * @param pName The name of the parameter to be read from the {@code
- * PageContext.REQUEST_SCOPE} scope.
- */
- public void setParam(String pName) {
- parameterName = pName;
- }
-
- /**
- * This method looks in the session scope for the session-scoped attribute
- * 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
- * session-scoped attribute. If it fails to find the session-scoped
- * attribute, it displays the body for this tag.
- *
- * @return If the session-scoped attribute is found, then this method will
- * return {@code TagSupport.SKIP_BODY}, otherwise it will return
- * {@code TagSupport.EVAL_BODY_INCLUDE}.
- * @exception JspException
- *
- */
- public int doStartTag() throws JspException {
- try {
- if (parameterExists()) {
- if (parameterValue instanceof JspFragment) {
- // OPARAM or PARAM
- ((JspFragment) parameterValue).service(pageContext);
- /*
- log("Service subpage " + pageContext.getServletContext().getRealPath(((Oparam) parameterValue).getName()));
-
- pageContext.include(((Oparam) parameterValue).getName());
- */
- }
- else {
- // Normal JSP parameter value
- JspWriter writer = pageContext.getOut();
- writer.print(parameterValue);
- }
-
- return SKIP_BODY;
- }
- else {
- return EVAL_BODY_INCLUDE;
- }
- }
- catch (ServletException se) {
- log(se.getMessage(), se);
- throw new JspException(se);
- }
- catch (IOException ioe) {
- String msg = "Caught an IOException in ValueOfTag.doStartTag()\n"
- + ioe.toString();
- log(msg, ioe);
- throw new JspException(msg);
- }
- }
-
- /**
- * This method is used to determine whether the parameter whose name is
- * stored in {@code mParameterName} exists within the {@code
- * PageContext.REQUEST_SCOPE} scope. If the parameter does exist,
- * then this method will return {@code true}, otherwise it returns
- * {@code false}. This method has the side affect of loading the
- * parameter value into {@code mParameterValue} if the parameter
- * does exist.
- *
- * @return {@code true} if the parameter whose name is in {@code
- * mParameterName} exists in the {@code PageContext.REQUEST_SCOPE
- * } scope, {@code false} otherwise.
- */
- private boolean parameterExists() {
- parameterValue = pageContext.getAttribute(parameterName, PageContext.REQUEST_SCOPE);
-
- // -- Harald K 20020726
- if (parameterValue == null) {
- parameterValue = pageContext.getRequest().getParameter(parameterName);
- }
-
- return (parameterValue != null);
- }
-}
+/*
+ * Copyright (c) 2002 TwelveMonkeys.
+ * All rights reserved.
+ *
+ * $Log: ValueOfTag.java,v $
+ * Revision 1.2 2003/10/06 14:26:14 WMHAKUR
+ * Code clean-up only.
+ *
+ * Revision 1.1 2002/10/18 14:03:52 WMHAKUR
+ * Moved to 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.taglib.ExTagSupport;
+
+import javax.servlet.ServletException;
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.JspWriter;
+import javax.servlet.jsp.PageContext;
+import java.io.IOException;
+
+/**
+ * ValueOf tag that emulates ATG Dynamo JHTML behaviour for JSP.
+ *
+ * @author Thomas Purcell (CSC Australia)
+ * @author Harald Kuhr
+ * @author last modified by $Author: haku $
+ *
+ * @version $Revision: #1 $, ($Date: 2008/05/05 $)
+ */
+public class ValueOfTag extends ExTagSupport {
+
+ /**
+ * 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}
+ * attribute.
+ */
+ private String parameterName;
+
+ /**
+ * This is the value of the parameter read from the {@code
+ * PageContext.REQUEST_SCOPE} scope. If the parameter doesn't exist,
+ * then this will be null.
+ */
+ private Object parameterValue;
+
+ /**
+ * 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
+ * PageContext.REQUEST_SCOPE} scope.
+ *
+ * @param pName The name of the parameter to be read from the {@code
+ * PageContext.REQUEST_SCOPE} scope.
+ */
+ public void setName(String pName) {
+ parameterName = pName;
+ }
+
+ /**
+ * 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
+ * PageContext.REQUEST_SCOPE} scope. This is just a synonym for
+ * setName, to be more like ATG Dynamo.
+ *
+ * @param pName The name of the parameter to be read from the {@code
+ * PageContext.REQUEST_SCOPE} scope.
+ */
+ public void setParam(String pName) {
+ parameterName = pName;
+ }
+
+ /**
+ * This method looks in the session scope for the session-scoped attribute
+ * 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
+ * session-scoped attribute. If it fails to find the session-scoped
+ * attribute, it displays the body for this tag.
+ *
+ * @return If the session-scoped attribute is found, then this method will
+ * return {@code TagSupport.SKIP_BODY}, otherwise it will return
+ * {@code TagSupport.EVAL_BODY_INCLUDE}.
+ * @exception JspException
+ *
+ */
+ public int doStartTag() throws JspException {
+ try {
+ if (parameterExists()) {
+ if (parameterValue instanceof JspFragment) {
+ // OPARAM or PARAM
+ ((JspFragment) parameterValue).service(pageContext);
+ /*
+ log("Service subpage " + pageContext.getServletContext().getRealPath(((Oparam) parameterValue).getName()));
+
+ pageContext.include(((Oparam) parameterValue).getName());
+ */
+ }
+ else {
+ // Normal JSP parameter value
+ JspWriter writer = pageContext.getOut();
+ writer.print(parameterValue);
+ }
+
+ return SKIP_BODY;
+ }
+ else {
+ return EVAL_BODY_INCLUDE;
+ }
+ }
+ catch (ServletException se) {
+ log(se.getMessage(), se);
+ throw new JspException(se);
+ }
+ catch (IOException ioe) {
+ String msg = "Caught an IOException in ValueOfTag.doStartTag()\n"
+ + ioe.toString();
+ log(msg, ioe);
+ throw new JspException(msg);
+ }
+ }
+
+ /**
+ * This method is used to determine whether the parameter whose name is
+ * stored in {@code mParameterName} exists within the {@code
+ * PageContext.REQUEST_SCOPE} scope. If the parameter does exist,
+ * then this method will return {@code true}, otherwise it returns
+ * {@code false}. This method has the side affect of loading the
+ * parameter value into {@code mParameterValue} if the parameter
+ * does exist.
+ *
+ * @return {@code true} if the parameter whose name is in {@code
+ * mParameterName} exists in the {@code PageContext.REQUEST_SCOPE
+ * } scope, {@code false} otherwise.
+ */
+ private boolean parameterExists() {
+ parameterValue = pageContext.getAttribute(parameterName, PageContext.REQUEST_SCOPE);
+
+ // -- Harald K 20020726
+ if (parameterValue == null) {
+ parameterValue = pageContext.getRequest().getParameter(parameterName);
+ }
+
+ return (parameterValue != null);
+ }
+}
diff --git a/sandbox/sandbox-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/BodyReaderTag.java b/sandbox/sandbox-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/BodyReaderTag.java
index 1d260c8d..60ea0a91 100755
--- a/sandbox/sandbox-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/BodyReaderTag.java
+++ b/sandbox/sandbox-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/BodyReaderTag.java
@@ -1,39 +1,39 @@
-package com.twelvemonkeys.servlet.jsp.taglib;
-
-import javax.servlet.jsp.JspException;
-
-/**
- *
- *
- * @author Thomas Purcell (CSC Australia)
- *
- * @version 1.0
- */
-public abstract class BodyReaderTag extends ExBodyTagSupport {
- /**
- * 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
- * method takes the content as a string and passes it to the {@code
- * processBody} method.
- *
- * @return This method returns the {@code BodyTagSupport.SKIP_BODY}
- * constant. This means that the body of the tag will only be
- * processed the one time.
- * @exception JspException
- */
- public int doAfterBody() throws JspException {
- processBody(bodyContent.getString());
- return SKIP_BODY;
- }
-
- /**
- * 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
- * the tag will have been interpreted to a String by the JSP engine before
- * this method is called.
- *
- * @param pContent The body for the custom tag converted to a String.
- * @exception JspException
- */
- protected abstract void processBody(String pContent) throws JspException;
-}
+package com.twelvemonkeys.servlet.jsp.taglib;
+
+import javax.servlet.jsp.JspException;
+
+/**
+ *
+ *
+ * @author Thomas Purcell (CSC Australia)
+ *
+ * @version 1.0
+ */
+public abstract class BodyReaderTag extends ExBodyTagSupport {
+ /**
+ * 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
+ * method takes the content as a string and passes it to the {@code
+ * processBody} method.
+ *
+ * @return This method returns the {@code BodyTagSupport.SKIP_BODY}
+ * constant. This means that the body of the tag will only be
+ * processed the one time.
+ * @exception JspException
+ */
+ public int doAfterBody() throws JspException {
+ processBody(bodyContent.getString());
+ return SKIP_BODY;
+ }
+
+ /**
+ * 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
+ * the tag will have been interpreted to a String by the JSP engine before
+ * this method is called.
+ *
+ * @param pContent The body for the custom tag converted to a String.
+ * @exception JspException
+ */
+ protected abstract void processBody(String pContent) throws JspException;
+}
diff --git a/sandbox/sandbox-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/CSVToTableTag.java b/sandbox/sandbox-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/CSVToTableTag.java
index e698f2ad..9db7b6be 100755
--- a/sandbox/sandbox-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/CSVToTableTag.java
+++ b/sandbox/sandbox-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/CSVToTableTag.java
@@ -1,235 +1,235 @@
-/*
- * Copyright (c) 2002 TwelveMonkeys.
- * All rights reserved.
- *
- * $Log: CSVToTableTag.java,v $
- * Revision 1.3 2003/10/06 14:24:50 WMHAKUR
- * Code clean-up only.
- *
- * Revision 1.2 2002/11/26 17:33:49 WMHAKUR
- * Added documentation & removed System.out.println()s.
- *
- * Revision 1.1 2002/11/19 10:50:10 WMHAKUR
- * Renamed from CSVToTable, to follow naming conventions.
- *
- * Revision 1.1 2002/11/18 22:11:16 WMHAKUR
- * Tag to convert CSV to HTML table.
- * Can be further transformed, using XSLT.
- *
- */
-
-package com.twelvemonkeys.servlet.jsp.taglib;
-
-import javax.servlet.jsp.JspException;
-import javax.servlet.jsp.JspWriter;
-import javax.servlet.jsp.tagext.BodyContent;
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.Reader;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.StringTokenizer;
-
-/**
- * Creates a table from a string of "comma-separated values" (CSV).
- * The delimiter character can be any character (or combination of characters).
- * The default delimiter is TAB ({@code \t}).
- *
- *
- *
- *
- *
- * The input may look like this:
- *
- * <c:totable firstRowIsHeader="true" delimiter=";">
- * header A;header B
- * data 1A; data 1B
- * data 2A; data 2B
- * </c:totable>
- *
- *
- * The output (source) will look like this:
- *
- * <TABLE>
- * <TR>
- * <TH>header A</TH><TH>header B</TH>
- * </TR>
- * <TR>
- * <TD>data 1A</TD><TD>data 1B</TD>
- * </TR>
- * <TR>
- * <TD>data 2A</TD><TD>data 2B</TD>
- * </TR>
- * </TABLE>
- *
- * You wil probably want to use XSLT to make the final output look nicer. :-)
- *
- * @see StringTokenizer
- * @see XSLT spec
- *
- * @author Harald Kuhr
- *
- * @version $Id: jsp/taglib/CSVToTableTag.java#1 $
- */
-public class CSVToTableTag extends ExBodyTagSupport {
- public final static String TAB = "\t";
-
- protected String delimiter = null;
- protected boolean firstRowIsHeader = false;
- protected boolean firstColIsHeader = false;
-
- public void setDelimiter(String pDelimiter) {
- delimiter = pDelimiter;
- }
-
- public String getDelimiter() {
- return delimiter != null ? delimiter : TAB;
- }
-
- public void setFirstRowIsHeader(String pBoolean) {
- firstRowIsHeader = Boolean.valueOf(pBoolean);
- }
-
- public void setFirstColIsHeader(String pBoolean) {
- firstColIsHeader = Boolean.valueOf(pBoolean);
- }
-
-
- public int doEndTag() throws JspException {
- BodyContent content = getBodyContent();
-
- try {
- Table table =
- Table.parseContent(content.getReader(), getDelimiter());
-
- JspWriter out = pageContext.getOut();
-
- //System.out.println("CSVToTable: " + table.getRows() + " rows, "
- // + table.getCols() + " cols.");
-
- if (table.getRows() > 0) {
- out.println("");
- // Loop over rows
- for (int row = 0; row < table.getRows(); row++) {
- out.println("
");
- }
- }
- catch (IOException ioe) {
- throw new JspException(ioe);
- }
-
- return super.doEndTag();
- }
-
- static class Table {
- List rows = null;
- int cols = 0;
-
- private Table(List pRows, int pCols) {
- rows = pRows;
- cols = pCols;
- }
-
- int getRows() {
- return rows != null ? rows.size() : 0;
- }
-
- int getCols() {
- return cols;
- }
-
- List getTableRows() {
- return rows;
- }
-
- List getTableRow(int pRow) {
- return rows != null
- ? (List) rows.get(pRow)
- : Collections.EMPTY_LIST;
- }
-
- String get(int pRow, int pCol) {
- List row = getTableRow(pRow);
- // Rows may contain unequal number of cols
- return (row.size() > pCol) ? (String) row.get(pCol) : "";
- }
-
- /**
- * Parses a BodyContent to a table.
- *
- */
- static Table parseContent(Reader pContent, String pDelim) throws IOException {
- List");
-
- // Loop over cells in each row
- for (int col = 0; col < table.getCols(); col++) {
- // Test if we are using headers, else normal cell
- if (firstRowIsHeader && row == 0 || firstColIsHeader && col == 0) {
- out.println(" ");
-
- }
- out.println("" + table.get(row, col) + " ");
- }
- else {
- out.println("" + table.get(row, col) + " ");
- }
- }
-
- out.println("> tableRows = new ArrayList
>();
- int tdsPerTR = 0;
-
- // Loop through TRs
- BufferedReader reader = new BufferedReader(pContent);
- String tr;
- while ((tr = reader.readLine()) != null) {
- // Discard blank lines
- if (tr.trim().length() <= 0 && tr.indexOf(pDelim) < 0) {
- continue;
- }
-
- //System.out.println("CSVToTable: read LINE=\"" + tr + "\"");
-
- List
+ *
+ *
+ * The input may look like this:
+ *
+ * <c:totable firstRowIsHeader="true" delimiter=";">
+ * header A;header B
+ * data 1A; data 1B
+ * data 2A; data 2B
+ * </c:totable>
+ *
+ *
+ * The output (source) will look like this:
+ *
+ * <TABLE>
+ * <TR>
+ * <TH>header A</TH><TH>header B</TH>
+ * </TR>
+ * <TR>
+ * <TD>data 1A</TD><TD>data 1B</TD>
+ * </TR>
+ * <TR>
+ * <TD>data 2A</TD><TD>data 2B</TD>
+ * </TR>
+ * </TABLE>
+ *
+ * You wil probably want to use XSLT to make the final output look nicer. :-)
+ *
+ * @see StringTokenizer
+ * @see XSLT spec
+ *
+ * @author Harald Kuhr
+ *
+ * @version $Id: jsp/taglib/CSVToTableTag.java#1 $
+ */
+public class CSVToTableTag extends ExBodyTagSupport {
+ public final static String TAB = "\t";
+
+ protected String delimiter = null;
+ protected boolean firstRowIsHeader = false;
+ protected boolean firstColIsHeader = false;
+
+ public void setDelimiter(String pDelimiter) {
+ delimiter = pDelimiter;
+ }
+
+ public String getDelimiter() {
+ return delimiter != null ? delimiter : TAB;
+ }
+
+ public void setFirstRowIsHeader(String pBoolean) {
+ firstRowIsHeader = Boolean.valueOf(pBoolean);
+ }
+
+ public void setFirstColIsHeader(String pBoolean) {
+ firstColIsHeader = Boolean.valueOf(pBoolean);
+ }
+
+
+ public int doEndTag() throws JspException {
+ BodyContent content = getBodyContent();
+
+ try {
+ Table table =
+ Table.parseContent(content.getReader(), getDelimiter());
+
+ JspWriter out = pageContext.getOut();
+
+ //System.out.println("CSVToTable: " + table.getRows() + " rows, "
+ // + table.getCols() + " cols.");
+
+ if (table.getRows() > 0) {
+ out.println("");
+ // Loop over rows
+ for (int row = 0; row < table.getRows(); row++) {
+ out.println("
");
+ }
+ }
+ catch (IOException ioe) {
+ throw new JspException(ioe);
+ }
+
+ return super.doEndTag();
+ }
+
+ static class Table {
+ List rows = null;
+ int cols = 0;
+
+ private Table(List pRows, int pCols) {
+ rows = pRows;
+ cols = pCols;
+ }
+
+ int getRows() {
+ return rows != null ? rows.size() : 0;
+ }
+
+ int getCols() {
+ return cols;
+ }
+
+ List getTableRows() {
+ return rows;
+ }
+
+ List getTableRow(int pRow) {
+ return rows != null
+ ? (List) rows.get(pRow)
+ : Collections.EMPTY_LIST;
+ }
+
+ String get(int pRow, int pCol) {
+ List row = getTableRow(pRow);
+ // Rows may contain unequal number of cols
+ return (row.size() > pCol) ? (String) row.get(pCol) : "";
+ }
+
+ /**
+ * Parses a BodyContent to a table.
+ *
+ */
+ static Table parseContent(Reader pContent, String pDelim) throws IOException {
+ List");
+
+ // Loop over cells in each row
+ for (int col = 0; col < table.getCols(); col++) {
+ // Test if we are using headers, else normal cell
+ if (firstRowIsHeader && row == 0 || firstColIsHeader && col == 0) {
+ out.println(" ");
+
+ }
+ out.println("" + table.get(row, col) + " ");
+ }
+ else {
+ out.println("" + table.get(row, col) + " ");
+ }
+ }
+
+ out.println("> tableRows = new ArrayList
>();
+ int tdsPerTR = 0;
+
+ // Loop through TRs
+ BufferedReader reader = new BufferedReader(pContent);
+ String tr;
+ while ((tr = reader.readLine()) != null) {
+ // Discard blank lines
+ if (tr.trim().length() <= 0 && tr.indexOf(pDelim) < 0) {
+ continue;
+ }
+
+ //System.out.println("CSVToTable: read LINE=\"" + tr + "\"");
+
+ List
- *
- *
equal | - *Availability: 1.0 | - *||||
Tag for testing if an attribute is equal to a given value. |
- * |||||
Tag Body | - *JSP | - *- * | - * | - * | - * |
Restrictions | - *None |
- * ||||
Attributes | - *Name | - *Required | - *Runtime Expression Evaluation | - *Availability | - *|
- * | name | - *Yes | - *Yes | - *1.0 | - *|
- * | The attribute name |
- * ||||
- * | value | - *No | - *Yes | - *1.0 | - *|
- * | The value for equality testing |
- * ||||
Variables | - *None | - *||||
Examples | - *
- * - *<%@ taglib prefix="twelvemonkeys" uri="twelvemonkeys-logic" %> - *<bean:cookie id="logonUsernameCookie" - * name="<%= com.strutscommand.Constants.LOGON_USERNAME_COOKIE_NAME %>" - * value="no_username_set" /> - *<twelvemonkeys:equal name="logonUsernameCookie" value="no_username_set"> - * <html:text property="username" /> - *</twelvemonkeys:equal> - *- * |
- *
- * NB! If the object value property ({@code value} -> {@code mObjectValue}) is empty than {@code true} will be returned. - *
- * - * @return {@code true} if and only if all conditions are met. - */ - protected boolean condition() throws JspException { - - if (StringUtil.isEmpty(objectName)) { - return false; - } - - if (StringUtil.isEmpty(objectValue)) { - return true; - } - - Object pageScopedAttribute = pageContext.getAttribute(objectName); - if (pageScopedAttribute == null) { - return false; - } - - String pageScopedStringAttribute; - - // String - if (pageScopedAttribute instanceof String) { - pageScopedStringAttribute = (String) pageScopedAttribute; - - // Cookie - } - else if (pageScopedAttribute instanceof Cookie) { - pageScopedStringAttribute = ((Cookie) pageScopedAttribute).getValue(); - - // Type not yet supported... - } - else { - return false; - } - - return (pageScopedStringAttribute.equals(objectValue)); - } - -} +/* + * Produced (p) 2002 TwelveMonkeys + * Address : Svovelstikka 1, Box 6432 Etterstad, 0605 Oslo, Norway. + * Phone : +47 22 57 70 00 + * Fax : +47 22 57 70 70 + */ + +package com.twelvemonkeys.servlet.jsp.taglib.logic; + + +import com.twelvemonkeys.lang.StringUtil; + +import javax.servlet.http.Cookie; +import javax.servlet.jsp.JspException; + + +/** + *+ * Custom tag for testing equality of an attribute against a given value. + * The attribute types supported so far is: + *
equal | + *Availability: 1.0 | + *||||
Tag for testing if an attribute is equal to a given value. |
+ * |||||
Tag Body | + *JSP | + *+ * | + * | + * | + * |
Restrictions | + *None |
+ * ||||
Attributes | + *Name | + *Required | + *Runtime Expression Evaluation | + *Availability | + *|
+ * | name | + *Yes | + *Yes | + *1.0 | + *|
+ * | The attribute name |
+ * ||||
+ * | value | + *No | + *Yes | + *1.0 | + *|
+ * | The value for equality testing |
+ * ||||
Variables | + *None | + *||||
Examples | + *
+ * + *<%@ taglib prefix="twelvemonkeys" uri="twelvemonkeys-logic" %> + *<bean:cookie id="logonUsernameCookie" + * name="<%= com.strutscommand.Constants.LOGON_USERNAME_COOKIE_NAME %>" + * value="no_username_set" /> + *<twelvemonkeys:equal name="logonUsernameCookie" value="no_username_set"> + * <html:text property="username" /> + *</twelvemonkeys:equal> + *+ * |
+ *
+ * NB! If the object value property ({@code value} -> {@code mObjectValue}) is empty than {@code true} will be returned. + *
+ * + * @return {@code true} if and only if all conditions are met. + */ + protected boolean condition() throws JspException { + + if (StringUtil.isEmpty(objectName)) { + return false; + } + + if (StringUtil.isEmpty(objectValue)) { + return true; + } + + Object pageScopedAttribute = pageContext.getAttribute(objectName); + if (pageScopedAttribute == null) { + return false; + } + + String pageScopedStringAttribute; + + // String + if (pageScopedAttribute instanceof String) { + pageScopedStringAttribute = (String) pageScopedAttribute; + + // Cookie + } + else if (pageScopedAttribute instanceof Cookie) { + pageScopedStringAttribute = ((Cookie) pageScopedAttribute).getValue(); + + // Type not yet supported... + } + else { + return false; + } + + return (pageScopedStringAttribute.equals(objectValue)); + } + +} diff --git a/sandbox/sandbox-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/IteratorProviderTEI.java b/sandbox/sandbox-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/IteratorProviderTEI.java index 013c11b1..1dcb412a 100755 --- a/sandbox/sandbox-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/IteratorProviderTEI.java +++ b/sandbox/sandbox-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/IteratorProviderTEI.java @@ -1,40 +1,40 @@ -package com.twelvemonkeys.servlet.jsp.taglib.logic; - -import javax.servlet.jsp.tagext.*; - -/** - * TagExtraInfo class for IteratorProvider tags. - * - * @author Harald Kuhr - * @version $id: $ - */ -public class IteratorProviderTEI extends TagExtraInfo { - /** - * Gets the variable info for IteratorProvider tags. The attribute with the - * name defined by the "id" attribute and type defined by the "type" - * attribute is declared with scope {@code VariableInfo.AT_END}. - * - * @param pData TagData instance provided by container - * @return an VariableInfo array of lenght 1, containing the attribute - * defined by the id parameter, declared, and with scope - * {@code VariableInfo.AT_END}. - */ - public VariableInfo[] getVariableInfo(TagData pData) { - // Get attribute name - String attributeName = pData.getId(); - if (attributeName == null) { - attributeName = IteratorProviderTag.getDefaultIteratorName(); - } - - // Get type - String type = pData.getAttributeString(IteratorProviderTag.ATTRIBUTE_TYPE); - if (type == null) { - type = IteratorProviderTag.getDefaultIteratorType(); - } - - // Return the variable info - return new VariableInfo[]{ - new VariableInfo(attributeName, type, true, VariableInfo.AT_END), - }; - } -} +package com.twelvemonkeys.servlet.jsp.taglib.logic; + +import javax.servlet.jsp.tagext.*; + +/** + * TagExtraInfo class for IteratorProvider tags. + * + * @author Harald Kuhr + * @version $id: $ + */ +public class IteratorProviderTEI extends TagExtraInfo { + /** + * Gets the variable info for IteratorProvider tags. The attribute with the + * name defined by the "id" attribute and type defined by the "type" + * attribute is declared with scope {@code VariableInfo.AT_END}. + * + * @param pData TagData instance provided by container + * @return an VariableInfo array of lenght 1, containing the attribute + * defined by the id parameter, declared, and with scope + * {@code VariableInfo.AT_END}. + */ + public VariableInfo[] getVariableInfo(TagData pData) { + // Get attribute name + String attributeName = pData.getId(); + if (attributeName == null) { + attributeName = IteratorProviderTag.getDefaultIteratorName(); + } + + // Get type + String type = pData.getAttributeString(IteratorProviderTag.ATTRIBUTE_TYPE); + if (type == null) { + type = IteratorProviderTag.getDefaultIteratorType(); + } + + // Return the variable info + return new VariableInfo[]{ + new VariableInfo(attributeName, type, true, VariableInfo.AT_END), + }; + } +} diff --git a/sandbox/sandbox-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/IteratorProviderTag.java b/sandbox/sandbox-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/IteratorProviderTag.java index 6409664d..1fd61a1a 100755 --- a/sandbox/sandbox-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/IteratorProviderTag.java +++ b/sandbox/sandbox-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/IteratorProviderTag.java @@ -1,86 +1,86 @@ -package com.twelvemonkeys.servlet.jsp.taglib.logic; - -import javax.servlet.jsp.JspException; -import javax.servlet.jsp.tagext.Tag; -import javax.servlet.jsp.tagext.TagSupport; -import java.util.Iterator; - -/** - * Abstract base class for adding iterators to a page. - * - * @todo Possible to use same strategy for all types of objects? Rename class - * to ObjectProviderTag? Hmmm... Might work. - * - * @author Harald Kuhr - * @version $id: $ - */ -public abstract class IteratorProviderTag extends TagSupport { - /** {@code iterator} */ - protected final static String DEFAULT_ITERATOR_NAME = "iterator"; - /** {@code java.util.iterator} */ - protected final static String DEFAULT_ITERATOR_TYPE = "java.util.Iterator"; - /** {@code type} */ - public final static String ATTRIBUTE_TYPE = "type"; - - /** */ - private String type = null; - - /** - * Gets the type. - * - * @return the type (class name) - */ - public String getType() { - return type; - } - - /** - * Sets the type. - * - * @param pType - */ - - public void setType(String pType) { - type = pType; - } - - /** - * doEndTag implementation. - * - * @return {@code Tag.EVAL_PAGE} - * @throws JspException - */ - - public int doEndTag() throws JspException { - // Set the iterator - pageContext.setAttribute(getId(), getIterator()); - - return Tag.EVAL_PAGE; - } - - /** - * Gets the iterator for this tag. - * - * @return an {@link java.util.Iterator} - */ - protected abstract Iterator getIterator(); - - /** - * Gets the default iterator name. - * - * @return {@link #DEFAULT_ITERATOR_NAME} - */ - protected static String getDefaultIteratorName() { - return DEFAULT_ITERATOR_NAME; - } - - /** - * Gets the default iterator type. - * - * @return {@link #DEFAULT_ITERATOR_TYPE} - */ - protected static String getDefaultIteratorType() { - return DEFAULT_ITERATOR_TYPE; - } - -} +package com.twelvemonkeys.servlet.jsp.taglib.logic; + +import javax.servlet.jsp.JspException; +import javax.servlet.jsp.tagext.Tag; +import javax.servlet.jsp.tagext.TagSupport; +import java.util.Iterator; + +/** + * Abstract base class for adding iterators to a page. + * + * @todo Possible to use same strategy for all types of objects? Rename class + * to ObjectProviderTag? Hmmm... Might work. + * + * @author Harald Kuhr + * @version $id: $ + */ +public abstract class IteratorProviderTag extends TagSupport { + /** {@code iterator} */ + protected final static String DEFAULT_ITERATOR_NAME = "iterator"; + /** {@code java.util.iterator} */ + protected final static String DEFAULT_ITERATOR_TYPE = "java.util.Iterator"; + /** {@code type} */ + public final static String ATTRIBUTE_TYPE = "type"; + + /** */ + private String type = null; + + /** + * Gets the type. + * + * @return the type (class name) + */ + public String getType() { + return type; + } + + /** + * Sets the type. + * + * @param pType + */ + + public void setType(String pType) { + type = pType; + } + + /** + * doEndTag implementation. + * + * @return {@code Tag.EVAL_PAGE} + * @throws JspException + */ + + public int doEndTag() throws JspException { + // Set the iterator + pageContext.setAttribute(getId(), getIterator()); + + return Tag.EVAL_PAGE; + } + + /** + * Gets the iterator for this tag. + * + * @return an {@link java.util.Iterator} + */ + protected abstract Iterator getIterator(); + + /** + * Gets the default iterator name. + * + * @return {@link #DEFAULT_ITERATOR_NAME} + */ + protected static String getDefaultIteratorName() { + return DEFAULT_ITERATOR_NAME; + } + + /** + * Gets the default iterator type. + * + * @return {@link #DEFAULT_ITERATOR_TYPE} + */ + protected static String getDefaultIteratorType() { + return DEFAULT_ITERATOR_TYPE; + } + +} diff --git a/sandbox/sandbox-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/NotEqualTag.java b/sandbox/sandbox-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/NotEqualTag.java index 3c489957..72082b08 100755 --- a/sandbox/sandbox-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/NotEqualTag.java +++ b/sandbox/sandbox-servlet/src/main/java/com/twelvemonkeys/servlet/jsp/taglib/logic/NotEqualTag.java @@ -1,168 +1,168 @@ -/* - * Produced (p) 2002 TwelveMonkeys - * Address : Svovelstikka 1, Box 6432 Etterstad, 0605 Oslo, Norway. - * Phone : +47 22 57 70 00 - * Fax : +47 22 57 70 70 - */ - -package com.twelvemonkeys.servlet.jsp.taglib.logic; - - -import com.twelvemonkeys.lang.StringUtil; - -import javax.servlet.http.Cookie; -import javax.servlet.jsp.JspException; - - -/** - *- * Custom tag for testing non-equality of an attribute against a given value. - * The attribute types supported so far is: - *
notEqual | - *Availability: 1.0 | - *||||
Tag for testing if an attribute is NOT equal to a given value. |
- * |||||
Tag Body | - *JSP | - *- * | - * | - * | - * |
Restrictions | - *None |
- * ||||
Attributes | - *Name | - *Required | - *Runtime Expression Evaluation | - *Availability | - *|
- * | name | - *Yes | - *Yes | - *1.0 | - *|
- * | The attribute name |
- * ||||
- * | value | - *No | - *Yes | - *1.0 | - *|
- * | The value for equality testing |
- * ||||
Variables | - *None | - *||||
Examples | - *
- * - *<%@ taglib prefix="twelvemonkeys" uri="twelvemonkeys-logic" %> - *<bean:cookie id="logonUsernameCookie" - * name="<%= com.strutscommand.Constants.LOGON_USERNAME_COOKIE_NAME %>" - * value="no_username_set" /> - *<twelvemonkeys:notEqual name="logonUsernameCookie" value="no_username_set"> - * <html:text property="username" value="<%= logonUsernameCookie.getValue() %>" /> - *</twelvemonkeys:notEqual> - *- * |
- *
- * NB! If the object value property ({@code value} -> {@code mObjectValue}) is empty than {@code true} will be returned. - *
- * - * @return {@code true} if and only if all conditions are met. - */ - protected boolean condition() throws JspException { - - if (StringUtil.isEmpty(objectName)) { - return false; - } - - if (StringUtil.isEmpty(objectValue)) { - return true; - } - - Object pageScopedAttribute = pageContext.getAttribute(objectName); - if (pageScopedAttribute == null) { - return false; - } - - String pageScopedStringAttribute; - - // String - if (pageScopedAttribute instanceof String) { - pageScopedStringAttribute = (String) pageScopedAttribute; - - // Cookie - } - else if (pageScopedAttribute instanceof Cookie) { - pageScopedStringAttribute = ((Cookie) pageScopedAttribute).getValue(); - - // Type not yet supported... - } - else { - return false; - } - - return (!(pageScopedStringAttribute.equals(objectValue))); - } - -} +/* + * Produced (p) 2002 TwelveMonkeys + * Address : Svovelstikka 1, Box 6432 Etterstad, 0605 Oslo, Norway. + * Phone : +47 22 57 70 00 + * Fax : +47 22 57 70 70 + */ + +package com.twelvemonkeys.servlet.jsp.taglib.logic; + + +import com.twelvemonkeys.lang.StringUtil; + +import javax.servlet.http.Cookie; +import javax.servlet.jsp.JspException; + + +/** + *+ * Custom tag for testing non-equality of an attribute against a given value. + * The attribute types supported so far is: + *
notEqual | + *Availability: 1.0 | + *||||
Tag for testing if an attribute is NOT equal to a given value. |
+ * |||||
Tag Body | + *JSP | + *+ * | + * | + * | + * |
Restrictions | + *None |
+ * ||||
Attributes | + *Name | + *Required | + *Runtime Expression Evaluation | + *Availability | + *|
+ * | name | + *Yes | + *Yes | + *1.0 | + *|
+ * | The attribute name |
+ * ||||
+ * | value | + *No | + *Yes | + *1.0 | + *|
+ * | The value for equality testing |
+ * ||||
Variables | + *None | + *||||
Examples | + *
+ * + *<%@ taglib prefix="twelvemonkeys" uri="twelvemonkeys-logic" %> + *<bean:cookie id="logonUsernameCookie" + * name="<%= com.strutscommand.Constants.LOGON_USERNAME_COOKIE_NAME %>" + * value="no_username_set" /> + *<twelvemonkeys:notEqual name="logonUsernameCookie" value="no_username_set"> + * <html:text property="username" value="<%= logonUsernameCookie.getValue() %>" /> + *</twelvemonkeys:notEqual> + *+ * |
+ *
+ * NB! If the object value property ({@code value} -> {@code mObjectValue}) is empty than {@code true} will be returned. + *
+ * + * @return {@code true} if and only if all conditions are met. + */ + protected boolean condition() throws JspException { + + if (StringUtil.isEmpty(objectName)) { + return false; + } + + if (StringUtil.isEmpty(objectValue)) { + return true; + } + + Object pageScopedAttribute = pageContext.getAttribute(objectName); + if (pageScopedAttribute == null) { + return false; + } + + String pageScopedStringAttribute; + + // String + if (pageScopedAttribute instanceof String) { + pageScopedStringAttribute = (String) pageScopedAttribute; + + // Cookie + } + else if (pageScopedAttribute instanceof Cookie) { + pageScopedStringAttribute = ((Cookie) pageScopedAttribute).getValue(); + + // Type not yet supported... + } + else { + return false; + } + + return (!(pageScopedStringAttribute.equals(objectValue))); + } + +} diff --git a/sandbox/sandbox-servlet/src/main/java/com/twelvemonkeys/servlet/log4j/Log4JContextWrapper.java b/sandbox/sandbox-servlet/src/main/java/com/twelvemonkeys/servlet/log4j/Log4JContextWrapper.java index 19194b5c..0599a468 100755 --- a/sandbox/sandbox-servlet/src/main/java/com/twelvemonkeys/servlet/log4j/Log4JContextWrapper.java +++ b/sandbox/sandbox-servlet/src/main/java/com/twelvemonkeys/servlet/log4j/Log4JContextWrapper.java @@ -1,183 +1,183 @@ -package com.twelvemonkeys.servlet.log4j; - -import org.apache.log4j.Logger; - -import javax.servlet.RequestDispatcher; -import javax.servlet.Servlet; -import javax.servlet.ServletContext; -import javax.servlet.ServletException; -import java.io.InputStream; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.Enumeration; -import java.util.Set; - -/** - * Log4JContextWrapper - * - * - * @author Harald Kuhr - * @version $Id: log4j/Log4JContextWrapper.java#1 $ - */ -final class Log4JContextWrapper implements ServletContext { - // TODO: Move to sandbox - - // TODO: This solution sucks... - // How about starting to create some kind of pluggable decorator system, - // something along the lines of AOP mixins/interceptor pattern.. - // Probably using a dynamic Proxy, delegating to the mixins and or the - // wrapped object based on configuration. - // This way we could simply call ServletUtil.decorate(ServletContext):ServletContext - // And the context would be decorated with all configured mixins at once, - // requiring less boilerplate delegation code, and less layers of wrapping - // (alternatively we could decorate the Servlet/FilterConfig objects). - // See the ServletUtil.createWrapper methods for some hints.. - - - // Something like this: - public static ServletContext wrap(final ServletContext pContext, final Object[] pDelegates, final ClassLoader pLoader) { - ClassLoader cl = pLoader != null ? pLoader : Thread.currentThread().getContextClassLoader(); - - // TODO: Create a "static" mapping between methods in the ServletContext - // and the corresponding delegate - - // TODO: Resolve super-invokations, to delegate to next delegate in - // chain, and finally invoke pContext - - return (ServletContext) Proxy.newProxyInstance(cl, new Class[] {ServletContext.class}, new InvocationHandler() { - public Object invoke(Object pProxy, Method pMethod, Object[] pArgs) throws Throwable { - // TODO: Test if any of the delegates should receive, if so invoke - - // Else, invoke on original object - return pMethod.invoke(pContext, pArgs); - } - }); - } - - private final ServletContext context; - - private final Logger logger; - - Log4JContextWrapper(ServletContext pContext) { - context = pContext; - - // TODO: We want a logger per servlet, not per servlet context, right? - logger = Logger.getLogger(pContext.getServletContextName()); - - // TODO: Automatic init/config of Log4J using context parameter for log4j.xml? - // See Log4JInit.java - - // TODO: Automatic config of properties in the context wrapper? - } - - public final void log(final Exception pException, final String pMessage) { - log(pMessage, pException); - } - - // TODO: Add more logging methods to interface info/warn/error? - // TODO: Implement these mehtods in GenericFilter/GenericServlet? - - public void log(String pMessage) { - // TODO: Get logger for caller.. - // Should be possible using some stack peek hack, but that's slow... - // Find a good way... - // Maybe just pass it into the constuctor, and have one wrapper per servlet - logger.info(pMessage); - } - - public void log(String pMessage, Throwable pCause) { - // TODO: Get logger for caller.. - - logger.error(pMessage, pCause); - } - - public Object getAttribute(String pMessage) { - return context.getAttribute(pMessage); - } - - public Enumeration getAttributeNames() { - return context.getAttributeNames(); - } - - public ServletContext getContext(String pMessage) { - return context.getContext(pMessage); - } - - public String getInitParameter(String pMessage) { - return context.getInitParameter(pMessage); - } - - public Enumeration getInitParameterNames() { - return context.getInitParameterNames(); - } - - public int getMajorVersion() { - return context.getMajorVersion(); - } - - public String getMimeType(String pMessage) { - return context.getMimeType(pMessage); - } - - public int getMinorVersion() { - return context.getMinorVersion(); - } - - public RequestDispatcher getNamedDispatcher(String pMessage) { - return context.getNamedDispatcher(pMessage); - } - - public String getRealPath(String pMessage) { - return context.getRealPath(pMessage); - } - - public RequestDispatcher getRequestDispatcher(String pMessage) { - return context.getRequestDispatcher(pMessage); - } - - public URL getResource(String pMessage) throws MalformedURLException { - return context.getResource(pMessage); - } - - public InputStream getResourceAsStream(String pMessage) { - return context.getResourceAsStream(pMessage); - } - - public Set getResourcePaths(String pMessage) { - return context.getResourcePaths(pMessage); - } - - public String getServerInfo() { - return context.getServerInfo(); - } - - public Servlet getServlet(String pMessage) throws ServletException { - //noinspection deprecation - return context.getServlet(pMessage); - } - - public String getServletContextName() { - return context.getServletContextName(); - } - - public Enumeration getServletNames() { - //noinspection deprecation - return context.getServletNames(); - } - - public Enumeration getServlets() { - //noinspection deprecation - return context.getServlets(); - } - - public void removeAttribute(String pMessage) { - context.removeAttribute(pMessage); - } - - public void setAttribute(String pMessage, Object pExtension) { - context.setAttribute(pMessage, pExtension); - } -} +package com.twelvemonkeys.servlet.log4j; + +import org.apache.log4j.Logger; + +import javax.servlet.RequestDispatcher; +import javax.servlet.Servlet; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import java.io.InputStream; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Enumeration; +import java.util.Set; + +/** + * Log4JContextWrapper + * + * + * @author Harald Kuhr + * @version $Id: log4j/Log4JContextWrapper.java#1 $ + */ +final class Log4JContextWrapper implements ServletContext { + // TODO: Move to sandbox + + // TODO: This solution sucks... + // How about starting to create some kind of pluggable decorator system, + // something along the lines of AOP mixins/interceptor pattern.. + // Probably using a dynamic Proxy, delegating to the mixins and or the + // wrapped object based on configuration. + // This way we could simply call ServletUtil.decorate(ServletContext):ServletContext + // And the context would be decorated with all configured mixins at once, + // requiring less boilerplate delegation code, and less layers of wrapping + // (alternatively we could decorate the Servlet/FilterConfig objects). + // See the ServletUtil.createWrapper methods for some hints.. + + + // Something like this: + public static ServletContext wrap(final ServletContext pContext, final Object[] pDelegates, final ClassLoader pLoader) { + ClassLoader cl = pLoader != null ? pLoader : Thread.currentThread().getContextClassLoader(); + + // TODO: Create a "static" mapping between methods in the ServletContext + // and the corresponding delegate + + // TODO: Resolve super-invokations, to delegate to next delegate in + // chain, and finally invoke pContext + + return (ServletContext) Proxy.newProxyInstance(cl, new Class[] {ServletContext.class}, new InvocationHandler() { + public Object invoke(Object pProxy, Method pMethod, Object[] pArgs) throws Throwable { + // TODO: Test if any of the delegates should receive, if so invoke + + // Else, invoke on original object + return pMethod.invoke(pContext, pArgs); + } + }); + } + + private final ServletContext context; + + private final Logger logger; + + Log4JContextWrapper(ServletContext pContext) { + context = pContext; + + // TODO: We want a logger per servlet, not per servlet context, right? + logger = Logger.getLogger(pContext.getServletContextName()); + + // TODO: Automatic init/config of Log4J using context parameter for log4j.xml? + // See Log4JInit.java + + // TODO: Automatic config of properties in the context wrapper? + } + + public final void log(final Exception pException, final String pMessage) { + log(pMessage, pException); + } + + // TODO: Add more logging methods to interface info/warn/error? + // TODO: Implement these mehtods in GenericFilter/GenericServlet? + + public void log(String pMessage) { + // TODO: Get logger for caller.. + // Should be possible using some stack peek hack, but that's slow... + // Find a good way... + // Maybe just pass it into the constuctor, and have one wrapper per servlet + logger.info(pMessage); + } + + public void log(String pMessage, Throwable pCause) { + // TODO: Get logger for caller.. + + logger.error(pMessage, pCause); + } + + public Object getAttribute(String pMessage) { + return context.getAttribute(pMessage); + } + + public Enumeration getAttributeNames() { + return context.getAttributeNames(); + } + + public ServletContext getContext(String pMessage) { + return context.getContext(pMessage); + } + + public String getInitParameter(String pMessage) { + return context.getInitParameter(pMessage); + } + + public Enumeration getInitParameterNames() { + return context.getInitParameterNames(); + } + + public int getMajorVersion() { + return context.getMajorVersion(); + } + + public String getMimeType(String pMessage) { + return context.getMimeType(pMessage); + } + + public int getMinorVersion() { + return context.getMinorVersion(); + } + + public RequestDispatcher getNamedDispatcher(String pMessage) { + return context.getNamedDispatcher(pMessage); + } + + public String getRealPath(String pMessage) { + return context.getRealPath(pMessage); + } + + public RequestDispatcher getRequestDispatcher(String pMessage) { + return context.getRequestDispatcher(pMessage); + } + + public URL getResource(String pMessage) throws MalformedURLException { + return context.getResource(pMessage); + } + + public InputStream getResourceAsStream(String pMessage) { + return context.getResourceAsStream(pMessage); + } + + public Set getResourcePaths(String pMessage) { + return context.getResourcePaths(pMessage); + } + + public String getServerInfo() { + return context.getServerInfo(); + } + + public Servlet getServlet(String pMessage) throws ServletException { + //noinspection deprecation + return context.getServlet(pMessage); + } + + public String getServletContextName() { + return context.getServletContextName(); + } + + public Enumeration getServletNames() { + //noinspection deprecation + return context.getServletNames(); + } + + public Enumeration getServlets() { + //noinspection deprecation + return context.getServlets(); + } + + public void removeAttribute(String pMessage) { + context.removeAttribute(pMessage); + } + + public void setAttribute(String pMessage, Object pExtension) { + context.setAttribute(pMessage, pExtension); + } +} diff --git a/servlet/src/main/java/com/twelvemonkeys/servlet/DebugServlet.java b/servlet/src/main/java/com/twelvemonkeys/servlet/DebugServlet.java index 004b1274..894dbf6c 100755 --- a/servlet/src/main/java/com/twelvemonkeys/servlet/DebugServlet.java +++ b/servlet/src/main/java/com/twelvemonkeys/servlet/DebugServlet.java @@ -1,118 +1,118 @@ -/* - * Copyright (c) 2008, Harald Kuhr - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name "TwelveMonkeys" nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package com.twelvemonkeys.servlet; - -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.ServletException; -import javax.servlet.ServletOutputStream; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.util.Enumeration; - -/** - * DebugServlet class description. - * - * @author Harald Kuhr - * @author last modified by $Author: haku $ - * @version $Id: DebugServlet.java#1 $ - */ -public class DebugServlet extends GenericServlet { - private long dateModified; - - public final void service(ServletRequest pRequest, ServletResponse pResponse) throws ServletException, IOException { - service((HttpServletRequest) pRequest, (HttpServletResponse) pResponse); - } - - public void init() throws ServletException { - super.init(); - dateModified = System.currentTimeMillis(); - } - - public void service(HttpServletRequest pRequest, HttpServletResponse pResponse) throws ServletException, IOException { - pResponse.setContentType("text/plain"); - // Include these to allow browser caching - pResponse.setDateHeader("Last-Modified", dateModified); - pResponse.setHeader("ETag", getServletName()); - - ServletOutputStream out = pResponse.getOutputStream(); - - out.println("Remote address: " + pRequest.getRemoteAddr()); - out.println("Remote host name: " + pRequest.getRemoteHost()); - out.println("Remote user: " + pRequest.getRemoteUser()); - out.println(); - - out.println("Request Method: " + pRequest.getMethod()); - out.println("Request Scheme: " + pRequest.getScheme()); - out.println("Request URI: " + pRequest.getRequestURI()); - out.println("Request URL: " + pRequest.getRequestURL().toString()); - out.println("Request PathInfo: " + pRequest.getPathInfo()); - out.println("Request ContentLength: " + pRequest.getContentLength()); - out.println(); - - out.println("Request Headers:"); - Enumeration headerNames = pRequest.getHeaderNames(); - while (headerNames.hasMoreElements()) { - String headerName = (String) headerNames.nextElement(); - Enumeration headerValues = pRequest.getHeaders(headerName); - - if (headerName != null) { - while (headerValues.hasMoreElements()) { - String value = (String) headerValues.nextElement(); - out.println(" " + headerName + ": " + value); - } - } - } - out.println(); - - out.println("Request parameters:"); - Enumeration paramNames = pRequest.getParameterNames(); - while (paramNames.hasMoreElements()) { - String name = (String) paramNames.nextElement(); - String[] values = pRequest.getParameterValues(name); - - for (String value : values) { - out.println(" " + name + ": " + value); - } - } - out.println(); - - out.println("Request attributes:"); - Enumeration attribNames = pRequest.getAttributeNames(); - while (attribNames.hasMoreElements()) { - String name = (String) attribNames.nextElement(); - Object value = pRequest.getAttribute(name); - out.println(" " + name + ": " + value); - } - - - out.flush(); - } -} +/* + * Copyright (c) 2008, Harald Kuhr + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name "TwelveMonkeys" nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.twelvemonkeys.servlet; + +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.ServletException; +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Enumeration; + +/** + * DebugServlet class description. + * + * @author Harald Kuhr + * @author last modified by $Author: haku $ + * @version $Id: DebugServlet.java#1 $ + */ +public class DebugServlet extends GenericServlet { + private long dateModified; + + public final void service(ServletRequest pRequest, ServletResponse pResponse) throws ServletException, IOException { + service((HttpServletRequest) pRequest, (HttpServletResponse) pResponse); + } + + public void init() throws ServletException { + super.init(); + dateModified = System.currentTimeMillis(); + } + + public void service(HttpServletRequest pRequest, HttpServletResponse pResponse) throws ServletException, IOException { + pResponse.setContentType("text/plain"); + // Include these to allow browser caching + pResponse.setDateHeader("Last-Modified", dateModified); + pResponse.setHeader("ETag", getServletName()); + + ServletOutputStream out = pResponse.getOutputStream(); + + out.println("Remote address: " + pRequest.getRemoteAddr()); + out.println("Remote host name: " + pRequest.getRemoteHost()); + out.println("Remote user: " + pRequest.getRemoteUser()); + out.println(); + + out.println("Request Method: " + pRequest.getMethod()); + out.println("Request Scheme: " + pRequest.getScheme()); + out.println("Request URI: " + pRequest.getRequestURI()); + out.println("Request URL: " + pRequest.getRequestURL().toString()); + out.println("Request PathInfo: " + pRequest.getPathInfo()); + out.println("Request ContentLength: " + pRequest.getContentLength()); + out.println(); + + out.println("Request Headers:"); + Enumeration headerNames = pRequest.getHeaderNames(); + while (headerNames.hasMoreElements()) { + String headerName = (String) headerNames.nextElement(); + Enumeration headerValues = pRequest.getHeaders(headerName); + + if (headerName != null) { + while (headerValues.hasMoreElements()) { + String value = (String) headerValues.nextElement(); + out.println(" " + headerName + ": " + value); + } + } + } + out.println(); + + out.println("Request parameters:"); + Enumeration paramNames = pRequest.getParameterNames(); + while (paramNames.hasMoreElements()) { + String name = (String) paramNames.nextElement(); + String[] values = pRequest.getParameterValues(name); + + for (String value : values) { + out.println(" " + name + ": " + value); + } + } + out.println(); + + out.println("Request attributes:"); + Enumeration attribNames = pRequest.getAttributeNames(); + while (attribNames.hasMoreElements()) { + String name = (String) attribNames.nextElement(); + Object value = pRequest.getAttribute(name); + out.println(" " + name + ": " + value); + } + + + out.flush(); + } +} diff --git a/servlet/src/main/java/com/twelvemonkeys/servlet/GenericFilter.java b/servlet/src/main/java/com/twelvemonkeys/servlet/GenericFilter.java index 29541b16..6397e182 100755 --- a/servlet/src/main/java/com/twelvemonkeys/servlet/GenericFilter.java +++ b/servlet/src/main/java/com/twelvemonkeys/servlet/GenericFilter.java @@ -1,382 +1,382 @@ -/* - * Copyright (c) 2008, Harald Kuhr - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name "TwelveMonkeys" nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package com.twelvemonkeys.servlet; - -import com.twelvemonkeys.lang.BeanUtil; - -import javax.servlet.*; -import java.io.IOException; -import java.io.Serializable; -import java.lang.reflect.InvocationTargetException; -import java.util.Enumeration; - -/** - * Defines a generic, protocol-independent filter. - * - * {@code GenericFilter} is inspired by {@link GenericServlet}, and - * implements the {@code Filter} and {@code FilterConfig} interfaces. - * - * {@code GenericFilter} makes writing filters easier. It provides simple - * versions of the lifecycle methods {@code init} and {@code destroy} - * and of the methods in the {@code FilterConfig} interface. - * {@code GenericFilter} also implements the {@code log} methods, - * declared in the {@code ServletContext} interface. - * - * To write a generic filter, you need only override the abstract - * {@link #doFilterImpl(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)} doFilterImpl} method. - * - * @author Harald Kuhr - * @author last modified by $Author: haku $ - * - * @version $Id: GenericFilter.java#1 $ - * - * @see Filter - * @see FilterConfig - */ -public abstract class GenericFilter implements Filter, FilterConfig, Serializable { - // TODO: Rewrite to use ServletConfigurator instead of BeanUtil - - /** - * The filter config. - */ - private transient FilterConfig filterConfig = null; - - /** - * Makes sure the filter runs once per request - * - * @see #isRunOnce - * @see #ATTRIB_RUN_ONCE_VALUE - * @see #oncePerRequest - */ - private final static String ATTRIB_RUN_ONCE_EXT = ".REQUEST_HANDLED"; - - /** - * Makes sure the filter runs once per request. - * Must be configured through init method, as the filter name is not - * available before we have a {@code FilterConfig} object. - * - * @see #isRunOnce - * @see #ATTRIB_RUN_ONCE_VALUE - * @see #oncePerRequest - */ - private String attribRunOnce = null; - - /** - * Makes sure the filter runs once per request - * - * @see #isRunOnce - * @see #ATTRIB_RUN_ONCE_EXT - * @see #oncePerRequest - */ - private static final Object ATTRIB_RUN_ONCE_VALUE = new Object(); - - /** - * Indicates if this filter should run once per request ({@code true}), - * or for each forward/include resource ({@code false}). - * - * Set this variable to true, to make sure the filter runs once per request. - * - * NOTE: As of Servlet 2.4, this field - * should always be left to it's default value ({@code false}). - *<dispatcher>REQUEST</dispatcher>- * - */ - protected boolean oncePerRequest = false; - - /** - * Does nothing. - */ - public GenericFilter() {} - - /** - * Called by the web container to indicate to a filter that it is being - * placed into service. - * - * This implementation stores the {@code FilterConfig} object it - * receives from the servlet container for later use. - * Generally, there's no reason to override this method, override the - * no-argument {@code init} instead. However, if you are - * overriding this form of the method, - * always call {@code super.init(config)}. - * - * This implementation will also set all configured key/value pairs, that - * have a matching setter method annotated with {@link InitParam}. - * - * @param pConfig the filter config - * @throws ServletException if an error occurs during init - * - * @see Filter#init(javax.servlet.FilterConfig) - * @see #init() init - * @see BeanUtil#configure(Object, java.util.Map, boolean) - */ - public void init(final FilterConfig pConfig) throws ServletException { - if (pConfig == null) { - throw new ServletConfigException("filter config == null"); - } - - // Store filter config - filterConfig = pConfig; - - // Configure this - try { - BeanUtil.configure(this, ServletUtil.asMap(pConfig), true); - } - catch (InvocationTargetException e) { - throw new ServletConfigException("Could not configure " + getFilterName(), e.getCause()); - } - - // Create run-once attribute name - attribRunOnce = pConfig.getFilterName() + ATTRIB_RUN_ONCE_EXT; - log("init (oncePerRequest=" + oncePerRequest + ", attribRunOnce=" + attribRunOnce + ")"); - init(); - } - - /** - * A convenience method which can be overridden so that there's no need to - * call {@code super.init(config)}. - * - * @see #init(FilterConfig) - * - * @throws ServletException if an error occurs during init - */ - public void init() throws ServletException {} - - /** - * The {@code doFilter} method of the Filter is called by the container - * each time a request/response pair is passed through the chain due to a - * client request for a resource at the end of the chain. - * - * Subclasses should not override this method, but rather the - * abstract {@link #doFilterImpl(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)} doFilterImpl} method. - * - * @param pRequest the servlet request - * @param pResponse the servlet response - * @param pFilterChain the filter chain - * - * @throws IOException - * @throws ServletException - * - * @see Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) Filter.doFilter - * @see #doFilterImpl(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) doFilterImpl - */ - public final void doFilter(final ServletRequest pRequest, final ServletResponse pResponse, final FilterChain pFilterChain) throws IOException, ServletException { - // If request filter and already run, continue chain and return fast - if (oncePerRequest && isRunOnce(pRequest)) { - pFilterChain.doFilter(pRequest, pResponse); - return; - } - - // Do real filter - doFilterImpl(pRequest, pResponse, pFilterChain); - } - - /** - * If request is filtered, returns true, otherwise marks request as filtered - * and returns false. - * A return value of false, indicates that the filter has not yet run. - * A return value of true, indicates that the filter has run for this - * request, and processing should not continue. - * - * Note that the method will mark the request as filtered on first - * invocation. - * - * @see #ATTRIB_RUN_ONCE_EXT - * @see #ATTRIB_RUN_ONCE_VALUE - * - * @param pRequest the servlet request - * @return {@code true} if the request is already filtered, otherwise - * {@code false}. - */ - private boolean isRunOnce(final ServletRequest pRequest) { - // If request already filtered, return true (skip) - if (pRequest.getAttribute(attribRunOnce) == ATTRIB_RUN_ONCE_VALUE) { - return true; - } - - // Set attribute and return false (continue) - pRequest.setAttribute(attribRunOnce, ATTRIB_RUN_ONCE_VALUE); - - return false; - } - - /** - * Invoked once, or each time a request/response pair is passed through the - * chain, depending on the {@link #oncePerRequest} member variable. - * - * @param pRequest the servlet request - * @param pResponse the servlet response - * @param pChain the filter chain - * - * @throws IOException if an I/O error occurs - * @throws ServletException if an exception occurs during the filter process - * - * @see #oncePerRequest - * @see #doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) doFilter - * @see Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) Filter.doFilter - */ - protected abstract void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain) - throws IOException, ServletException; - - /** - * Called by the web container to indicate to a filter that it is being - * taken out of service. - * - * @see Filter#destroy - */ - public void destroy() { - log("destroy"); - filterConfig = null; - } - - /** - * Returns the filter-name of this filter as defined in the deployment - * descriptor. - * - * @return the filter-name - * @see FilterConfig#getFilterName - */ - public String getFilterName() { - return filterConfig.getFilterName(); - } - - /** - * Returns a reference to the {@link ServletContext} in which the caller is - * executing. - * - * @return the {@code ServletContext} object, used by the caller to - * interact with its servlet container - * @see FilterConfig#getServletContext - * @see ServletContext - */ - public ServletContext getServletContext() { - return filterConfig.getServletContext(); - } - - /** - * Returns a {@code String} containing the value of the named - * initialization parameter, or null if the parameter does not exist. - * - * @param pKey a {@code String} specifying the name of the - * initialization parameter - * @return a {@code String} containing the value of the initialization - * parameter - */ - public String getInitParameter(final String pKey) { - return filterConfig.getInitParameter(pKey); - } - - /** - * Returns the names of the servlet's initialization parameters as an - * {@code Enumeration} of {@code String} objects, or an empty - * {@code Enumeration} if the servlet has no initialization parameters. - * - * @return an {@code Enumeration} of {@code String} objects - * containing the mNames of the servlet's initialization parameters - */ - public Enumeration getInitParameterNames() { - return filterConfig.getInitParameterNames(); - } - - /** - * Writes the specified message to a servlet log file, prepended by the - * filter's name. - * - * @param pMessage the log message - * @see ServletContext#log(String) - */ - protected void log(final String pMessage) { - getServletContext().log(getFilterName() + ": " + pMessage); - } - - /** - * Writes an explanatory message and a stack trace for a given - * {@code Throwable} to the servlet log file, prepended by the - * filter's name. - * - * @param pMessage the log message - * @param pThrowable the exception - * @see ServletContext#log(String,Throwable) - */ - protected void log(final String pMessage, final Throwable pThrowable) { - getServletContext().log(getFilterName() + ": " + pMessage, pThrowable); - } - - /** - * Initializes the filter. - * - * @param pFilterConfig the filter config - * @see #init init - * - * @deprecated For compatibility only, use {@link #init init} instead. - */ - @SuppressWarnings("UnusedDeclaration") - public void setFilterConfig(final FilterConfig pFilterConfig) { - try { - init(pFilterConfig); - } - catch (ServletException e) { - log("Error in init(), see stack trace for details.", e); - } - } - - /** - * Gets the {@code FilterConfig} for this filter. - * - * @return the {@code FilterConfig} for this filter - * @see FilterConfig - */ - public FilterConfig getFilterConfig() { - return filterConfig; - } - - /** - * Specifies if this filter should run once per request ({@code true}), - * or for each forward/include resource ({@code false}). - * Called automatically from the {@code init}-method, with settings - * from web.xml. - * - * @param pOncePerRequest {@code true} if the filter should run only - * once per request - * @see #oncePerRequest - */ - @InitParam(name = "once-per-request") - public void setOncePerRequest(final boolean pOncePerRequest) { - oncePerRequest = pOncePerRequest; - } -} +/* + * Copyright (c) 2008, Harald Kuhr + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name "TwelveMonkeys" nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.twelvemonkeys.servlet; + +import com.twelvemonkeys.lang.BeanUtil; + +import javax.servlet.*; +import java.io.IOException; +import java.io.Serializable; +import java.lang.reflect.InvocationTargetException; +import java.util.Enumeration; + +/** + * Defines a generic, protocol-independent filter. + * + * {@code GenericFilter} is inspired by {@link GenericServlet}, and + * implements the {@code Filter} and {@code FilterConfig} interfaces. + * + * {@code GenericFilter} makes writing filters easier. It provides simple + * versions of the lifecycle methods {@code init} and {@code destroy} + * and of the methods in the {@code FilterConfig} interface. + * {@code GenericFilter} also implements the {@code log} methods, + * declared in the {@code ServletContext} interface. + * + * To write a generic filter, you need only override the abstract + * {@link #doFilterImpl(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)} doFilterImpl} method. + * + * @author Harald Kuhr + * @author last modified by $Author: haku $ + * + * @version $Id: GenericFilter.java#1 $ + * + * @see Filter + * @see FilterConfig + */ +public abstract class GenericFilter implements Filter, FilterConfig, Serializable { + // TODO: Rewrite to use ServletConfigurator instead of BeanUtil + + /** + * The filter config. + */ + private transient FilterConfig filterConfig = null; + + /** + * Makes sure the filter runs once per request + * + * @see #isRunOnce + * @see #ATTRIB_RUN_ONCE_VALUE + * @see #oncePerRequest + */ + private final static String ATTRIB_RUN_ONCE_EXT = ".REQUEST_HANDLED"; + + /** + * Makes sure the filter runs once per request. + * Must be configured through init method, as the filter name is not + * available before we have a {@code FilterConfig} object. + * + * @see #isRunOnce + * @see #ATTRIB_RUN_ONCE_VALUE + * @see #oncePerRequest + */ + private String attribRunOnce = null; + + /** + * Makes sure the filter runs once per request + * + * @see #isRunOnce + * @see #ATTRIB_RUN_ONCE_EXT + * @see #oncePerRequest + */ + private static final Object ATTRIB_RUN_ONCE_VALUE = new Object(); + + /** + * Indicates if this filter should run once per request ({@code true}), + * or for each forward/include resource ({@code false}). + * + * Set this variable to true, to make sure the filter runs once per request. + * + * NOTE: As of Servlet 2.4, this field + * should always be left to it's default value ({@code false}). + *
<dispatcher>REQUEST</dispatcher>+ * + */ + protected boolean oncePerRequest = false; + + /** + * Does nothing. + */ + public GenericFilter() {} + + /** + * Called by the web container to indicate to a filter that it is being + * placed into service. + * + * This implementation stores the {@code FilterConfig} object it + * receives from the servlet container for later use. + * Generally, there's no reason to override this method, override the + * no-argument {@code init} instead. However, if you are + * overriding this form of the method, + * always call {@code super.init(config)}. + * + * This implementation will also set all configured key/value pairs, that + * have a matching setter method annotated with {@link InitParam}. + * + * @param pConfig the filter config + * @throws ServletException if an error occurs during init + * + * @see Filter#init(javax.servlet.FilterConfig) + * @see #init() init + * @see BeanUtil#configure(Object, java.util.Map, boolean) + */ + public void init(final FilterConfig pConfig) throws ServletException { + if (pConfig == null) { + throw new ServletConfigException("filter config == null"); + } + + // Store filter config + filterConfig = pConfig; + + // Configure this + try { + BeanUtil.configure(this, ServletUtil.asMap(pConfig), true); + } + catch (InvocationTargetException e) { + throw new ServletConfigException("Could not configure " + getFilterName(), e.getCause()); + } + + // Create run-once attribute name + attribRunOnce = pConfig.getFilterName() + ATTRIB_RUN_ONCE_EXT; + log("init (oncePerRequest=" + oncePerRequest + ", attribRunOnce=" + attribRunOnce + ")"); + init(); + } + + /** + * A convenience method which can be overridden so that there's no need to + * call {@code super.init(config)}. + * + * @see #init(FilterConfig) + * + * @throws ServletException if an error occurs during init + */ + public void init() throws ServletException {} + + /** + * The {@code doFilter} method of the Filter is called by the container + * each time a request/response pair is passed through the chain due to a + * client request for a resource at the end of the chain. + * + * Subclasses should not override this method, but rather the + * abstract {@link #doFilterImpl(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)} doFilterImpl} method. + * + * @param pRequest the servlet request + * @param pResponse the servlet response + * @param pFilterChain the filter chain + * + * @throws IOException + * @throws ServletException + * + * @see Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) Filter.doFilter + * @see #doFilterImpl(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) doFilterImpl + */ + public final void doFilter(final ServletRequest pRequest, final ServletResponse pResponse, final FilterChain pFilterChain) throws IOException, ServletException { + // If request filter and already run, continue chain and return fast + if (oncePerRequest && isRunOnce(pRequest)) { + pFilterChain.doFilter(pRequest, pResponse); + return; + } + + // Do real filter + doFilterImpl(pRequest, pResponse, pFilterChain); + } + + /** + * If request is filtered, returns true, otherwise marks request as filtered + * and returns false. + * A return value of false, indicates that the filter has not yet run. + * A return value of true, indicates that the filter has run for this + * request, and processing should not continue. + * + * Note that the method will mark the request as filtered on first + * invocation. + * + * @see #ATTRIB_RUN_ONCE_EXT + * @see #ATTRIB_RUN_ONCE_VALUE + * + * @param pRequest the servlet request + * @return {@code true} if the request is already filtered, otherwise + * {@code false}. + */ + private boolean isRunOnce(final ServletRequest pRequest) { + // If request already filtered, return true (skip) + if (pRequest.getAttribute(attribRunOnce) == ATTRIB_RUN_ONCE_VALUE) { + return true; + } + + // Set attribute and return false (continue) + pRequest.setAttribute(attribRunOnce, ATTRIB_RUN_ONCE_VALUE); + + return false; + } + + /** + * Invoked once, or each time a request/response pair is passed through the + * chain, depending on the {@link #oncePerRequest} member variable. + * + * @param pRequest the servlet request + * @param pResponse the servlet response + * @param pChain the filter chain + * + * @throws IOException if an I/O error occurs + * @throws ServletException if an exception occurs during the filter process + * + * @see #oncePerRequest + * @see #doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) doFilter + * @see Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) Filter.doFilter + */ + protected abstract void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain) + throws IOException, ServletException; + + /** + * Called by the web container to indicate to a filter that it is being + * taken out of service. + * + * @see Filter#destroy + */ + public void destroy() { + log("destroy"); + filterConfig = null; + } + + /** + * Returns the filter-name of this filter as defined in the deployment + * descriptor. + * + * @return the filter-name + * @see FilterConfig#getFilterName + */ + public String getFilterName() { + return filterConfig.getFilterName(); + } + + /** + * Returns a reference to the {@link ServletContext} in which the caller is + * executing. + * + * @return the {@code ServletContext} object, used by the caller to + * interact with its servlet container + * @see FilterConfig#getServletContext + * @see ServletContext + */ + public ServletContext getServletContext() { + return filterConfig.getServletContext(); + } + + /** + * Returns a {@code String} containing the value of the named + * initialization parameter, or null if the parameter does not exist. + * + * @param pKey a {@code String} specifying the name of the + * initialization parameter + * @return a {@code String} containing the value of the initialization + * parameter + */ + public String getInitParameter(final String pKey) { + return filterConfig.getInitParameter(pKey); + } + + /** + * Returns the names of the servlet's initialization parameters as an + * {@code Enumeration} of {@code String} objects, or an empty + * {@code Enumeration} if the servlet has no initialization parameters. + * + * @return an {@code Enumeration} of {@code String} objects + * containing the mNames of the servlet's initialization parameters + */ + public Enumeration getInitParameterNames() { + return filterConfig.getInitParameterNames(); + } + + /** + * Writes the specified message to a servlet log file, prepended by the + * filter's name. + * + * @param pMessage the log message + * @see ServletContext#log(String) + */ + protected void log(final String pMessage) { + getServletContext().log(getFilterName() + ": " + pMessage); + } + + /** + * Writes an explanatory message and a stack trace for a given + * {@code Throwable} to the servlet log file, prepended by the + * filter's name. + * + * @param pMessage the log message + * @param pThrowable the exception + * @see ServletContext#log(String,Throwable) + */ + protected void log(final String pMessage, final Throwable pThrowable) { + getServletContext().log(getFilterName() + ": " + pMessage, pThrowable); + } + + /** + * Initializes the filter. + * + * @param pFilterConfig the filter config + * @see #init init + * + * @deprecated For compatibility only, use {@link #init init} instead. + */ + @SuppressWarnings("UnusedDeclaration") + public void setFilterConfig(final FilterConfig pFilterConfig) { + try { + init(pFilterConfig); + } + catch (ServletException e) { + log("Error in init(), see stack trace for details.", e); + } + } + + /** + * Gets the {@code FilterConfig} for this filter. + * + * @return the {@code FilterConfig} for this filter + * @see FilterConfig + */ + public FilterConfig getFilterConfig() { + return filterConfig; + } + + /** + * Specifies if this filter should run once per request ({@code true}), + * or for each forward/include resource ({@code false}). + * Called automatically from the {@code init}-method, with settings + * from web.xml. + * + * @param pOncePerRequest {@code true} if the filter should run only + * once per request + * @see #oncePerRequest + */ + @InitParam(name = "once-per-request") + public void setOncePerRequest(final boolean pOncePerRequest) { + oncePerRequest = pOncePerRequest; + } +} diff --git a/servlet/src/main/java/com/twelvemonkeys/servlet/GenericServlet.java b/servlet/src/main/java/com/twelvemonkeys/servlet/GenericServlet.java index 56641a05..7f2198ca 100755 --- a/servlet/src/main/java/com/twelvemonkeys/servlet/GenericServlet.java +++ b/servlet/src/main/java/com/twelvemonkeys/servlet/GenericServlet.java @@ -1,88 +1,88 @@ -/* - * Copyright (c) 2008, Harald Kuhr - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name "TwelveMonkeys" nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package com.twelvemonkeys.servlet; - -import com.twelvemonkeys.lang.BeanUtil; - -import javax.servlet.ServletConfig; -import javax.servlet.ServletException; -import java.lang.reflect.InvocationTargetException; - -/** - * Defines a generic, protocol-independent servlet. - * - * {@code GenericServlet} has an auto-init system, that automatically invokes - * the method matching the signature {@code void setX(<Type>)}, - * for every init-parameter {@code x}. Both camelCase and lisp-style parameter - * naming is supported, lisp-style names will be converted to camelCase. - * Parameter values are automatically converted from string representation to - * most basic types, if necessary. - * - * @author Harald Kuhr - * @author last modified by $Author: haku $ - * - * @version $Id: GenericServlet.java#1 $ - */ -public abstract class GenericServlet extends javax.servlet.GenericServlet { - // TODO: Rewrite to use ServletConfigurator instead of BeanUtil - - /** - * Called by the web container to indicate to a servlet that it is being - * placed into service. - * - * This implementation stores the {@code ServletConfig} object it - * receives from the servlet container for later use. When overriding this - * form of the method, call {@code super.init(config)}. - * - * This implementation will also set all configured key/value pairs, that - * have a matching setter method annotated with {@link InitParam}. - * - * @param pConfig the servlet config - * @throws ServletException - * - * @see javax.servlet.GenericServlet#init - * @see #init() init - * @see BeanUtil#configure(Object, java.util.Map, boolean) - */ - @Override - public void init(final ServletConfig pConfig) throws ServletException { - if (pConfig == null) { - throw new ServletConfigException("servlet config == null"); - } - - try { - BeanUtil.configure(this, ServletUtil.asMap(pConfig), true); - } - catch (InvocationTargetException e) { - throw new ServletConfigException("Could not configure " + getServletName(), e.getCause()); - } - - super.init(pConfig); - } -} +/* + * Copyright (c) 2008, Harald Kuhr + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name "TwelveMonkeys" nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.twelvemonkeys.servlet; + +import com.twelvemonkeys.lang.BeanUtil; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; +import java.lang.reflect.InvocationTargetException; + +/** + * Defines a generic, protocol-independent servlet. + * + * {@code GenericServlet} has an auto-init system, that automatically invokes + * the method matching the signature {@code void setX(<Type>)}, + * for every init-parameter {@code x}. Both camelCase and lisp-style parameter + * naming is supported, lisp-style names will be converted to camelCase. + * Parameter values are automatically converted from string representation to + * most basic types, if necessary. + * + * @author Harald Kuhr + * @author last modified by $Author: haku $ + * + * @version $Id: GenericServlet.java#1 $ + */ +public abstract class GenericServlet extends javax.servlet.GenericServlet { + // TODO: Rewrite to use ServletConfigurator instead of BeanUtil + + /** + * Called by the web container to indicate to a servlet that it is being + * placed into service. + * + * This implementation stores the {@code ServletConfig} object it + * receives from the servlet container for later use. When overriding this + * form of the method, call {@code super.init(config)}. + * + * This implementation will also set all configured key/value pairs, that + * have a matching setter method annotated with {@link InitParam}. + * + * @param pConfig the servlet config + * @throws ServletException + * + * @see javax.servlet.GenericServlet#init + * @see #init() init + * @see BeanUtil#configure(Object, java.util.Map, boolean) + */ + @Override + public void init(final ServletConfig pConfig) throws ServletException { + if (pConfig == null) { + throw new ServletConfigException("servlet config == null"); + } + + try { + BeanUtil.configure(this, ServletUtil.asMap(pConfig), true); + } + catch (InvocationTargetException e) { + throw new ServletConfigException("Could not configure " + getServletName(), e.getCause()); + } + + super.init(pConfig); + } +} diff --git a/servlet/src/main/java/com/twelvemonkeys/servlet/HttpServlet.java b/servlet/src/main/java/com/twelvemonkeys/servlet/HttpServlet.java index f1ab6060..cff681bf 100755 --- a/servlet/src/main/java/com/twelvemonkeys/servlet/HttpServlet.java +++ b/servlet/src/main/java/com/twelvemonkeys/servlet/HttpServlet.java @@ -1,88 +1,88 @@ -/* - * Copyright (c) 2008, Harald Kuhr - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name "TwelveMonkeys" nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package com.twelvemonkeys.servlet; - -import com.twelvemonkeys.lang.BeanUtil; - -import javax.servlet.ServletConfig; -import javax.servlet.ServletException; -import java.lang.reflect.InvocationTargetException; - -/** - * Defines a generic, HTTP specific servlet. - * - * {@code HttpServlet} has an auto-init system, that automatically invokes - * the method matching the signature {@code void setX(<Type>)}, - * for every init-parameter {@code x}. Both camelCase and lisp-style parameter - * naming is supported, lisp-style names will be converted to camelCase. - * Parameter values are automatically converted from string representation to - * most basic types, if necessary. - * - * @author Harald Kuhr - * @author last modified by $Author: haku $ - * - * @version $Id: HttpServlet.java#1 $ - */ -public abstract class HttpServlet extends javax.servlet.http.HttpServlet { - // TODO: Rewrite to use ServletConfigurator instead of BeanUtil - - /** - * Called by the web container to indicate to a servlet that it is being - * placed into service. - * - * This implementation stores the {@code ServletConfig} object it - * receives from the servlet container for later use. When overriding this - * form of the method, call {@code super.init(config)}. - * - * This implementation will also set all configured key/value pairs, that - * have a matching setter method annotated with {@link InitParam}. - * - * @param pConfig the servlet config - * @throws ServletException if an error occurred during init - * - * @see javax.servlet.GenericServlet#init - * @see #init() init - * @see BeanUtil#configure(Object, java.util.Map, boolean) - */ - @Override - public void init(ServletConfig pConfig) throws ServletException { - if (pConfig == null) { - throw new ServletConfigException("servlet config == null"); - } - - try { - BeanUtil.configure(this, ServletUtil.asMap(pConfig), true); - } - catch (InvocationTargetException e) { - throw new ServletConfigException("Could not configure " + getServletName(), e.getCause()); - } - - super.init(pConfig); - } -} +/* + * Copyright (c) 2008, Harald Kuhr + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name "TwelveMonkeys" nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.twelvemonkeys.servlet; + +import com.twelvemonkeys.lang.BeanUtil; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; +import java.lang.reflect.InvocationTargetException; + +/** + * Defines a generic, HTTP specific servlet. + * + * {@code HttpServlet} has an auto-init system, that automatically invokes + * the method matching the signature {@code void setX(<Type>)}, + * for every init-parameter {@code x}. Both camelCase and lisp-style parameter + * naming is supported, lisp-style names will be converted to camelCase. + * Parameter values are automatically converted from string representation to + * most basic types, if necessary. + * + * @author Harald Kuhr + * @author last modified by $Author: haku $ + * + * @version $Id: HttpServlet.java#1 $ + */ +public abstract class HttpServlet extends javax.servlet.http.HttpServlet { + // TODO: Rewrite to use ServletConfigurator instead of BeanUtil + + /** + * Called by the web container to indicate to a servlet that it is being + * placed into service. + * + * This implementation stores the {@code ServletConfig} object it + * receives from the servlet container for later use. When overriding this + * form of the method, call {@code super.init(config)}. + * + * This implementation will also set all configured key/value pairs, that + * have a matching setter method annotated with {@link InitParam}. + * + * @param pConfig the servlet config + * @throws ServletException if an error occurred during init + * + * @see javax.servlet.GenericServlet#init + * @see #init() init + * @see BeanUtil#configure(Object, java.util.Map, boolean) + */ + @Override + public void init(ServletConfig pConfig) throws ServletException { + if (pConfig == null) { + throw new ServletConfigException("servlet config == null"); + } + + try { + BeanUtil.configure(this, ServletUtil.asMap(pConfig), true); + } + catch (InvocationTargetException e) { + throw new ServletConfigException("Could not configure " + getServletName(), e.getCause()); + } + + super.init(pConfig); + } +} diff --git a/servlet/src/main/java/com/twelvemonkeys/servlet/OutputStreamAdapter.java b/servlet/src/main/java/com/twelvemonkeys/servlet/OutputStreamAdapter.java index 35c9ca48..d3316f08 100755 --- a/servlet/src/main/java/com/twelvemonkeys/servlet/OutputStreamAdapter.java +++ b/servlet/src/main/java/com/twelvemonkeys/servlet/OutputStreamAdapter.java @@ -1,120 +1,120 @@ -/* - * Copyright (c) 2008, Harald Kuhr - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name "TwelveMonkeys" nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package com.twelvemonkeys.servlet; - -import com.twelvemonkeys.lang.Validate; - -import javax.servlet.ServletOutputStream; -import java.io.IOException; -import java.io.OutputStream; - -/** - * A {@code ServletOutputStream} implementation backed by a - * {@link java.io.OutputStream}. For filters that need to buffer the - * response and do post filtering, it may be used like this:
- * ByteArrayOutputStream buffer = new ByteArraOutputStream(); - * ServletOutputStream adapter = new OutputStreamAdapter(buffer); - *- * - * As a {@code ServletOutputStream} is itself an {@code OutputStream}, this - * class may also be used as a superclass for wrappers of other - * {@code ServletOutputStream}s, like this:
- * class FilterServletOutputStream extends OutputStreamAdapter { - * public FilterServletOutputStream(ServletOutputStream out) { - * super(out); - * } - * - * public void write(int abyte) { - * // do filtering... - * super.write(...); - * } - * } - * - * ... - * - * ServletOutputStream original = response.getOutputStream(); - * ServletOutputStream wrapper = new FilterServletOutputStream(original); - *- * @author Harald Kuhr - * @author $Author: haku $ - * @version $Id: OutputStreamAdapter.java#1 $ - * - */ -public class OutputStreamAdapter extends ServletOutputStream { - - /** The wrapped {@code OutputStream}. */ - protected final OutputStream out; - - /** - * Creates an {@code OutputStreamAdapter}. - * - * @param pOut the wrapped {@code OutputStream} - * - * @throws IllegalArgumentException if {@code pOut} is {@code null}. - */ - public OutputStreamAdapter(final OutputStream pOut) { - Validate.notNull(pOut, "out"); - out = pOut; - } - - /** - * Returns the wrapped {@code OutputStream}. - * - * @return the wrapped {@code OutputStream}. - */ - public OutputStream getOutputStream() { - return out; - } - - @Override - public String toString() { - return "ServletOutputStream adapted from " + out.toString(); - } - - /** - * Writes a byte to the underlying stream. - * - * @param pByte the byte to write. - * - * @throws IOException if an error occurs during writing - */ - public void write(final int pByte) throws IOException { - out.write(pByte); - } - - // Overide for efficiency - public void write(final byte pBytes[]) throws IOException { - out.write(pBytes); - } - - // Overide for efficiency - public void write(final byte pBytes[], final int pOff, final int pLen) throws IOException { - out.write(pBytes, pOff, pLen); - } -} +/* + * Copyright (c) 2008, Harald Kuhr + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name "TwelveMonkeys" nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.twelvemonkeys.servlet; + +import com.twelvemonkeys.lang.Validate; + +import javax.servlet.ServletOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +/** + * A {@code ServletOutputStream} implementation backed by a + * {@link java.io.OutputStream}. For filters that need to buffer the + * response and do post filtering, it may be used like this:
+ * ByteArrayOutputStream buffer = new ByteArraOutputStream(); + * ServletOutputStream adapter = new OutputStreamAdapter(buffer); + *+ * + * As a {@code ServletOutputStream} is itself an {@code OutputStream}, this + * class may also be used as a superclass for wrappers of other + * {@code ServletOutputStream}s, like this:
+ * class FilterServletOutputStream extends OutputStreamAdapter { + * public FilterServletOutputStream(ServletOutputStream out) { + * super(out); + * } + * + * public void write(int abyte) { + * // do filtering... + * super.write(...); + * } + * } + * + * ... + * + * ServletOutputStream original = response.getOutputStream(); + * ServletOutputStream wrapper = new FilterServletOutputStream(original); + *+ * @author Harald Kuhr + * @author $Author: haku $ + * @version $Id: OutputStreamAdapter.java#1 $ + * + */ +public class OutputStreamAdapter extends ServletOutputStream { + + /** The wrapped {@code OutputStream}. */ + protected final OutputStream out; + + /** + * Creates an {@code OutputStreamAdapter}. + * + * @param pOut the wrapped {@code OutputStream} + * + * @throws IllegalArgumentException if {@code pOut} is {@code null}. + */ + public OutputStreamAdapter(final OutputStream pOut) { + Validate.notNull(pOut, "out"); + out = pOut; + } + + /** + * Returns the wrapped {@code OutputStream}. + * + * @return the wrapped {@code OutputStream}. + */ + public OutputStream getOutputStream() { + return out; + } + + @Override + public String toString() { + return "ServletOutputStream adapted from " + out.toString(); + } + + /** + * Writes a byte to the underlying stream. + * + * @param pByte the byte to write. + * + * @throws IOException if an error occurs during writing + */ + public void write(final int pByte) throws IOException { + out.write(pByte); + } + + // Overide for efficiency + public void write(final byte pBytes[]) throws IOException { + out.write(pBytes); + } + + // Overide for efficiency + public void write(final byte pBytes[], final int pOff, final int pLen) throws IOException { + out.write(pBytes, pOff, pLen); + } +} diff --git a/servlet/src/main/java/com/twelvemonkeys/servlet/ProxyServlet.java b/servlet/src/main/java/com/twelvemonkeys/servlet/ProxyServlet.java index f66df68d..ae16403f 100755 --- a/servlet/src/main/java/com/twelvemonkeys/servlet/ProxyServlet.java +++ b/servlet/src/main/java/com/twelvemonkeys/servlet/ProxyServlet.java @@ -1,435 +1,435 @@ -/* - * Copyright (c) 2008, Harald Kuhr - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name "TwelveMonkeys" nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package com.twelvemonkeys.servlet; - -import com.twelvemonkeys.io.FileUtil; -import com.twelvemonkeys.lang.StringUtil; - -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.ConnectException; -import java.net.HttpURLConnection; -import java.net.URL; -import java.util.Enumeration; - -/** - * A simple proxy servlet implementation. Supports HTTP and HTTPS. - * - * Note: The servlet is not a true HTTP proxy as described in - * RFC 2616, - * instead it passes on all incoming HTTP requests to the configured remote - * server. - * Useful for bypassing firewalls or to avoid exposing internal network - * infrastructure to external clients. - * - * At the moment, no caching of content is implemented. - * - * 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. - * If the configured remote server is down, or unreachable, the servlet will - * respond by sending a {@code 502 Bad Gateway} response to the client. - * Otherwise, the response from the remote server will be tunneled unmodified - * to the client. - * - * @author Harald Kuhr - * @author last modified by $Author: haku $ - * - * @version $Id: ProxyServlet.java#1 $ - */ -public class ProxyServlet extends GenericServlet { - - /** Remote server host name or IP address */ - protected String remoteServer = null; - /** Remote server port */ - protected int remotePort = 80; - /** Remote server "mount" path */ - protected String remotePath = ""; - - private static final String HTTP_REQUEST_HEADER_HOST = "host"; - private static final String HTTP_RESPONSE_HEADER_SERVER = "server"; - 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 - * name or IP address. No default. - * - * @param pRemoteServer - */ - public void setRemoteServer(String pRemoteServer) { - remoteServer = pRemoteServer; - } - - /** - * Called by {@code init} to set the remote port. Must be a number. - * Default is {@code 80}. - * - * @param pRemotePort - */ - public void setRemotePort(String pRemotePort) { - try { - remotePort = Integer.parseInt(pRemotePort); - } - catch (NumberFormatException e) { - log("RemotePort must be a number!", e); - } - } - - /** - * 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. - * Default is {@code ""}. - * - * @param pRemotePath - */ - public void setRemotePath(String pRemotePath) { - if (StringUtil.isEmpty(pRemotePath)) { - pRemotePath = ""; - } - else if (pRemotePath.charAt(0) != '/') { - pRemotePath = "/" + pRemotePath; - } - - remotePath = pRemotePath; - } - - /** - * Override {@code service} to use HTTP specifics. - * - * @param pRequest - * @param pResponse - * - * @throws ServletException - * @throws IOException - * - * @see #service(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) - */ - public final void service(ServletRequest pRequest, ServletResponse pResponse) throws ServletException, IOException { - service((HttpServletRequest) pRequest, (HttpServletResponse) pResponse); - } - - /** - * Services a single request. - * Supports HTTP and HTTPS. - * - * @param pRequest - * @param pResponse - * - * @throws ServletException - * @throws IOException - * - * @see ProxyServlet Class descrition - */ - protected void service(HttpServletRequest pRequest, HttpServletResponse pResponse) throws ServletException, IOException { - // Sanity check configuration - if (remoteServer == null) { - log(MESSAGE_REMOTE_SERVER_NOT_CONFIGURED); - pResponse.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, - MESSAGE_REMOTE_SERVER_NOT_CONFIGURED); - return; - } - - HttpURLConnection remoteConnection = null; - try { - // Recreate request URI for remote request - String requestURI = createRemoteRequestURI(pRequest); - URL remoteURL = new URL(pRequest.getScheme(), remoteServer, remotePort, requestURI); - - // Get connection, with method from original request - // NOTE: The actual connection is not done before we ask for streams... - // NOTE: The HttpURLConnection is supposed to handle multiple - // requests to the same server internally - String method = pRequest.getMethod(); - remoteConnection = (HttpURLConnection) remoteURL.openConnection(); - remoteConnection.setRequestMethod(method); - - // Copy header fields - copyHeadersFromClient(pRequest, remoteConnection); - - // Do proxy specifc stuff? - // 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 - // persistent connection with an HTTP/1.0 client" - - // Copy message body from client to remote server - copyBodyFromClient(pRequest, remoteConnection); - - // Set response status code from remote server to client - int responseCode = remoteConnection.getResponseCode(); - pResponse.setStatus(responseCode); - //System.out.println("Response is: " + responseCode + " " + remoteConnection.getResponseMessage()); - - // Copy header fields back - copyHeadersToClient(remoteConnection, pResponse); - - // More proxy specific stuff? - - // Copy message body from remote server to client - copyBodyToClient(remoteConnection, pResponse); - } - catch (ConnectException e) { - // In case we could not connecto to the remote server - log("Could not connect to remote server.", e); - pResponse.sendError(HttpServletResponse.SC_BAD_GATEWAY, e.getMessage()); - } - finally { - // Disconnect from server - // TODO: Should we actually do this? - if (remoteConnection != null) { - remoteConnection.disconnect(); - } - } - } - - /** - * Copies the message body from the remote server to the client (outgoing - * {@code HttpServletResponse}). - * - * @param pRemoteConnection - * @param pResponse - * - * @throws IOException - */ - private void copyBodyToClient(HttpURLConnection pRemoteConnection, HttpServletResponse pResponse) throws IOException { - InputStream fromRemote = null; - OutputStream toClient = null; - - try { - // Get either input or error stream - try { - fromRemote = pRemoteConnection.getInputStream(); - } - catch (IOException e) { - // If exception, use errorStream instead - fromRemote = pRemoteConnection.getErrorStream(); - } - - // I guess the stream might be null if there is no response other - // than headers (Continue, No Content, etc). - if (fromRemote != null) { - toClient = pResponse.getOutputStream(); - FileUtil.copy(fromRemote, toClient); - } - } - finally { - if (fromRemote != null) { - try { - fromRemote.close(); - } - catch (IOException e) { - log("Stream from remote could not be closed.", e); - } - } - if (toClient != null) { - try { - toClient.close(); - } - catch (IOException e) { - log("Stream to client could not be closed.", e); - } - } - } - } - - /** - * Copies the message body from the client (incomming - * {@code HttpServletRequest}) to the remote server if the request method - * is {@code POST} or PUT. - * Otherwise this method does nothing. - * - * @param pRequest - * @param pRemoteConnection - * - * @throws java.io.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 (!("POST".equals(pRequest.getMethod()) || "PUT".equals(pRequest.getMethod()))) { - return; - } - - // NOTE: Setting doOutput to true, will make it a POST request (why?)... - pRemoteConnection.setDoOutput(true); - - // Get streams and do the copying - InputStream fromClient = null; - OutputStream toRemote = null; - try { - fromClient = pRequest.getInputStream(); - toRemote = pRemoteConnection.getOutputStream(); - FileUtil.copy(fromClient, toRemote); - } - finally { - if (fromClient != null) { - try { - fromClient.close(); - } - catch (IOException e) { - log("Stream from client could not be closed.", e); - } - } - if (toRemote != null) { - try { - toRemote.close(); - } - catch (IOException e) { - log("Stream to remote could not be closed.", e); - } - } - } - } - - /** - * Creates the remote request URI based on the incoming request. - * The URI will include any query strings etc. - * - * @param pRequest - * - * @return a {@code String} representing the remote request URI - */ - private String createRemoteRequestURI(HttpServletRequest pRequest) { - StringBuilder requestURI = new StringBuilder(remotePath); - requestURI.append(pRequest.getPathInfo()); - - if (!StringUtil.isEmpty(pRequest.getQueryString())) { - requestURI.append("?"); - requestURI.append(pRequest.getQueryString()); - } - - return requestURI.toString(); - } - - /** - * Copies headers from the remote connection back to the client - * (the outgoing HttpServletResponse). All headers except the "Server" - * header are copied. - * - * @param pRemoteConnection - * @param pResponse - */ - private void copyHeadersToClient(HttpURLConnection pRemoteConnection, HttpServletResponse pResponse) { - // NOTE: There is no getHeaderFieldCount method or similar... - // Also, the getHeaderFields() method was introduced in J2SE 1.4, and - // we want to be 1.2 compatible. - // So, just try to loop until there are no more headers. - int i = 0; - while (true) { - String key = pRemoteConnection.getHeaderFieldKey(i); - // NOTE: getHeaderField(String) returns only the last value - String value = pRemoteConnection.getHeaderField(i); - - // If the key is not null, life is simple, and Sun is shining - // However, the default implementations includes the HTTP response - // code ("HTTP/1.1 200 Ok" or similar) as a header field with - // key "null" (why..?)... - // In addition, we want to skip the original "Server" header - if (key != null && !HTTP_RESPONSE_HEADER_SERVER.equalsIgnoreCase(key)) { - //System.out.println("client <<<-- remote: " + key + ": " + value); - pResponse.setHeader(key, value); - } - else if (value == null) { - // If BOTH key and value is null, there are no more header fields - break; - } - - i++; - } - - /* 1.4+ version below.... - Map headers = pRemoteConnection.getHeaderFields(); - for (Iterator iterator = headers.entrySet().iterator(); iterator.hasNext();) { - Map.Entry header = (Map.Entry) iterator.next(); - - List values = (List) header.getValue(); - - for (Iterator valueIter = values.iterator(); valueIter.hasNext();) { - String value = (String) valueIter.next(); - String key = (String) header.getKey(); - - // Skip the server header - if (HTTP_RESPONSE_HEADER_SERVER.equalsIgnoreCase(key)) { - key = null; - } - - // The default implementations includes the HTTP response code - // ("HTTP/1.1 200 Ok" or similar) as a header field with - // key "null" (why..?)... - if (key != null) { - //System.out.println("client <<<-- remote: " + key + ": " + value); - pResponse.setHeader(key, value); - } - } - } - */ - } - - /** - * Copies headers from the client (the incoming {@code HttpServletRequest}) - * to the outgoing connection. - * All headers except the "Host" header are copied. - * - * @param pRequest - * @param pRemoteConnection - */ - private void copyHeadersFromClient(HttpServletRequest pRequest, HttpURLConnection pRemoteConnection) { - Enumeration headerNames = pRequest.getHeaderNames(); - while (headerNames.hasMoreElements()) { - String headerName = (String) headerNames.nextElement(); - Enumeration headerValues = pRequest.getHeaders(headerName); - - // Skip the "host" header, as we want something else - if (HTTP_REQUEST_HEADER_HOST.equalsIgnoreCase(headerName)) { - // Skip this header - headerName = null; - } - - // Set the the header to the remoteConnection - if (headerName != null) { - // Convert from multiple line to single line, comma separated, as - // there seems to be a shortcoming in the URLConneciton API... - StringBuilder headerValue = new StringBuilder(); - while (headerValues.hasMoreElements()) { - String value = (String) headerValues.nextElement(); - headerValue.append(value); - if (headerValues.hasMoreElements()) { - headerValue.append(", "); - } - } - - //System.out.println("client -->>> remote: " + headerName + ": " + headerValue); - pRemoteConnection.setRequestProperty(headerName, headerValue.toString()); - } - } - } -} +/* + * Copyright (c) 2008, Harald Kuhr + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name "TwelveMonkeys" nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.twelvemonkeys.servlet; + +import com.twelvemonkeys.io.FileUtil; +import com.twelvemonkeys.lang.StringUtil; + +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.ConnectException; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.Enumeration; + +/** + * A simple proxy servlet implementation. Supports HTTP and HTTPS. + * + * Note: The servlet is not a true HTTP proxy as described in + * RFC 2616, + * instead it passes on all incoming HTTP requests to the configured remote + * server. + * Useful for bypassing firewalls or to avoid exposing internal network + * infrastructure to external clients. + * + * At the moment, no caching of content is implemented. + * + * 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. + * If the configured remote server is down, or unreachable, the servlet will + * respond by sending a {@code 502 Bad Gateway} response to the client. + * Otherwise, the response from the remote server will be tunneled unmodified + * to the client. + * + * @author Harald Kuhr + * @author last modified by $Author: haku $ + * + * @version $Id: ProxyServlet.java#1 $ + */ +public class ProxyServlet extends GenericServlet { + + /** Remote server host name or IP address */ + protected String remoteServer = null; + /** Remote server port */ + protected int remotePort = 80; + /** Remote server "mount" path */ + protected String remotePath = ""; + + private static final String HTTP_REQUEST_HEADER_HOST = "host"; + private static final String HTTP_RESPONSE_HEADER_SERVER = "server"; + 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 + * name or IP address. No default. + * + * @param pRemoteServer + */ + public void setRemoteServer(String pRemoteServer) { + remoteServer = pRemoteServer; + } + + /** + * Called by {@code init} to set the remote port. Must be a number. + * Default is {@code 80}. + * + * @param pRemotePort + */ + public void setRemotePort(String pRemotePort) { + try { + remotePort = Integer.parseInt(pRemotePort); + } + catch (NumberFormatException e) { + log("RemotePort must be a number!", e); + } + } + + /** + * 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. + * Default is {@code ""}. + * + * @param pRemotePath + */ + public void setRemotePath(String pRemotePath) { + if (StringUtil.isEmpty(pRemotePath)) { + pRemotePath = ""; + } + else if (pRemotePath.charAt(0) != '/') { + pRemotePath = "/" + pRemotePath; + } + + remotePath = pRemotePath; + } + + /** + * Override {@code service} to use HTTP specifics. + * + * @param pRequest + * @param pResponse + * + * @throws ServletException + * @throws IOException + * + * @see #service(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) + */ + public final void service(ServletRequest pRequest, ServletResponse pResponse) throws ServletException, IOException { + service((HttpServletRequest) pRequest, (HttpServletResponse) pResponse); + } + + /** + * Services a single request. + * Supports HTTP and HTTPS. + * + * @param pRequest + * @param pResponse + * + * @throws ServletException + * @throws IOException + * + * @see ProxyServlet Class descrition + */ + protected void service(HttpServletRequest pRequest, HttpServletResponse pResponse) throws ServletException, IOException { + // Sanity check configuration + if (remoteServer == null) { + log(MESSAGE_REMOTE_SERVER_NOT_CONFIGURED); + pResponse.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, + MESSAGE_REMOTE_SERVER_NOT_CONFIGURED); + return; + } + + HttpURLConnection remoteConnection = null; + try { + // Recreate request URI for remote request + String requestURI = createRemoteRequestURI(pRequest); + URL remoteURL = new URL(pRequest.getScheme(), remoteServer, remotePort, requestURI); + + // Get connection, with method from original request + // NOTE: The actual connection is not done before we ask for streams... + // NOTE: The HttpURLConnection is supposed to handle multiple + // requests to the same server internally + String method = pRequest.getMethod(); + remoteConnection = (HttpURLConnection) remoteURL.openConnection(); + remoteConnection.setRequestMethod(method); + + // Copy header fields + copyHeadersFromClient(pRequest, remoteConnection); + + // Do proxy specifc stuff? + // 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 + // persistent connection with an HTTP/1.0 client" + + // Copy message body from client to remote server + copyBodyFromClient(pRequest, remoteConnection); + + // Set response status code from remote server to client + int responseCode = remoteConnection.getResponseCode(); + pResponse.setStatus(responseCode); + //System.out.println("Response is: " + responseCode + " " + remoteConnection.getResponseMessage()); + + // Copy header fields back + copyHeadersToClient(remoteConnection, pResponse); + + // More proxy specific stuff? + + // Copy message body from remote server to client + copyBodyToClient(remoteConnection, pResponse); + } + catch (ConnectException e) { + // In case we could not connecto to the remote server + log("Could not connect to remote server.", e); + pResponse.sendError(HttpServletResponse.SC_BAD_GATEWAY, e.getMessage()); + } + finally { + // Disconnect from server + // TODO: Should we actually do this? + if (remoteConnection != null) { + remoteConnection.disconnect(); + } + } + } + + /** + * Copies the message body from the remote server to the client (outgoing + * {@code HttpServletResponse}). + * + * @param pRemoteConnection + * @param pResponse + * + * @throws IOException + */ + private void copyBodyToClient(HttpURLConnection pRemoteConnection, HttpServletResponse pResponse) throws IOException { + InputStream fromRemote = null; + OutputStream toClient = null; + + try { + // Get either input or error stream + try { + fromRemote = pRemoteConnection.getInputStream(); + } + catch (IOException e) { + // If exception, use errorStream instead + fromRemote = pRemoteConnection.getErrorStream(); + } + + // I guess the stream might be null if there is no response other + // than headers (Continue, No Content, etc). + if (fromRemote != null) { + toClient = pResponse.getOutputStream(); + FileUtil.copy(fromRemote, toClient); + } + } + finally { + if (fromRemote != null) { + try { + fromRemote.close(); + } + catch (IOException e) { + log("Stream from remote could not be closed.", e); + } + } + if (toClient != null) { + try { + toClient.close(); + } + catch (IOException e) { + log("Stream to client could not be closed.", e); + } + } + } + } + + /** + * Copies the message body from the client (incomming + * {@code HttpServletRequest}) to the remote server if the request method + * is {@code POST} or PUT. + * Otherwise this method does nothing. + * + * @param pRequest + * @param pRemoteConnection + * + * @throws java.io.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 (!("POST".equals(pRequest.getMethod()) || "PUT".equals(pRequest.getMethod()))) { + return; + } + + // NOTE: Setting doOutput to true, will make it a POST request (why?)... + pRemoteConnection.setDoOutput(true); + + // Get streams and do the copying + InputStream fromClient = null; + OutputStream toRemote = null; + try { + fromClient = pRequest.getInputStream(); + toRemote = pRemoteConnection.getOutputStream(); + FileUtil.copy(fromClient, toRemote); + } + finally { + if (fromClient != null) { + try { + fromClient.close(); + } + catch (IOException e) { + log("Stream from client could not be closed.", e); + } + } + if (toRemote != null) { + try { + toRemote.close(); + } + catch (IOException e) { + log("Stream to remote could not be closed.", e); + } + } + } + } + + /** + * Creates the remote request URI based on the incoming request. + * The URI will include any query strings etc. + * + * @param pRequest + * + * @return a {@code String} representing the remote request URI + */ + private String createRemoteRequestURI(HttpServletRequest pRequest) { + StringBuilder requestURI = new StringBuilder(remotePath); + requestURI.append(pRequest.getPathInfo()); + + if (!StringUtil.isEmpty(pRequest.getQueryString())) { + requestURI.append("?"); + requestURI.append(pRequest.getQueryString()); + } + + return requestURI.toString(); + } + + /** + * Copies headers from the remote connection back to the client + * (the outgoing HttpServletResponse). All headers except the "Server" + * header are copied. + * + * @param pRemoteConnection + * @param pResponse + */ + private void copyHeadersToClient(HttpURLConnection pRemoteConnection, HttpServletResponse pResponse) { + // NOTE: There is no getHeaderFieldCount method or similar... + // Also, the getHeaderFields() method was introduced in J2SE 1.4, and + // we want to be 1.2 compatible. + // So, just try to loop until there are no more headers. + int i = 0; + while (true) { + String key = pRemoteConnection.getHeaderFieldKey(i); + // NOTE: getHeaderField(String) returns only the last value + String value = pRemoteConnection.getHeaderField(i); + + // If the key is not null, life is simple, and Sun is shining + // However, the default implementations includes the HTTP response + // code ("HTTP/1.1 200 Ok" or similar) as a header field with + // key "null" (why..?)... + // In addition, we want to skip the original "Server" header + if (key != null && !HTTP_RESPONSE_HEADER_SERVER.equalsIgnoreCase(key)) { + //System.out.println("client <<<-- remote: " + key + ": " + value); + pResponse.setHeader(key, value); + } + else if (value == null) { + // If BOTH key and value is null, there are no more header fields + break; + } + + i++; + } + + /* 1.4+ version below.... + Map headers = pRemoteConnection.getHeaderFields(); + for (Iterator iterator = headers.entrySet().iterator(); iterator.hasNext();) { + Map.Entry header = (Map.Entry) iterator.next(); + + List values = (List) header.getValue(); + + for (Iterator valueIter = values.iterator(); valueIter.hasNext();) { + String value = (String) valueIter.next(); + String key = (String) header.getKey(); + + // Skip the server header + if (HTTP_RESPONSE_HEADER_SERVER.equalsIgnoreCase(key)) { + key = null; + } + + // The default implementations includes the HTTP response code + // ("HTTP/1.1 200 Ok" or similar) as a header field with + // key "null" (why..?)... + if (key != null) { + //System.out.println("client <<<-- remote: " + key + ": " + value); + pResponse.setHeader(key, value); + } + } + } + */ + } + + /** + * Copies headers from the client (the incoming {@code HttpServletRequest}) + * to the outgoing connection. + * All headers except the "Host" header are copied. + * + * @param pRequest + * @param pRemoteConnection + */ + private void copyHeadersFromClient(HttpServletRequest pRequest, HttpURLConnection pRemoteConnection) { + Enumeration headerNames = pRequest.getHeaderNames(); + while (headerNames.hasMoreElements()) { + String headerName = (String) headerNames.nextElement(); + Enumeration headerValues = pRequest.getHeaders(headerName); + + // Skip the "host" header, as we want something else + if (HTTP_REQUEST_HEADER_HOST.equalsIgnoreCase(headerName)) { + // Skip this header + headerName = null; + } + + // Set the the header to the remoteConnection + if (headerName != null) { + // Convert from multiple line to single line, comma separated, as + // there seems to be a shortcoming in the URLConneciton API... + StringBuilder headerValue = new StringBuilder(); + while (headerValues.hasMoreElements()) { + String value = (String) headerValues.nextElement(); + headerValue.append(value); + if (headerValues.hasMoreElements()) { + headerValue.append(", "); + } + } + + //System.out.println("client -->>> remote: " + headerName + ": " + headerValue); + pRemoteConnection.setRequestProperty(headerName, headerValue.toString()); + } + } + } +} diff --git a/servlet/src/main/java/com/twelvemonkeys/servlet/ServletConfigException.java b/servlet/src/main/java/com/twelvemonkeys/servlet/ServletConfigException.java index 9fbcbaf8..4d0b6389 100755 --- a/servlet/src/main/java/com/twelvemonkeys/servlet/ServletConfigException.java +++ b/servlet/src/main/java/com/twelvemonkeys/servlet/ServletConfigException.java @@ -1,81 +1,81 @@ -/* - * Copyright (c) 2008, Harald Kuhr - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name "TwelveMonkeys" nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package com.twelvemonkeys.servlet; - -import javax.servlet.ServletException; - -/** - * ServletConfigException. - * - * @author Harald Kuhr - * @version $Id: ServletConfigException.java#2 $ - */ -public class ServletConfigException extends ServletException { - - // TODO: Parameters for init-param at fault, and possibly servlet name? - - /** - * Creates a {@code ServletConfigException} with the given message. - * - * @param pMessage the exception message - */ - public ServletConfigException(String pMessage) { - super(pMessage); - } - - /** - * Creates a {@code ServletConfigException} with the given message and cause. - * - * @param pMessage the exception message - * @param pCause the exception cause - */ - public ServletConfigException(final String pMessage, final Throwable pCause) { - super(pMessage, pCause); - - maybeInitCause(pCause); - } - - /** - * Creates a {@code ServletConfigException} with the cause. - * - * @param pCause the exception cause - */ - public ServletConfigException(final Throwable pCause) { - super(String.format("Error in Servlet configuration: %s", pCause.getMessage()), pCause); - - maybeInitCause(pCause); - } - - private void maybeInitCause(Throwable pCause) { - // Workaround for ServletExceptions that does not do proper exception chaining - if (getCause() == null) { - initCause(pCause); - } - } -} +/* + * Copyright (c) 2008, Harald Kuhr + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name "TwelveMonkeys" nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.twelvemonkeys.servlet; + +import javax.servlet.ServletException; + +/** + * ServletConfigException. + * + * @author Harald Kuhr + * @version $Id: ServletConfigException.java#2 $ + */ +public class ServletConfigException extends ServletException { + + // TODO: Parameters for init-param at fault, and possibly servlet name? + + /** + * Creates a {@code ServletConfigException} with the given message. + * + * @param pMessage the exception message + */ + public ServletConfigException(String pMessage) { + super(pMessage); + } + + /** + * Creates a {@code ServletConfigException} with the given message and cause. + * + * @param pMessage the exception message + * @param pCause the exception cause + */ + public ServletConfigException(final String pMessage, final Throwable pCause) { + super(pMessage, pCause); + + maybeInitCause(pCause); + } + + /** + * Creates a {@code ServletConfigException} with the cause. + * + * @param pCause the exception cause + */ + public ServletConfigException(final Throwable pCause) { + super(String.format("Error in Servlet configuration: %s", pCause.getMessage()), pCause); + + maybeInitCause(pCause); + } + + private void maybeInitCause(Throwable pCause) { + // Workaround for ServletExceptions that does not do proper exception chaining + if (getCause() == null) { + initCause(pCause); + } + } +} diff --git a/servlet/src/main/java/com/twelvemonkeys/servlet/ServletConfigMapAdapter.java b/servlet/src/main/java/com/twelvemonkeys/servlet/ServletConfigMapAdapter.java index 6806f5ea..1d23226a 100755 --- a/servlet/src/main/java/com/twelvemonkeys/servlet/ServletConfigMapAdapter.java +++ b/servlet/src/main/java/com/twelvemonkeys/servlet/ServletConfigMapAdapter.java @@ -1,282 +1,282 @@ -/* - * Copyright (c) 2008, Harald Kuhr - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name "TwelveMonkeys" nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package com.twelvemonkeys.servlet; - -import com.twelvemonkeys.lang.StringUtil; -import com.twelvemonkeys.lang.Validate; - -import javax.servlet.FilterConfig; -import javax.servlet.ServletConfig; -import javax.servlet.ServletContext; -import java.io.Serializable; -import java.util.*; - -/** - * {@code ServletConfig} or {@code FilterConfig} adapter, that implements - * the {@code Map} interface for interoperability with collection-based API's. - * - * This {@code Map} is not synchronized. - * - * - * @author Harald Kuhr - * @version $Id: ServletConfigMapAdapter.java#2 $ - */ -class ServletConfigMapAdapter extends AbstractMap
- * requestURI = "/webapp/index.jsp" - * contextPath = "/webapp" - *- * The method will return {@code "/index.jsp"}. - * - * @param pRequest the current HTTP request - * @return the request URI relative to the current context path. - */ - public static String getContextRelativeURI(final HttpServletRequest pRequest) { - String context = pRequest.getContextPath(); - - if (!StringUtil.isEmpty(context)) { // "" for root context - return pRequest.getRequestURI().substring(context.length()); - } - - return pRequest.getRequestURI(); - } - - /** - * Returns a {@code URL} containing the real path for a given virtual - * path, on URL form. - * Note that this method will return {@code null} for all the same reasons - * as {@code ServletContext.getRealPath(java.lang.String)} does. - * - * @param pContext the servlet context - * @param pPath the virtual path - * @return a {@code URL} object containing the path, or {@code null}. - * @throws MalformedURLException if the path refers to a malformed URL - * @see ServletContext#getRealPath(java.lang.String) - * @see ServletContext#getResource(java.lang.String) - */ - public static URL getRealURL(final ServletContext pContext, final String pPath) throws MalformedURLException { - String realPath = pContext.getRealPath(pPath); - - if (realPath != null) { - // NOTE: First convert to URI, as of Java 6 File.toURL is deprecated - return new File(realPath).toURI().toURL(); - } - - return null; - } - - /** - * Gets the temp directory for the given {@code ServletContext} (web app). - * - * @param pContext the servlet context - * @return the temp directory - */ - public static File getTempDir(final ServletContext pContext) { - return (File) pContext.getAttribute("javax.servlet.context.tempdir"); - } - - /** - * Gets the unique identifier assigned to this session. - * The identifier is assigned by the servlet container and is implementation - * dependent. - * - * @param pRequest The HTTP servlet request object. - * @return the session Id - */ - public static String getSessionId(final HttpServletRequest pRequest) { - HttpSession session = pRequest.getSession(); - - return (session != null) ? session.getId() : null; - } - - /** - * Creates an unmodifiable {@code Map} view of the given - * {@code ServletConfig}s init-parameters. - * Note: The returned {@code Map} is optimized for {@code get} - * operations and iterating over it's {@code keySet}. - * For other operations it may not perform well. - * - * @param pConfig the servlet configuration - * @return a {@code Map} view of the config - * @throws IllegalArgumentException if {@code pConfig} is {@code null} - */ - public static Map
+ * requestURI = "/webapp/index.jsp" + * contextPath = "/webapp" + *+ * The method will return {@code "/index.jsp"}. + * + * @param pRequest the current HTTP request + * @return the request URI relative to the current context path. + */ + public static String getContextRelativeURI(final HttpServletRequest pRequest) { + String context = pRequest.getContextPath(); + + if (!StringUtil.isEmpty(context)) { // "" for root context + return pRequest.getRequestURI().substring(context.length()); + } + + return pRequest.getRequestURI(); + } + + /** + * Returns a {@code URL} containing the real path for a given virtual + * path, on URL form. + * Note that this method will return {@code null} for all the same reasons + * as {@code ServletContext.getRealPath(java.lang.String)} does. + * + * @param pContext the servlet context + * @param pPath the virtual path + * @return a {@code URL} object containing the path, or {@code null}. + * @throws MalformedURLException if the path refers to a malformed URL + * @see ServletContext#getRealPath(java.lang.String) + * @see ServletContext#getResource(java.lang.String) + */ + public static URL getRealURL(final ServletContext pContext, final String pPath) throws MalformedURLException { + String realPath = pContext.getRealPath(pPath); + + if (realPath != null) { + // NOTE: First convert to URI, as of Java 6 File.toURL is deprecated + return new File(realPath).toURI().toURL(); + } + + return null; + } + + /** + * Gets the temp directory for the given {@code ServletContext} (web app). + * + * @param pContext the servlet context + * @return the temp directory + */ + public static File getTempDir(final ServletContext pContext) { + return (File) pContext.getAttribute("javax.servlet.context.tempdir"); + } + + /** + * Gets the unique identifier assigned to this session. + * The identifier is assigned by the servlet container and is implementation + * dependent. + * + * @param pRequest The HTTP servlet request object. + * @return the session Id + */ + public static String getSessionId(final HttpServletRequest pRequest) { + HttpSession session = pRequest.getSession(); + + return (session != null) ? session.getId() : null; + } + + /** + * Creates an unmodifiable {@code Map} view of the given + * {@code ServletConfig}s init-parameters. + * Note: The returned {@code Map} is optimized for {@code get} + * operations and iterating over it's {@code keySet}. + * For other operations it may not perform well. + * + * @param pConfig the servlet configuration + * @return a {@code Map} view of the config + * @throws IllegalArgumentException if {@code pConfig} is {@code null} + */ + public static Map
- * <!-- TrimWS Filter Configuration --> - * <filter> - * <filter-name>trimws</filter-name> - * <filter-class>com.twelvemonkeys.servlet.TrimWhiteSpaceFilter</filter-class> - * <!-- auto-flush=true is the default, may be omitted --> - * <init-param> - * <param-name>auto-flush</param-name> - * <param-value>true</param-value> - * </init-param> - * </filter> - *- * Filter-mapping section:
- * <!-- TimWS Filter Mapping --> - * <filter-mapping> - * <filter-name>trimws</filter-name> - * <url-pattern>*.html</url-pattern> - * <dispatcher>REQUEST</dispatcher> - * <dispatcher>FORWARD</dispatcher> - * </filter-mapping> - * <filter-mapping> - * <filter-name>trimws</filter-name> - * <url-pattern>*.jsp</url-pattern> - * <dispatcher>REQUEST</dispatcher> - * <dispatcher>FORWARD</dispatcher> - * </filter-mapping> - *- * - * @author Harald Kuhr - * @author last modified by $Author: haku $ - * @version $Id: TrimWhiteSpaceFilter.java#2 $ - */ -public class TrimWhiteSpaceFilter extends GenericFilter { - - private boolean autoFlush = true; - - @InitParam - public void setAutoFlush(final boolean pAutoFlush) { - autoFlush = pAutoFlush; - } - - public void init() throws ServletException { - super.init(); - log("Automatic flushing is " + (autoFlush ? "enabled" : "disabled")); - } - - protected void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain) throws IOException, ServletException { - ServletResponseWrapper wrapped = new TrimWSServletResponseWrapper(pResponse); - pChain.doFilter(pRequest, ServletUtil.createWrapper(wrapped)); - if (autoFlush) { - wrapped.flushBuffer(); - } - } - - static final class TrimWSFilterOutputStream extends FilterOutputStream { - boolean lastWasWS = true; // Avoids leading WS by init to true - - public TrimWSFilterOutputStream(OutputStream pOut) { - super(pOut); - } - - // Override this, in case the wrapped outputstream overrides... - public final void write(byte pBytes[]) throws IOException { - write(pBytes, 0, pBytes.length); - } - - // Override this, in case the wrapped outputstream overrides... - public final void write(byte pBytes[], int pOff, int pLen) throws IOException { - if (pBytes == null) { - throw new NullPointerException("bytes == null"); - } - else if (pOff < 0 || pLen < 0 || (pOff + pLen > pBytes.length)) { - throw new IndexOutOfBoundsException("Bytes: " + pBytes.length + " Offset: " + pOff + " Length: " + pLen); - } - - for (int i = 0; i < pLen ; i++) { - write(pBytes[pOff + i]); - } - } - - public void write(int pByte) throws IOException { - // TODO: Is this good enough for multi-byte encodings like UTF-16? - // Consider writing through a Writer that does that for us, and - // also buffer whitespace, so we write a linefeed every time there's - // one in the original... - - // According to http://en.wikipedia.org/wiki/UTF-8: - // "[...] US-ASCII octet values do not appear otherwise in a UTF-8 - // encoded character stream. This provides compatibility with file - // systems or other software (e.g., the printf() function in - // C libraries) that parse based on US-ASCII values but are - // transparent to other values." - - if (!Character.isWhitespace((char) pByte)) { - // If char is not WS, just store - super.write(pByte); - lastWasWS = false; - } - else { - // TODO: Consider writing only 0x0a (LF) and 0x20 (space) - // Else, if char is WS, store first, skip the rest - if (!lastWasWS) { - if (pByte == 0x0d) { // Convert all CR/LF's to 0x0a - super.write(0x0a); - } - else { - super.write(pByte); - } - } - lastWasWS = true; - } - } - } - - private static class TrimWSStreamDelegate extends ServletResponseStreamDelegate { - public TrimWSStreamDelegate(ServletResponse pResponse) { - super(pResponse); - } - - protected OutputStream createOutputStream() throws IOException { - return new TrimWSFilterOutputStream(response.getOutputStream()); - } - } - - static class TrimWSServletResponseWrapper extends ServletResponseWrapper { - private final ServletResponseStreamDelegate streamDelegate = new TrimWSStreamDelegate(getResponse()); - - public TrimWSServletResponseWrapper(ServletResponse pResponse) { - super(pResponse); - } - - public ServletOutputStream getOutputStream() throws IOException { - return streamDelegate.getOutputStream(); - } - - public PrintWriter getWriter() throws IOException { - return streamDelegate.getWriter(); - } - - public void setContentLength(int pLength) { - // Will be changed by filter, so don't set. - } - - @Override - public void flushBuffer() throws IOException { - streamDelegate.flushBuffer(); - } - - @Override - public void resetBuffer() { - streamDelegate.resetBuffer(); - } - - // TODO: Consider picking up content-type/encoding, as we can only - // filter US-ASCII, UTF-8 and other compatible encodings? - } +/* + * Copyright (c) 2008, Harald Kuhr + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name "TwelveMonkeys" nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.twelvemonkeys.servlet; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletOutputStream; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.ServletResponseWrapper; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.OutputStream; +import java.io.FilterOutputStream; + +/** + * Removes extra unneccessary white space from a servlet response. + * White space is defined as per {@link Character#isWhitespace(char)}. + * + * This filter has no understanding of the content in the reponse, and will + * remove repeated white space anywhere in the stream. It is intended for + * removing white space from HTML or XML streams, but this limitation makes it + * less suited for filtering HTML/XHTML with embedded CSS or JavaScript, + * in case white space should be significant here. It is strongly reccommended + * you keep CSS and JavaScript in separate files (this will have the added + * benefit of further reducing the ammount of data communicated between + * server and client). + * + * At the moment this filter has no concept of encoding. + * This means, that if some multi-byte escape sequence contains one or more + * bytes that individually is treated as a white space, these bytes + * may be skipped. + * As UTF-8 + * guarantees that no bytes are repeated in this way, this filter can safely + * filter UTF-8. + * Simple 8 bit character encodings, like the + * ISO/IEC 8859 standard, or + * + * are always safe. + * + * Configuration
+ * <!-- TrimWS Filter Configuration --> + * <filter> + * <filter-name>trimws</filter-name> + * <filter-class>com.twelvemonkeys.servlet.TrimWhiteSpaceFilter</filter-class> + * <!-- auto-flush=true is the default, may be omitted --> + * <init-param> + * <param-name>auto-flush</param-name> + * <param-value>true</param-value> + * </init-param> + * </filter> + *+ * Filter-mapping section:
+ * <!-- TimWS Filter Mapping --> + * <filter-mapping> + * <filter-name>trimws</filter-name> + * <url-pattern>*.html</url-pattern> + * <dispatcher>REQUEST</dispatcher> + * <dispatcher>FORWARD</dispatcher> + * </filter-mapping> + * <filter-mapping> + * <filter-name>trimws</filter-name> + * <url-pattern>*.jsp</url-pattern> + * <dispatcher>REQUEST</dispatcher> + * <dispatcher>FORWARD</dispatcher> + * </filter-mapping> + *+ * + * @author Harald Kuhr + * @author last modified by $Author: haku $ + * @version $Id: TrimWhiteSpaceFilter.java#2 $ + */ +public class TrimWhiteSpaceFilter extends GenericFilter { + + private boolean autoFlush = true; + + @InitParam + public void setAutoFlush(final boolean pAutoFlush) { + autoFlush = pAutoFlush; + } + + public void init() throws ServletException { + super.init(); + log("Automatic flushing is " + (autoFlush ? "enabled" : "disabled")); + } + + protected void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain) throws IOException, ServletException { + ServletResponseWrapper wrapped = new TrimWSServletResponseWrapper(pResponse); + pChain.doFilter(pRequest, ServletUtil.createWrapper(wrapped)); + if (autoFlush) { + wrapped.flushBuffer(); + } + } + + static final class TrimWSFilterOutputStream extends FilterOutputStream { + boolean lastWasWS = true; // Avoids leading WS by init to true + + public TrimWSFilterOutputStream(OutputStream pOut) { + super(pOut); + } + + // Override this, in case the wrapped outputstream overrides... + public final void write(byte pBytes[]) throws IOException { + write(pBytes, 0, pBytes.length); + } + + // Override this, in case the wrapped outputstream overrides... + public final void write(byte pBytes[], int pOff, int pLen) throws IOException { + if (pBytes == null) { + throw new NullPointerException("bytes == null"); + } + else if (pOff < 0 || pLen < 0 || (pOff + pLen > pBytes.length)) { + throw new IndexOutOfBoundsException("Bytes: " + pBytes.length + " Offset: " + pOff + " Length: " + pLen); + } + + for (int i = 0; i < pLen ; i++) { + write(pBytes[pOff + i]); + } + } + + public void write(int pByte) throws IOException { + // TODO: Is this good enough for multi-byte encodings like UTF-16? + // Consider writing through a Writer that does that for us, and + // also buffer whitespace, so we write a linefeed every time there's + // one in the original... + + // According to http://en.wikipedia.org/wiki/UTF-8: + // "[...] US-ASCII octet values do not appear otherwise in a UTF-8 + // encoded character stream. This provides compatibility with file + // systems or other software (e.g., the printf() function in + // C libraries) that parse based on US-ASCII values but are + // transparent to other values." + + if (!Character.isWhitespace((char) pByte)) { + // If char is not WS, just store + super.write(pByte); + lastWasWS = false; + } + else { + // TODO: Consider writing only 0x0a (LF) and 0x20 (space) + // Else, if char is WS, store first, skip the rest + if (!lastWasWS) { + if (pByte == 0x0d) { // Convert all CR/LF's to 0x0a + super.write(0x0a); + } + else { + super.write(pByte); + } + } + lastWasWS = true; + } + } + } + + private static class TrimWSStreamDelegate extends ServletResponseStreamDelegate { + public TrimWSStreamDelegate(ServletResponse pResponse) { + super(pResponse); + } + + protected OutputStream createOutputStream() throws IOException { + return new TrimWSFilterOutputStream(response.getOutputStream()); + } + } + + static class TrimWSServletResponseWrapper extends ServletResponseWrapper { + private final ServletResponseStreamDelegate streamDelegate = new TrimWSStreamDelegate(getResponse()); + + public TrimWSServletResponseWrapper(ServletResponse pResponse) { + super(pResponse); + } + + public ServletOutputStream getOutputStream() throws IOException { + return streamDelegate.getOutputStream(); + } + + public PrintWriter getWriter() throws IOException { + return streamDelegate.getWriter(); + } + + public void setContentLength(int pLength) { + // Will be changed by filter, so don't set. + } + + @Override + public void flushBuffer() throws IOException { + streamDelegate.flushBuffer(); + } + + @Override + public void resetBuffer() { + streamDelegate.resetBuffer(); + } + + // TODO: Consider picking up content-type/encoding, as we can only + // filter US-ASCII, UTF-8 and other compatible encodings? + } } \ No newline at end of file diff --git a/servlet/src/main/java/com/twelvemonkeys/servlet/cache/CacheFilter.java b/servlet/src/main/java/com/twelvemonkeys/servlet/cache/CacheFilter.java index ec917a37..1996060f 100755 --- a/servlet/src/main/java/com/twelvemonkeys/servlet/cache/CacheFilter.java +++ b/servlet/src/main/java/com/twelvemonkeys/servlet/cache/CacheFilter.java @@ -1,200 +1,200 @@ -/* - * Copyright (c) 2008, Harald Kuhr - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name "TwelveMonkeys" nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package com.twelvemonkeys.servlet.cache; - -import com.twelvemonkeys.lang.StringUtil; -import com.twelvemonkeys.servlet.GenericFilter; -import com.twelvemonkeys.servlet.ServletConfigException; -import com.twelvemonkeys.servlet.ServletUtil; - -import javax.servlet.*; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.File; -import java.io.IOException; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * A Filter that provides response caching, for HTTP {@code GET} requests. - * - * Originally based on ideas and code found in the ONJava article - * Two - * Servlet Filters Every Web Application Should Have - * by Jayson Falkner. - * - * @author Jayson Falkner - * @author Harald Kuhr - * @author last modified by $Author: haku $ - * @version $Id: CacheFilter.java#4 $ - * - */ -public class CacheFilter extends GenericFilter { - - HTTPCache cache; - - /** - * Initializes the filter - * - * @throws javax.servlet.ServletException - */ - public void init() throws ServletException { - FilterConfig config = getFilterConfig(); - - // Default don't delete cache files on exit (persistent cache) - boolean deleteCacheOnExit = "TRUE".equalsIgnoreCase(config.getInitParameter("deleteCacheOnExit")); - - // Default expiry time 10 minutes - int expiryTime = 10 * 60 * 1000; - - String expiryTimeStr = config.getInitParameter("expiryTime"); - if (!StringUtil.isEmpty(expiryTimeStr)) { - try { - // TODO: This is insane.. :-P Let the expiry time be in minutes or seconds.. - expiryTime = Integer.parseInt(expiryTimeStr); - } - catch (NumberFormatException e) { - throw new ServletConfigException("Could not parse expiryTime: " + e.toString(), e); - } - } - - // Default max mem cache size 10 MB - int memCacheSize = 10; - - String memCacheSizeStr = config.getInitParameter("memCacheSize"); - if (!StringUtil.isEmpty(memCacheSizeStr)) { - try { - memCacheSize = Integer.parseInt(memCacheSizeStr); - } - catch (NumberFormatException e) { - throw new ServletConfigException("Could not parse memCacheSize: " + e.toString(), e); - } - } - - int maxCachedEntites = 10000; - - try { - cache = new HTTPCache( - getTempFolder(), - expiryTime, - memCacheSize * 1024 * 1024, - maxCachedEntites, - deleteCacheOnExit, - new ServletContextLoggerAdapter(getFilterName(), getServletContext()) - ) { - @Override - protected File getRealFile(CacheRequest pRequest) { - String contextRelativeURI = ServletUtil.getContextRelativeURI(((ServletCacheRequest) pRequest).getRequest()); - - String path = getServletContext().getRealPath(contextRelativeURI); - - if (path != null) { - return new File(path); - } - - return null; - } - }; - log("Created cache: " + cache); - } - catch (IllegalArgumentException e) { - throw new ServletConfigException("Could not create cache: " + e.toString(), e); - } - } - - private File getTempFolder() { - File tempRoot = (File) getServletContext().getAttribute("javax.servlet.context.tempdir"); - if (tempRoot == null) { - throw new IllegalStateException("Missing context attribute \"javax.servlet.context.tempdir\""); - } - return new File(tempRoot, getFilterName()); - } - - public void destroy() { - log("Destroying cache: " + cache); - cache = null; - super.destroy(); - } - - protected void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain) throws IOException, ServletException { - // We can only cache HTTP GET/HEAD requests - if (!(pRequest instanceof HttpServletRequest - && pResponse instanceof HttpServletResponse - && isCachable((HttpServletRequest) pRequest))) { - pChain.doFilter(pRequest, pResponse); // Continue chain - } - else { - ServletCacheRequest cacheRequest = new ServletCacheRequest((HttpServletRequest) pRequest); - ServletCacheResponse cacheResponse = new ServletCacheResponse((HttpServletResponse) pResponse); - ServletResponseResolver resolver = new ServletResponseResolver(cacheRequest, cacheResponse, pChain); - - // Render fast - try { - cache.doCached(cacheRequest, cacheResponse, resolver); - } - catch (CacheException e) { - if (e.getCause() instanceof ServletException) { - throw (ServletException) e.getCause(); - } - else { - throw new ServletException(e); - } - } - finally { - pResponse.flushBuffer(); - } - } - } - - private boolean isCachable(HttpServletRequest pRequest) { - // TODO: Get Cache-Control: no-cache/max-age=0 and Pragma: no-cache from REQUEST too? - return "GET".equals(pRequest.getMethod()) || "HEAD".equals(pRequest.getMethod()); - } - - // TODO: Extract, complete and document this class, might be useful in other cases - // Maybe add it to the ServletUtil class - static class ServletContextLoggerAdapter extends Logger { - private final ServletContext context; - - public ServletContextLoggerAdapter(String pName, ServletContext pContext) { - super(pName, null); - context = pContext; - } - - @Override - public void log(Level pLevel, String pMessage) { - context.log(pMessage); - } - - @Override - public void log(Level pLevel, String pMessage, Throwable pThrowable) { - context.log(pMessage, pThrowable); - } - } +/* + * Copyright (c) 2008, Harald Kuhr + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name "TwelveMonkeys" nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.twelvemonkeys.servlet.cache; + +import com.twelvemonkeys.lang.StringUtil; +import com.twelvemonkeys.servlet.GenericFilter; +import com.twelvemonkeys.servlet.ServletConfigException; +import com.twelvemonkeys.servlet.ServletUtil; + +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.File; +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * A Filter that provides response caching, for HTTP {@code GET} requests. + * + * Originally based on ideas and code found in the ONJava article + * Two + * Servlet Filters Every Web Application Should Have + * by Jayson Falkner. + * + * @author Jayson Falkner + * @author Harald Kuhr + * @author last modified by $Author: haku $ + * @version $Id: CacheFilter.java#4 $ + * + */ +public class CacheFilter extends GenericFilter { + + HTTPCache cache; + + /** + * Initializes the filter + * + * @throws javax.servlet.ServletException + */ + public void init() throws ServletException { + FilterConfig config = getFilterConfig(); + + // Default don't delete cache files on exit (persistent cache) + boolean deleteCacheOnExit = "TRUE".equalsIgnoreCase(config.getInitParameter("deleteCacheOnExit")); + + // Default expiry time 10 minutes + int expiryTime = 10 * 60 * 1000; + + String expiryTimeStr = config.getInitParameter("expiryTime"); + if (!StringUtil.isEmpty(expiryTimeStr)) { + try { + // TODO: This is insane.. :-P Let the expiry time be in minutes or seconds.. + expiryTime = Integer.parseInt(expiryTimeStr); + } + catch (NumberFormatException e) { + throw new ServletConfigException("Could not parse expiryTime: " + e.toString(), e); + } + } + + // Default max mem cache size 10 MB + int memCacheSize = 10; + + String memCacheSizeStr = config.getInitParameter("memCacheSize"); + if (!StringUtil.isEmpty(memCacheSizeStr)) { + try { + memCacheSize = Integer.parseInt(memCacheSizeStr); + } + catch (NumberFormatException e) { + throw new ServletConfigException("Could not parse memCacheSize: " + e.toString(), e); + } + } + + int maxCachedEntites = 10000; + + try { + cache = new HTTPCache( + getTempFolder(), + expiryTime, + memCacheSize * 1024 * 1024, + maxCachedEntites, + deleteCacheOnExit, + new ServletContextLoggerAdapter(getFilterName(), getServletContext()) + ) { + @Override + protected File getRealFile(CacheRequest pRequest) { + String contextRelativeURI = ServletUtil.getContextRelativeURI(((ServletCacheRequest) pRequest).getRequest()); + + String path = getServletContext().getRealPath(contextRelativeURI); + + if (path != null) { + return new File(path); + } + + return null; + } + }; + log("Created cache: " + cache); + } + catch (IllegalArgumentException e) { + throw new ServletConfigException("Could not create cache: " + e.toString(), e); + } + } + + private File getTempFolder() { + File tempRoot = (File) getServletContext().getAttribute("javax.servlet.context.tempdir"); + if (tempRoot == null) { + throw new IllegalStateException("Missing context attribute \"javax.servlet.context.tempdir\""); + } + return new File(tempRoot, getFilterName()); + } + + public void destroy() { + log("Destroying cache: " + cache); + cache = null; + super.destroy(); + } + + protected void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain) throws IOException, ServletException { + // We can only cache HTTP GET/HEAD requests + if (!(pRequest instanceof HttpServletRequest + && pResponse instanceof HttpServletResponse + && isCachable((HttpServletRequest) pRequest))) { + pChain.doFilter(pRequest, pResponse); // Continue chain + } + else { + ServletCacheRequest cacheRequest = new ServletCacheRequest((HttpServletRequest) pRequest); + ServletCacheResponse cacheResponse = new ServletCacheResponse((HttpServletResponse) pResponse); + ServletResponseResolver resolver = new ServletResponseResolver(cacheRequest, cacheResponse, pChain); + + // Render fast + try { + cache.doCached(cacheRequest, cacheResponse, resolver); + } + catch (CacheException e) { + if (e.getCause() instanceof ServletException) { + throw (ServletException) e.getCause(); + } + else { + throw new ServletException(e); + } + } + finally { + pResponse.flushBuffer(); + } + } + } + + private boolean isCachable(HttpServletRequest pRequest) { + // TODO: Get Cache-Control: no-cache/max-age=0 and Pragma: no-cache from REQUEST too? + return "GET".equals(pRequest.getMethod()) || "HEAD".equals(pRequest.getMethod()); + } + + // TODO: Extract, complete and document this class, might be useful in other cases + // Maybe add it to the ServletUtil class + static class ServletContextLoggerAdapter extends Logger { + private final ServletContext context; + + public ServletContextLoggerAdapter(String pName, ServletContext pContext) { + super(pName, null); + context = pContext; + } + + @Override + public void log(Level pLevel, String pMessage) { + context.log(pMessage); + } + + @Override + public void log(Level pLevel, String pMessage, Throwable pThrowable) { + context.log(pMessage, pThrowable); + } + } } \ No newline at end of file diff --git a/servlet/src/main/java/com/twelvemonkeys/servlet/cache/CacheResponseWrapper.java b/servlet/src/main/java/com/twelvemonkeys/servlet/cache/CacheResponseWrapper.java index a666d68e..719bb378 100755 --- a/servlet/src/main/java/com/twelvemonkeys/servlet/cache/CacheResponseWrapper.java +++ b/servlet/src/main/java/com/twelvemonkeys/servlet/cache/CacheResponseWrapper.java @@ -1,261 +1,261 @@ -/* - * Copyright (c) 2008, Harald Kuhr - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name "TwelveMonkeys" nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package com.twelvemonkeys.servlet.cache; - -import com.twelvemonkeys.lang.StringUtil; -import com.twelvemonkeys.net.HTTPUtil; -import com.twelvemonkeys.servlet.ServletResponseStreamDelegate; - -import javax.servlet.ServletOutputStream; -import javax.servlet.http.HttpServletResponseWrapper; -import java.io.IOException; -import java.io.OutputStream; -import java.io.PrintWriter; - -/** - * CacheResponseWrapper class description. - * - * Based on ideas and code found in the ONJava article - * Two - * Servlet Filters Every Web Application Should Have - * by Jayson Falkner. - * - * @author Jayson Falkner - * @author Harald Kuhr - * @author last modified by $Author: haku $ - * @version $Id: CacheResponseWrapper.java#3 $ - */ -class CacheResponseWrapper extends HttpServletResponseWrapper { - private ServletResponseStreamDelegate streamDelegate; - - private CacheResponse response; - private CachedEntity cached; - private WritableCachedResponse cachedResponse; - - private Boolean cacheable; - private int status; - - public CacheResponseWrapper(final ServletCacheResponse pResponse, final CachedEntity pCached) { - super(pResponse.getResponse()); - response = pResponse; - cached = pCached; - init(); - } - - /* - NOTE: This class defers determining if a response is cacheable until the - output stream is needed. - This it the reason for the somewhat complicated logic in the add/setHeader - methods below. - */ - private void init() { - cacheable = null; - status = SC_OK; - cachedResponse = cached.createCachedResponse(); - streamDelegate = new ServletResponseStreamDelegate(this) { - protected OutputStream createOutputStream() throws IOException { - // Test if this request is really cacheable, otherwise, - // just write through to underlying response, and don't cache - if (isCacheable()) { - return cachedResponse.getOutputStream(); - } - else { - cachedResponse.setStatus(status); - cachedResponse.writeHeadersTo(CacheResponseWrapper.this.response); - return super.getOutputStream(); - } - } - }; - } - - CachedResponse getCachedResponse() { - return cachedResponse.getCachedResponse(); - } - - public boolean isCacheable() { - // NOTE: Intentionally not synchronized - if (cacheable == null) { - cacheable = isCacheableImpl(); - } - - return cacheable; - } - - private boolean isCacheableImpl() { - if (status != SC_OK) { - return false; - } - - // Vary: * - String[] values = cachedResponse.getHeaderValues(HTTPCache.HEADER_VARY); - if (values != null) { - for (String value : values) { - if ("*".equals(value)) { - return false; - } - } - } - - // Cache-Control: no-cache, no-store, must-revalidate - values = cachedResponse.getHeaderValues(HTTPCache.HEADER_CACHE_CONTROL); - if (values != null) { - for (String value : values) { - if (StringUtil.contains(value, "no-cache") - || StringUtil.contains(value, "no-store") - || StringUtil.contains(value, "must-revalidate")) { - return false; - } - } - } - - // Pragma: no-cache - values = cachedResponse.getHeaderValues(HTTPCache.HEADER_PRAGMA); - if (values != null) { - for (String value : values) { - if (StringUtil.contains(value, "no-cache")) { - return false; - } - } - } - - return true; - } - - public void flushBuffer() throws IOException { - streamDelegate.flushBuffer(); - } - - public void resetBuffer() { - // Servlet 2.3 - streamDelegate.resetBuffer(); - } - - public void reset() { - if (Boolean.FALSE.equals(cacheable)) { - super.reset(); - } - // No else, might be cacheable after all.. - init(); - } - - public ServletOutputStream getOutputStream() throws IOException { - return streamDelegate.getOutputStream(); - } - - public PrintWriter getWriter() throws IOException { - return streamDelegate.getWriter(); - } - - public boolean containsHeader(String name) { - return cachedResponse.getHeaderValues(name) != null; - } - - public void sendError(int pStatusCode, String msg) throws IOException { - // NOT cacheable - status = pStatusCode; - super.sendError(pStatusCode, msg); - } - - public void sendError(int pStatusCode) throws IOException { - // NOT cacheable - status = pStatusCode; - super.sendError(pStatusCode); - } - - public void setStatus(int pStatusCode, String sm) { - // NOTE: This method is deprecated - setStatus(pStatusCode); - } - - public void setStatus(int pStatusCode) { - // NOT cacheable unless pStatusCode == 200 (or a FEW others?) - if (pStatusCode != SC_OK) { - status = pStatusCode; - super.setStatus(pStatusCode); - } - } - - public void sendRedirect(String pLocation) throws IOException { - // NOT cacheable - status = SC_MOVED_TEMPORARILY; - super.sendRedirect(pLocation); - } - - public void setDateHeader(String pName, long pValue) { - // If in write-trough-mode, set headers directly - if (Boolean.FALSE.equals(cacheable)) { - super.setDateHeader(pName, pValue); - } - cachedResponse.setHeader(pName, HTTPUtil.formatHTTPDate(pValue)); - } - - public void addDateHeader(String pName, long pValue) { - // If in write-trough-mode, set headers directly - if (Boolean.FALSE.equals(cacheable)) { - super.addDateHeader(pName, pValue); - } - cachedResponse.addHeader(pName, HTTPUtil.formatHTTPDate(pValue)); - } - - public void setHeader(String pName, String pValue) { - // If in write-trough-mode, set headers directly - if (Boolean.FALSE.equals(cacheable)) { - super.setHeader(pName, pValue); - } - cachedResponse.setHeader(pName, pValue); - } - - public void addHeader(String pName, String pValue) { - // If in write-trough-mode, set headers directly - if (Boolean.FALSE.equals(cacheable)) { - super.addHeader(pName, pValue); - } - cachedResponse.addHeader(pName, pValue); - } - - public void setIntHeader(String pName, int pValue) { - // If in write-trough-mode, set headers directly - if (Boolean.FALSE.equals(cacheable)) { - super.setIntHeader(pName, pValue); - } - cachedResponse.setHeader(pName, String.valueOf(pValue)); - } - - public void addIntHeader(String pName, int pValue) { - // If in write-trough-mode, set headers directly - if (Boolean.FALSE.equals(cacheable)) { - super.addIntHeader(pName, pValue); - } - cachedResponse.addHeader(pName, String.valueOf(pValue)); - } - - public final void setContentType(String type) { - setHeader(HTTPCache.HEADER_CONTENT_TYPE, type); - } +/* + * Copyright (c) 2008, Harald Kuhr + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name "TwelveMonkeys" nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.twelvemonkeys.servlet.cache; + +import com.twelvemonkeys.lang.StringUtil; +import com.twelvemonkeys.net.HTTPUtil; +import com.twelvemonkeys.servlet.ServletResponseStreamDelegate; + +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletResponseWrapper; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintWriter; + +/** + * CacheResponseWrapper class description. + * + * Based on ideas and code found in the ONJava article + * Two + * Servlet Filters Every Web Application Should Have + * by Jayson Falkner. + * + * @author Jayson Falkner + * @author Harald Kuhr + * @author last modified by $Author: haku $ + * @version $Id: CacheResponseWrapper.java#3 $ + */ +class CacheResponseWrapper extends HttpServletResponseWrapper { + private ServletResponseStreamDelegate streamDelegate; + + private CacheResponse response; + private CachedEntity cached; + private WritableCachedResponse cachedResponse; + + private Boolean cacheable; + private int status; + + public CacheResponseWrapper(final ServletCacheResponse pResponse, final CachedEntity pCached) { + super(pResponse.getResponse()); + response = pResponse; + cached = pCached; + init(); + } + + /* + NOTE: This class defers determining if a response is cacheable until the + output stream is needed. + This it the reason for the somewhat complicated logic in the add/setHeader + methods below. + */ + private void init() { + cacheable = null; + status = SC_OK; + cachedResponse = cached.createCachedResponse(); + streamDelegate = new ServletResponseStreamDelegate(this) { + protected OutputStream createOutputStream() throws IOException { + // Test if this request is really cacheable, otherwise, + // just write through to underlying response, and don't cache + if (isCacheable()) { + return cachedResponse.getOutputStream(); + } + else { + cachedResponse.setStatus(status); + cachedResponse.writeHeadersTo(CacheResponseWrapper.this.response); + return super.getOutputStream(); + } + } + }; + } + + CachedResponse getCachedResponse() { + return cachedResponse.getCachedResponse(); + } + + public boolean isCacheable() { + // NOTE: Intentionally not synchronized + if (cacheable == null) { + cacheable = isCacheableImpl(); + } + + return cacheable; + } + + private boolean isCacheableImpl() { + if (status != SC_OK) { + return false; + } + + // Vary: * + String[] values = cachedResponse.getHeaderValues(HTTPCache.HEADER_VARY); + if (values != null) { + for (String value : values) { + if ("*".equals(value)) { + return false; + } + } + } + + // Cache-Control: no-cache, no-store, must-revalidate + values = cachedResponse.getHeaderValues(HTTPCache.HEADER_CACHE_CONTROL); + if (values != null) { + for (String value : values) { + if (StringUtil.contains(value, "no-cache") + || StringUtil.contains(value, "no-store") + || StringUtil.contains(value, "must-revalidate")) { + return false; + } + } + } + + // Pragma: no-cache + values = cachedResponse.getHeaderValues(HTTPCache.HEADER_PRAGMA); + if (values != null) { + for (String value : values) { + if (StringUtil.contains(value, "no-cache")) { + return false; + } + } + } + + return true; + } + + public void flushBuffer() throws IOException { + streamDelegate.flushBuffer(); + } + + public void resetBuffer() { + // Servlet 2.3 + streamDelegate.resetBuffer(); + } + + public void reset() { + if (Boolean.FALSE.equals(cacheable)) { + super.reset(); + } + // No else, might be cacheable after all.. + init(); + } + + public ServletOutputStream getOutputStream() throws IOException { + return streamDelegate.getOutputStream(); + } + + public PrintWriter getWriter() throws IOException { + return streamDelegate.getWriter(); + } + + public boolean containsHeader(String name) { + return cachedResponse.getHeaderValues(name) != null; + } + + public void sendError(int pStatusCode, String msg) throws IOException { + // NOT cacheable + status = pStatusCode; + super.sendError(pStatusCode, msg); + } + + public void sendError(int pStatusCode) throws IOException { + // NOT cacheable + status = pStatusCode; + super.sendError(pStatusCode); + } + + public void setStatus(int pStatusCode, String sm) { + // NOTE: This method is deprecated + setStatus(pStatusCode); + } + + public void setStatus(int pStatusCode) { + // NOT cacheable unless pStatusCode == 200 (or a FEW others?) + if (pStatusCode != SC_OK) { + status = pStatusCode; + super.setStatus(pStatusCode); + } + } + + public void sendRedirect(String pLocation) throws IOException { + // NOT cacheable + status = SC_MOVED_TEMPORARILY; + super.sendRedirect(pLocation); + } + + public void setDateHeader(String pName, long pValue) { + // If in write-trough-mode, set headers directly + if (Boolean.FALSE.equals(cacheable)) { + super.setDateHeader(pName, pValue); + } + cachedResponse.setHeader(pName, HTTPUtil.formatHTTPDate(pValue)); + } + + public void addDateHeader(String pName, long pValue) { + // If in write-trough-mode, set headers directly + if (Boolean.FALSE.equals(cacheable)) { + super.addDateHeader(pName, pValue); + } + cachedResponse.addHeader(pName, HTTPUtil.formatHTTPDate(pValue)); + } + + public void setHeader(String pName, String pValue) { + // If in write-trough-mode, set headers directly + if (Boolean.FALSE.equals(cacheable)) { + super.setHeader(pName, pValue); + } + cachedResponse.setHeader(pName, pValue); + } + + public void addHeader(String pName, String pValue) { + // If in write-trough-mode, set headers directly + if (Boolean.FALSE.equals(cacheable)) { + super.addHeader(pName, pValue); + } + cachedResponse.addHeader(pName, pValue); + } + + public void setIntHeader(String pName, int pValue) { + // If in write-trough-mode, set headers directly + if (Boolean.FALSE.equals(cacheable)) { + super.setIntHeader(pName, pValue); + } + cachedResponse.setHeader(pName, String.valueOf(pValue)); + } + + public void addIntHeader(String pName, int pValue) { + // If in write-trough-mode, set headers directly + if (Boolean.FALSE.equals(cacheable)) { + super.addIntHeader(pName, pValue); + } + cachedResponse.addHeader(pName, String.valueOf(pValue)); + } + + public final void setContentType(String type) { + setHeader(HTTPCache.HEADER_CONTENT_TYPE, type); + } } \ No newline at end of file diff --git a/servlet/src/main/java/com/twelvemonkeys/servlet/cache/CachedEntity.java b/servlet/src/main/java/com/twelvemonkeys/servlet/cache/CachedEntity.java index d68708a5..e82e5d82 100755 --- a/servlet/src/main/java/com/twelvemonkeys/servlet/cache/CachedEntity.java +++ b/servlet/src/main/java/com/twelvemonkeys/servlet/cache/CachedEntity.java @@ -1,75 +1,75 @@ -/* - * Copyright (c) 2008, Harald Kuhr - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name "TwelveMonkeys" nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package com.twelvemonkeys.servlet.cache; - -import java.io.IOException; - -/** - * CachedEntity - * - * @author Harald Kuhr - * @version $Id: CachedEntity.java#3 $ - */ -interface CachedEntity { - - /** - * Renders the cached entity to the response. - * - * @param pRequest the request - * @param pResponse the response - * @throws java.io.IOException if an I/O exception occurs - */ - void render(CacheRequest pRequest, CacheResponse pResponse) throws IOException; - - /** - * Captures (caches) the response for the given request. - * - * @param pRequest the request - * @param pResponse the response - * @throws java.io.IOException if an I/O exception occurs - * - * @see #createCachedResponse() - */ - void capture(CacheRequest pRequest, CachedResponse pResponse) throws IOException; - - /** - * Tests if the content of this entity is stale for the given request. - * - * @param pRequest the request - * @return {@code true} if content is stale - */ - boolean isStale(CacheRequest pRequest); - - /** - * Creates a {@code WritableCachedResponse} to use to capture the response. - * - * @return a {@code WritableCachedResponse} - */ - WritableCachedResponse createCachedResponse(); -} +/* + * Copyright (c) 2008, Harald Kuhr + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name "TwelveMonkeys" nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.twelvemonkeys.servlet.cache; + +import java.io.IOException; + +/** + * CachedEntity + * + * @author Harald Kuhr + * @version $Id: CachedEntity.java#3 $ + */ +interface CachedEntity { + + /** + * Renders the cached entity to the response. + * + * @param pRequest the request + * @param pResponse the response + * @throws java.io.IOException if an I/O exception occurs + */ + void render(CacheRequest pRequest, CacheResponse pResponse) throws IOException; + + /** + * Captures (caches) the response for the given request. + * + * @param pRequest the request + * @param pResponse the response + * @throws java.io.IOException if an I/O exception occurs + * + * @see #createCachedResponse() + */ + void capture(CacheRequest pRequest, CachedResponse pResponse) throws IOException; + + /** + * Tests if the content of this entity is stale for the given request. + * + * @param pRequest the request + * @return {@code true} if content is stale + */ + boolean isStale(CacheRequest pRequest); + + /** + * Creates a {@code WritableCachedResponse} to use to capture the response. + * + * @return a {@code WritableCachedResponse} + */ + WritableCachedResponse createCachedResponse(); +} diff --git a/servlet/src/main/java/com/twelvemonkeys/servlet/cache/CachedEntityImpl.java b/servlet/src/main/java/com/twelvemonkeys/servlet/cache/CachedEntityImpl.java index 1a502456..f612bca5 100755 --- a/servlet/src/main/java/com/twelvemonkeys/servlet/cache/CachedEntityImpl.java +++ b/servlet/src/main/java/com/twelvemonkeys/servlet/cache/CachedEntityImpl.java @@ -1,170 +1,170 @@ -/* - * Copyright (c) 2008, Harald Kuhr - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name "TwelveMonkeys" nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package com.twelvemonkeys.servlet.cache; - -import com.twelvemonkeys.lang.Validate; - -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.util.List; - -/** - * CachedEntity - * - * @author Harald Kuhr - * @version $Id: CachedEntityImpl.java#3 $ - */ -class CachedEntityImpl implements CachedEntity { - private String cacheURI; - private HTTPCache cache; - - CachedEntityImpl(String pCacheURI, HTTPCache pCache) { - cacheURI = Validate.notNull(pCacheURI, "cacheURI"); - cache = pCache; - } - - public void render(CacheRequest pRequest, CacheResponse pResponse) throws IOException { - // Get cached content - CachedResponse cached = cache.getContent(cacheURI, pRequest); - - // Sanity check - if (cached == null) { - throw new IllegalStateException("Tried to render non-cached response (cache == null)."); - } - - // If the cached entity is not modified since the date of the browsers - // version, then simply send a "304 Not Modified" response - // Otherwise send the full response. - - // TODO: WHY DID I COMMENT OUT THIS LINE AND REPLACE IT WITH THE ONE BELOW?? - //long lastModified = HTTPCache.getDateHeader(cached.getHeaderValue(HTTPCache.HEADER_LAST_MODIFIED)); - long lastModified = HTTPCache.getDateHeader(cached.getHeaderValue(HTTPCache.HEADER_CACHED_TIME)); - - // TODO: Consider handling time skews between server "now" and client "now"? - // NOTE: The If-Modified-Since is probably right according to the server - // even in a time skew situation, as the client should use either the - // Date or Last-Modifed dates from the response headers (server generated) - long ifModifiedSince = -1L; - try { - List
- * <!-- GZIP Filter Configuration --> - * <filter> - * <filter-name>gzip</filter-name> - * <filter-class>com.twelvemonkeys.servlet.GZIPFilter</filter-class> - * </filter> - *- * Filter-mapping section:
- * <!-- GZIP Filter Mapping --> - * <filter-mapping> - * <filter-name>gzip</filter-name> - * <url-pattern>*.html</url-pattern> - * <dispatcher>REQUEST</dispatcher> - * <dispatcher>FORWARD</dispatcher> - * </filter-mapping> - * <filter-mapping> - * <filter-name>gzip</filter-name> - * <url-pattern>*.jsp< /url-pattern> - * <dispatcher>REQUEST</dispatcher> - * <dispatcher>FORWARD</dispatcher> - * </filter-mapping> - *- * - * Based on ideas and code found in the ONJava article - * Two - * Servlet Filters Every Web Application Should Have - * by Jayson Falkner. - * - * - * @author Jayson Falkner - * @author Harald Kuhr - * @author last modified by $Author: haku $ - * @version $Id: GZIPFilter.java#1 $ - */ -public class GZIPFilter extends GenericFilter { - - { - oncePerRequest = true; - } - - protected void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain) throws IOException, ServletException { - // Can only filter HTTP responses - if (pRequest instanceof HttpServletRequest) { - HttpServletRequest request = (HttpServletRequest) pRequest; - HttpServletResponse response = (HttpServletResponse) pResponse; - - // If GZIP is supported, use compression - String accept = request.getHeader("Accept-Encoding"); - if (accept != null && accept.contains("gzip")) { - //System.out.println("GZIP supported, compressing."); - GZIPResponseWrapper wrapped = new GZIPResponseWrapper(response); - - try { - pChain.doFilter(pRequest, wrapped); - } - finally { - wrapped.flushResponse(); - } - - return; - } - } - - // Else, continue chain - pChain.doFilter(pRequest, pResponse); - } -} +/* + * Copyright (c) 2008, Harald Kuhr + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name "TwelveMonkeys" nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.twelvemonkeys.servlet.gzip; + +import com.twelvemonkeys.servlet.GenericFilter; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * A filter to reduce the output size of web resources. + * + * The HTTP protocol supports compression of the content to reduce network + * bandwidth. The important headers involved, are the {@code Accept-Encoding} + * request header, and the {@code Content-Encoding} response header. + * This feature can be used to further reduce the number of bytes transferred + * over the network, at the cost of some extra processing time at both endpoints. + * Most modern browsers supports compression in GZIP format, which is fairly + * efficient in cost/compression ratio. + * + * The filter tests for the presence of an {@code Accept-Encoding} header with a + * value of {@code "gzip"} (several different encoding header values are + * possible in one header). If not present, the filter simply passes the + * request/response pair through, leaving it untouched. If present, the + * {@code Content-Encoding} header is set, with the value {@code "gzip"}, + * and the response is wrapped. + * The response output stream is wrapped in a + * {@link java.util.zip.GZIPOutputStream} which performs the GZIP encoding. + * For efficiency, the filter does not buffer the response, but writes through + * the gzipped output stream. + * + * Configuration
+ * <!-- GZIP Filter Configuration --> + * <filter> + * <filter-name>gzip</filter-name> + * <filter-class>com.twelvemonkeys.servlet.GZIPFilter</filter-class> + * </filter> + *+ * Filter-mapping section:
+ * <!-- GZIP Filter Mapping --> + * <filter-mapping> + * <filter-name>gzip</filter-name> + * <url-pattern>*.html</url-pattern> + * <dispatcher>REQUEST</dispatcher> + * <dispatcher>FORWARD</dispatcher> + * </filter-mapping> + * <filter-mapping> + * <filter-name>gzip</filter-name> + * <url-pattern>*.jsp< /url-pattern> + * <dispatcher>REQUEST</dispatcher> + * <dispatcher>FORWARD</dispatcher> + * </filter-mapping> + *+ * + * Based on ideas and code found in the ONJava article + * Two + * Servlet Filters Every Web Application Should Have + * by Jayson Falkner. + * + * + * @author Jayson Falkner + * @author Harald Kuhr + * @author last modified by $Author: haku $ + * @version $Id: GZIPFilter.java#1 $ + */ +public class GZIPFilter extends GenericFilter { + + { + oncePerRequest = true; + } + + protected void doFilterImpl(ServletRequest pRequest, ServletResponse pResponse, FilterChain pChain) throws IOException, ServletException { + // Can only filter HTTP responses + if (pRequest instanceof HttpServletRequest) { + HttpServletRequest request = (HttpServletRequest) pRequest; + HttpServletResponse response = (HttpServletResponse) pResponse; + + // If GZIP is supported, use compression + String accept = request.getHeader("Accept-Encoding"); + if (accept != null && accept.contains("gzip")) { + //System.out.println("GZIP supported, compressing."); + GZIPResponseWrapper wrapped = new GZIPResponseWrapper(response); + + try { + pChain.doFilter(pRequest, wrapped); + } + finally { + wrapped.flushResponse(); + } + + return; + } + } + + // Else, continue chain + pChain.doFilter(pRequest, pResponse); + } +} diff --git a/servlet/src/main/java/com/twelvemonkeys/servlet/gzip/GZIPResponseWrapper.java b/servlet/src/main/java/com/twelvemonkeys/servlet/gzip/GZIPResponseWrapper.java index 4ee781da..84bc0357 100755 --- a/servlet/src/main/java/com/twelvemonkeys/servlet/gzip/GZIPResponseWrapper.java +++ b/servlet/src/main/java/com/twelvemonkeys/servlet/gzip/GZIPResponseWrapper.java @@ -1,147 +1,147 @@ -/* - * Copyright (c) 2008, Harald Kuhr - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name "TwelveMonkeys" nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package com.twelvemonkeys.servlet.gzip; - -import com.twelvemonkeys.servlet.OutputStreamAdapter; - -import javax.servlet.ServletOutputStream; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpServletResponseWrapper; -import java.io.IOException; -import java.io.OutputStreamWriter; -import java.io.PrintWriter; -import java.util.zip.GZIPOutputStream; - -/** - * GZIPResponseWrapper class description. - * - * Based on ideas and code found in the ONJava article - * Two Servlet Filters Every Web Application Should Have - * by Jayson Falkner. - * - * @author Jayson Falkner - * @author Harald Kuhr - * @author last modified by $Author: haku $ - * @version $Id: GZIPResponseWrapper.java#1 $ - */ -public class GZIPResponseWrapper extends HttpServletResponseWrapper { - // TODO: Remove/update ETags if needed? Read the spec (RFC 2616) on Vary/ETag for caching - - protected ServletOutputStream out; - protected PrintWriter writer; - protected GZIPOutputStream gzipOut; - protected int contentLength = -1; - - public GZIPResponseWrapper(final HttpServletResponse response) { - super(response); - - response.addHeader("Content-Encoding", "gzip"); - response.addHeader("Vary", "Accept"); - } - - public ServletOutputStream createOutputStream() throws IOException { - // FIX: Write directly to servlet output stream, for faster responses. - // Relies on chunked streams, or buffering in the servlet engine. - if (contentLength >= 0) { - gzipOut = new GZIPOutputStream(getResponse().getOutputStream(), contentLength); - } - else { - gzipOut = new GZIPOutputStream(getResponse().getOutputStream()); - } - - // Wrap in ServletOutputStream and return - return new OutputStreamAdapter(gzipOut); - } - - // TODO: Move this to flushbuffer or something? Hmmm.. - public void flushResponse() throws IOException { - try { - // Finish GZIP encodig - if (gzipOut != null) { - gzipOut.finish(); - } - - flushBuffer(); - } - finally { - // Close stream - if (writer != null) { - writer.close(); - } - else { - if (out != null) { - out.close(); - } - } - } - } - - public void flushBuffer() throws IOException { - if (writer != null) { - writer.flush(); - } - else if (out != null) { - out.flush(); - } - } - - public ServletOutputStream getOutputStream() throws IOException { - if (writer != null) { - throw new IllegalStateException("getWriter() has already been called!"); - } - - if (out == null) { - out = createOutputStream(); - } - - return out; - } - - public PrintWriter getWriter() throws IOException { - if (writer != null) { - return (writer); - } - - if (out != null) { - throw new IllegalStateException("getOutputStream() has already been called!"); - } - - out = createOutputStream(); - - // TODO: This is wrong. Should use getCharacterEncoding() or "ISO-8859-1" if getCE returns null. - writer = new PrintWriter(new OutputStreamWriter(out, "UTF-8")); - - return writer; - } - - public void setContentLength(int pLength) { - // NOTE: Do not call super, as we will shrink the size. - contentLength = pLength; - } -} +/* + * Copyright (c) 2008, Harald Kuhr + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name "TwelveMonkeys" nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.twelvemonkeys.servlet.gzip; + +import com.twelvemonkeys.servlet.OutputStreamAdapter; + +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpServletResponseWrapper; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.zip.GZIPOutputStream; + +/** + * GZIPResponseWrapper class description. + * + * Based on ideas and code found in the ONJava article + * Two Servlet Filters Every Web Application Should Have + * by Jayson Falkner. + * + * @author Jayson Falkner + * @author Harald Kuhr + * @author last modified by $Author: haku $ + * @version $Id: GZIPResponseWrapper.java#1 $ + */ +public class GZIPResponseWrapper extends HttpServletResponseWrapper { + // TODO: Remove/update ETags if needed? Read the spec (RFC 2616) on Vary/ETag for caching + + protected ServletOutputStream out; + protected PrintWriter writer; + protected GZIPOutputStream gzipOut; + protected int contentLength = -1; + + public GZIPResponseWrapper(final HttpServletResponse response) { + super(response); + + response.addHeader("Content-Encoding", "gzip"); + response.addHeader("Vary", "Accept"); + } + + public ServletOutputStream createOutputStream() throws IOException { + // FIX: Write directly to servlet output stream, for faster responses. + // Relies on chunked streams, or buffering in the servlet engine. + if (contentLength >= 0) { + gzipOut = new GZIPOutputStream(getResponse().getOutputStream(), contentLength); + } + else { + gzipOut = new GZIPOutputStream(getResponse().getOutputStream()); + } + + // Wrap in ServletOutputStream and return + return new OutputStreamAdapter(gzipOut); + } + + // TODO: Move this to flushbuffer or something? Hmmm.. + public void flushResponse() throws IOException { + try { + // Finish GZIP encodig + if (gzipOut != null) { + gzipOut.finish(); + } + + flushBuffer(); + } + finally { + // Close stream + if (writer != null) { + writer.close(); + } + else { + if (out != null) { + out.close(); + } + } + } + } + + public void flushBuffer() throws IOException { + if (writer != null) { + writer.flush(); + } + else if (out != null) { + out.flush(); + } + } + + public ServletOutputStream getOutputStream() throws IOException { + if (writer != null) { + throw new IllegalStateException("getWriter() has already been called!"); + } + + if (out == null) { + out = createOutputStream(); + } + + return out; + } + + public PrintWriter getWriter() throws IOException { + if (writer != null) { + return (writer); + } + + if (out != null) { + throw new IllegalStateException("getOutputStream() has already been called!"); + } + + out = createOutputStream(); + + // TODO: This is wrong. Should use getCharacterEncoding() or "ISO-8859-1" if getCE returns null. + writer = new PrintWriter(new OutputStreamWriter(out, "UTF-8")); + + return writer; + } + + public void setContentLength(int pLength) { + // NOTE: Do not call super, as we will shrink the size. + contentLength = pLength; + } +} diff --git a/servlet/src/main/java/com/twelvemonkeys/servlet/image/AWTImageFilterAdapter.java b/servlet/src/main/java/com/twelvemonkeys/servlet/image/AWTImageFilterAdapter.java index 7bee9158..11d231c1 100755 --- a/servlet/src/main/java/com/twelvemonkeys/servlet/image/AWTImageFilterAdapter.java +++ b/servlet/src/main/java/com/twelvemonkeys/servlet/image/AWTImageFilterAdapter.java @@ -1,72 +1,72 @@ -/* - * Copyright (c) 2008, Harald Kuhr - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name "TwelveMonkeys" nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package com.twelvemonkeys.servlet.image; - -import com.twelvemonkeys.image.ImageUtil; - -import javax.servlet.ServletRequest; -import java.awt.*; -import java.awt.image.BufferedImage; -import java.awt.image.RenderedImage; - -/** - * AWTImageFilterAdapter - * - * @author Harald Kuhr - * @version $Id: AWTImageFilterAdapter.java#1 $ - * - */ -public class AWTImageFilterAdapter extends ImageFilter { - - private java.awt.image.ImageFilter imageFilter = null; - - public void setImageFilter(String pFilterClass) { - try { - Class filterClass = Class.forName(pFilterClass); - imageFilter = (java.awt.image.ImageFilter) filterClass.newInstance(); - } - catch (ClassNotFoundException e) { - log("Could not load filter class.", e); - } - catch (InstantiationException e) { - log("Could not instantiate filter.", e); - } - catch (IllegalAccessException e) { - log("Could not access filter class.", e); - } - } - - protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) { - // Filter - Image img = ImageUtil.filter(pImage, imageFilter); - - // Create BufferedImage & return - return ImageUtil.toBuffered(img, BufferedImage.TYPE_INT_RGB); // TODO: This is ok for JPEG only... - } -} +/* + * Copyright (c) 2008, Harald Kuhr + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name "TwelveMonkeys" nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.twelvemonkeys.servlet.image; + +import com.twelvemonkeys.image.ImageUtil; + +import javax.servlet.ServletRequest; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.awt.image.RenderedImage; + +/** + * AWTImageFilterAdapter + * + * @author Harald Kuhr + * @version $Id: AWTImageFilterAdapter.java#1 $ + * + */ +public class AWTImageFilterAdapter extends ImageFilter { + + private java.awt.image.ImageFilter imageFilter = null; + + public void setImageFilter(String pFilterClass) { + try { + Class filterClass = Class.forName(pFilterClass); + imageFilter = (java.awt.image.ImageFilter) filterClass.newInstance(); + } + catch (ClassNotFoundException e) { + log("Could not load filter class.", e); + } + catch (InstantiationException e) { + log("Could not instantiate filter.", e); + } + catch (IllegalAccessException e) { + log("Could not access filter class.", e); + } + } + + protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) { + // Filter + Image img = ImageUtil.filter(pImage, imageFilter); + + // Create BufferedImage & return + return ImageUtil.toBuffered(img, BufferedImage.TYPE_INT_RGB); // TODO: This is ok for JPEG only... + } +} diff --git a/servlet/src/main/java/com/twelvemonkeys/servlet/image/BufferedImageOpAdapter.java b/servlet/src/main/java/com/twelvemonkeys/servlet/image/BufferedImageOpAdapter.java index d64540e6..cc0b52f9 100755 --- a/servlet/src/main/java/com/twelvemonkeys/servlet/image/BufferedImageOpAdapter.java +++ b/servlet/src/main/java/com/twelvemonkeys/servlet/image/BufferedImageOpAdapter.java @@ -1,67 +1,67 @@ -/* - * Copyright (c) 2008, Harald Kuhr - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name "TwelveMonkeys" nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package com.twelvemonkeys.servlet.image; - -import javax.servlet.ServletRequest; -import java.awt.image.BufferedImage; -import java.awt.image.BufferedImageOp; -import java.awt.image.RenderedImage; - -/** - * BufferedImageOpAdapter - * - * @author Harald Kuhr - * @version $Id: BufferedImageOpAdapter.java#1 $ - * - */ -public class BufferedImageOpAdapter extends ImageFilter { - - private BufferedImageOp filter = null; - - public void setImageFilter(String pFilterClass) { - try { - Class filterClass = Class.forName(pFilterClass); - filter = (BufferedImageOp) filterClass.newInstance(); - } - catch (ClassNotFoundException e) { - log("Could not instantiate filter class.", e); - } - catch (InstantiationException e) { - log("Could not instantiate filter.", e); - } - catch (IllegalAccessException e) { - log("Could not access filter class.", e); - } - } - - protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) { - // Filter & return - return filter.filter(pImage, null); - } -} +/* + * Copyright (c) 2008, Harald Kuhr + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name "TwelveMonkeys" nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.twelvemonkeys.servlet.image; + +import javax.servlet.ServletRequest; +import java.awt.image.BufferedImage; +import java.awt.image.BufferedImageOp; +import java.awt.image.RenderedImage; + +/** + * BufferedImageOpAdapter + * + * @author Harald Kuhr + * @version $Id: BufferedImageOpAdapter.java#1 $ + * + */ +public class BufferedImageOpAdapter extends ImageFilter { + + private BufferedImageOp filter = null; + + public void setImageFilter(String pFilterClass) { + try { + Class filterClass = Class.forName(pFilterClass); + filter = (BufferedImageOp) filterClass.newInstance(); + } + catch (ClassNotFoundException e) { + log("Could not instantiate filter class.", e); + } + catch (InstantiationException e) { + log("Could not instantiate filter.", e); + } + catch (IllegalAccessException e) { + log("Could not access filter class.", e); + } + } + + protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) { + // Filter & return + return filter.filter(pImage, null); + } +} diff --git a/servlet/src/main/java/com/twelvemonkeys/servlet/image/ColorServlet.java b/servlet/src/main/java/com/twelvemonkeys/servlet/image/ColorServlet.java index 22b40225..47ae42dd 100755 --- a/servlet/src/main/java/com/twelvemonkeys/servlet/image/ColorServlet.java +++ b/servlet/src/main/java/com/twelvemonkeys/servlet/image/ColorServlet.java @@ -1,211 +1,211 @@ -/* - * Copyright (c) 2008, Harald Kuhr - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name "TwelveMonkeys" nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package com.twelvemonkeys.servlet.image; - -import com.twelvemonkeys.servlet.GenericServlet; - -import javax.servlet.ServletException; -import javax.servlet.ServletOutputStream; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import java.io.IOException; -import java.util.zip.CRC32; - -/** - * Creates a minimal 1 x 1 pixel PNG image, in a color specified by the - * {@code "color"} parameter. The color is HTML-style #RRGGBB, with two - * digits hex number for red, green and blue (the hash, '#', is optional). - * - * The class does only byte manipulation, there is no server-side image - * processing involving AWT ({@code Toolkit} class) of any kind. - * - * @author Harald Kuhr - * @author last modified by $Author: haku $ - * @version $Id: ColorServlet.java#2 $ - */ -public class ColorServlet extends GenericServlet { - private final static String RGB_PARAME = "color"; - - // A minimal, one color indexed PNG - private final static byte[] PNG_IMG = new byte[]{ - (byte) 0x89, (byte) 'P', (byte) 'N', (byte) 'G', // PNG signature (8 bytes) - 0x0d, 0x0a, 0x1a, 0x0a, - - 0x00, 0x00, 0x00, 0x0d, // IHDR length (13) - (byte) 'I', (byte) 'H', (byte) 'D', (byte) 'R', // Image header - 0x00, 0x00, 0x00, 0x01, // width - 0x00, 0x00, 0x00, 0x01, // height - 0x01, 0x03, 0x00, 0x00, 0x00, // bits, color type, compression, filter, interlace - 0x25, (byte) 0xdb, 0x56, (byte) 0xca, // IHDR CRC - - 0x00, 0x00, 0x00, 0x03, // PLTE length (3) - (byte) 'P', (byte) 'L', (byte) 'T', (byte) 'E', // Palette - 0x00, 0x00, (byte) 0xff, // red, green, blue (updated by this servlet) - (byte) 0x8a, (byte) 0x78, (byte) 0xd2, 0x57, // PLTE CRC - - 0x00, 0x00, 0x00, 0x0a, // IDAT length (10) - (byte) 'I', (byte) 'D', (byte) 'A', (byte) 'T', // Image data - 0x78, (byte) 0xda, 0x63, 0x60, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, - (byte) 0xe5, 0x27, (byte) 0xde, (byte) 0xfc, // IDAT CRC - - - 0x00, 0x00, 0x00, 0x00, // IEND length (0) - (byte) 'I', (byte) 'E', (byte) 'N', (byte) 'D', // Image end - (byte) 0xae, (byte) 0x42, (byte) 0x60, (byte) 0x82 // IEND CRC - }; - - private final static int PLTE_CHUNK_START = 37; // after chunk length - private final static int PLTE_CHUNK_LENGTH = 7; // chunk name & data - - private final static int RED_IDX = 4; - private final static int GREEN_IDX = RED_IDX + 1; - private final static int BLUE_IDX = GREEN_IDX + 1; - - private final CRC32 crc = new CRC32(); - - /** - * Creates a ColorDroplet. - */ - public ColorServlet() { - super(); - } - - /** - * Renders the 1 x 1 single color PNG to the response. - * - * @see ColorServlet class description - * - * @param pRequest the request - * @param pResponse the response - * - * @throws IOException - * @throws ServletException - */ - public void service(ServletRequest pRequest, ServletResponse pResponse) throws IOException, ServletException { - int red = 0; - int green = 0; - int blue = 0; - - // Get color parameter and parse color - String rgb = pRequest.getParameter(RGB_PARAME); - if (rgb != null && rgb.length() >= 6 && rgb.length() <= 7) { - int index = 0; - - // If the hash ('#') character is included, skip it. - if (rgb.length() == 7) { - index++; - } - - try { - // Two digit hex for each color - String r = rgb.substring(index, index += 2); - red = Integer.parseInt(r, 0x10); - - String g = rgb.substring(index, index += 2); - green = Integer.parseInt(g, 0x10); - - String b = rgb.substring(index, index += 2); - blue = Integer.parseInt(b, 0x10); - } - catch (NumberFormatException nfe) { - log("Wrong color format for ColorDroplet: " + rgb + ". Must be RRGGBB."); - } - } - - // Set MIME type for PNG - pResponse.setContentType("image/png"); - ServletOutputStream out = pResponse.getOutputStream(); - - try { - // Write header (and palette chunk length) - out.write(PNG_IMG, 0, PLTE_CHUNK_START); - - // Create palette chunk, excl lenght, and write - byte[] palette = makePalette(red, green, blue); - out.write(palette); - - // Write image data until end - int pos = PLTE_CHUNK_START + PLTE_CHUNK_LENGTH + 4; - out.write(PNG_IMG, pos, PNG_IMG.length - pos); - } - finally { - out.flush(); - } - } - - /** - * Updates the CRC for a byte array. Note that the byte array must be at - * least {@code pOff + pLen + 4} bytes long, as the CRC is stored in the - * 4 last bytes. - * - * @param pBytes the bytes to create CRC for - * @param pOff the offset into the byte array to create CRC for - * @param pLen the length of the byte array to create CRC for - */ - private void updateCRC(byte[] pBytes, int pOff, int pLen) { - int value; - - synchronized (crc) { - crc.reset(); - crc.update(pBytes, pOff, pLen); - value = (int) crc.getValue(); - } - - pBytes[pOff + pLen ] = (byte) ((value >> 24) & 0xff); - pBytes[pOff + pLen + 1] = (byte) ((value >> 16) & 0xff); - pBytes[pOff + pLen + 2] = (byte) ((value >> 8) & 0xff); - pBytes[pOff + pLen + 3] = (byte) ( value & 0xff); - } - - /** - * Creates a PNG palette (PLTE) chunk with one color. - * The palette chunk data is always 3 bytes in length (one byte per color - * component). - * The returned byte array is then {@code 4 + 3 + 4 = 11} bytes, - * including chunk header, data and CRC. - * - * @param pRed the red component - * @param pGreen the reen component - * @param pBlue the blue component - * - * @return the bytes for the PLTE chunk, including CRC (but not length) - */ - private byte[] makePalette(int pRed, int pGreen, int pBlue) { - byte[] palette = new byte[PLTE_CHUNK_LENGTH + 4]; - System.arraycopy(PNG_IMG, PLTE_CHUNK_START, palette, 0, PLTE_CHUNK_LENGTH); - - palette[RED_IDX] = (byte) pRed; - palette[GREEN_IDX] = (byte) pGreen; - palette[BLUE_IDX] = (byte) pBlue; - - updateCRC(palette, 0, PLTE_CHUNK_LENGTH); - - return palette; - } -} +/* + * Copyright (c) 2008, Harald Kuhr + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name "TwelveMonkeys" nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.twelvemonkeys.servlet.image; + +import com.twelvemonkeys.servlet.GenericServlet; + +import javax.servlet.ServletException; +import javax.servlet.ServletOutputStream; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import java.io.IOException; +import java.util.zip.CRC32; + +/** + * Creates a minimal 1 x 1 pixel PNG image, in a color specified by the + * {@code "color"} parameter. The color is HTML-style #RRGGBB, with two + * digits hex number for red, green and blue (the hash, '#', is optional). + * + * The class does only byte manipulation, there is no server-side image + * processing involving AWT ({@code Toolkit} class) of any kind. + * + * @author Harald Kuhr + * @author last modified by $Author: haku $ + * @version $Id: ColorServlet.java#2 $ + */ +public class ColorServlet extends GenericServlet { + private final static String RGB_PARAME = "color"; + + // A minimal, one color indexed PNG + private final static byte[] PNG_IMG = new byte[]{ + (byte) 0x89, (byte) 'P', (byte) 'N', (byte) 'G', // PNG signature (8 bytes) + 0x0d, 0x0a, 0x1a, 0x0a, + + 0x00, 0x00, 0x00, 0x0d, // IHDR length (13) + (byte) 'I', (byte) 'H', (byte) 'D', (byte) 'R', // Image header + 0x00, 0x00, 0x00, 0x01, // width + 0x00, 0x00, 0x00, 0x01, // height + 0x01, 0x03, 0x00, 0x00, 0x00, // bits, color type, compression, filter, interlace + 0x25, (byte) 0xdb, 0x56, (byte) 0xca, // IHDR CRC + + 0x00, 0x00, 0x00, 0x03, // PLTE length (3) + (byte) 'P', (byte) 'L', (byte) 'T', (byte) 'E', // Palette + 0x00, 0x00, (byte) 0xff, // red, green, blue (updated by this servlet) + (byte) 0x8a, (byte) 0x78, (byte) 0xd2, 0x57, // PLTE CRC + + 0x00, 0x00, 0x00, 0x0a, // IDAT length (10) + (byte) 'I', (byte) 'D', (byte) 'A', (byte) 'T', // Image data + 0x78, (byte) 0xda, 0x63, 0x60, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, + (byte) 0xe5, 0x27, (byte) 0xde, (byte) 0xfc, // IDAT CRC + + + 0x00, 0x00, 0x00, 0x00, // IEND length (0) + (byte) 'I', (byte) 'E', (byte) 'N', (byte) 'D', // Image end + (byte) 0xae, (byte) 0x42, (byte) 0x60, (byte) 0x82 // IEND CRC + }; + + private final static int PLTE_CHUNK_START = 37; // after chunk length + private final static int PLTE_CHUNK_LENGTH = 7; // chunk name & data + + private final static int RED_IDX = 4; + private final static int GREEN_IDX = RED_IDX + 1; + private final static int BLUE_IDX = GREEN_IDX + 1; + + private final CRC32 crc = new CRC32(); + + /** + * Creates a ColorDroplet. + */ + public ColorServlet() { + super(); + } + + /** + * Renders the 1 x 1 single color PNG to the response. + * + * @see ColorServlet class description + * + * @param pRequest the request + * @param pResponse the response + * + * @throws IOException + * @throws ServletException + */ + public void service(ServletRequest pRequest, ServletResponse pResponse) throws IOException, ServletException { + int red = 0; + int green = 0; + int blue = 0; + + // Get color parameter and parse color + String rgb = pRequest.getParameter(RGB_PARAME); + if (rgb != null && rgb.length() >= 6 && rgb.length() <= 7) { + int index = 0; + + // If the hash ('#') character is included, skip it. + if (rgb.length() == 7) { + index++; + } + + try { + // Two digit hex for each color + String r = rgb.substring(index, index += 2); + red = Integer.parseInt(r, 0x10); + + String g = rgb.substring(index, index += 2); + green = Integer.parseInt(g, 0x10); + + String b = rgb.substring(index, index += 2); + blue = Integer.parseInt(b, 0x10); + } + catch (NumberFormatException nfe) { + log("Wrong color format for ColorDroplet: " + rgb + ". Must be RRGGBB."); + } + } + + // Set MIME type for PNG + pResponse.setContentType("image/png"); + ServletOutputStream out = pResponse.getOutputStream(); + + try { + // Write header (and palette chunk length) + out.write(PNG_IMG, 0, PLTE_CHUNK_START); + + // Create palette chunk, excl lenght, and write + byte[] palette = makePalette(red, green, blue); + out.write(palette); + + // Write image data until end + int pos = PLTE_CHUNK_START + PLTE_CHUNK_LENGTH + 4; + out.write(PNG_IMG, pos, PNG_IMG.length - pos); + } + finally { + out.flush(); + } + } + + /** + * Updates the CRC for a byte array. Note that the byte array must be at + * least {@code pOff + pLen + 4} bytes long, as the CRC is stored in the + * 4 last bytes. + * + * @param pBytes the bytes to create CRC for + * @param pOff the offset into the byte array to create CRC for + * @param pLen the length of the byte array to create CRC for + */ + private void updateCRC(byte[] pBytes, int pOff, int pLen) { + int value; + + synchronized (crc) { + crc.reset(); + crc.update(pBytes, pOff, pLen); + value = (int) crc.getValue(); + } + + pBytes[pOff + pLen ] = (byte) ((value >> 24) & 0xff); + pBytes[pOff + pLen + 1] = (byte) ((value >> 16) & 0xff); + pBytes[pOff + pLen + 2] = (byte) ((value >> 8) & 0xff); + pBytes[pOff + pLen + 3] = (byte) ( value & 0xff); + } + + /** + * Creates a PNG palette (PLTE) chunk with one color. + * The palette chunk data is always 3 bytes in length (one byte per color + * component). + * The returned byte array is then {@code 4 + 3 + 4 = 11} bytes, + * including chunk header, data and CRC. + * + * @param pRed the red component + * @param pGreen the reen component + * @param pBlue the blue component + * + * @return the bytes for the PLTE chunk, including CRC (but not length) + */ + private byte[] makePalette(int pRed, int pGreen, int pBlue) { + byte[] palette = new byte[PLTE_CHUNK_LENGTH + 4]; + System.arraycopy(PNG_IMG, PLTE_CHUNK_START, palette, 0, PLTE_CHUNK_LENGTH); + + palette[RED_IDX] = (byte) pRed; + palette[GREEN_IDX] = (byte) pGreen; + palette[BLUE_IDX] = (byte) pBlue; + + updateCRC(palette, 0, PLTE_CHUNK_LENGTH); + + return palette; + } +} diff --git a/servlet/src/main/java/com/twelvemonkeys/servlet/image/ComposeFilter.java b/servlet/src/main/java/com/twelvemonkeys/servlet/image/ComposeFilter.java index 6dec5d60..7fa3f02c 100755 --- a/servlet/src/main/java/com/twelvemonkeys/servlet/image/ComposeFilter.java +++ b/servlet/src/main/java/com/twelvemonkeys/servlet/image/ComposeFilter.java @@ -1,59 +1,59 @@ -/* - * Copyright (c) 2008, Harald Kuhr - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name "TwelveMonkeys" nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package com.twelvemonkeys.servlet.image; - -import javax.servlet.ServletRequest; -import java.awt.image.RenderedImage; -import java.awt.image.BufferedImage; -import java.io.IOException; - -/** - * ComposeFilter - * - * - * @author Harald Kuhr - * @version $Id: ComposeFilter.java#1 $ - */ -public class ComposeFilter extends ImageFilter { - protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) throws IOException { - // 1. Load different image, locally (using ServletContext.getResource) - // - Allow loading other filtered sources, or servlets? For example to - // apply filename or timestamps? - // - Allow applying text directly? Variables? - // 2. Apply transformations from config - // - Relative positioning - // - Relative scaling - // - Repeat (fill-pattern)? - // - Rotation? - // - Transparency? - // - Background or foreground (layers)? - // 3. Apply loaded image to original image (or vice versa?). - return pImage; - } -} +/* + * Copyright (c) 2008, Harald Kuhr + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name "TwelveMonkeys" nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.twelvemonkeys.servlet.image; + +import javax.servlet.ServletRequest; +import java.awt.image.RenderedImage; +import java.awt.image.BufferedImage; +import java.io.IOException; + +/** + * ComposeFilter + * + * + * @author Harald Kuhr + * @version $Id: ComposeFilter.java#1 $ + */ +public class ComposeFilter extends ImageFilter { + protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) throws IOException { + // 1. Load different image, locally (using ServletContext.getResource) + // - Allow loading other filtered sources, or servlets? For example to + // apply filename or timestamps? + // - Allow applying text directly? Variables? + // 2. Apply transformations from config + // - Relative positioning + // - Relative scaling + // - Repeat (fill-pattern)? + // - Rotation? + // - Transparency? + // - Background or foreground (layers)? + // 3. Apply loaded image to original image (or vice versa?). + return pImage; + } +} diff --git a/servlet/src/main/java/com/twelvemonkeys/servlet/image/ContentNegotiationFilter.java b/servlet/src/main/java/com/twelvemonkeys/servlet/image/ContentNegotiationFilter.java index 90920683..b0d33327 100755 --- a/servlet/src/main/java/com/twelvemonkeys/servlet/image/ContentNegotiationFilter.java +++ b/servlet/src/main/java/com/twelvemonkeys/servlet/image/ContentNegotiationFilter.java @@ -1,439 +1,439 @@ -/* - * Copyright (c) 2008, Harald Kuhr - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name "TwelveMonkeys" nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package com.twelvemonkeys.servlet.image; - -import com.twelvemonkeys.image.ImageUtil; -import com.twelvemonkeys.lang.StringUtil; - -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.imageio.ImageIO; -import java.awt.*; -import java.awt.image.BufferedImage; -import java.awt.image.IndexColorModel; -import java.awt.image.RenderedImage; -import java.io.IOException; -import java.net.HttpURLConnection; -import java.util.*; - -/** - * This filter implements server side content negotiation and transcoding for - * images. - * - * @todo Add support for automatic recognition of known browsers, to avoid - * unneccessary conversion (as IE supports PNG, the latests FireFox supports - * JPEG and GIF, etc. even though they both don't explicitly list these formats - * in their Accept headers). - * - * @author Harald Kuhr - * @version $Id: ContentNegotiationFilter.java#1 $ - */ -public class ContentNegotiationFilter extends ImageFilter { - - private final static String MIME_TYPE_IMAGE_PREFIX = "image/"; - private static final String MIME_TYPE_IMAGE_ANY = MIME_TYPE_IMAGE_PREFIX + "*"; - private static final String MIME_TYPE_ANY = "*/*"; - private static final String HTTP_HEADER_ACCEPT = "Accept"; - private static final String HTTP_HEADER_VARY = "Vary"; - protected static final String HTTP_HEADER_USER_AGENT = "User-Agent"; - - private static final String FORMAT_JPEG = "image/jpeg"; - private static final String FORMAT_WBMP = "image/wbmp"; - private static final String FORMAT_GIF = "image/gif"; - private static final String FORMAT_PNG = "image/png"; - - private final static String[] sKnownFormats = new String[] { - FORMAT_JPEG, FORMAT_PNG, FORMAT_GIF, FORMAT_WBMP - }; - private float[] knownFormatQuality = new float[] { - 1f, 1f, 0.99f, 0.5f - }; - - private HashMap
- * @author Harald Kuhr - * @version $Id: ImageServletResponseImpl.java#10 $ - */ -// TODO: Refactor out HTTP specifics (if possible). -// TODO: Is it a good ide to throw IIOException? -// TODO: This implementation has a problem if two filters does scaling, as the second will overwrite the SIZE attribute -// TODO: Allow different scaling algorithm based on input image (use case: IndexColorModel does not scale well using default, smooth may be slow for large images) -// TODO: Support pluggable pre- and post-processing steps -class ImageServletResponseImpl extends HttpServletResponseWrapper implements ImageServletResponse { - private ServletRequest originalRequest; - private final ServletContext context; - private final ServletResponseStreamDelegate streamDelegate; - - private FastByteArrayOutputStream bufferedOut; - - private RenderedImage image; - private String outputContentType; - - private String originalContentType; - private int originalContentLength = -1; - - /** - * Creates an {@code ImageServletResponseImpl}. - * - * @param pRequest the request - * @param pResponse the response - * @param pContext the servlet context - */ - public ImageServletResponseImpl(final HttpServletRequest pRequest, final HttpServletResponse pResponse, final ServletContext pContext) { - super(pResponse); - originalRequest = pRequest; - streamDelegate = new ServletResponseStreamDelegate(pResponse) { - @Override - protected OutputStream createOutputStream() throws IOException { - if (originalContentLength >= 0) { - bufferedOut = new FastByteArrayOutputStream(originalContentLength); - } - else { - bufferedOut = new FastByteArrayOutputStream(0); - } - - return bufferedOut; - } - }; - context = pContext; - } - - /** - * Creates an {@code ImageServletResponseImpl}. - * - * @param pRequest the request - * @param pResponse the response - * @param pContext the servlet context - * - * @throws ClassCastException if {@code pRequest} is not an {@link javax.servlet.http.HttpServletRequest} or - * {@code pResponse} is not an {@link javax.servlet.http.HttpServletResponse}. - */ - public ImageServletResponseImpl(final ServletRequest pRequest, final ServletResponse pResponse, final ServletContext pContext) { - // Cheat for now... - this((HttpServletRequest) pRequest, (HttpServletResponse) pResponse, pContext); - } - - /** - * Called by the container, do not invoke. - * - * @param pMimeType the content (MIME) type - */ - public void setContentType(final String pMimeType) { - // Throw exception is already set - if (originalContentType != null) { - throw new IllegalStateException("ContentType already set."); - } - - originalContentType = pMimeType; - } - - /** - * Called by the container. Do not invoke. - * - * @return the response's {@code OutputStream} - * @throws IOException - */ - public ServletOutputStream getOutputStream() throws IOException { - return streamDelegate.getOutputStream(); - } - - /** - * Called by the container. Do not invoke. - * - * @return the response's {@code PrintWriter} - * @throws IOException - */ - public PrintWriter getWriter() throws IOException { - return streamDelegate.getWriter(); - } - - /** - * Called by the container. Do not invoke. - * - * @param pLength the content length - */ - public void setContentLength(final int pLength) { - if (originalContentLength != -1) { - throw new IllegalStateException("ContentLength already set."); - } - - originalContentLength = pLength; - } - - @Override - public void setHeader(String name, String value) { - // NOTE: Clients could also specify content type/content length using the setHeader method, special handling - if (name != null && name.equals("Content-Length")) { - setContentLength(Integer.valueOf(value)); // Value might be too large, but we don't support that anyway - } - else if (name != null && name.equals("Content-Type")) { - setContentType(value); - } - else { - super.setHeader(name, value); - } - } - - /** - * Writes the image to the original {@code ServletOutputStream}. - * If no format is set in this response, the image is encoded in the same - * format as the original image. - * - * @throws IOException if an I/O exception occurs during writing - */ - public void flush() throws IOException { - String outputType = getOutputContentType(); - - // Force transcoding, if no other filtering is done - if (outputType != null && !outputType.equals(originalContentType)) { - getImage(); - } - - if (image != null) { - Iterator writers = ImageIO.getImageWritersByMIMEType(outputType); - if (writers.hasNext()) { - super.setContentType(outputType); - OutputStream out = super.getOutputStream(); - try { - ImageWriter writer = (ImageWriter) writers.next(); - try { - ImageWriteParam param = writer.getDefaultWriteParam(); - /////////////////// - // POST-PROCESS - // For known formats that don't support transparency, convert to opaque - if (isNonAlphaFormat(outputType) && image.getColorModel().getTransparency() != Transparency.OPAQUE) { - image = ImageUtil.toBuffered(image, BufferedImage.TYPE_INT_RGB); - } - - Float requestQuality = (Float) originalRequest.getAttribute(ImageServletResponse.ATTRIB_OUTPUT_QUALITY); - - // The default JPEG quality is not good enough, so always adjust compression/quality - if ((requestQuality != null || "jpeg".equalsIgnoreCase(getFormatNameSafe(writer))) && param.canWriteCompressed()) { - // TODO: See http://blog.apokalyptik.com/2009/09/16/quality-time-with-your-jpegs/ for better adjusting the (default) JPEG quality - // OR: Use the metadata of the original image - - param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); - - // WORKAROUND: Known bug in GIFImageWriter in certain JDK versions, compression type is not set by default - if (param.getCompressionTypes() != null && param.getCompressionType() == null) { - param.setCompressionType(param.getCompressionTypes()[0]); // Just choose any, to keep param happy - } - - param.setCompressionQuality(requestQuality != null ? requestQuality : 0.8f); - } - - if ("gif".equalsIgnoreCase(getFormatNameSafe(writer)) && !(image.getColorModel() instanceof IndexColorModel) - /*&& image.getColorModel().getTransparency() != Transparency.OPAQUE*/) { - // WORKAROUND: Bug in GIFImageWriter may throw NPE if transparent pixels - // See: http://bugs.sun.com/view_bug.do?bug_id=6287936 - image = ImageUtil.createIndexed( - ImageUtil.toBuffered(image), 256, null, - (image.getColorModel().getTransparency() == Transparency.OPAQUE ? ImageUtil.TRANSPARENCY_OPAQUE : ImageUtil.TRANSPARENCY_BITMASK) | ImageUtil.DITHER_DIFFUSION_ALTSCANS - ); - } - ////////////////// - ImageOutputStream stream = ImageIO.createImageOutputStream(out); - - writer.setOutput(stream); - try { - writer.write(null, new IIOImage(image, null, null), param); - } - finally { - stream.close(); - } - } - finally { - writer.dispose(); - } - } - finally { - out.flush(); - } - } - else { - context.log("ERROR: No writer for content-type: " + outputType); - throw new IIOException("Unable to transcode image: No suitable image writer found (content-type: " + outputType + ")."); - } - } - else { - super.setContentType(originalContentType); - - ServletOutputStream out = super.getOutputStream(); - - try { - if (bufferedOut != null) { - bufferedOut.writeTo(out); - } - } - finally { - out.flush(); - } - } - } - - private boolean isNonAlphaFormat(String outputType) { - return "image/jpeg".equals(outputType) || "image/jpg".equals(outputType) || - "image/bmp".equals(outputType) || "image/x-bmp".equals(outputType); - } - - private String getFormatNameSafe(final ImageWriter pWriter) { - try { - return pWriter.getOriginatingProvider().getFormatNames()[0]; - } - catch (RuntimeException e) { - // NPE, AIOOBE, etc.. - return null; - } - } - - public String getOutputContentType() { - return outputContentType != null ? outputContentType : originalContentType; - } - - public void setOutputContentType(final String pImageFormat) { - outputContentType = pImageFormat; - } - - /** - * Sets the image for this response. - * - * @param pImage the {@code RenderedImage} that will be written to the - * response stream - */ - public void setImage(final RenderedImage pImage) { - image = pImage; - } - - /** - * Gets the decoded image from the response. - * - * @return a {@code BufferedImage} or {@code null} if the image could - * not be read. - * - * @throws java.io.IOException if an I/O exception occurs during reading - */ - public BufferedImage getImage() throws IOException { - if (image == null) { - // No content, no image - if (bufferedOut == null) { - return null; - } - - // Read from the byte buffer - InputStream byteStream = bufferedOut.createInputStream(); - ImageInputStream input = null; - try { - input = ImageIO.createImageInputStream(byteStream); - Iterator readers = ImageIO.getImageReaders(input); - if (readers.hasNext()) { - // Get the correct reader - ImageReader reader = (ImageReader) readers.next(); - try { - reader.setInput(input); - - ImageReadParam param = reader.getDefaultReadParam(); - - // Get default size - int originalWidth = reader.getWidth(0); - int originalHeight = reader.getHeight(0); -////////////////// -// PRE-PROCESS (prepare): param, size, format?, request, response? - // TODO: AOI strategy? - // Extract AOI from request - Rectangle aoi = extractAOIFromRequest(originalWidth, originalHeight, originalRequest); - - if (aoi != null) { - param.setSourceRegion(aoi); - originalWidth = aoi.width; - originalHeight = aoi.height; - } - - // TODO: Size and subsampling strategy? - // If possible, extract size from request - Dimension size = extractSizeFromRequest(originalWidth, originalHeight, originalRequest); - double readSubSamplingFactor = getReadSubsampleFactorFromRequest(originalRequest); - - if (size != null) { - //System.out.println("Size: " + size); - if (param.canSetSourceRenderSize()) { - param.setSourceRenderSize(size); - } - else { - int subX = (int) Math.max(originalWidth / (size.width * readSubSamplingFactor), 1.0); - int subY = (int) Math.max(originalHeight / (size.height * readSubSamplingFactor), 1.0); - - if (subX > 1 || subY > 1) { - param.setSourceSubsampling(subX, subY, subX > 1 ? subX / 2 : 0, subY > 1 ? subY / 2 : 0); - } - } - } - - // Need base URI for SVG with links/stylesheets etc - maybeSetBaseURIFromRequest(param); - -///////////////////// - - // Finally, read the image using the supplied parameter - BufferedImage image = reader.read(0, param); - - // TODO: If we sub-sampled, it would be a good idea to blur before resampling, - // to avoid jagged lines artifacts - - // If reader doesn't support dynamic sizing, scale now - image = resampleImage(image, size); - - // Fill bgcolor behind image, if transparent - extractAndSetBackgroundColor(image); // TODO: Move to flush/POST-PROCESS - - // Set image - this.image = image; - } - finally { - reader.dispose(); - } - } - else { - context.log("ERROR: No suitable image reader found (content-type: " + originalContentType + ")."); - context.log("ERROR: Available formats: " + getFormatsString()); - - throw new IIOException("Unable to transcode image: No suitable image reader found (content-type: " + originalContentType + ")."); - } - - // Free resources, as the image is now either read, or unreadable - bufferedOut = null; - } - finally { - if (input != null) { - input.close(); - } - } - } - - // Image is usually a BufferedImage, but may also be a RenderedImage - return image != null ? ImageUtil.toBuffered(image) : null; - } - - private BufferedImage resampleImage(final BufferedImage image, final Dimension size) { - if (image != null && size != null && (image.getWidth() != size.width || image.getHeight() != size.height)) { - int resampleAlgorithm = getResampleAlgorithmFromRequest(); - - // TODO: One possibility is to NOT handle index color here, and only handle it later, IF NEEDED (read: GIF, - // possibly also for PNG) when we know the output format (flush method). - // This will make the filter faster (and better quality, possibly at the expense of more bytes being sent - // over the wire) in the general case. Who uses GIF nowadays anyway? - // Also, this means we could either keep the original IndexColorModel in the filter, or go through the - // expensive operation of re-calculating the optimal palette for the new image (the latter might improve quality). - - // NOTE: Only use createScaled if IndexColorModel, as it's more expensive due to color conversion -/* if (image.getColorModel() instanceof IndexColorModel) { -// return ImageUtil.createScaled(image, size.width, size.height, resampleAlgorithm); - BufferedImage resampled = ImageUtil.createResampled(image, size.width, size.height, resampleAlgorithm); - return ImageUtil.createIndexed(resampled, (IndexColorModel) image.getColorModel(), null, ImageUtil.DITHER_NONE | ImageUtil.TRANSPARENCY_BITMASK); -// return ImageUtil.createIndexed(resampled, 256, null, ImageUtil.COLOR_SELECTION_QUALITY | ImageUtil.DITHER_NONE | ImageUtil.TRANSPARENCY_BITMASK); - } - else { - */ - return ImageUtil.createResampled(image, size.width, size.height, resampleAlgorithm); -// } - } - return image; - } - - int getResampleAlgorithmFromRequest() { - Object algorithm = originalRequest.getAttribute(ATTRIB_IMAGE_RESAMPLE_ALGORITHM); - if (algorithm instanceof Integer && ((Integer) algorithm == Image.SCALE_SMOOTH || (Integer) algorithm == Image.SCALE_FAST || (Integer) algorithm == Image.SCALE_DEFAULT)) { - return (Integer) algorithm; - } - else { - if (algorithm != null) { - context.log("WARN: Illegal image resampling algorithm: " + algorithm); - } - - return BufferedImage.SCALE_DEFAULT; - } - } - - private double getReadSubsampleFactorFromRequest(final ServletRequest pOriginalRequest) { - double subsampleFactor; - - Object factor = pOriginalRequest.getAttribute(ATTRIB_READ_SUBSAMPLING_FACTOR); - if (factor instanceof Number && ((Number) factor).doubleValue() >= 1.0) { - subsampleFactor = ((Number) factor).doubleValue(); - } - else { - if (factor != null) { - context.log("WARN: Illegal read subsampling factor: " + factor); - } - - subsampleFactor = 2.0; - } - - return subsampleFactor; - } - - private void extractAndSetBackgroundColor(final BufferedImage pImage) { - // TODO: bgColor request attribute instead of parameter? - if (pImage.getColorModel().hasAlpha()) { - String bgColor = originalRequest.getParameter("bg.color"); - if (bgColor != null) { - Color color = StringUtil.toColor(bgColor); - - Graphics2D g = pImage.createGraphics(); - try { - g.setColor(color); - g.setComposite(AlphaComposite.DstOver); - g.fillRect(0, 0, pImage.getWidth(), pImage.getHeight()); - } - finally { - g.dispose(); - } - } - } - } - - private static String getFormatsString() { - String[] formats = ImageIO.getReaderFormatNames(); - StringBuilder buf = new StringBuilder(); - for (int i = 0; i < formats.length; i++) { - String format = formats[i]; - if (i > 0) { - buf.append(", "); - } - buf.append(format); - } - return buf.toString(); - } - - private void maybeSetBaseURIFromRequest(final ImageReadParam pParam) { - if (originalRequest instanceof HttpServletRequest) { - try { - // If there's a setBaseURI method, we'll try to use that (uses reflection, to avoid dependency on plugins) - Method setBaseURI; - try { - setBaseURI = pParam.getClass().getMethod("setBaseURI", String.class); - } - catch (NoSuchMethodException ignore) { - return; - } - - // Get URL for resource and set as base - String baseURI = ServletUtil.getContextRelativeURI((HttpServletRequest) originalRequest); - - URL resourceURL = context.getResource(baseURI); - - if (resourceURL == null) { - resourceURL = ServletUtil.getRealURL(context, baseURI); - } - - if (resourceURL != null) { - setBaseURI.invoke(pParam, resourceURL.toExternalForm()); - } - else { - context.log("WARN: Resource URL not found for URI: " + baseURI); - } - } - catch (Exception e) { - context.log("WARN: Could not set base URI: ", e); - } - } - } - - private Dimension extractSizeFromRequest(final int pDefaultWidth, final int pDefaultHeight, final ServletRequest pOriginalRequest) { - // TODO: Allow extraction from request parameters - /* - int sizeW = ServletUtil.getIntParameter(originalRequest, "size.w", -1); - int sizeH = ServletUtil.getIntParameter(originalRequest, "size.h", -1); - boolean sizePercent = ServletUtil.getBooleanParameter(originalRequest, "size.percent", false); - boolean sizeUniform = ServletUtil.getBooleanParameter(originalRequest, "size.uniform", true); - */ - Dimension size = (Dimension) pOriginalRequest.getAttribute(ATTRIB_SIZE); - int sizeW = size != null ? size.width : -1; - int sizeH = size != null ? size.height : -1; - - Boolean b = (Boolean) pOriginalRequest.getAttribute(ATTRIB_SIZE_PERCENT); - boolean sizePercent = b != null && b; // default: false - - b = (Boolean) pOriginalRequest.getAttribute(ATTRIB_SIZE_UNIFORM); - boolean sizeUniform = b == null || b; // default: true - - if (sizeW >= 0 || sizeH >= 0) { - size = getSize(pDefaultWidth, pDefaultHeight, sizeW, sizeH, sizePercent, sizeUniform); - } - - return size; - } - - private Rectangle extractAOIFromRequest(final int pDefaultWidth, final int pDefaultHeight, final ServletRequest pOriginalRequest) { - // TODO: Allow extraction from request parameters - /* - int aoiX = ServletUtil.getIntParameter(originalRequest, "aoi.x", -1); - int aoiY = ServletUtil.getIntParameter(originalRequest, "aoi.y", -1); - int aoiW = ServletUtil.getIntParameter(originalRequest, "aoi.w", -1); - int aoiH = ServletUtil.getIntParameter(originalRequest, "aoi.h", -1); - boolean aoiPercent = ServletUtil.getBooleanParameter(originalRequest, "aoi.percent", false); - boolean aoiUniform = ServletUtil.getBooleanParameter(originalRequest, "aoi.uniform", false); - */ - Rectangle aoi = (Rectangle) pOriginalRequest.getAttribute(ATTRIB_AOI); - int aoiX = aoi != null ? aoi.x : -1; - int aoiY = aoi != null ? aoi.y : -1; - int aoiW = aoi != null ? aoi.width : -1; - int aoiH = aoi != null ? aoi.height : -1; - - Boolean b = (Boolean) pOriginalRequest.getAttribute(ATTRIB_AOI_PERCENT); - boolean aoiPercent = b != null && b; // default: false - - b = (Boolean) pOriginalRequest.getAttribute(ATTRIB_AOI_UNIFORM); - boolean aoiUniform = b != null && b; // default: false - - if (aoiX >= 0 || aoiY >= 0 || aoiW >= 0 || aoiH >= 0) { - - AreaOfInterest areaOfInterest = AreaOfInterestFactory.getDefault(). - createAreaOfInterest(pDefaultWidth, pDefaultHeight, aoiPercent, aoiUniform); - aoi = areaOfInterest.getAOI(new Rectangle(aoiX, aoiY, aoiW, aoiH)); - return aoi; - } - - return null; - } - - // TODO: Move these to ImageUtil or similar, as they are often used... - // TODO: Consider separate methods for percent and pixels - /** - * Gets the dimensions (height and width) of the scaled image. The - * dimensions are computed based on the old image's dimensions, the units - * used for specifying new dimensions and whether or not uniform scaling - * should be used (se algorithm below). - * - * @param pOriginalWidth the original width of the image - * @param pOriginalHeight the original height of the image - * @param pWidth the new width of the image, or -1 if unknown - * @param pHeight the new height of the image, or -1 if unknown - * @param pPercent the constant specifying units for width and height - * parameter (UNITS_PIXELS or UNITS_PERCENT) - * @param pUniform boolean specifying uniform scale or not - * @return a Dimension object, with the correct width and heigth - * in pixels, for the scaled version of the image. - */ - static Dimension getSize(int pOriginalWidth, int pOriginalHeight, - int pWidth, int pHeight, - boolean pPercent, boolean pUniform) { - - // If uniform, make sure width and height are scaled the same amount - // (use ONLY height or ONLY width). - // - // Algorithm: - // if uniform - // if newHeight not set - // find ratio newWidth / oldWidth - // oldHeight *= ratio - // else if newWidth not set - // find ratio newWidth / oldWidth - // oldHeight *= ratio - // else - // find both ratios and use the smallest one - // (this will be the largest version of the image that fits - // inside the rectangle given) - // (if PERCENT, just use smallest percentage). - // - // If units is percent, we only need old height and width - - float ratio; - - if (pPercent) { - if (pWidth >= 0 && pHeight >= 0) { - // Non-uniform - pWidth = Math.round((float) pOriginalWidth * (float) pWidth / 100f); - pHeight = Math.round((float) pOriginalHeight * (float) pHeight / 100f); - } - else if (pWidth >= 0) { - // Find ratio from pWidth - ratio = (float) pWidth / 100f; - pWidth = Math.round((float) pOriginalWidth * ratio); - pHeight = Math.round((float) pOriginalHeight * ratio); - } - else if (pHeight >= 0) { - // Find ratio from pHeight - ratio = (float) pHeight / 100f; - pWidth = Math.round((float) pOriginalWidth * ratio); - pHeight = Math.round((float) pOriginalHeight * ratio); - } - // Else: No scale - } - else { - if (pUniform) { - if (pWidth >= 0 && pHeight >= 0) { - // Compute both ratios - ratio = (float) pWidth / (float) pOriginalWidth; - float heightRatio = (float) pHeight / (float) pOriginalHeight; - - // Find the largest ratio, and use that for both - if (heightRatio < ratio) { - ratio = heightRatio; - pWidth = Math.round((float) pOriginalWidth * ratio); - } - else { - pHeight = Math.round((float) pOriginalHeight * ratio); - } - } - else if (pWidth >= 0) { - // Find ratio from pWidth - ratio = (float) pWidth / (float) pOriginalWidth; - pHeight = Math.round((float) pOriginalHeight * ratio); - } - else if (pHeight >= 0) { - // Find ratio from pHeight - ratio = (float) pHeight / (float) pOriginalHeight; - pWidth = Math.round((float) pOriginalWidth * ratio); - } - // Else: No scale - } - } - - // Default is no scale, just work as a proxy - if (pWidth < 0) { - pWidth = pOriginalWidth; - } - if (pHeight < 0) { - pHeight = pOriginalHeight; - } - - // Create new Dimension object and return - return new Dimension(pWidth, pHeight); - } - - static Rectangle getAOI(int pOriginalWidth, int pOriginalHeight, - int pX, int pY, int pWidth, int pHeight, - boolean pPercent, boolean pMaximizeToAspect) { - // Algorithm: - // Try to get x and y (default 0,0). - // Try to get width and height (default width-x, height-y) - // - // If percent, get ratio - // - // If uniform - // - - float ratio; - - if (pPercent) { - if (pWidth >= 0 && pHeight >= 0) { - // Non-uniform - pWidth = Math.round((float) pOriginalWidth * (float) pWidth / 100f); - pHeight = Math.round((float) pOriginalHeight * (float) pHeight / 100f); - } - else if (pWidth >= 0) { - // Find ratio from pWidth - ratio = (float) pWidth / 100f; - pWidth = Math.round((float) pOriginalWidth * ratio); - pHeight = Math.round((float) pOriginalHeight * ratio); - } - else if (pHeight >= 0) { - // Find ratio from pHeight - ratio = (float) pHeight / 100f; - pWidth = Math.round((float) pOriginalWidth * ratio); - pHeight = Math.round((float) pOriginalHeight * ratio); - } - // Else: No crop - } - else { - // Uniform - if (pMaximizeToAspect) { - if (pWidth >= 0 && pHeight >= 0) { - // Compute both ratios - ratio = (float) pWidth / (float) pHeight; - float originalRatio = (float) pOriginalWidth / (float) pOriginalHeight; - if (ratio > originalRatio) { - pWidth = pOriginalWidth; - pHeight = Math.round((float) pOriginalWidth / ratio); - } - else { - pHeight = pOriginalHeight; - pWidth = Math.round((float) pOriginalHeight * ratio); - } - } - else if (pWidth >= 0) { - // Find ratio from pWidth - ratio = (float) pWidth / (float) pOriginalWidth; - pHeight = Math.round((float) pOriginalHeight * ratio); - } - else if (pHeight >= 0) { - // Find ratio from pHeight - ratio = (float) pHeight / (float) pOriginalHeight; - pWidth = Math.round((float) pOriginalWidth * ratio); - } - // Else: No crop - } - } - - // Not specified, or outside bounds: Use original dimensions - if (pWidth < 0 || (pX < 0 && pWidth > pOriginalWidth) - || (pX >= 0 && (pX + pWidth) > pOriginalWidth)) { - pWidth = (pX >= 0 ? pOriginalWidth - pX : pOriginalWidth); - } - if (pHeight < 0 || (pY < 0 && pHeight > pOriginalHeight) - || (pY >= 0 && (pY + pHeight) > pOriginalHeight)) { - pHeight = (pY >= 0 ? pOriginalHeight - pY : pOriginalHeight); - } - - // Center - if (pX < 0) { - pX = (pOriginalWidth - pWidth) / 2; - } - if (pY < 0) { - pY = (pOriginalHeight - pHeight) / 2; - } - -// System.out.println("x: " + pX + " y: " + pY -// + " w: " + pWidth + " h " + pHeight); - - return new Rectangle(pX, pY, pWidth, pHeight); - } +/* + * Copyright (c) 2008, Harald Kuhr + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name "TwelveMonkeys" nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.twelvemonkeys.servlet.image; + +import com.twelvemonkeys.image.ImageUtil; +import com.twelvemonkeys.io.FastByteArrayOutputStream; +import com.twelvemonkeys.lang.StringUtil; +import com.twelvemonkeys.servlet.ServletResponseStreamDelegate; +import com.twelvemonkeys.servlet.ServletUtil; +import com.twelvemonkeys.servlet.image.aoi.AreaOfInterest; +import com.twelvemonkeys.servlet.image.aoi.AreaOfInterestFactory; + +import javax.imageio.*; +import javax.imageio.stream.ImageInputStream; +import javax.imageio.stream.ImageOutputStream; +import javax.servlet.ServletContext; +import javax.servlet.ServletOutputStream; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpServletResponseWrapper; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.awt.image.IndexColorModel; +import java.awt.image.RenderedImage; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.lang.reflect.Method; +import java.net.URL; +import java.util.Iterator; + +/** + * This {@link ImageServletResponse} implementation can be used with image + * requests, to have the image immediately decoded to a {@code BufferedImage}. + * The image may be optionally subsampled, scaled and/or cropped. + * The response also automatically handles writing the image back to the underlying response stream + * in the preferred format, when the response is flushed. + *
+ * @author Harald Kuhr + * @version $Id: ImageServletResponseImpl.java#10 $ + */ +// TODO: Refactor out HTTP specifics (if possible). +// TODO: Is it a good ide to throw IIOException? +// TODO: This implementation has a problem if two filters does scaling, as the second will overwrite the SIZE attribute +// TODO: Allow different scaling algorithm based on input image (use case: IndexColorModel does not scale well using default, smooth may be slow for large images) +// TODO: Support pluggable pre- and post-processing steps +class ImageServletResponseImpl extends HttpServletResponseWrapper implements ImageServletResponse { + private ServletRequest originalRequest; + private final ServletContext context; + private final ServletResponseStreamDelegate streamDelegate; + + private FastByteArrayOutputStream bufferedOut; + + private RenderedImage image; + private String outputContentType; + + private String originalContentType; + private int originalContentLength = -1; + + /** + * Creates an {@code ImageServletResponseImpl}. + * + * @param pRequest the request + * @param pResponse the response + * @param pContext the servlet context + */ + public ImageServletResponseImpl(final HttpServletRequest pRequest, final HttpServletResponse pResponse, final ServletContext pContext) { + super(pResponse); + originalRequest = pRequest; + streamDelegate = new ServletResponseStreamDelegate(pResponse) { + @Override + protected OutputStream createOutputStream() throws IOException { + if (originalContentLength >= 0) { + bufferedOut = new FastByteArrayOutputStream(originalContentLength); + } + else { + bufferedOut = new FastByteArrayOutputStream(0); + } + + return bufferedOut; + } + }; + context = pContext; + } + + /** + * Creates an {@code ImageServletResponseImpl}. + * + * @param pRequest the request + * @param pResponse the response + * @param pContext the servlet context + * + * @throws ClassCastException if {@code pRequest} is not an {@link javax.servlet.http.HttpServletRequest} or + * {@code pResponse} is not an {@link javax.servlet.http.HttpServletResponse}. + */ + public ImageServletResponseImpl(final ServletRequest pRequest, final ServletResponse pResponse, final ServletContext pContext) { + // Cheat for now... + this((HttpServletRequest) pRequest, (HttpServletResponse) pResponse, pContext); + } + + /** + * Called by the container, do not invoke. + * + * @param pMimeType the content (MIME) type + */ + public void setContentType(final String pMimeType) { + // Throw exception is already set + if (originalContentType != null) { + throw new IllegalStateException("ContentType already set."); + } + + originalContentType = pMimeType; + } + + /** + * Called by the container. Do not invoke. + * + * @return the response's {@code OutputStream} + * @throws IOException + */ + public ServletOutputStream getOutputStream() throws IOException { + return streamDelegate.getOutputStream(); + } + + /** + * Called by the container. Do not invoke. + * + * @return the response's {@code PrintWriter} + * @throws IOException + */ + public PrintWriter getWriter() throws IOException { + return streamDelegate.getWriter(); + } + + /** + * Called by the container. Do not invoke. + * + * @param pLength the content length + */ + public void setContentLength(final int pLength) { + if (originalContentLength != -1) { + throw new IllegalStateException("ContentLength already set."); + } + + originalContentLength = pLength; + } + + @Override + public void setHeader(String name, String value) { + // NOTE: Clients could also specify content type/content length using the setHeader method, special handling + if (name != null && name.equals("Content-Length")) { + setContentLength(Integer.valueOf(value)); // Value might be too large, but we don't support that anyway + } + else if (name != null && name.equals("Content-Type")) { + setContentType(value); + } + else { + super.setHeader(name, value); + } + } + + /** + * Writes the image to the original {@code ServletOutputStream}. + * If no format is set in this response, the image is encoded in the same + * format as the original image. + * + * @throws IOException if an I/O exception occurs during writing + */ + public void flush() throws IOException { + String outputType = getOutputContentType(); + + // Force transcoding, if no other filtering is done + if (outputType != null && !outputType.equals(originalContentType)) { + getImage(); + } + + if (image != null) { + Iterator writers = ImageIO.getImageWritersByMIMEType(outputType); + if (writers.hasNext()) { + super.setContentType(outputType); + OutputStream out = super.getOutputStream(); + try { + ImageWriter writer = (ImageWriter) writers.next(); + try { + ImageWriteParam param = writer.getDefaultWriteParam(); + /////////////////// + // POST-PROCESS + // For known formats that don't support transparency, convert to opaque + if (isNonAlphaFormat(outputType) && image.getColorModel().getTransparency() != Transparency.OPAQUE) { + image = ImageUtil.toBuffered(image, BufferedImage.TYPE_INT_RGB); + } + + Float requestQuality = (Float) originalRequest.getAttribute(ImageServletResponse.ATTRIB_OUTPUT_QUALITY); + + // The default JPEG quality is not good enough, so always adjust compression/quality + if ((requestQuality != null || "jpeg".equalsIgnoreCase(getFormatNameSafe(writer))) && param.canWriteCompressed()) { + // TODO: See http://blog.apokalyptik.com/2009/09/16/quality-time-with-your-jpegs/ for better adjusting the (default) JPEG quality + // OR: Use the metadata of the original image + + param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); + + // WORKAROUND: Known bug in GIFImageWriter in certain JDK versions, compression type is not set by default + if (param.getCompressionTypes() != null && param.getCompressionType() == null) { + param.setCompressionType(param.getCompressionTypes()[0]); // Just choose any, to keep param happy + } + + param.setCompressionQuality(requestQuality != null ? requestQuality : 0.8f); + } + + if ("gif".equalsIgnoreCase(getFormatNameSafe(writer)) && !(image.getColorModel() instanceof IndexColorModel) + /*&& image.getColorModel().getTransparency() != Transparency.OPAQUE*/) { + // WORKAROUND: Bug in GIFImageWriter may throw NPE if transparent pixels + // See: http://bugs.sun.com/view_bug.do?bug_id=6287936 + image = ImageUtil.createIndexed( + ImageUtil.toBuffered(image), 256, null, + (image.getColorModel().getTransparency() == Transparency.OPAQUE ? ImageUtil.TRANSPARENCY_OPAQUE : ImageUtil.TRANSPARENCY_BITMASK) | ImageUtil.DITHER_DIFFUSION_ALTSCANS + ); + } + ////////////////// + ImageOutputStream stream = ImageIO.createImageOutputStream(out); + + writer.setOutput(stream); + try { + writer.write(null, new IIOImage(image, null, null), param); + } + finally { + stream.close(); + } + } + finally { + writer.dispose(); + } + } + finally { + out.flush(); + } + } + else { + context.log("ERROR: No writer for content-type: " + outputType); + throw new IIOException("Unable to transcode image: No suitable image writer found (content-type: " + outputType + ")."); + } + } + else { + super.setContentType(originalContentType); + + ServletOutputStream out = super.getOutputStream(); + + try { + if (bufferedOut != null) { + bufferedOut.writeTo(out); + } + } + finally { + out.flush(); + } + } + } + + private boolean isNonAlphaFormat(String outputType) { + return "image/jpeg".equals(outputType) || "image/jpg".equals(outputType) || + "image/bmp".equals(outputType) || "image/x-bmp".equals(outputType); + } + + private String getFormatNameSafe(final ImageWriter pWriter) { + try { + return pWriter.getOriginatingProvider().getFormatNames()[0]; + } + catch (RuntimeException e) { + // NPE, AIOOBE, etc.. + return null; + } + } + + public String getOutputContentType() { + return outputContentType != null ? outputContentType : originalContentType; + } + + public void setOutputContentType(final String pImageFormat) { + outputContentType = pImageFormat; + } + + /** + * Sets the image for this response. + * + * @param pImage the {@code RenderedImage} that will be written to the + * response stream + */ + public void setImage(final RenderedImage pImage) { + image = pImage; + } + + /** + * Gets the decoded image from the response. + * + * @return a {@code BufferedImage} or {@code null} if the image could + * not be read. + * + * @throws java.io.IOException if an I/O exception occurs during reading + */ + public BufferedImage getImage() throws IOException { + if (image == null) { + // No content, no image + if (bufferedOut == null) { + return null; + } + + // Read from the byte buffer + InputStream byteStream = bufferedOut.createInputStream(); + ImageInputStream input = null; + try { + input = ImageIO.createImageInputStream(byteStream); + Iterator readers = ImageIO.getImageReaders(input); + if (readers.hasNext()) { + // Get the correct reader + ImageReader reader = (ImageReader) readers.next(); + try { + reader.setInput(input); + + ImageReadParam param = reader.getDefaultReadParam(); + + // Get default size + int originalWidth = reader.getWidth(0); + int originalHeight = reader.getHeight(0); +////////////////// +// PRE-PROCESS (prepare): param, size, format?, request, response? + // TODO: AOI strategy? + // Extract AOI from request + Rectangle aoi = extractAOIFromRequest(originalWidth, originalHeight, originalRequest); + + if (aoi != null) { + param.setSourceRegion(aoi); + originalWidth = aoi.width; + originalHeight = aoi.height; + } + + // TODO: Size and subsampling strategy? + // If possible, extract size from request + Dimension size = extractSizeFromRequest(originalWidth, originalHeight, originalRequest); + double readSubSamplingFactor = getReadSubsampleFactorFromRequest(originalRequest); + + if (size != null) { + //System.out.println("Size: " + size); + if (param.canSetSourceRenderSize()) { + param.setSourceRenderSize(size); + } + else { + int subX = (int) Math.max(originalWidth / (size.width * readSubSamplingFactor), 1.0); + int subY = (int) Math.max(originalHeight / (size.height * readSubSamplingFactor), 1.0); + + if (subX > 1 || subY > 1) { + param.setSourceSubsampling(subX, subY, subX > 1 ? subX / 2 : 0, subY > 1 ? subY / 2 : 0); + } + } + } + + // Need base URI for SVG with links/stylesheets etc + maybeSetBaseURIFromRequest(param); + +///////////////////// + + // Finally, read the image using the supplied parameter + BufferedImage image = reader.read(0, param); + + // TODO: If we sub-sampled, it would be a good idea to blur before resampling, + // to avoid jagged lines artifacts + + // If reader doesn't support dynamic sizing, scale now + image = resampleImage(image, size); + + // Fill bgcolor behind image, if transparent + extractAndSetBackgroundColor(image); // TODO: Move to flush/POST-PROCESS + + // Set image + this.image = image; + } + finally { + reader.dispose(); + } + } + else { + context.log("ERROR: No suitable image reader found (content-type: " + originalContentType + ")."); + context.log("ERROR: Available formats: " + getFormatsString()); + + throw new IIOException("Unable to transcode image: No suitable image reader found (content-type: " + originalContentType + ")."); + } + + // Free resources, as the image is now either read, or unreadable + bufferedOut = null; + } + finally { + if (input != null) { + input.close(); + } + } + } + + // Image is usually a BufferedImage, but may also be a RenderedImage + return image != null ? ImageUtil.toBuffered(image) : null; + } + + private BufferedImage resampleImage(final BufferedImage image, final Dimension size) { + if (image != null && size != null && (image.getWidth() != size.width || image.getHeight() != size.height)) { + int resampleAlgorithm = getResampleAlgorithmFromRequest(); + + // TODO: One possibility is to NOT handle index color here, and only handle it later, IF NEEDED (read: GIF, + // possibly also for PNG) when we know the output format (flush method). + // This will make the filter faster (and better quality, possibly at the expense of more bytes being sent + // over the wire) in the general case. Who uses GIF nowadays anyway? + // Also, this means we could either keep the original IndexColorModel in the filter, or go through the + // expensive operation of re-calculating the optimal palette for the new image (the latter might improve quality). + + // NOTE: Only use createScaled if IndexColorModel, as it's more expensive due to color conversion +/* if (image.getColorModel() instanceof IndexColorModel) { +// return ImageUtil.createScaled(image, size.width, size.height, resampleAlgorithm); + BufferedImage resampled = ImageUtil.createResampled(image, size.width, size.height, resampleAlgorithm); + return ImageUtil.createIndexed(resampled, (IndexColorModel) image.getColorModel(), null, ImageUtil.DITHER_NONE | ImageUtil.TRANSPARENCY_BITMASK); +// return ImageUtil.createIndexed(resampled, 256, null, ImageUtil.COLOR_SELECTION_QUALITY | ImageUtil.DITHER_NONE | ImageUtil.TRANSPARENCY_BITMASK); + } + else { + */ + return ImageUtil.createResampled(image, size.width, size.height, resampleAlgorithm); +// } + } + return image; + } + + int getResampleAlgorithmFromRequest() { + Object algorithm = originalRequest.getAttribute(ATTRIB_IMAGE_RESAMPLE_ALGORITHM); + if (algorithm instanceof Integer && ((Integer) algorithm == Image.SCALE_SMOOTH || (Integer) algorithm == Image.SCALE_FAST || (Integer) algorithm == Image.SCALE_DEFAULT)) { + return (Integer) algorithm; + } + else { + if (algorithm != null) { + context.log("WARN: Illegal image resampling algorithm: " + algorithm); + } + + return BufferedImage.SCALE_DEFAULT; + } + } + + private double getReadSubsampleFactorFromRequest(final ServletRequest pOriginalRequest) { + double subsampleFactor; + + Object factor = pOriginalRequest.getAttribute(ATTRIB_READ_SUBSAMPLING_FACTOR); + if (factor instanceof Number && ((Number) factor).doubleValue() >= 1.0) { + subsampleFactor = ((Number) factor).doubleValue(); + } + else { + if (factor != null) { + context.log("WARN: Illegal read subsampling factor: " + factor); + } + + subsampleFactor = 2.0; + } + + return subsampleFactor; + } + + private void extractAndSetBackgroundColor(final BufferedImage pImage) { + // TODO: bgColor request attribute instead of parameter? + if (pImage.getColorModel().hasAlpha()) { + String bgColor = originalRequest.getParameter("bg.color"); + if (bgColor != null) { + Color color = StringUtil.toColor(bgColor); + + Graphics2D g = pImage.createGraphics(); + try { + g.setColor(color); + g.setComposite(AlphaComposite.DstOver); + g.fillRect(0, 0, pImage.getWidth(), pImage.getHeight()); + } + finally { + g.dispose(); + } + } + } + } + + private static String getFormatsString() { + String[] formats = ImageIO.getReaderFormatNames(); + StringBuilder buf = new StringBuilder(); + for (int i = 0; i < formats.length; i++) { + String format = formats[i]; + if (i > 0) { + buf.append(", "); + } + buf.append(format); + } + return buf.toString(); + } + + private void maybeSetBaseURIFromRequest(final ImageReadParam pParam) { + if (originalRequest instanceof HttpServletRequest) { + try { + // If there's a setBaseURI method, we'll try to use that (uses reflection, to avoid dependency on plugins) + Method setBaseURI; + try { + setBaseURI = pParam.getClass().getMethod("setBaseURI", String.class); + } + catch (NoSuchMethodException ignore) { + return; + } + + // Get URL for resource and set as base + String baseURI = ServletUtil.getContextRelativeURI((HttpServletRequest) originalRequest); + + URL resourceURL = context.getResource(baseURI); + + if (resourceURL == null) { + resourceURL = ServletUtil.getRealURL(context, baseURI); + } + + if (resourceURL != null) { + setBaseURI.invoke(pParam, resourceURL.toExternalForm()); + } + else { + context.log("WARN: Resource URL not found for URI: " + baseURI); + } + } + catch (Exception e) { + context.log("WARN: Could not set base URI: ", e); + } + } + } + + private Dimension extractSizeFromRequest(final int pDefaultWidth, final int pDefaultHeight, final ServletRequest pOriginalRequest) { + // TODO: Allow extraction from request parameters + /* + int sizeW = ServletUtil.getIntParameter(originalRequest, "size.w", -1); + int sizeH = ServletUtil.getIntParameter(originalRequest, "size.h", -1); + boolean sizePercent = ServletUtil.getBooleanParameter(originalRequest, "size.percent", false); + boolean sizeUniform = ServletUtil.getBooleanParameter(originalRequest, "size.uniform", true); + */ + Dimension size = (Dimension) pOriginalRequest.getAttribute(ATTRIB_SIZE); + int sizeW = size != null ? size.width : -1; + int sizeH = size != null ? size.height : -1; + + Boolean b = (Boolean) pOriginalRequest.getAttribute(ATTRIB_SIZE_PERCENT); + boolean sizePercent = b != null && b; // default: false + + b = (Boolean) pOriginalRequest.getAttribute(ATTRIB_SIZE_UNIFORM); + boolean sizeUniform = b == null || b; // default: true + + if (sizeW >= 0 || sizeH >= 0) { + size = getSize(pDefaultWidth, pDefaultHeight, sizeW, sizeH, sizePercent, sizeUniform); + } + + return size; + } + + private Rectangle extractAOIFromRequest(final int pDefaultWidth, final int pDefaultHeight, final ServletRequest pOriginalRequest) { + // TODO: Allow extraction from request parameters + /* + int aoiX = ServletUtil.getIntParameter(originalRequest, "aoi.x", -1); + int aoiY = ServletUtil.getIntParameter(originalRequest, "aoi.y", -1); + int aoiW = ServletUtil.getIntParameter(originalRequest, "aoi.w", -1); + int aoiH = ServletUtil.getIntParameter(originalRequest, "aoi.h", -1); + boolean aoiPercent = ServletUtil.getBooleanParameter(originalRequest, "aoi.percent", false); + boolean aoiUniform = ServletUtil.getBooleanParameter(originalRequest, "aoi.uniform", false); + */ + Rectangle aoi = (Rectangle) pOriginalRequest.getAttribute(ATTRIB_AOI); + int aoiX = aoi != null ? aoi.x : -1; + int aoiY = aoi != null ? aoi.y : -1; + int aoiW = aoi != null ? aoi.width : -1; + int aoiH = aoi != null ? aoi.height : -1; + + Boolean b = (Boolean) pOriginalRequest.getAttribute(ATTRIB_AOI_PERCENT); + boolean aoiPercent = b != null && b; // default: false + + b = (Boolean) pOriginalRequest.getAttribute(ATTRIB_AOI_UNIFORM); + boolean aoiUniform = b != null && b; // default: false + + if (aoiX >= 0 || aoiY >= 0 || aoiW >= 0 || aoiH >= 0) { + + AreaOfInterest areaOfInterest = AreaOfInterestFactory.getDefault(). + createAreaOfInterest(pDefaultWidth, pDefaultHeight, aoiPercent, aoiUniform); + aoi = areaOfInterest.getAOI(new Rectangle(aoiX, aoiY, aoiW, aoiH)); + return aoi; + } + + return null; + } + + // TODO: Move these to ImageUtil or similar, as they are often used... + // TODO: Consider separate methods for percent and pixels + /** + * Gets the dimensions (height and width) of the scaled image. The + * dimensions are computed based on the old image's dimensions, the units + * used for specifying new dimensions and whether or not uniform scaling + * should be used (se algorithm below). + * + * @param pOriginalWidth the original width of the image + * @param pOriginalHeight the original height of the image + * @param pWidth the new width of the image, or -1 if unknown + * @param pHeight the new height of the image, or -1 if unknown + * @param pPercent the constant specifying units for width and height + * parameter (UNITS_PIXELS or UNITS_PERCENT) + * @param pUniform boolean specifying uniform scale or not + * @return a Dimension object, with the correct width and heigth + * in pixels, for the scaled version of the image. + */ + static Dimension getSize(int pOriginalWidth, int pOriginalHeight, + int pWidth, int pHeight, + boolean pPercent, boolean pUniform) { + + // If uniform, make sure width and height are scaled the same amount + // (use ONLY height or ONLY width). + // + // Algorithm: + // if uniform + // if newHeight not set + // find ratio newWidth / oldWidth + // oldHeight *= ratio + // else if newWidth not set + // find ratio newWidth / oldWidth + // oldHeight *= ratio + // else + // find both ratios and use the smallest one + // (this will be the largest version of the image that fits + // inside the rectangle given) + // (if PERCENT, just use smallest percentage). + // + // If units is percent, we only need old height and width + + float ratio; + + if (pPercent) { + if (pWidth >= 0 && pHeight >= 0) { + // Non-uniform + pWidth = Math.round((float) pOriginalWidth * (float) pWidth / 100f); + pHeight = Math.round((float) pOriginalHeight * (float) pHeight / 100f); + } + else if (pWidth >= 0) { + // Find ratio from pWidth + ratio = (float) pWidth / 100f; + pWidth = Math.round((float) pOriginalWidth * ratio); + pHeight = Math.round((float) pOriginalHeight * ratio); + } + else if (pHeight >= 0) { + // Find ratio from pHeight + ratio = (float) pHeight / 100f; + pWidth = Math.round((float) pOriginalWidth * ratio); + pHeight = Math.round((float) pOriginalHeight * ratio); + } + // Else: No scale + } + else { + if (pUniform) { + if (pWidth >= 0 && pHeight >= 0) { + // Compute both ratios + ratio = (float) pWidth / (float) pOriginalWidth; + float heightRatio = (float) pHeight / (float) pOriginalHeight; + + // Find the largest ratio, and use that for both + if (heightRatio < ratio) { + ratio = heightRatio; + pWidth = Math.round((float) pOriginalWidth * ratio); + } + else { + pHeight = Math.round((float) pOriginalHeight * ratio); + } + } + else if (pWidth >= 0) { + // Find ratio from pWidth + ratio = (float) pWidth / (float) pOriginalWidth; + pHeight = Math.round((float) pOriginalHeight * ratio); + } + else if (pHeight >= 0) { + // Find ratio from pHeight + ratio = (float) pHeight / (float) pOriginalHeight; + pWidth = Math.round((float) pOriginalWidth * ratio); + } + // Else: No scale + } + } + + // Default is no scale, just work as a proxy + if (pWidth < 0) { + pWidth = pOriginalWidth; + } + if (pHeight < 0) { + pHeight = pOriginalHeight; + } + + // Create new Dimension object and return + return new Dimension(pWidth, pHeight); + } + + static Rectangle getAOI(int pOriginalWidth, int pOriginalHeight, + int pX, int pY, int pWidth, int pHeight, + boolean pPercent, boolean pMaximizeToAspect) { + // Algorithm: + // Try to get x and y (default 0,0). + // Try to get width and height (default width-x, height-y) + // + // If percent, get ratio + // + // If uniform + // + + float ratio; + + if (pPercent) { + if (pWidth >= 0 && pHeight >= 0) { + // Non-uniform + pWidth = Math.round((float) pOriginalWidth * (float) pWidth / 100f); + pHeight = Math.round((float) pOriginalHeight * (float) pHeight / 100f); + } + else if (pWidth >= 0) { + // Find ratio from pWidth + ratio = (float) pWidth / 100f; + pWidth = Math.round((float) pOriginalWidth * ratio); + pHeight = Math.round((float) pOriginalHeight * ratio); + } + else if (pHeight >= 0) { + // Find ratio from pHeight + ratio = (float) pHeight / 100f; + pWidth = Math.round((float) pOriginalWidth * ratio); + pHeight = Math.round((float) pOriginalHeight * ratio); + } + // Else: No crop + } + else { + // Uniform + if (pMaximizeToAspect) { + if (pWidth >= 0 && pHeight >= 0) { + // Compute both ratios + ratio = (float) pWidth / (float) pHeight; + float originalRatio = (float) pOriginalWidth / (float) pOriginalHeight; + if (ratio > originalRatio) { + pWidth = pOriginalWidth; + pHeight = Math.round((float) pOriginalWidth / ratio); + } + else { + pHeight = pOriginalHeight; + pWidth = Math.round((float) pOriginalHeight * ratio); + } + } + else if (pWidth >= 0) { + // Find ratio from pWidth + ratio = (float) pWidth / (float) pOriginalWidth; + pHeight = Math.round((float) pOriginalHeight * ratio); + } + else if (pHeight >= 0) { + // Find ratio from pHeight + ratio = (float) pHeight / (float) pOriginalHeight; + pWidth = Math.round((float) pOriginalWidth * ratio); + } + // Else: No crop + } + } + + // Not specified, or outside bounds: Use original dimensions + if (pWidth < 0 || (pX < 0 && pWidth > pOriginalWidth) + || (pX >= 0 && (pX + pWidth) > pOriginalWidth)) { + pWidth = (pX >= 0 ? pOriginalWidth - pX : pOriginalWidth); + } + if (pHeight < 0 || (pY < 0 && pHeight > pOriginalHeight) + || (pY >= 0 && (pY + pHeight) > pOriginalHeight)) { + pHeight = (pY >= 0 ? pOriginalHeight - pY : pOriginalHeight); + } + + // Center + if (pX < 0) { + pX = (pOriginalWidth - pWidth) / 2; + } + if (pY < 0) { + pY = (pOriginalHeight - pHeight) / 2; + } + +// System.out.println("x: " + pX + " y: " + pY +// + " w: " + pWidth + " h " + pHeight); + + return new Rectangle(pX, pY, pWidth, pHeight); + } } \ No newline at end of file diff --git a/servlet/src/main/java/com/twelvemonkeys/servlet/image/NullImageFilter.java b/servlet/src/main/java/com/twelvemonkeys/servlet/image/NullImageFilter.java index c2700cad..107a1a3f 100755 --- a/servlet/src/main/java/com/twelvemonkeys/servlet/image/NullImageFilter.java +++ b/servlet/src/main/java/com/twelvemonkeys/servlet/image/NullImageFilter.java @@ -1,46 +1,46 @@ -/* - * Copyright (c) 2008, Harald Kuhr - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name "TwelveMonkeys" nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package com.twelvemonkeys.servlet.image; - -import javax.servlet.ServletRequest; -import java.awt.image.BufferedImage; -import java.awt.image.RenderedImage; - -/** - * An {@code ImageFilter} that does nothing. Useful for debugging purposes. - * - * @author Harald Kuhr - * @version $Id: NullImageFilter.java $ - * - */ -public final class NullImageFilter extends ImageFilter { - protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) { - return pImage; - } -} +/* + * Copyright (c) 2008, Harald Kuhr + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name "TwelveMonkeys" nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.twelvemonkeys.servlet.image; + +import javax.servlet.ServletRequest; +import java.awt.image.BufferedImage; +import java.awt.image.RenderedImage; + +/** + * An {@code ImageFilter} that does nothing. Useful for debugging purposes. + * + * @author Harald Kuhr + * @version $Id: NullImageFilter.java $ + * + */ +public final class NullImageFilter extends ImageFilter { + protected RenderedImage doFilter(BufferedImage pImage, ServletRequest pRequest, ImageServletResponse pResponse) { + return pImage; + } +} diff --git a/servlet/src/main/java/com/twelvemonkeys/servlet/image/RotateFilter.java b/servlet/src/main/java/com/twelvemonkeys/servlet/image/RotateFilter.java index 98b692ce..cc74d4b5 100755 --- a/servlet/src/main/java/com/twelvemonkeys/servlet/image/RotateFilter.java +++ b/servlet/src/main/java/com/twelvemonkeys/servlet/image/RotateFilter.java @@ -1,202 +1,202 @@ -/* - * Copyright (c) 2008, Harald Kuhr - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name "TwelveMonkeys" nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package com.twelvemonkeys.servlet.image; - -import com.twelvemonkeys.image.ImageUtil; -import com.twelvemonkeys.lang.StringUtil; -import com.twelvemonkeys.servlet.ServletUtil; - -import javax.servlet.ServletRequest; -import java.awt.*; -import java.awt.geom.AffineTransform; -import java.awt.geom.Rectangle2D; -import java.awt.image.BufferedImage; -import java.awt.image.RenderedImage; - -/** - * This Servlet is able to render a cropped part of an image. - * - *
- *
- *
- * Parameters:+ *
+ *
+ * Parameters:- * 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 - * run with JRE 1.4 or later, and with the option: - *
- * If you cannot use JRE 1.4 or later, or do not want to use the X - * libraries, one possibility is to use the - * PJA package (com.eteks.pja), - * and start the JVM with the following options: - *
- * 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, - * will result in errors. - * - * @see com.twelvemonkeys.servlet.image.ImageServlet - * @see com.twelvemonkeys.servlet.image.ImagePainterServlet - */ +/** + * Contains various image-outputting filters, that should run under any + * servlet engine. + *
+ * 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 + * run with JRE 1.4 or later, and with the option: + *
+ * If you cannot use JRE 1.4 or later, or do not want to use the X + * libraries, one possibility is to use the + * PJA package (com.eteks.pja), + * and start the JVM with the following options: + *
+ * 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, + * will result in errors. + * + * @see com.twelvemonkeys.servlet.image.ImageServlet + * @see com.twelvemonkeys.servlet.image.ImagePainterServlet + */ package com.twelvemonkeys.servlet.image; \ No newline at end of file diff --git a/servlet/src/main/java/com/twelvemonkeys/servlet/package_info.java b/servlet/src/main/java/com/twelvemonkeys/servlet/package_info.java index c14599d4..09d358cb 100755 --- a/servlet/src/main/java/com/twelvemonkeys/servlet/package_info.java +++ b/servlet/src/main/java/com/twelvemonkeys/servlet/package_info.java @@ -1,4 +1,4 @@ -/** - * Contains servlet support classes. - */ -package com.twelvemonkeys.servlet; +/** + * Contains servlet support classes. + */ +package com.twelvemonkeys.servlet; diff --git a/servlet/src/test/java/com/twelvemonkeys/servlet/FilterAbstractTestCase.java b/servlet/src/test/java/com/twelvemonkeys/servlet/FilterAbstractTestCase.java index 42501e1e..88570a3c 100755 --- a/servlet/src/test/java/com/twelvemonkeys/servlet/FilterAbstractTestCase.java +++ b/servlet/src/test/java/com/twelvemonkeys/servlet/FilterAbstractTestCase.java @@ -1,438 +1,438 @@ -package com.twelvemonkeys.servlet; - -import com.twelvemonkeys.lang.ObjectAbstractTestCase; -import org.junit.Test; - -import javax.servlet.*; -import java.io.*; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.*; - -import static org.junit.Assert.*; - -/** - * FilterAbstractTestCase - *
- * - * @author Harald Kuhr - * @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 $ - */ -public abstract class FilterAbstractTestCase extends ObjectAbstractTestCase { - protected Object makeObject() { - return makeFilter(); - } - - protected abstract Filter makeFilter(); - - // TODO: Is it a good thing to have an API like this? - protected FilterConfig makeFilterConfig() { - return makeFilterConfig(new HashMap()); - } - - protected FilterConfig makeFilterConfig(Map pParams) { - return new MockFilterConfig(pParams); - } - - protected ServletRequest makeRequest() { - return new MockServletRequest(); - } - - protected ServletResponse makeResponse() { - return new MockServletResponse(); - } - - protected FilterChain makeFilterChain() { - return new MockFilterChain(); - } - - @Test - public void testInitNull() { - Filter filter = makeFilter(); - - // The spec seems to be a little unclear on this issue, but anyway, - // the container should never invoke init(null)... - try { - filter.init(null); - fail("Should throw Exception on init(null)"); - } - catch (IllegalArgumentException e) { - // Good - } - catch (NullPointerException e) { - // Bad (but not unreasonable) - } - catch (ServletException e) { - // Hmmm.. The jury is still out. - } - } - - @Test - public void testInit() { - Filter filter = makeFilter(); - - try { - filter.init(makeFilterConfig()); - } - catch (ServletException e) { - assertNotNull(e.getMessage()); - } - finally { - filter.destroy(); - } - } - - @Test - public void testLifeCycle() throws ServletException { - Filter filter = makeFilter(); - - try { - filter.init(makeFilterConfig()); - } - finally { - filter.destroy(); - } - } - - @Test - public void testFilterBasic() throws ServletException, IOException { - Filter filter = makeFilter(); - - try { - filter.init(makeFilterConfig()); - - filter.doFilter(makeRequest(), makeResponse(), makeFilterChain()); - } - finally { - filter.destroy(); - } - } - - @Test - public void testDestroy() { - // TODO: Implement - } - - static class MockFilterConfig implements FilterConfig { - private final Map