Projekt von de.halbekunst.juplo nach de.juplo verschoben und aufgeräumt
authorKai Moritz <kai@coolibri.de>
Thu, 23 Aug 2012 20:20:53 +0000 (22:20 +0200)
committerKai Moritz <kai@coolibri.de>
Fri, 31 Aug 2012 09:54:40 +0000 (11:54 +0200)
162 files changed:
accelerator-examples/jsp/jetty.sh [new file with mode: 0755]
accelerator-examples/jsp/pom.xml [new file with mode: 0644]
accelerator-examples/jsp/src/main/resources/config.xml [new file with mode: 0644]
accelerator-examples/jsp/src/main/resources/log4j.xml [new file with mode: 0644]
accelerator-examples/jsp/src/main/webapp/WEB-INF/c.tld [new file with mode: 0644]
accelerator-examples/jsp/src/main/webapp/WEB-INF/included.jsp [new file with mode: 0644]
accelerator-examples/jsp/src/main/webapp/WEB-INF/web.xml [new file with mode: 0644]
accelerator-examples/jsp/src/main/webapp/faulty-page.jsp [new file with mode: 0644]
accelerator-examples/jsp/src/main/webapp/index.html [new file with mode: 0644]
accelerator-examples/jsp/src/main/webapp/page-with-forward.jsp [new file with mode: 0644]
accelerator-examples/jsp/src/main/webapp/page-with-include.jsp [new file with mode: 0644]
accelerator-examples/jsp/src/main/webapp/simple-page.jsp [new file with mode: 0644]
accelerator-examples/jsp/src/test/java/de/juplo/accelerator/examples/JspTest.java [new file with mode: 0644]
accelerator-examples/jsp/tomcat.sh [new file with mode: 0755]
accelerator-examples/pom.xml [new file with mode: 0644]
accelerator-examples/servlet/jetty.sh [new file with mode: 0755]
accelerator-examples/servlet/pom.xml [new file with mode: 0644]
accelerator-examples/servlet/src/main/java/de/juplo/accelerator/examples/FaultyServlet.java [new file with mode: 0644]
accelerator-examples/servlet/src/main/resources/config.xml [new file with mode: 0644]
accelerator-examples/servlet/src/main/resources/log4j.xml [new file with mode: 0644]
accelerator-examples/servlet/src/main/webapp/WEB-INF/web.xml [new file with mode: 0644]
accelerator-examples/servlet/src/main/webapp/index.html [new file with mode: 0644]
accelerator-examples/servlet/src/test/java/de/juplo/accelerator/examples/ServletTest.java [new file with mode: 0644]
accelerator-examples/servlet/tomcat.sh [new file with mode: 0755]
accelerator-examples/spring/jetty.sh [new file with mode: 0755]
accelerator-examples/spring/pom.xml [new file with mode: 0644]
accelerator-examples/spring/src/main/java/de/juplo/accelerator/examples/spring/FaultyController.java [new file with mode: 0644]
accelerator-examples/spring/src/main/java/de/juplo/accelerator/examples/spring/SpringController.java [new file with mode: 0644]
accelerator-examples/spring/src/main/resources/config.xml [new file with mode: 0644]
accelerator-examples/spring/src/main/resources/log4j.xml [new file with mode: 0644]
accelerator-examples/spring/src/main/webapp/WEB-INF/views/controller-view.jsp [new file with mode: 0644]
accelerator-examples/spring/src/main/webapp/WEB-INF/views/faulty-page.jsp [new file with mode: 0644]
accelerator-examples/spring/src/main/webapp/WEB-INF/views/index.jsp [new file with mode: 0644]
accelerator-examples/spring/src/main/webapp/WEB-INF/views/spring-page.jsp [new file with mode: 0644]
accelerator-examples/spring/src/main/webapp/WEB-INF/web.xml [new file with mode: 0644]
accelerator-examples/spring/src/main/webapp/index.jsp [new file with mode: 0644]
accelerator-examples/spring/src/test/java/de/juplo/accelerator/examples/SpringMVCTest.java [new file with mode: 0644]
accelerator-examples/spring/tomcat.sh [new file with mode: 0755]
accelerator-examples/static/jetty.sh [new file with mode: 0755]
accelerator-examples/static/pom.xml [new file with mode: 0644]
accelerator-examples/static/src/main/resources/config.xml [new file with mode: 0644]
accelerator-examples/static/src/main/resources/log4j.xml [new file with mode: 0644]
accelerator-examples/static/src/main/webapp/WEB-INF/web.xml [new file with mode: 0644]
accelerator-examples/static/src/main/webapp/index.html [new file with mode: 0644]
accelerator-examples/static/src/main/webapp/static/page.html [new file with mode: 0644]
accelerator-examples/static/src/main/webapp/static/stylesheets.css [new file with mode: 0644]
accelerator-examples/static/src/test/java/de/juplo/accelerator/examples/StaticTest.java [new file with mode: 0644]
accelerator-examples/static/src/test/java/org/eclipse/jetty/servlet/DefaultServlet.java [new file with mode: 0644]
accelerator/pom.xml [new file with mode: 0644]
accelerator/src/main/java/de/juplo/accelerator/AcceleratorFilter.java [new file with mode: 0644]
accelerator/src/main/java/de/juplo/accelerator/CacheControl.java [new file with mode: 0644]
accelerator/src/main/java/de/juplo/accelerator/CacheControlInterceptor.java [new file with mode: 0644]
accelerator/src/main/java/de/juplo/accelerator/CacheMethodHandle.java [new file with mode: 0644]
accelerator/src/main/java/de/juplo/accelerator/Headers.java [new file with mode: 0644]
accelerator/src/main/java/de/juplo/accelerator/annotations/Accepts.java [new file with mode: 0644]
accelerator/src/main/java/de/juplo/accelerator/annotations/AdditionalHeaders.java [new file with mode: 0644]
accelerator/src/main/java/de/juplo/accelerator/annotations/CacheControl.java [new file with mode: 0644]
accelerator/src/main/java/de/juplo/accelerator/annotations/CacheSeconds.java [new file with mode: 0644]
accelerator/src/main/java/de/juplo/accelerator/annotations/Cacheable.java [new file with mode: 0644]
accelerator/src/main/java/de/juplo/accelerator/annotations/ETag.java [new file with mode: 0644]
accelerator/src/main/java/de/juplo/accelerator/annotations/LastModified.java [new file with mode: 0644]
accelerator/src/test/java/com/meterware/servletunit/ServletUnitHttpResponse.java [new file with mode: 0644]
accelerator/src/test/java/de/juplo/accelerator/ParameterGuessingTest.java [new file with mode: 0644]
accelerator/src/test/java/de/juplo/accelerator/RequestSizeTest.java [new file with mode: 0644]
accelerator/src/test/java/de/juplo/accelerator/TestServlet.java [new file with mode: 0644]
accelerator/src/test/resources/config.xml [new file with mode: 0644]
accelerator/src/test/resources/log4j.xml [new file with mode: 0644]
accelerator/src/test/resources/web.xml [new file with mode: 0644]
cachecontrol/pom.xml [deleted file]
cachecontrol/src/main/java/de/halbekunst/juplo/cachecontrol/AcceleratorFilter.java [deleted file]
cachecontrol/src/main/java/de/halbekunst/juplo/cachecontrol/CacheControl.java [deleted file]
cachecontrol/src/main/java/de/halbekunst/juplo/cachecontrol/CacheControlInterceptor.java [deleted file]
cachecontrol/src/main/java/de/halbekunst/juplo/cachecontrol/CacheMethodHandle.java [deleted file]
cachecontrol/src/main/java/de/halbekunst/juplo/cachecontrol/Headers.java [deleted file]
cachecontrol/src/main/java/de/halbekunst/juplo/cachecontrol/annotations/Accepts.java [deleted file]
cachecontrol/src/main/java/de/halbekunst/juplo/cachecontrol/annotations/AdditionalHeaders.java [deleted file]
cachecontrol/src/main/java/de/halbekunst/juplo/cachecontrol/annotations/CacheControl.java [deleted file]
cachecontrol/src/main/java/de/halbekunst/juplo/cachecontrol/annotations/CacheSeconds.java [deleted file]
cachecontrol/src/main/java/de/halbekunst/juplo/cachecontrol/annotations/Cacheable.java [deleted file]
cachecontrol/src/main/java/de/halbekunst/juplo/cachecontrol/annotations/ETag.java [deleted file]
cachecontrol/src/main/java/de/halbekunst/juplo/cachecontrol/annotations/LastModified.java [deleted file]
cachecontrol/src/test/java/com/meterware/servletunit/ServletUnitHttpResponse.java [deleted file]
cachecontrol/src/test/java/de/halbekunst/juplo/cachecontrol/ParameterGuessingTest.java [deleted file]
cachecontrol/src/test/java/de/halbekunst/juplo/cachecontrol/RequestSizeTest.java [deleted file]
cachecontrol/src/test/resources/config.xml [deleted file]
cachecontrol/src/test/resources/log4j.xml [deleted file]
cachecontrol/src/test/resources/web.xml [deleted file]
examples/jsp/catalog.xml [deleted file]
examples/jsp/jetty.sh [deleted file]
examples/jsp/pom.xml [deleted file]
examples/jsp/src/main/resources/config.xml [deleted file]
examples/jsp/src/main/resources/log4j.xml [deleted file]
examples/jsp/src/main/webapp/WEB-INF/c.tld [deleted file]
examples/jsp/src/main/webapp/WEB-INF/included.jsp [deleted file]
examples/jsp/src/main/webapp/WEB-INF/web.xml [deleted file]
examples/jsp/src/main/webapp/faulty-page.jsp [deleted file]
examples/jsp/src/main/webapp/index.html [deleted file]
examples/jsp/src/main/webapp/page-with-forward.jsp [deleted file]
examples/jsp/src/main/webapp/page-with-include.jsp [deleted file]
examples/jsp/src/main/webapp/simple-page.jsp [deleted file]
examples/jsp/src/test/java/de/halbekunst/cachecontrol/examples/JspTest.java [deleted file]
examples/jsp/tomcat.sh [deleted file]
examples/pom.xml [deleted file]
examples/servlet/jetty.sh [deleted file]
examples/servlet/pom.xml [deleted file]
examples/servlet/src/main/java/de/halbekunst/cachecontrol/examples/FaultyServlet.java [deleted file]
examples/servlet/src/main/resources/config.xml [deleted file]
examples/servlet/src/main/resources/log4j.xml [deleted file]
examples/servlet/src/main/webapp/WEB-INF/web.xml [deleted file]
examples/servlet/src/main/webapp/index.html [deleted file]
examples/servlet/src/test/java/de/halbekunst/cachecontrol/examples/ServletTest.java [deleted file]
examples/servlet/tomcat.sh [deleted file]
examples/spring/jetty.sh [deleted file]
examples/spring/pom.xml [deleted file]
examples/spring/src/main/java/de/halbekunst/juplo/examples/spring/FaultyController.java [deleted file]
examples/spring/src/main/java/de/halbekunst/juplo/examples/spring/SpringController.java [deleted file]
examples/spring/src/main/resources/config.xml [deleted file]
examples/spring/src/main/resources/log4j.xml [deleted file]
examples/spring/src/main/webapp/WEB-INF/views/controller-view.jsp [deleted file]
examples/spring/src/main/webapp/WEB-INF/views/faulty-page.jsp [deleted file]
examples/spring/src/main/webapp/WEB-INF/views/index.jsp [deleted file]
examples/spring/src/main/webapp/WEB-INF/views/spring-page.jsp [deleted file]
examples/spring/src/main/webapp/WEB-INF/web.xml [deleted file]
examples/spring/src/main/webapp/index.jsp [deleted file]
examples/spring/src/test/java/de/halbekunst/cachecontrol/examples/SpringMVCTest.java [deleted file]
examples/spring/tomcat.sh [deleted file]
examples/static/jetty.sh [deleted file]
examples/static/pom.xml [deleted file]
examples/static/src/main/resources/config.xml [deleted file]
examples/static/src/main/resources/log4j.xml [deleted file]
examples/static/src/main/webapp/WEB-INF/web.xml [deleted file]
examples/static/src/main/webapp/index.html [deleted file]
examples/static/src/main/webapp/static/page.html [deleted file]
examples/static/src/main/webapp/static/stylesheets.css [deleted file]
examples/static/src/test/java/de/halbekunst/cachecontrol/examples/StaticTest.java [deleted file]
examples/static/src/test/java/org/eclipse/jetty/servlet/DefaultServlet.java [deleted file]
maven-plugins/hibernate4/pom.xml [new file with mode: 0644]
maven-plugins/hibernate4/src/main/java/de/juplo/plugins/hibernate4/Hbm2DdlMojo.java [new file with mode: 0644]
maven-plugins/pom.xml [new file with mode: 0644]
maven/hibernate4-maven-plugin/pom.xml [deleted file]
maven/hibernate4-maven-plugin/src/main/java/de/halbekunst/Hbm2DdlMojo.java [deleted file]
maven/pom.xml [deleted file]
percentcodec/pom.xml [new file with mode: 0644]
percentcodec/src/main/java/de/juplo/percentcodec/PercentCodec.java [new file with mode: 0644]
percentcodec/src/test/java/de/juplo/percentcodec/PercentCodecTest.java [new file with mode: 0644]
percentcodec/src/test/resources/log4j.xml [new file with mode: 0644]
pom.xml
test/pom.xml [deleted file]
test/src/main/java/de/halbekunst/juplo/test/HttpTestCase.java [deleted file]
test/src/main/java/de/halbekunst/juplo/test/LoggingHttpServletResponseFilter.java [deleted file]
test/src/main/java/de/halbekunst/juplo/test/LoggingHttpServletResponseWrapper.java [deleted file]
test/src/main/java/de/halbekunst/juplo/test/TestServlet.java [deleted file]
test/src/main/java/de/halbekunst/juplo/test/WebContextTestExecutionListener.java [deleted file]
testingtools/pom.xml [new file with mode: 0644]
testingtools/src/main/java/de/juplo/testingtools/HttpTestCase.java [new file with mode: 0644]
testingtools/src/main/java/de/juplo/testingtools/LoggingHttpServletResponseFilter.java [new file with mode: 0644]
testingtools/src/main/java/de/juplo/testingtools/LoggingHttpServletResponseWrapper.java [new file with mode: 0644]
testingtools/src/main/java/de/juplo/testingtools/WebContextTestExecutionListener.java [new file with mode: 0644]
utils/pom.xml [deleted file]
utils/src/main/java/de/halbekunst/juplo/utils/PercentCodec.java [deleted file]
utils/src/test/java/de/halbekunst/juplo/utils/PercentCodecTest.java [deleted file]
utils/src/test/resources/log4j.xml [deleted file]

diff --git a/accelerator-examples/jsp/jetty.sh b/accelerator-examples/jsp/jetty.sh
new file mode 100755 (executable)
index 0000000..4950796
--- /dev/null
@@ -0,0 +1,11 @@
+#!/bin/bash
+#
+
+# OutOfMemoryException beim "mvn jetty:run" umgehen und
+# Parameter zum nachträglichen anhängen eines Debuggers
+# setzen
+export MAVEN_OPTS="-Xmx512m -XX:MaxPermSize=256m -XX:-UseGCOverheadLimit -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=4000,server=y,suspend=n"
+
+rm -v src/main/webapp/WEB-INF/lib/juplo*
+
+mvn jetty:run
diff --git a/accelerator-examples/jsp/pom.xml b/accelerator-examples/jsp/pom.xml
new file mode 100644 (file)
index 0000000..dc99eb6
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>de.juplo</groupId>
+    <artifactId>juplo-accelerator-examples</artifactId>
+    <version>0.1-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>juplo-accelerator-examples-jsp</artifactId>
+  <packaging>war</packaging>
+  <name>Juplo - Accelerator-Examples: JSP-Pages</name>
+
+</project>
diff --git a/accelerator-examples/jsp/src/main/resources/config.xml b/accelerator-examples/jsp/src/main/resources/config.xml
new file mode 100644 (file)
index 0000000..a04b130
--- /dev/null
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:context="http://www.springframework.org/schema/context"
+       xsi:schemaLocation="
+           http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
+           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
+
+  <!-- Activates the AspectJ-Weaver -->
+  <context:component-scan base-package="de.juplo"/>
+  <context:spring-configured/>
+
+  <bean id="eTag" class="java.lang.String">
+    <constructor-arg value="Hallo Welt!"/>
+  </bean>
+
+  <bean id="weak" class="java.lang.Boolean">
+    <constructor-arg value="true"/>
+  </bean>
+
+  <bean id="lastModified" class="java.lang.Long">
+    <constructor-arg value="1338593731"/>
+  </bean>
+
+  <bean id="cacheSeconds" class="java.lang.Integer">
+    <constructor-arg value="3600"/>
+  </bean>
+
+</beans>
diff --git a/accelerator-examples/jsp/src/main/resources/log4j.xml b/accelerator-examples/jsp/src/main/resources/log4j.xml
new file mode 100644 (file)
index 0000000..b1e35f8
--- /dev/null
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+
+  <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
+    <layout class="org.apache.log4j.PatternLayout">
+      <param name="ConversionPattern" value="%p - %C{1}.%M(%L) | %m%n"/>
+    </layout>
+  </appender>
+
+  <logger name="de.juplo">
+   <level value="trace"/>
+  </logger>
+
+  <root>
+    <level value="info"/>
+    <appender-ref ref="CONSOLE"/>
+  </root>
+
+</log4j:configuration>
diff --git a/accelerator-examples/jsp/src/main/webapp/WEB-INF/c.tld b/accelerator-examples/jsp/src/main/webapp/WEB-INF/c.tld
new file mode 100644 (file)
index 0000000..4b85b44
--- /dev/null
@@ -0,0 +1,578 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<!--
+Workaround for a bug in ServletUnit:
+The TLD in jstl.jar is not found when the webapp is running in this
+environment. Because of that, the TLD must be copied here and this
+copy must be referenced explicitly in all JSP's
+-->
+<taglib xmlns="http://java.sun.com/xml/ns/javaee"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
+    version="2.1">
+    
+  <description>JSTL 1.1 core library</description>
+  <display-name>JSTL core</display-name>
+  <tlib-version>1.1</tlib-version>
+  <short-name>c</short-name>
+  <uri>http://java.sun.com/jsp/jstl/core</uri>
+
+  <validator>
+    <description>
+        Provides core validation features for JSTL tags.
+    </description>
+    <validator-class>
+        org.apache.taglibs.standard.tlv.JstlCoreTLV
+    </validator-class>
+  </validator>
+
+  <tag>
+    <description>
+        Catches any Throwable that occurs in its body and optionally
+        exposes it.
+    </description>
+    <name>catch</name>
+    <tag-class>org.apache.taglibs.standard.tag.common.core.CatchTag</tag-class>
+    <body-content>JSP</body-content>
+    <attribute>
+        <description>
+Name of the exported scoped variable for the
+exception thrown from a nested action. The type of the
+scoped variable is the type of the exception thrown.
+        </description>
+        <name>var</name>
+        <required>false</required>
+        <rtexprvalue>false</rtexprvalue>
+    </attribute>
+  </tag>
+
+  <tag>
+    <description>
+       Simple conditional tag that establishes a context for
+       mutually exclusive conditional operations, marked by
+       &lt;when&gt; and &lt;otherwise&gt;
+    </description>
+    <name>choose</name>
+    <tag-class>org.apache.taglibs.standard.tag.common.core.ChooseTag</tag-class>
+    <body-content>JSP</body-content>
+  </tag>
+
+  <tag>
+    <description>
+       Simple conditional tag, which evalutes its body if the
+       supplied condition is true and optionally exposes a Boolean
+       scripting variable representing the evaluation of this condition
+    </description>
+    <name>if</name>
+    <tag-class>org.apache.taglibs.standard.tag.rt.core.IfTag</tag-class>
+    <body-content>JSP</body-content>
+    <attribute>
+        <description>
+The test condition that determines whether or
+not the body content should be processed.
+        </description>
+        <name>test</name>
+        <required>true</required>
+        <rtexprvalue>true</rtexprvalue>
+       <type>boolean</type>
+    </attribute>
+    <attribute>
+        <description>
+Name of the exported scoped variable for the
+resulting value of the test condition. The type
+of the scoped variable is Boolean.        
+        </description>
+        <name>var</name>
+        <required>false</required>
+        <rtexprvalue>false</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Scope for var.
+        </description>
+        <name>scope</name>
+        <required>false</required>
+        <rtexprvalue>false</rtexprvalue>
+    </attribute>
+  </tag>
+
+  <tag>
+    <description>
+        Retrieves an absolute or relative URL and exposes its contents
+        to either the page, a String in 'var', or a Reader in 'varReader'.
+    </description>
+    <name>import</name>
+    <tag-class>org.apache.taglibs.standard.tag.rt.core.ImportTag</tag-class>
+    <tei-class>org.apache.taglibs.standard.tei.ImportTEI</tei-class>
+    <body-content>JSP</body-content>
+    <attribute>
+        <description>
+The URL of the resource to import.
+        </description>
+        <name>url</name>
+        <required>true</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Name of the exported scoped variable for the
+resource's content. The type of the scoped
+variable is String.
+        </description>
+        <name>var</name>
+        <required>false</required>
+        <rtexprvalue>false</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Scope for var.
+        </description>
+        <name>scope</name>
+        <required>false</required>
+        <rtexprvalue>false</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Name of the exported scoped variable for the
+resource's content. The type of the scoped
+variable is Reader.
+        </description>
+        <name>varReader</name>
+        <required>false</required>
+        <rtexprvalue>false</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Name of the context when accessing a relative
+URL resource that belongs to a foreign
+context.
+        </description>
+        <name>context</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Character encoding of the content at the input
+resource.
+        </description>
+        <name>charEncoding</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+  </tag>
+
+  <tag>
+    <description>
+       The basic iteration tag, accepting many different
+        collection types and supporting subsetting and other
+        functionality
+    </description>
+    <name>forEach</name>
+    <tag-class>org.apache.taglibs.standard.tag.rt.core.ForEachTag</tag-class>
+    <tei-class>org.apache.taglibs.standard.tei.ForEachTEI</tei-class>
+    <body-content>JSP</body-content>
+    <attribute>
+        <description>
+Collection of items to iterate over.
+        </description>
+       <name>items</name>
+       <required>false</required>
+       <rtexprvalue>true</rtexprvalue>
+       <type>java.lang.Object</type>
+        <deferred-value>
+           <type>java.lang.Object</type>
+        </deferred-value>
+    </attribute>
+    <attribute>
+        <description>
+If items specified:
+Iteration begins at the item located at the
+specified index. First item of the collection has
+index 0.
+If items not specified:
+Iteration begins with index set at the value
+specified.
+        </description>
+       <name>begin</name>
+       <required>false</required>
+       <rtexprvalue>true</rtexprvalue>
+       <type>int</type>
+    </attribute>
+    <attribute>
+        <description>
+If items specified:
+Iteration ends at the item located at the
+specified index (inclusive).
+If items not specified:
+Iteration ends when index reaches the value
+specified.
+        </description>
+       <name>end</name>
+       <required>false</required>
+       <rtexprvalue>true</rtexprvalue>
+       <type>int</type>
+    </attribute>
+    <attribute>
+        <description>
+Iteration will only process every step items of
+the collection, starting with the first one.
+        </description>
+       <name>step</name>
+       <required>false</required>
+       <rtexprvalue>true</rtexprvalue>
+       <type>int</type>
+    </attribute>
+    <attribute>
+        <description>
+Name of the exported scoped variable for the
+current item of the iteration. This scoped
+variable has nested visibility. Its type depends
+on the object of the underlying collection.
+        </description>
+       <name>var</name>
+       <required>false</required>
+       <rtexprvalue>false</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Name of the exported scoped variable for the
+status of the iteration. Object exported is of type
+javax.servlet.jsp.jstl.core.LoopTagStatus. This scoped variable has nested
+visibility.
+        </description>
+       <name>varStatus</name>
+       <required>false</required>
+       <rtexprvalue>false</rtexprvalue>
+    </attribute>
+  </tag>
+
+  <tag>
+    <description>
+       Iterates over tokens, separated by the supplied delimeters
+    </description>
+    <name>forTokens</name>
+    <tag-class>org.apache.taglibs.standard.tag.rt.core.ForTokensTag</tag-class>
+    <body-content>JSP</body-content>
+    <attribute>
+        <description>
+String of tokens to iterate over.
+        </description>
+       <name>items</name>
+       <required>true</required>
+       <rtexprvalue>true</rtexprvalue>
+       <type>java.lang.String</type>
+        <deferred-value>
+           <type>java.lang.String</type>
+        </deferred-value>
+    </attribute>
+    <attribute>
+        <description>
+The set of delimiters (the characters that
+separate the tokens in the string).
+        </description>
+       <name>delims</name>
+       <required>true</required>
+       <rtexprvalue>true</rtexprvalue>
+       <type>java.lang.String</type>
+    </attribute>
+    <attribute>
+        <description>
+Iteration begins at the token located at the
+specified index. First token has index 0.
+        </description>
+       <name>begin</name>
+       <required>false</required>
+       <rtexprvalue>true</rtexprvalue>
+       <type>int</type>
+    </attribute>
+    <attribute>
+        <description>
+Iteration ends at the token located at the
+specified index (inclusive).
+        </description>
+       <name>end</name>
+       <required>false</required>
+       <rtexprvalue>true</rtexprvalue>
+       <type>int</type>
+    </attribute>
+    <attribute>
+        <description>
+Iteration will only process every step tokens
+of the string, starting with the first one.
+        </description>
+       <name>step</name>
+       <required>false</required>
+       <rtexprvalue>true</rtexprvalue>
+       <type>int</type>
+    </attribute>
+    <attribute>
+        <description>
+Name of the exported scoped variable for the
+current item of the iteration. This scoped
+variable has nested visibility.
+        </description>
+       <name>var</name>
+       <required>false</required>
+       <rtexprvalue>false</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Name of the exported scoped variable for the
+status of the iteration. Object exported is of
+type
+javax.servlet.jsp.jstl.core.LoopTag
+Status. This scoped variable has nested
+visibility.
+        </description>
+       <name>varStatus</name>
+       <required>false</required>
+       <rtexprvalue>false</rtexprvalue>
+    </attribute>
+  </tag>
+
+  <tag>
+    <description>
+        Like &lt;%= ... &gt;, but for expressions.
+    </description> 
+    <name>out</name>
+    <tag-class>org.apache.taglibs.standard.tag.rt.core.OutTag</tag-class>
+    <body-content>JSP</body-content>
+    <attribute>
+        <description>
+Expression to be evaluated.
+        </description>
+        <name>value</name>
+        <required>true</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Default value if the resulting value is null.
+        </description>
+        <name>default</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Determines whether characters &lt;,&gt;,&amp;,'," in the
+resulting string should be converted to their
+corresponding character entity codes. Default value is
+true.
+        </description>
+        <name>escapeXml</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+  </tag>
+
+
+  <tag>
+    <description>
+        Subtag of &lt;choose&gt; that follows &lt;when&gt; tags
+        and runs only if all of the prior conditions evaluated to
+        'false'
+    </description>
+    <name>otherwise</name>
+    <tag-class>org.apache.taglibs.standard.tag.common.core.OtherwiseTag</tag-class>
+    <body-content>JSP</body-content>
+  </tag>
+
+  <tag>
+    <description>
+        Adds a parameter to a containing 'import' tag's URL.
+    </description>
+    <name>param</name>
+    <tag-class>org.apache.taglibs.standard.tag.rt.core.ParamTag</tag-class>
+    <body-content>JSP</body-content>
+    <attribute>
+        <description>
+Name of the query string parameter.
+        </description>
+        <name>name</name>
+        <required>true</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Value of the parameter.
+        </description>
+        <name>value</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+  </tag>
+
+  <tag>
+    <description>
+        Redirects to a new URL.
+    </description>
+    <name>redirect</name>
+    <tag-class>org.apache.taglibs.standard.tag.rt.core.RedirectTag</tag-class>
+    <body-content>JSP</body-content>
+    <attribute>
+        <description>
+The URL of the resource to redirect to.
+        </description>
+        <name>url</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Name of the context when redirecting to a relative URL
+resource that belongs to a foreign context.
+        </description>
+        <name>context</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+  </tag>
+
+  <tag>
+    <description>
+        Removes a scoped variable (from a particular scope, if specified).
+    </description>
+    <name>remove</name>
+    <tag-class>org.apache.taglibs.standard.tag.common.core.RemoveTag</tag-class>
+    <body-content>empty</body-content>
+    <attribute>
+        <description>
+Name of the scoped variable to be removed.
+        </description>
+        <name>var</name>
+        <required>true</required>
+        <rtexprvalue>false</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Scope for var.
+        </description>
+        <name>scope</name>
+        <required>false</required>
+        <rtexprvalue>false</rtexprvalue>
+    </attribute>
+  </tag>
+
+ <tag>
+    <description>
+        Sets the result of an expression evaluation in a 'scope'
+    </description>
+    <name>set</name>
+    <tag-class>org.apache.taglibs.standard.tag.rt.core.SetTag</tag-class>
+    <body-content>JSP</body-content>
+    <attribute>
+        <description>
+Name of the exported scoped variable to hold the value
+specified in the action. The type of the scoped variable is
+whatever type the value expression evaluates to.
+        </description>
+        <name>var</name>
+        <required>false</required>
+        <rtexprvalue>false</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Expression to be evaluated.
+        </description>
+        <name>value</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+        <deferred-value>
+           <type>java.lang.Object</type>
+        </deferred-value>
+    </attribute>
+    <attribute>
+        <description>
+Target object whose property will be set. Must evaluate to
+a JavaBeans object with setter property property, or to a
+java.util.Map object.
+        </description>
+        <name>target</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Name of the property to be set in the target object.
+        </description>
+        <name>property</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Scope for var.
+        </description>
+        <name>scope</name>
+        <required>false</required>
+        <rtexprvalue>false</rtexprvalue>
+    </attribute>
+  </tag>
+
+  <tag>
+    <description>
+        Creates a URL with optional query parameters.
+    </description>
+    <name>url</name>
+    <tag-class>org.apache.taglibs.standard.tag.rt.core.UrlTag</tag-class>
+    <body-content>JSP</body-content>
+    <attribute>
+        <description>
+Name of the exported scoped variable for the
+processed url. The type of the scoped variable is
+String.
+        </description>
+        <name>var</name>
+        <required>false</required>
+        <rtexprvalue>false</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Scope for var.
+        </description>
+        <name>scope</name>
+        <required>false</required>
+        <rtexprvalue>false</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+URL to be processed.
+        </description>
+        <name>value</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+    <attribute>
+        <description>
+Name of the context when specifying a relative URL
+resource that belongs to a foreign context.
+        </description>
+        <name>context</name>
+        <required>false</required>
+        <rtexprvalue>true</rtexprvalue>
+    </attribute>
+  </tag>
+
+  <tag>
+    <description>
+       Subtag of &lt;choose&gt; that includes its body if its
+       condition evalutes to 'true'
+    </description>
+    <name>when</name>
+    <tag-class>org.apache.taglibs.standard.tag.rt.core.WhenTag</tag-class>
+    <body-content>JSP</body-content>
+    <attribute>
+        <description>
+The test condition that determines whether or not the
+body content should be processed.
+        </description>
+        <name>test</name>
+        <required>true</required>
+        <rtexprvalue>true</rtexprvalue>
+       <type>boolean</type>
+    </attribute>
+  </tag>
+
+</taglib>
diff --git a/accelerator-examples/jsp/src/main/webapp/WEB-INF/included.jsp b/accelerator-examples/jsp/src/main/webapp/WEB-INF/included.jsp
new file mode 100644 (file)
index 0000000..1931d89
--- /dev/null
@@ -0,0 +1,2 @@
+<%@page contentType="text/html" pageEncoding="UTF-8" session="false"%>
+<p>Hello World, again...</p>
diff --git a/accelerator-examples/jsp/src/main/webapp/WEB-INF/web.xml b/accelerator-examples/jsp/src/main/webapp/WEB-INF/web.xml
new file mode 100644 (file)
index 0000000..938240c
--- /dev/null
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+
+  <!-- Context Configuration locations for Spring XML files -->
+
+  <context-param>
+    <param-name>contextConfigLocation</param-name>
+    <param-value>classpath:/config.xml</param-value>
+  </context-param>
+
+
+  <!-- Listener-Definitions -->
+
+  <listener>
+    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
+  </listener>
+
+
+  <!-- Filter-Definitions -->
+
+  <filter>
+    <filter-name>accelerator</filter-name>
+    <filter-class>de.juplo.accelerator.AcceleratorFilter</filter-class>
+  </filter>
+
+  <filter>
+    <filter-name>logger</filter-name>
+    <filter-class>de.juplo.testingtools.LoggingHttpServletResponseFilter</filter-class>
+  </filter>
+
+
+  <!-- Filter-Mappings -->
+
+  <filter-mapping>
+    <filter-name>logger</filter-name>
+    <url-pattern>*.jsp</url-pattern>
+  </filter-mapping>
+
+  <filter-mapping>
+    <filter-name>accelerator</filter-name>
+    <url-pattern>*.jsp</url-pattern>
+  </filter-mapping>
+
+</web-app>
diff --git a/accelerator-examples/jsp/src/main/webapp/faulty-page.jsp b/accelerator-examples/jsp/src/main/webapp/faulty-page.jsp
new file mode 100644 (file)
index 0000000..e40c213
--- /dev/null
@@ -0,0 +1,21 @@
+<%@page contentType="text/html" pageEncoding="UTF-8" session="false" buffer="1kb" %>
+<%@taglib uri="/WEB-INF/c.tld" prefix="c"%>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>Faulty Page</title>
+  </head>
+  <body>
+    <h1>Faulty Page</h1>
+    <p>
+      This page will raise an error<c:forEach begin="1" end="${param['n']}" step="1">.</c:forEach>
+      after a while!
+    </p>
+    <p>
+      <strong>Ecactly, NOW:</strong>
+      <% if (true) throw new RuntimeException("Oh no!"); %>
+    </p>
+  </body>
+</html>
diff --git a/accelerator-examples/jsp/src/main/webapp/index.html b/accelerator-examples/jsp/src/main/webapp/index.html
new file mode 100644 (file)
index 0000000..2246263
--- /dev/null
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>JSP Examples</title>
+  </head>
+  <body>
+    <h1>JSP Examples</h1>
+    <ul>
+      <li><a href="/simple-page.jsp">A really simple JSP-page</a></li>
+      <li><a href="/page-with-include.jsp">A JSP-page with several includes</a></li>
+      <li><a href="/page-with-forward.jsp">A JSP-page with a forward to /simple-page.jsp</a></li>
+      <li><a href="/faulty-page.jsp?n=8822">A JSP-page with raises an error</a></li>
+    </ul>
+  </body>
+</html>
diff --git a/accelerator-examples/jsp/src/main/webapp/page-with-forward.jsp b/accelerator-examples/jsp/src/main/webapp/page-with-forward.jsp
new file mode 100644 (file)
index 0000000..35e4905
--- /dev/null
@@ -0,0 +1,14 @@
+<%@page contentType="text/html" pageEncoding="UTF-8" session="false" buffer="1kb" %>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>Page with forward</title>
+  </head>
+  <body>
+    <h1>Hello World!</h1>
+    <p>This should not be seen, because the page is forwardes to /simple-page.jsp</p>
+    <jsp:forward page="/simple-page.jsp" />
+  </body>
+</html>
diff --git a/accelerator-examples/jsp/src/main/webapp/page-with-include.jsp b/accelerator-examples/jsp/src/main/webapp/page-with-include.jsp
new file mode 100644 (file)
index 0000000..3347af7
--- /dev/null
@@ -0,0 +1,14 @@
+<%@page contentType="text/html" pageEncoding="UTF-8" session="false" buffer="1kb" %>
+<%@taglib uri="/WEB-INF/c.tld" prefix="c"%>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>Page with include</title>
+  </head>
+  <body>
+    <h1>Hello World!</h1>
+    <c:forEach var="i" begin="1" end="100" step="1">${i}:<jsp:include page="/WEB-INF/included.jsp" /></c:forEach>
+  </body>
+</html>
diff --git a/accelerator-examples/jsp/src/main/webapp/simple-page.jsp b/accelerator-examples/jsp/src/main/webapp/simple-page.jsp
new file mode 100644 (file)
index 0000000..99d92d7
--- /dev/null
@@ -0,0 +1,13 @@
+<%@page contentType="text/html" pageEncoding="UTF-8" session="false" buffer="1kb" %>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>Simple Page</title>
+  </head>
+  <body>
+    <h1>Simple Page</h1>
+    <p>This page is a simple jsp-page</p>
+  </body>
+</html>
diff --git a/accelerator-examples/jsp/src/test/java/de/juplo/accelerator/examples/JspTest.java b/accelerator-examples/jsp/src/test/java/de/juplo/accelerator/examples/JspTest.java
new file mode 100644 (file)
index 0000000..5000a9c
--- /dev/null
@@ -0,0 +1,43 @@
+package de.juplo.accelerator.examples;
+
+import com.meterware.httpunit.WebResponse;
+import de.juplo.testingtools.HttpTestCase;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ *
+ * @author kai
+ */
+public class JspTest extends HttpTestCase {
+
+  private static final Logger log = LoggerFactory.getLogger(JspTest.class);
+
+
+  public JspTest() {
+    super("src/main/webapp/WEB-INF/web.xml");
+  }
+
+
+  @Test
+  public void testSimplePage() throws Exception {
+    WebResponse response = executeRequest("http://localhost:8080/simple-page.jsp");
+    log.info("Title:\t\t{}", response.getTitle());
+    log.debug("Text:\t\t{}", response.getText());
+  }
+
+  @Test
+  public void testPageWithInclude() throws Exception {
+    WebResponse response = executeRequest("http://localhost:8080/page-with-include.jsp");
+    log.info("Title:\t\t{}", response.getTitle());
+    log.debug("Text:\t\t{}", response.getText());
+  }
+
+  @Test
+  public void testPageWithForward() throws Exception {
+    WebResponse response = executeRequest("http://localhost:8080/page-with-forward.jsp");
+    log.info("Title:\t\t{}", response.getTitle());
+    log.debug("Text:\t\t{}", response.getText());
+  }
+}
diff --git a/accelerator-examples/jsp/tomcat.sh b/accelerator-examples/jsp/tomcat.sh
new file mode 100755 (executable)
index 0000000..4378a12
--- /dev/null
@@ -0,0 +1,11 @@
+#!/bin/bash
+#
+
+# OutOfMemoryException beim "mvn jetty:run" umgehen und
+# Parameter zum nachträglichen anhängen eines Debuggers
+# setzen
+export MAVEN_OPTS="-Xmx512m -XX:MaxPermSize=256m -XX:-UseGCOverheadLimit -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=4000,server=y,suspend=n"
+
+rm -v src/main/webapp/WEB-INF/lib/juplo*
+
+mvn tomcat:run-war
diff --git a/accelerator-examples/pom.xml b/accelerator-examples/pom.xml
new file mode 100644 (file)
index 0000000..fcd9e1d
--- /dev/null
@@ -0,0 +1,202 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <groupId>de.juplo</groupId>
+  <artifactId>juplo-accelerator-examples</artifactId>
+  <name>Juplo - Accelerator-Examples</name>
+  <version>0.1-SNAPSHOT</version>
+  <packaging>pom</packaging>
+  <url>http://www.juplo.de/accelerator-examples</url>
+
+  <prerequisites>
+    <maven>2.0.6</maven>
+  </prerequisites>
+
+  <developers>
+    <developer>
+      <id>kai</id>
+      <name>Kai Moritz</name>
+      <email>kai@juplo.de</email>
+    </developer>
+  </developers>
+
+  <modules>
+    <module>jsp</module>
+    <module>static</module>
+    <module>servlet</module>
+    <module>spring</module>
+  </modules>
+
+  <properties>
+    <!-- Zeichensatz -->
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <!-- Verwendete Versionen -->
+    <aspectj.version>1.6.11</aspectj.version>
+    <commons-io.version>1.3.2</commons-io.version>
+    <jasper.version>6.0.29</jasper.version>
+    <jpa.version>1.0</jpa.version>
+    <jstl.version>1.2</jstl.version>
+    <junit.version>4.8.1</junit.version>
+    <juplo-testingtools.version>1.0</juplo-testingtools.version>
+    <juplo-accelerator.version>0.1-SNAPSHOT</juplo-accelerator.version>
+    <log4j.version>1.2.16</log4j.version>
+    <servlet-api.version>2.5</servlet-api.version>
+    <slf4j.binding>slf4j-log4j12</slf4j.binding>
+    <slf4j.version>1.6.1</slf4j.version>
+    <springframework.version>3.0.6.RELEASE</springframework.version>
+  </properties>
+
+  <dependencies>
+    <!-- Juplo -->
+    <dependency>
+      <groupId>de.juplo</groupId>
+      <artifactId>juplo-accelerator</artifactId>
+      <version>${juplo-accelerator.version}</version>
+    </dependency>
+    <!-- JSP-Stuff... -->
+    <dependency>
+      <groupId>javax.servlet</groupId>
+      <artifactId>jstl</artifactId>
+      <version>${jstl.version}</version>
+      <scope>runtime</scope>
+    </dependency>
+    <!--  Spring -->
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-webmvc</artifactId>
+      <version>${springframework.version}</version>
+      <scope>runtime</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-context</artifactId>
+      <version>${springframework.version}</version>
+      <scope>runtime</scope>
+      <exclusions>
+        <!-- Exclude Commons Logging in favor of SLF4j -->
+        <exclusion>
+          <groupId>commons-logging</groupId>
+          <artifactId>commons-logging</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-aspects</artifactId>
+      <version>${springframework.version}</version>
+      <scope>runtime</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.aspectj</groupId>
+      <artifactId>aspectjrt</artifactId>
+      <version>${aspectj.version}</version>
+      <scope>runtime</scope>
+    </dependency>
+    <!-- Logging -->
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>${slf4j.binding}</artifactId>
+      <version>${slf4j.version}</version>
+      <scope>runtime</scope>
+    </dependency>
+    <!-- Test -->
+    <dependency>
+      <groupId>de.juplo</groupId>
+      <artifactId>juplo-testingtools</artifactId>
+      <version>${juplo-testingtools.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.tomcat</groupId>
+      <artifactId>jasper</artifactId>
+      <version>${jasper.version}</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <configuration>
+          <source>1.6</source>
+          <target>1.6</target>
+          <encoding>utf8</encoding>
+          <showWarnings>true</showWarnings>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.mortbay.jetty</groupId>
+        <artifactId>jetty-maven-plugin</artifactId>
+        <configuration>
+          <connectors>
+            <connector implementation="org.eclipse.jetty.server.nio.BlockingChannelConnector">
+              <host>0.0.0.0</host>
+              <port>8080</port>
+              <acceptors>2</acceptors>
+            </connector>
+          </connectors>
+          <scanIntervalSeconds>0</scanIntervalSeconds>
+          <scanTargetPatterns>
+            <scanTargetPattern>
+              <directory>${project.basedir}/src/main/webapp/WEB-INF</directory>
+              <excludes>
+                <exclude>**/*.jsp</exclude>
+              </excludes>
+              <includes>
+                <include>**/*.properties</include>
+                <include>**/*.xml</include>
+              </includes>
+            </scanTargetPattern>
+          </scanTargetPatterns>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>tomcat-maven-plugin</artifactId>
+        <version>1.1</version>
+        <configuration>
+          <path>/</path>
+          <uriEncoding>UTF-8</uriEncoding>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
+  <reporting>
+    <plugins>
+      <plugin>
+        <artifactId>maven-changes-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>cobertura-maven-plugin</artifactId>
+        <version>2.0</version>
+      </plugin>
+      <plugin>
+        <artifactId>maven-javadoc-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <artifactId>maven-jxr-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <artifactId>maven-pmd-plugin</artifactId>
+        <configuration>
+          <linkXref>true</linkXref>
+          <targetJdk>1.5</targetJdk>
+        </configuration>
+      </plugin>
+      <plugin>
+        <artifactId>maven-surefire-report-plugin</artifactId>
+        <version>2.7.2</version>
+      </plugin>
+    </plugins>
+  </reporting>
+
+</project>
diff --git a/accelerator-examples/servlet/jetty.sh b/accelerator-examples/servlet/jetty.sh
new file mode 100755 (executable)
index 0000000..4950796
--- /dev/null
@@ -0,0 +1,11 @@
+#!/bin/bash
+#
+
+# OutOfMemoryException beim "mvn jetty:run" umgehen und
+# Parameter zum nachträglichen anhängen eines Debuggers
+# setzen
+export MAVEN_OPTS="-Xmx512m -XX:MaxPermSize=256m -XX:-UseGCOverheadLimit -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=4000,server=y,suspend=n"
+
+rm -v src/main/webapp/WEB-INF/lib/juplo*
+
+mvn jetty:run
diff --git a/accelerator-examples/servlet/pom.xml b/accelerator-examples/servlet/pom.xml
new file mode 100644 (file)
index 0000000..92a5e6f
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>de.juplo</groupId>
+    <artifactId>juplo-accelerator-examples</artifactId>
+    <version>0.1-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>juplo-accelerator-examples-servlet</artifactId>
+  <packaging>war</packaging>
+  <name>Juplo - Accelerator-Examples: Servlet</name>
+
+</project>
diff --git a/accelerator-examples/servlet/src/main/java/de/juplo/accelerator/examples/FaultyServlet.java b/accelerator-examples/servlet/src/main/java/de/juplo/accelerator/examples/FaultyServlet.java
new file mode 100644 (file)
index 0000000..d9eee5f
--- /dev/null
@@ -0,0 +1,43 @@
+package de.juplo.accelerator.examples;
+
+import java.io.IOException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ *
+ * @author kai
+ */
+public class FaultyServlet extends HttpServlet {
+  private final static Logger log = LoggerFactory.getLogger(FaultyServlet.class);
+  private final static long lastModified = System.currentTimeMillis();
+
+
+  @Override
+  protected long getLastModified(HttpServletRequest req) {
+    return lastModified;
+  }
+
+  @Override
+  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
+    int n = 0;
+    try {
+      /**
+       * Wenn der Parameter n gesetzt ist, wird ein Antwort-Body erzeugt, der
+       * exakt die Anzahl der geforderten Bytes enthält.
+       */
+      n = Integer.parseInt(request.getParameter("n"));
+    }
+    catch(Exception e) {}
+    log.debug("Error will be risen after {} bytes: {}", n, request.getRequestURI());
+    ServletOutputStream out = response.getOutputStream();
+    for (int i=0; i<n; i++)
+      out.write(i%2 + 48); /** ASCII-Codes für "0" und "1" */
+    log.debug("Failing.... NOW:");
+    throw new RuntimeException("Oh, no!");
+  }
+}
diff --git a/accelerator-examples/servlet/src/main/resources/config.xml b/accelerator-examples/servlet/src/main/resources/config.xml
new file mode 100644 (file)
index 0000000..cab8042
--- /dev/null
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:context="http://www.springframework.org/schema/context"
+       xsi:schemaLocation="
+           http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
+           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
+
+  <!-- Activates the AspectJ-Weaver -->
+  <context:component-scan base-package="de.juplo"/>
+  <context:spring-configured/>
+
+  <bean id="eTag" class="java.lang.String">
+    <constructor-arg value="Hallo Welt!"/>
+  </bean>
+
+  <bean id="weak" class="java.lang.Boolean">
+    <constructor-arg value="true"/>
+  </bean>
+
+  <bean id="lastModified" class="java.lang.Long">
+    <constructor-arg value="1338593731"/>
+  </bean>
+
+  <bean id="cacheSeconds" class="java.lang.Integer">
+    <constructor-arg value="3600"/>
+  </bean>
+
+  <bean id="simpleUrlHandlerMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
+    <property name="defaultHandler" value="urlFilenameViewController"/>
+  </bean>
+
+  <bean id="urlFilenameViewController" class="org.springframework.web.servlet.mvc.UrlFilenameViewController"/>
+
+  <!-- Resolves view names to protected .jsp resources within the /WEB-INF/views directory -->
+  <bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
+    <property name="prefix" value="/WEB-INF/views/"/>
+    <property name="suffix" value=".jsp"/>
+  </bean>
+
+</beans>
diff --git a/accelerator-examples/servlet/src/main/resources/log4j.xml b/accelerator-examples/servlet/src/main/resources/log4j.xml
new file mode 100644 (file)
index 0000000..b1e35f8
--- /dev/null
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+
+  <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
+    <layout class="org.apache.log4j.PatternLayout">
+      <param name="ConversionPattern" value="%p - %C{1}.%M(%L) | %m%n"/>
+    </layout>
+  </appender>
+
+  <logger name="de.juplo">
+   <level value="trace"/>
+  </logger>
+
+  <root>
+    <level value="info"/>
+    <appender-ref ref="CONSOLE"/>
+  </root>
+
+</log4j:configuration>
diff --git a/accelerator-examples/servlet/src/main/webapp/WEB-INF/web.xml b/accelerator-examples/servlet/src/main/webapp/WEB-INF/web.xml
new file mode 100644 (file)
index 0000000..994a904
--- /dev/null
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+
+  <!-- Context Configuration locations for Spring XML files -->
+
+  <context-param>
+    <param-name>contextConfigLocation</param-name>
+    <param-value>classpath:/config.xml</param-value>
+  </context-param>
+
+
+  <!-- Listener-Definitions -->
+
+  <listener>
+    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
+  </listener>
+
+
+  <!-- Filter-Definitions -->
+
+  <filter>
+    <filter-name>accelerator</filter-name>
+    <filter-class>de.juplo.accelerator.AcceleratorFilter</filter-class>
+  </filter>
+
+  <filter>
+    <filter-name>logger</filter-name>
+    <filter-class>de.juplo.testingtools.LoggingHttpServletResponseFilter</filter-class>
+  </filter>
+
+
+  <!-- Filter-Mappings -->
+
+  <filter-mapping>
+    <filter-name>logger</filter-name>
+    <url-pattern>/test-servlet</url-pattern>
+  </filter-mapping>
+  <filter-mapping>
+    <filter-name>logger</filter-name>
+    <url-pattern>/faulty-servlet</url-pattern>
+  </filter-mapping>
+
+  <filter-mapping>
+    <filter-name>accelerator</filter-name>
+    <url-pattern>/test-servlet</url-pattern>
+  </filter-mapping>
+  <filter-mapping>
+    <filter-name>accelerator</filter-name>
+    <url-pattern>/faulty-servlet</url-pattern>
+  </filter-mapping>
+
+
+  <!-- Servlet-Definitions -->
+
+  <servlet>
+    <servlet-name>test-servlet</servlet-name>
+    <servlet-class>de.juplo.testintools.TestServlet</servlet-class>
+  </servlet>
+  <servlet>
+    <servlet-name>faulty-servlet</servlet-name>
+    <servlet-class>de.juplo.accelerator.examples.FaultyServlet</servlet-class>
+  </servlet>
+
+
+  <!-- Servlet-Mappings -->
+
+  <servlet-mapping>
+    <servlet-name>test-servlet</servlet-name>
+    <url-pattern>/test-servlet</url-pattern>
+  </servlet-mapping>
+  <servlet-mapping>
+    <servlet-name>faulty-servlet</servlet-name>
+    <url-pattern>/faulty-servlet</url-pattern>
+  </servlet-mapping>
+
+</web-app>
diff --git a/accelerator-examples/servlet/src/main/webapp/index.html b/accelerator-examples/servlet/src/main/webapp/index.html
new file mode 100644 (file)
index 0000000..c08ba7c
--- /dev/null
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>Servlet Examples</title>
+  </head>
+  <body>
+    <h1>Servlet Examples</h1>
+    <p>This page is a static HTML-page</p>
+    <ul>
+      <li><a href="/test-servlet">Empty Answer</a></li>
+      <li><a href="/test-servlet?n=16">16-Bytes-Answer</a></li>
+      <li><a href="/test-servlet?n=32">32-Bytes-Answer</a></li>
+      <li><a href="/test-servlet?n=64">64-Bytes-Answer</a></li>
+      <li><a href="/test-servlet?n=128">128-Bytes-Answer</a></li>
+      <li><a href="/test-servlet?n=256">256-Bytes-Answer</a></li>
+      <li><a href="/test-servlet?n=512">512-Bytes-Answer</a></li>
+      <li><a href="/test-servlet?n=1024">1024-Bytes-Answer</a></li>
+      <li><a href="/test-servlet?n=2048">2048-Bytes-Answer</a></li>
+      <li><a href="/test-servlet?n=4096">4096-Bytes-Answer</a></li>
+      <li><a href="/test-servlet?n=8192">8192-Bytes-Answer</a></li>
+      <li><a href="/test-servlet?n=16384">16384-Bytes-Answer</a></li>
+    </ul>
+    <ul>
+      <li><a href="/faulty-servlet">Empty Faulty Answer</a></li>
+      <li><a href="/faulty-servlet?n=16">Error after 16 Bytes</a></li>
+      <li><a href="/faulty-servlet?n=32">Error after 32 Bytes</a></li>
+      <li><a href="/faulty-servlet?n=64">Error after 64 Bytes</a></li>
+      <li><a href="/faulty-servlet?n=128">Error after 128 Bytes</a></li>
+      <li><a href="/faulty-servlet?n=256">Error after 256 Bytes</a></li>
+      <li><a href="/faulty-servlet?n=512">Error after 512 Bytes</a></li>
+      <li><a href="/faulty-servlet?n=1024">Error after 1024 Bytes</a></li>
+      <li><a href="/faulty-servlet?n=2048">Error after 2048 Bytes</a></li>
+      <li><a href="/faulty-servlet?n=4096">Error after 4096 Bytes</a></li>
+      <li><a href="/faulty-servlet?n=8192">Error after 8192 Bytes</a></li>
+      <li><a href="/faulty-servlet?n=16384">Error after 16384 Bytes</a></li>
+    </ul>
+  </body>
+</html>
diff --git a/accelerator-examples/servlet/src/test/java/de/juplo/accelerator/examples/ServletTest.java b/accelerator-examples/servlet/src/test/java/de/juplo/accelerator/examples/ServletTest.java
new file mode 100644 (file)
index 0000000..77592ab
--- /dev/null
@@ -0,0 +1,28 @@
+package de.juplo.accelerator.examples;
+
+import de.juplo.testingtools.HttpTestCase;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ *
+ * @author kai
+ */
+public class ServletTest extends HttpTestCase {
+
+  private static final Logger log = LoggerFactory.getLogger(ServletTest.class);
+
+
+  public ServletTest() {
+    super("src/main/webapp/WEB-INF/web.xml");
+  }
+
+
+  @Test
+  public void test() throws Exception {
+//    WebResponse response = executeRequest("http://localhost:8080/simple-page.jsp");
+//    log.info("Title:\t\t{}", response.getTitle());
+//    log.debug("Text:\t\t{}", response.getText());
+  }
+}
diff --git a/accelerator-examples/servlet/tomcat.sh b/accelerator-examples/servlet/tomcat.sh
new file mode 100755 (executable)
index 0000000..4378a12
--- /dev/null
@@ -0,0 +1,11 @@
+#!/bin/bash
+#
+
+# OutOfMemoryException beim "mvn jetty:run" umgehen und
+# Parameter zum nachträglichen anhängen eines Debuggers
+# setzen
+export MAVEN_OPTS="-Xmx512m -XX:MaxPermSize=256m -XX:-UseGCOverheadLimit -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=4000,server=y,suspend=n"
+
+rm -v src/main/webapp/WEB-INF/lib/juplo*
+
+mvn tomcat:run-war
diff --git a/accelerator-examples/spring/jetty.sh b/accelerator-examples/spring/jetty.sh
new file mode 100755 (executable)
index 0000000..4950796
--- /dev/null
@@ -0,0 +1,11 @@
+#!/bin/bash
+#
+
+# OutOfMemoryException beim "mvn jetty:run" umgehen und
+# Parameter zum nachträglichen anhängen eines Debuggers
+# setzen
+export MAVEN_OPTS="-Xmx512m -XX:MaxPermSize=256m -XX:-UseGCOverheadLimit -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=4000,server=y,suspend=n"
+
+rm -v src/main/webapp/WEB-INF/lib/juplo*
+
+mvn jetty:run
diff --git a/accelerator-examples/spring/pom.xml b/accelerator-examples/spring/pom.xml
new file mode 100644 (file)
index 0000000..4eddbc9
--- /dev/null
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>de.juplo</groupId>
+    <artifactId>juplo-accelerator-examples</artifactId>
+    <version>0.1-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>juplo-accelerator-examples-spring</artifactId>
+  <packaging>war</packaging>
+  <name>Juplo - Accelerator-Examples: Spring-MVC</name>
+
+  <dependencies>
+
+    <!--  Spring -->
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-context</artifactId>
+      <version>${springframework.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-web</artifactId>
+      <version>${springframework.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-webmvc</artifactId>
+      <version>${springframework.version}</version>
+    </dependency>
+
+  </dependencies>
+
+</project>
diff --git a/accelerator-examples/spring/src/main/java/de/juplo/accelerator/examples/spring/FaultyController.java b/accelerator-examples/spring/src/main/java/de/juplo/accelerator/examples/spring/FaultyController.java
new file mode 100644 (file)
index 0000000..58c45c6
--- /dev/null
@@ -0,0 +1,40 @@
+package de.juplo.accelerator.examples.spring;
+
+import de.juplo.accelerator.annotations.CacheSeconds;
+import de.juplo.accelerator.annotations.Cacheable;
+import de.juplo.accelerator.annotations.LastModified;
+import javax.servlet.http.HttpServletRequest;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.servlet.ModelAndView;
+
+
+/**
+ * Simple Spring-MVC Controller
+ * @author kai
+ */
+@Controller
+@Cacheable(eager=true)
+public class FaultyController
+{
+  public static final String ACCESS_TIME = FaultyController.class.getCanonicalName() + ".ACCESS_TIME";
+  public static final Integer DEFAULT_MAX_AGE = 60;
+
+  private final static long lastModified = System.currentTimeMillis();
+
+  @CacheSeconds
+  public int cacheSeconds(HttpServletRequest request) {
+    return DEFAULT_MAX_AGE;
+  }
+
+  @LastModified
+  public long lastModified(HttpServletRequest request) {
+    return lastModified;
+  }
+
+  @RequestMapping("/faulty-controller.html")
+  public ModelAndView process(HttpServletRequest request)
+  {
+    throw new RuntimeException("Oh, no!");
+  }
+}
\ No newline at end of file
diff --git a/accelerator-examples/spring/src/main/java/de/juplo/accelerator/examples/spring/SpringController.java b/accelerator-examples/spring/src/main/java/de/juplo/accelerator/examples/spring/SpringController.java
new file mode 100644 (file)
index 0000000..6671658
--- /dev/null
@@ -0,0 +1,65 @@
+package de.juplo.accelerator.examples.spring;
+
+import de.juplo.accelerator.annotations.CacheSeconds;
+import de.juplo.accelerator.annotations.Cacheable;
+import de.juplo.accelerator.annotations.LastModified;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TreeMap;
+import javax.servlet.http.HttpServletRequest;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.servlet.ModelAndView;
+
+
+/**
+ * Simple Spring-MVC Controller
+ * @author kai
+ */
+@Controller
+@Cacheable(eager=true)
+public class SpringController
+{
+  public static final String ACCESS_TIME = SpringController.class.getCanonicalName() + ".ACCESS_TIME";
+  public static final Integer DEFAULT_MAX_AGE = 60;
+
+  private Date lastModified = new Date();
+  private Map<Date,String> requests = new TreeMap<Date,String>();
+  private Map<String,Date> accessTimes = new HashMap<String,Date>();
+
+
+  @CacheSeconds
+  public int cacheSeconds(HttpServletRequest request) {
+    String maxAge = request.getParameter("max-age");
+    if (maxAge != null && maxAge.matches("^\\s*\\d+\\s*$"))
+      return Integer.parseInt(maxAge);
+    else
+      return DEFAULT_MAX_AGE;
+  }
+
+  @LastModified
+  public long lastModified(HttpServletRequest request) {
+    String query = request.getQueryString();
+    query = query == null ? "" : query;
+    Date accessTime = accessTimes.get(query);
+    if (accessTime == null || !accessTime.equals(lastModified))
+      /** Neuer Zugriff! */
+      accessTime = new Date();
+    request.setAttribute(ACCESS_TIME, accessTime);
+    return accessTime.getTime();
+  }
+
+  @RequestMapping("/spring-controller.html")
+  public ModelAndView process(HttpServletRequest request)
+  {
+    lastModified = (Date)request.getAttribute(ACCESS_TIME);
+    String query = request.getQueryString();
+    query = query == null ? "" : query;
+    requests.put(lastModified, query);
+    accessTimes.put(query, lastModified);
+    ModelAndView mav = new ModelAndView("controller-view");
+    mav.addObject("requests", requests);
+    return mav;
+  }
+}
\ No newline at end of file
diff --git a/accelerator-examples/spring/src/main/resources/config.xml b/accelerator-examples/spring/src/main/resources/config.xml
new file mode 100644 (file)
index 0000000..fc1de60
--- /dev/null
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:context="http://www.springframework.org/schema/context"
+       xmlns:mvc="http://www.springframework.org/schema/mvc"
+       xsi:schemaLocation="
+           http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
+           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
+           http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
+
+  <!-- Activates the AspectJ-Weaver -->
+  <context:component-scan base-package="de.juplo"/>
+  <context:spring-configured/>
+
+  <bean id="eTag" class="java.lang.String">
+    <constructor-arg value="Hallo Welt!"/>
+  </bean>
+
+  <bean id="weak" class="java.lang.Boolean">
+    <constructor-arg value="true"/>
+  </bean>
+
+  <bean id="lastModified" class="java.lang.Long">
+    <constructor-arg value="1338593731"/>
+  </bean>
+
+  <bean id="cacheSeconds" class="java.lang.Integer">
+    <constructor-arg value="3600"/>
+  </bean>
+
+  <!-- Configures the CacheControlInterceptor -->
+  <mvc:interceptors>
+    <bean class="de.juplo.accelerator.CacheControlInterceptor"/>
+  </mvc:interceptors>
+
+  <bean id="defaultAnnotationlHandlerMapping" class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
+    <property name="defaultHandler" value="urlFilenameViewController"/>
+  </bean>
+
+  <bean id="urlFilenameViewController" class="org.springframework.web.servlet.mvc.UrlFilenameViewController"/>
+
+  <!-- Resolves view names to protected .jsp resources within the /WEB-INF/views directory -->
+  <bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
+    <property name="prefix" value="/WEB-INF/views/"/>
+    <property name="suffix" value=".jsp"/>
+  </bean>
+
+</beans>
diff --git a/accelerator-examples/spring/src/main/resources/log4j.xml b/accelerator-examples/spring/src/main/resources/log4j.xml
new file mode 100644 (file)
index 0000000..b6030f9
--- /dev/null
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+
+  <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
+    <layout class="org.apache.log4j.PatternLayout">
+      <param name="ConversionPattern" value="%p - %C{1}.%M(%L) | %m%n"/>
+    </layout>
+  </appender>
+
+  <logger name="de.juplo">
+   <level value="trace"/>
+  </logger>
+
+  <logger name="org.springframework">
+    <level value="debug" />
+  </logger>
+
+  <root>
+    <level value="info"/>
+    <appender-ref ref="CONSOLE"/>
+  </root>
+
+</log4j:configuration>
diff --git a/accelerator-examples/spring/src/main/webapp/WEB-INF/views/controller-view.jsp b/accelerator-examples/spring/src/main/webapp/WEB-INF/views/controller-view.jsp
new file mode 100644 (file)
index 0000000..26abdbd
--- /dev/null
@@ -0,0 +1,43 @@
+<%@page contentType="text/html" pageEncoding="UTF-8" session="false" buffer="1kb" %>
+<%@page import="java.util.Map" %>
+<%@page import="java.util.Date"%>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>Page, that is handled by a controller</title>
+  </head>
+  <body>
+    <% Map<Date,String> requests = (Map<Date,String>)request.getAttribute("requests");
+       if (requests == null) { %>
+    <h1 style="color: red">This page associated with a controller and should be viewed as such!</h1>
+    <p style="color: red">Go to: <a href="/spring-controller.html">/spring-controller.html</a></p>
+    <% } else { %>
+    <h1>Controlled Page</h1>
+    <form action="/spring-controller.html">
+      <label for="max-age">Max Age:</label>
+      <input type="text" name="max-age" value="<% if (request.getParameter("max-age") != null) { %><%= request.getParameter("max-age") %><% } %>" />
+      <br />
+    </form>
+    <p>(Uncached) requests so far:</p>
+    <ol>
+      <% for(Map.Entry<Date,String> entry : requests.entrySet()) { %>
+      <li>
+        <%= entry.getKey() %>:
+        <% if (entry.getValue().equals("")) { %>
+          <a href="/spring-controller.html">No parameters...</a>
+        <% } else { %>
+          <a href="/spring-controller.html?<%= entry.getValue() %>">
+            <% for (String parameter : entry.getValue().split("&")) { %>
+            <strong><%= parameter %></strong>
+            <% } %>
+          </a>
+        <% } %>
+      </li>
+      <% } %>
+    </ol>
+    <% } %>
+    <p>This page was delivered via SPRING!</p>
+  </body>
+</html>
diff --git a/accelerator-examples/spring/src/main/webapp/WEB-INF/views/faulty-page.jsp b/accelerator-examples/spring/src/main/webapp/WEB-INF/views/faulty-page.jsp
new file mode 100644 (file)
index 0000000..8718f54
--- /dev/null
@@ -0,0 +1,17 @@
+<%@page contentType="text/html" pageEncoding="UTF-8" session="false" buffer="1kb" %>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>Simple Page</title>
+  </head>
+  <body>
+    <h1>Faulty Page</h1>
+    <p>This page will raise an error!</p>
+    <p>
+      <strong>Ecactly, NOW:</strong>
+      <% if (true) throw new RuntimeException("Oh no!"); %>
+    </p>
+  </body>
+</html>
diff --git a/accelerator-examples/spring/src/main/webapp/WEB-INF/views/index.jsp b/accelerator-examples/spring/src/main/webapp/WEB-INF/views/index.jsp
new file mode 100644 (file)
index 0000000..a329acd
--- /dev/null
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>Simple Spring-MVC Examples</title>
+  </head>
+  <body>
+    <h1>Simple Spring-MVC Examples</h1>
+    <ul>
+      <li><a href="/spring-page.html">Simple Spring-View</a></li>
+      <li><a href="/spring-controller.html">Simple Spring-Controller</a></li>
+      <li><a href="/faulty-page.html">Spring-View, which will raise an error</a></li>
+      <li><a href="/faulty-controller.html">Spring-Controller, which will raise an error</a></li>
+    </ul>
+    <p>This page was delivered via SPRING!</p>
+    <h2>Note:</h2>
+    <p>
+      Since the <code>org.springframework.web.servlet.DispatcherServlet</code>
+      ist configured to handle all <code>*.html</code>-requests, the path
+      <code>/index.html</code> points to a view.
+    </p>
+    <p>
+      Therefore, this page must be stored under
+      <code>/WEB-INF/views/index.jsp</code> in order to be served as
+      <code>/index.html</code>
+    </p>
+    <p>
+      Additionatly, a file <code>/index.jsp</code> in the root-directory of
+      the webappliction is needed, to forward unqualified requests
+      (like <code>http://HOSTNAME/</code>) to the welcome-page served by
+      the Spring-Dispatcher-Servlet.
+    </p>
+  </body>
+</html>
diff --git a/accelerator-examples/spring/src/main/webapp/WEB-INF/views/spring-page.jsp b/accelerator-examples/spring/src/main/webapp/WEB-INF/views/spring-page.jsp
new file mode 100644 (file)
index 0000000..892bf1a
--- /dev/null
@@ -0,0 +1,14 @@
+<%@page contentType="text/html" pageEncoding="UTF-8" session="false" buffer="1kb" %>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>Simple Page</title>
+  </head>
+  <body>
+    <h1>Hello World!</h1>
+    <p>This is a really simple page...</p>
+    <p>This page was delivered via SPRING!</p>
+  </body>
+</html>
diff --git a/accelerator-examples/spring/src/main/webapp/WEB-INF/web.xml b/accelerator-examples/spring/src/main/webapp/WEB-INF/web.xml
new file mode 100644 (file)
index 0000000..683aa66
--- /dev/null
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+
+  <!-- Context Configuration locations for Spring XML files -->
+
+  <context-param>
+    <param-name>contextConfigLocation</param-name>
+    <param-value>classpath:/config.xml</param-value>
+  </context-param>
+
+
+  <!-- Listener-Definitions -->
+
+  <listener>
+    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
+  </listener>
+
+
+  <!-- Filter-Definitions -->
+
+  <filter>
+    <filter-name>accelerator</filter-name>
+    <filter-class>de.juplo.accelerator.AcceleratorFilter</filter-class>
+  </filter>
+
+  <filter>
+    <filter-name>logger</filter-name>
+    <filter-class>de.juplo.testingtools.LoggingHttpServletResponseFilter</filter-class>
+  </filter>
+
+
+  <!-- Filter-Mappings -->
+
+  <filter-mapping>
+    <filter-name>logger</filter-name>
+    <url-pattern>*.html</url-pattern>
+  </filter-mapping>
+
+  <filter-mapping>
+    <filter-name>accelerator</filter-name>
+    <url-pattern>*.html</url-pattern>
+  </filter-mapping>
+
+
+  <!-- Servlet-Definitions -->
+
+  <servlet>
+    <servlet-name>dispatcher-servlet</servlet-name>
+    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
+    <init-param>
+      <param-name>contextConfigLocation</param-name>
+      <param-value>
+      </param-value>
+    </init-param>
+    <load-on-startup>1</load-on-startup>
+  </servlet>
+
+
+  <!-- Servlet-Mappings -->
+
+  <servlet-mapping>
+    <servlet-name>dispatcher-servlet</servlet-name>
+    <url-pattern>*.html</url-pattern>
+  </servlet-mapping>
+
+</web-app>
diff --git a/accelerator-examples/spring/src/main/webapp/index.jsp b/accelerator-examples/spring/src/main/webapp/index.jsp
new file mode 100644 (file)
index 0000000..9082000
--- /dev/null
@@ -0,0 +1,8 @@
+<%@page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" session="false" %>
+<jsp:forward page="/index.html"/>
+<%--
+
+Unfortionatly, the welcome-mechanism in web.xml does not work with a page
+served by a servlet...
+
+--%>
diff --git a/accelerator-examples/spring/src/test/java/de/juplo/accelerator/examples/SpringMVCTest.java b/accelerator-examples/spring/src/test/java/de/juplo/accelerator/examples/SpringMVCTest.java
new file mode 100644 (file)
index 0000000..f2ab9f1
--- /dev/null
@@ -0,0 +1,29 @@
+package de.juplo.accelerator.examples;
+
+import com.meterware.httpunit.WebResponse;
+import de.juplo.testingtools.HttpTestCase;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ *
+ * @author kai
+ */
+public class SpringMVCTest extends HttpTestCase {
+
+  private static final Logger log = LoggerFactory.getLogger(SpringMVCTest.class);
+
+
+  public SpringMVCTest() {
+    super("src/main/webapp/WEB-INF/web.xml");
+  }
+
+
+  @Test
+  public void testSpringPage() throws Exception {
+    WebResponse response = executeRequest("http://localhost:8080/spring-page.html");
+    log.info("Title:\t\t{}", response.getTitle());
+    log.debug("Text:\t\t{}", response.getText());
+  }
+}
diff --git a/accelerator-examples/spring/tomcat.sh b/accelerator-examples/spring/tomcat.sh
new file mode 100755 (executable)
index 0000000..4378a12
--- /dev/null
@@ -0,0 +1,11 @@
+#!/bin/bash
+#
+
+# OutOfMemoryException beim "mvn jetty:run" umgehen und
+# Parameter zum nachträglichen anhängen eines Debuggers
+# setzen
+export MAVEN_OPTS="-Xmx512m -XX:MaxPermSize=256m -XX:-UseGCOverheadLimit -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=4000,server=y,suspend=n"
+
+rm -v src/main/webapp/WEB-INF/lib/juplo*
+
+mvn tomcat:run-war
diff --git a/accelerator-examples/static/jetty.sh b/accelerator-examples/static/jetty.sh
new file mode 100755 (executable)
index 0000000..4950796
--- /dev/null
@@ -0,0 +1,11 @@
+#!/bin/bash
+#
+
+# OutOfMemoryException beim "mvn jetty:run" umgehen und
+# Parameter zum nachträglichen anhängen eines Debuggers
+# setzen
+export MAVEN_OPTS="-Xmx512m -XX:MaxPermSize=256m -XX:-UseGCOverheadLimit -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=4000,server=y,suspend=n"
+
+rm -v src/main/webapp/WEB-INF/lib/juplo*
+
+mvn jetty:run
diff --git a/accelerator-examples/static/pom.xml b/accelerator-examples/static/pom.xml
new file mode 100644 (file)
index 0000000..a4af6ba
--- /dev/null
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>de.juplo</groupId>
+    <artifactId>juplo-accelerator-examples</artifactId>
+    <version>0.1-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>juplo-accelerator-examples-static</artifactId>
+  <packaging>war</packaging>
+  <name>Juplo - Accelerator-Examples: Static Content</name>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-io</artifactId>
+      <version>${commons-io.version}</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+</project>
diff --git a/accelerator-examples/static/src/main/resources/config.xml b/accelerator-examples/static/src/main/resources/config.xml
new file mode 100644 (file)
index 0000000..a04b130
--- /dev/null
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:context="http://www.springframework.org/schema/context"
+       xsi:schemaLocation="
+           http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
+           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
+
+  <!-- Activates the AspectJ-Weaver -->
+  <context:component-scan base-package="de.juplo"/>
+  <context:spring-configured/>
+
+  <bean id="eTag" class="java.lang.String">
+    <constructor-arg value="Hallo Welt!"/>
+  </bean>
+
+  <bean id="weak" class="java.lang.Boolean">
+    <constructor-arg value="true"/>
+  </bean>
+
+  <bean id="lastModified" class="java.lang.Long">
+    <constructor-arg value="1338593731"/>
+  </bean>
+
+  <bean id="cacheSeconds" class="java.lang.Integer">
+    <constructor-arg value="3600"/>
+  </bean>
+
+</beans>
diff --git a/accelerator-examples/static/src/main/resources/log4j.xml b/accelerator-examples/static/src/main/resources/log4j.xml
new file mode 100644 (file)
index 0000000..b1e35f8
--- /dev/null
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+
+  <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
+    <layout class="org.apache.log4j.PatternLayout">
+      <param name="ConversionPattern" value="%p - %C{1}.%M(%L) | %m%n"/>
+    </layout>
+  </appender>
+
+  <logger name="de.juplo">
+   <level value="trace"/>
+  </logger>
+
+  <root>
+    <level value="info"/>
+    <appender-ref ref="CONSOLE"/>
+  </root>
+
+</log4j:configuration>
diff --git a/accelerator-examples/static/src/main/webapp/WEB-INF/web.xml b/accelerator-examples/static/src/main/webapp/WEB-INF/web.xml
new file mode 100644 (file)
index 0000000..5edc75f
--- /dev/null
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+
+  <!-- Context Configuration locations for Spring XML files -->
+
+  <context-param>
+    <param-name>contextConfigLocation</param-name>
+    <param-value>classpath:/config.xml</param-value>
+  </context-param>
+
+
+  <!-- Listener-Definitions -->
+
+  <listener>
+    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
+  </listener>
+
+
+  <!-- Filter-Definitions -->
+
+  <filter>
+    <filter-name>accelerator</filter-name>
+    <filter-class>de.juplo.accelerator.AcceleratorFilter</filter-class>
+  </filter>
+
+  <filter>
+    <filter-name>logger</filter-name>
+    <filter-class>de.juplo.testingtools.LoggingHttpServletResponseFilter</filter-class>
+  </filter>
+
+
+  <!-- Filter-Mappings -->
+
+  <filter-mapping>
+    <filter-name>logger</filter-name>
+    <url-pattern>/static/*</url-pattern>
+  </filter-mapping>
+
+  <filter-mapping>
+    <filter-name>accelerator</filter-name>
+    <url-pattern>/static/*</url-pattern>
+  </filter-mapping>
+
+
+  <!-- Servlet-Definitions -->
+
+  <servlet>
+    <servlet-name>default</servlet-name>
+    <servlet-class>org.eclipse.jetty.servlet.DefaultServlet</servlet-class>
+  </servlet>
+
+
+  <!-- Servlet-Mappings -->
+
+  <servlet-mapping>
+    <servlet-name>default</servlet-name>
+    <url-pattern>/</url-pattern>
+  </servlet-mapping>
+
+
+</web-app>
diff --git a/accelerator-examples/static/src/main/webapp/index.html b/accelerator-examples/static/src/main/webapp/index.html
new file mode 100644 (file)
index 0000000..22c61d0
--- /dev/null
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>Examples for Static Content</title>
+  </head>
+  <body>
+    <h1>Examples for Static Content</h1>
+    <ul>
+      <li><a href="/static/page.html">A plain static HTML-file</a></li>
+      <li><a href="/static/stylesheets.css">A plain static CSS-file</a></li>
+    </ul>
+  </body>
+</html>
diff --git a/accelerator-examples/static/src/main/webapp/static/page.html b/accelerator-examples/static/src/main/webapp/static/page.html
new file mode 100644 (file)
index 0000000..f851795
--- /dev/null
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>A Static Page</title>
+    <link rel="stylesheet" type="text/css" media="all" href="/static/stylesheets.css">
+  </head>
+  <body>
+    <h1>A Static Page</h1>
+    <p>This page is a static HTML-page</p>
+  </body>
+</html>
diff --git a/accelerator-examples/static/src/main/webapp/static/stylesheets.css b/accelerator-examples/static/src/main/webapp/static/stylesheets.css
new file mode 100644 (file)
index 0000000..e04bba2
--- /dev/null
@@ -0,0 +1,7 @@
+body {
+  background-color: #ccc;
+  color: #444;
+}
+h1,h2,h3,h4 {
+  color: #000;
+}
\ No newline at end of file
diff --git a/accelerator-examples/static/src/test/java/de/juplo/accelerator/examples/StaticTest.java b/accelerator-examples/static/src/test/java/de/juplo/accelerator/examples/StaticTest.java
new file mode 100644 (file)
index 0000000..f27fec4
--- /dev/null
@@ -0,0 +1,28 @@
+package de.juplo.accelerator.examples;
+
+import com.meterware.httpunit.WebResponse;
+import de.juplo.testingtools.HttpTestCase;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ *
+ * @author kai
+ */
+public class StaticTest extends HttpTestCase {
+
+  private static final Logger log = LoggerFactory.getLogger(StaticTest.class);
+
+
+  public StaticTest() {
+    super("src/main/webapp/WEB-INF/web.xml");
+  }
+
+  @Test
+  public void testStaticContent() throws Exception {
+    WebResponse response = executeRequest("http://localhost:8080/static/page.html");
+    log.info("Title:\t\t{}", response.getTitle());
+    log.debug("Text:\t\t{}", response.getText());
+  }
+}
diff --git a/accelerator-examples/static/src/test/java/org/eclipse/jetty/servlet/DefaultServlet.java b/accelerator-examples/static/src/test/java/org/eclipse/jetty/servlet/DefaultServlet.java
new file mode 100644 (file)
index 0000000..0173a1f
--- /dev/null
@@ -0,0 +1,28 @@
+package org.eclipse.jetty.servlet;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.commons.io.IOUtils;
+
+/**
+ * Möglichst simple Fake-Implementierung für die Ausführung des Testfalls
+ *
+ * @author kai
+ */
+public class DefaultServlet extends HttpServlet {
+
+  @Override
+  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+    String path = getServletContext().getRealPath(request.getRequestURI());
+    if (path == null) {
+      response.sendError(HttpServletResponse.SC_NOT_FOUND);
+      return;
+    }
+    response.setContentType("text/html");
+    IOUtils.copy(new FileInputStream(path), response.getOutputStream());
+  }
+}
diff --git a/accelerator/pom.xml b/accelerator/pom.xml
new file mode 100644 (file)
index 0000000..fd36da9
--- /dev/null
@@ -0,0 +1,211 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <groupId>de.juplo</groupId>
+  <artifactId>juplo-accelerator</artifactId>
+  <name>Juplo - Accelerator</name>
+  <version>0.1-SNAPSHOT</version>
+  <url>http://www.juplo.de/accelerator</url>
+
+  <prerequisites>
+    <maven>2.0.6</maven>
+  </prerequisites>
+
+  <developers>
+    <developer>
+      <id>kai</id>
+      <name>Kai Moritz</name>
+      <email>kai@juplo.de</email>
+    </developer>
+  </developers>
+
+  <properties>
+    <!-- Zeichensatz -->
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <!-- Verwendete Versionen -->
+    <aspectj.version>1.6.11</aspectj.version>
+    <jpa.version>1.0</jpa.version>
+    <junit.version>4.8.1</junit.version>
+    <juplo-testingtools.version>1.0</juplo-testingtools.version>
+    <log4j.version>1.2.16</log4j.version>
+    <servlet-api.version>2.5</servlet-api.version>
+    <slf4j.binding>slf4j-log4j12</slf4j.binding>
+    <slf4j.version>1.6.1</slf4j.version>
+    <springframework.version>3.0.6.RELEASE</springframework.version>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-webmvc</artifactId>
+      <version>${springframework.version}</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-aspects</artifactId>
+      <version>${springframework.version}</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-tx</artifactId>
+      <version>${springframework.version}</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.aspectj</groupId>
+      <artifactId>aspectjrt</artifactId>
+      <version>${aspectj.version}</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>javax.servlet</groupId>
+      <artifactId>servlet-api</artifactId>
+      <version>${servlet-api.version}</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>javax.persistence</groupId>
+      <artifactId>persistence-api</artifactId>
+      <version>${jpa.version}</version>
+      <scope>provided</scope>
+    </dependency>
+    <!-- Logging -->
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+      <version>${slf4j.version}</version>
+    </dependency>
+    <!-- Test -->
+    <dependency>
+      <groupId>de.juplo</groupId>
+      <artifactId>juplo-testingtools</artifactId>
+      <version>${juplo-testingtools.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>${junit.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>${slf4j.binding}</artifactId>
+      <version>${slf4j.version}</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <distributionManagement>
+    <repository>
+      <id>juplo.internal</id>
+      <name>Internal Release Repository</name>
+      <url>http://juplo.de/archiva/repository/internal/</url>
+    </repository>
+    <snapshotRepository>
+      <id>juplo.snapshots</id>
+      <name>Internal Snapshot Repository</name>
+      <url>http://juplo.de/archiva/repository/snapshots/</url>
+    </snapshotRepository>
+  </distributionManagement>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <configuration>
+          <source>1.6</source>
+          <target>1.6</target>
+          <encoding>utf8</encoding>
+          <showWarnings>true</showWarnings>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-dependency-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>install</id>
+            <phase>install</phase>
+            <goals>
+              <goal>sources</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-source-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>attach-sources</id>
+            <phase>verify</phase>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>aspectj-maven-plugin</artifactId>
+        <configuration>
+          <complianceLevel>1.6</complianceLevel>
+          <aspectLibraries>
+            <aspectLibrary>
+              <groupId>org.springframework</groupId>
+              <artifactId>spring-aspects</artifactId>
+            </aspectLibrary>
+          </aspectLibraries>
+        </configuration>
+        <executions>
+          <execution>
+            <goals>
+              <goal>compile</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+  <reporting>
+    <plugins>
+      <plugin>
+        <artifactId>maven-changes-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>cobertura-maven-plugin</artifactId>
+        <version>2.0</version>
+      </plugin>
+      <plugin>
+        <artifactId>maven-javadoc-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <artifactId>maven-jxr-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <artifactId>maven-pmd-plugin</artifactId>
+        <configuration>
+          <linkXref>true</linkXref>
+          <targetJdk>1.5</targetJdk>
+        </configuration>
+      </plugin>
+      <plugin>
+        <artifactId>maven-surefire-report-plugin</artifactId>
+        <version>2.7.2</version>
+      </plugin>
+    </plugins>
+  </reporting>
+
+</project>
diff --git a/accelerator/src/main/java/de/juplo/accelerator/AcceleratorFilter.java b/accelerator/src/main/java/de/juplo/accelerator/AcceleratorFilter.java
new file mode 100644 (file)
index 0000000..7b1e306
--- /dev/null
@@ -0,0 +1,724 @@
+package de.juplo.accelerator;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.zip.GZIPOutputStream;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowire;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Configurable;
+import org.springframework.beans.factory.annotation.Qualifier;
+
+
+
+/**
+ *
+ * @author kai
+ */
+@Configurable(autowire=Autowire.BY_NAME)
+public class AcceleratorFilter implements Filter {
+  private final static Logger log = LoggerFactory.getLogger(AcceleratorFilter.class);
+
+  private final static Map<String,String> EMPTY = Collections.unmodifiableMap(new HashMap<String,String>());
+
+  public final static Integer DEFAULT_BUFFER_SIZE = 1024;
+  public final static String REQUEST_URI_ATTRIBUTE = "javax.servlet.include.request_uri";
+  public final static String RESPONSE_WRAPPER = AcceleratorFilter.class.getName() + ".RESPONSE_WRAPPER";
+
+
+  @Autowired CacheControl cacheControl;
+  @Autowired(required=false) @Qualifier("defaultBufferSize") Integer defaultBufferSize = DEFAULT_BUFFER_SIZE;
+  @Autowired String eTag;
+  @Autowired Boolean weak;
+  @Autowired Long lastModified;
+  @Autowired Integer cacheSeconds;
+
+
+  @Override
+  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+    if (!(request instanceof HttpServletRequest)) {
+      log.error("AcceleratorFilter can only handle HTTP-requests");
+      chain.doFilter(request, response);
+      return;
+    }
+
+    HttpServletRequest httpRequest = (HttpServletRequest)request;
+    HttpServletResponse httpResponse = (HttpServletResponse)response;
+
+    AccelerationWrapper wrapper;
+
+    wrapper = (AccelerationWrapper)request.getAttribute(AcceleratorFilter.RESPONSE_WRAPPER);
+    if (wrapper != null) {
+      if (wrapper.getFilter() == this) {
+        /** Ignore multiple mappings of the same filter-instance */
+        log.warn("Ignoring multiple mappings on same URL: {}", httpRequest.getRequestURI());
+        chain.doFilter(request, response);
+        return;
+      }
+      else {
+        log.error("Only one instance of AcceleratorFilter must be mapped to any URL: {}", httpRequest.getRequestURI());
+        httpResponse.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Only one instance of AcceleratorFilter must be mapped to any URL!");
+        return;
+      }
+    }
+
+    wrapper = new AccelerationWrapper(httpRequest, httpResponse);
+    httpRequest.setAttribute(RESPONSE_WRAPPER, wrapper);
+    cacheControl.init(wrapper);
+    try {
+      chain.doFilter(request, wrapper);
+      wrapper.finish();
+    }
+    catch (NotModifiedException nm) {
+      log.trace("Not modified: {}", httpRequest.getRequestURI());
+    }
+  }
+
+  @Override
+  public void init(FilterConfig filterConfig) throws ServletException {
+  }
+
+  @Override
+  public void destroy() {
+  }
+
+
+  class AccelerationWrapper implements HttpServletResponse, CacheMethodHandle {
+
+    private final HttpServletRequest request;
+    private final HttpServletResponse response;
+
+    private final AcceleratorServletOutputStream out;
+
+    private ServletOutputStream stream;
+    private PrintWriter writer;
+
+    private boolean guessing = true;
+    protected boolean zipped = false; // << CacheControll greift direkt auf diese Variable zu!
+
+    private long now;
+    private int status;
+    private int cacheSeconds;
+    private boolean cacheSecondsSet = false;
+    private long lastModified, expires = 0l;
+    private String eTag;
+    private boolean weak;
+    private Map<String,String> cacheParams;
+
+    /** Für den AcceleratorOutputStream */
+    private boolean committed = false;
+    private OutputStream os = null;
+    private int bufferSize;
+    private byte[] buffer;
+    private int pos = 0;
+    private int size = 0;
+
+
+
+    AccelerationWrapper(HttpServletRequest request, HttpServletResponse response) throws IOException {
+
+      this.request = request;
+      this.response = response;
+
+      now = System.currentTimeMillis();
+      status = HttpServletResponse.SC_OK;
+      cacheSeconds = AcceleratorFilter.this.cacheSeconds;
+      lastModified = AcceleratorFilter.this.lastModified;
+      eTag = AcceleratorFilter.this.eTag;
+      weak = AcceleratorFilter.this.weak;
+      cacheParams = new HashMap<String,String>();
+
+      Enumeration values = request.getHeaders(Headers.HEADER_ACCEPT_ENCODING);
+      while (values.hasMoreElements()) {
+        String value = (String) values.nextElement();
+        if (value.indexOf("gzip") != -1) {
+          zipped = true;
+          break;
+        }
+      }
+
+      out = new AcceleratorServletOutputStream();
+    }
+
+
+    private AcceleratorFilter getFilter() {
+      return AcceleratorFilter.this;
+    }
+
+    private void finish() throws IOException {
+      flushBuffer();
+      out.close();
+    }
+
+    @Override
+    public void setStatus(int sc) {
+      response.setStatus(sc);
+      status = sc;
+    }
+
+    @Override
+    public void setStatus(int sc, String sm) {
+      response.setStatus(sc,sm);
+      status = sc;
+    }
+
+    @Override
+    public void addDateHeader(String name, long value) {
+
+      if (!guessing) {
+        response.addDateHeader(name, value);
+        return;
+      }
+
+      if (Headers.HEADER_DATE.equalsIgnoreCase(name)) {
+        now = value;
+        calculateCacheSeconds();
+        return;
+      }
+
+      if (Headers.HEADER_EXPIRES.equalsIgnoreCase(name)) {
+        expires = value;
+        calculateCacheSeconds();
+        return;
+      }
+
+      if (Headers.HEADER_LAST_MODIFIED.equalsIgnoreCase(name)) {
+        lastModified = value;
+        return;
+      }
+
+      /** Unknown header: pass throug! */
+      response.addDateHeader(name, value);
+    }
+
+    @Override
+    public void addHeader(String name, String value) {
+
+      if (!guessing) {
+        response.addHeader(name, value);
+        return;
+      }
+
+      if (value == null)
+        return;
+      analyzeHeader(name, value, false);
+    }
+
+    @Override
+    public void addIntHeader(String name, int value) {
+
+      if (!guessing) {
+        response.addIntHeader(name, value);
+        return;
+      }
+
+      analyzeHeader(name, Integer.toString(value), false);
+    }
+
+    @Override
+    public void setDateHeader(String name, long value) {
+
+      if (!guessing) {
+        response.setDateHeader(name, value);
+        return;
+      }
+
+      if (Headers.HEADER_DATE.equalsIgnoreCase(name)) {
+        now = value;
+        calculateCacheSeconds();
+        return;
+      }
+
+      if (Headers.HEADER_EXPIRES.equalsIgnoreCase(name)) {
+        expires = value;
+        calculateCacheSeconds();
+        return;
+      }
+
+      if (Headers.HEADER_LAST_MODIFIED.equalsIgnoreCase(name)) {
+        lastModified = value;
+        return;
+      }
+
+      /** Unknown header: pass throug! */
+      response.setDateHeader(name, value);
+    }
+
+    @Override
+    public void setHeader(String name, String value) {
+
+      if (!guessing) {
+        response.setHeader(name, value);
+        return;
+      }
+
+      analyzeHeader(name, value, true);
+    }
+
+    @Override
+    public void setIntHeader(String name, int value) {
+
+      if (!guessing) {
+        response.setIntHeader(name, value);
+        return;
+      }
+
+      analyzeHeader(name, Integer.toString(value), true);
+    }
+
+    @Override
+    public ServletOutputStream getOutputStream() throws IOException {
+
+      if (writer != null)
+        throw new IllegalStateException("ServletOutputStream and PrintWriter cannot be requested both!");
+
+      if (stream == null) {
+        stream = out;
+      }
+
+      return out;
+    }
+
+    @Override
+    public PrintWriter getWriter() throws IOException {
+
+      if (stream != null)
+        throw new IllegalStateException("ServletOutputStream and PrintWriter cannot be requested both!");
+
+      if (writer == null) {
+        writer = new PrintWriter(new OutputStreamWriter(out, response.getCharacterEncoding()));
+      }
+
+      return writer;
+    }
+
+    @Override
+    public void setContentLength(int len) {
+      if (zipped)
+        log.info("Supressing explicit content-length {} for request {}, because content will be zipped!", len, request.getRequestURI());
+      else
+        response.setContentLength(len);
+    }
+
+    @Override
+    public int getBufferSize() {
+      return bufferSize;
+    }
+
+    @Override
+    public void setBufferSize(int size) {
+      if (this.size > 0)
+        throw new IllegalStateException("cannot change buffer size, because content was already written!");
+      bufferSize = size;
+      buffer = new byte[size];
+    }
+
+    @Override
+    public void resetBuffer() {
+      if (committed)
+        throw new IllegalStateException("cannot reset buffer, because response is already commited!");
+      pos = 0;
+      stream = null;
+      writer = null;
+    }
+
+    @Override
+    public void reset() {
+      if (committed)
+        throw new IllegalStateException("cannot reset response, because response is already commited!");
+      /**
+       * Da committed==false gilt, wurde die Dekoration noch nicht angestßen
+       * und muss entsprechend auch nicht rückgängig gemacht werden!
+       */
+      response.reset();
+      pos = 0;
+      size = 0;
+      stream = null;
+      writer = null;
+    }
+
+    @Override
+    public void flushBuffer() throws IOException {
+      if (writer != null)
+        writer.flush();
+      else if (stream != null)
+        stream.flush();
+    }
+
+    @Override
+    public void addCookie(Cookie cookie) {
+      // TODO: Je nach Vary-Einstellung ETag anpassen?
+      response.addCookie(cookie);
+    }
+
+    @Override
+    public boolean containsHeader(String name) {
+      return response.containsHeader(name);
+    }
+
+    @Override
+    public String encodeURL(String url) {
+      return response.encodeURL(url);
+    }
+
+    @Override
+    public String encodeRedirectURL(String url) {
+      return response.encodeRedirectURL(url);
+    }
+
+    @Override
+    public String encodeUrl(String url) {
+      return response.encodeUrl(url);
+    }
+
+    @Override
+    public String encodeRedirectUrl(String url) {
+      return response.encodeRedirectUrl(url);
+    }
+
+    @Override
+    public void sendError(int sc, String msg) throws IOException {
+      response.sendError(sc,msg);
+    }
+
+    @Override
+    public void sendError(int sc) throws IOException {
+      response.sendError(sc);
+    }
+
+    @Override
+    public void sendRedirect(String location) throws IOException {
+      response.sendRedirect(location);
+    }
+
+    @Override
+    public String getCharacterEncoding() {
+      return response.getCharacterEncoding();
+    }
+
+    @Override
+    public String getContentType() {
+      return response.getContentType();
+    }
+
+    @Override
+    public void setCharacterEncoding(String charset) {
+      // TODO: Je nach Vary-Einstellung ETag anpassen?
+      response.setCharacterEncoding(charset);
+    }
+
+    @Override
+    public void setContentType(String type) {
+      // TODO: Je nach Vary-Einstellung ETag anpassen?
+      response.setContentType(type);
+    }
+
+    @Override
+    public boolean isCommitted() {
+      return committed;
+    }
+
+    @Override
+    public void setLocale(Locale loc) {
+      // TODO: Je nach Vary-Einstellung ETag anpassen?
+      response.setLocale(loc);
+    }
+
+    @Override
+    public Locale getLocale() {
+      return getLocale();
+    }
+
+
+
+    @Override
+    public boolean isZipped() {
+      return zipped;
+    }
+
+    @Override
+    public long getTimestamp() {
+      return now;
+    }
+
+    @Override
+    public int accepts(HttpServletRequest request) {
+      return status;
+    }
+
+    @Override
+    public int getCacheSeconds(HttpServletRequest request) {
+      return cacheSeconds;
+    }
+
+    @Override
+    public long getLastModified(HttpServletRequest request) {
+      return lastModified;
+    }
+
+    @Override
+    public String getETag(HttpServletRequest request) {
+      return eTag;
+    }
+
+    @Override
+    public boolean isETagWeak() {
+      return weak;
+    }
+
+    @Override
+    public void cacheControl(HttpServletRequest request, Map<String, String> cacheControlMap) {
+      cacheControlMap.putAll(cacheParams);
+    }
+
+    @Override
+    public Map<String,String> getAdditionalHeaders(HttpServletRequest request) {
+      return EMPTY;
+    }
+
+    public void supressGuessing() {
+      guessing = false;
+    }
+
+
+    private void analyzeHeader(String name, String value, boolean overwrite) {
+      if (name == null)
+        return;
+      name = name.trim();
+
+      if (name.equalsIgnoreCase(Headers.HEADER_DATE)) {
+        if (value == null) {
+          if (overwrite) {
+            now = System.currentTimeMillis();
+            cacheSeconds = AcceleratorFilter.this.cacheSeconds;
+          }
+          return;
+        }
+        try {
+          SimpleDateFormat parser = new SimpleDateFormat(Headers.RFC_1123_DATE_FORMAT, Locale.US);
+          now = parser.parse(value).getTime();
+          calculateCacheSeconds();
+        }
+        catch (ParseException e) {
+          log.warn("ignoring date for header \"Date\" in invalid format: {}", value);
+        }
+        return;
+      }
+
+      if (name.equalsIgnoreCase(Headers.HEADER_EXPIRES)) {
+        if (value == null) {
+          if (overwrite) {
+            expires = 0;
+            cacheSeconds = AcceleratorFilter.this.cacheSeconds;
+          }
+          return;
+        }
+        try {
+          SimpleDateFormat parser = new SimpleDateFormat(Headers.RFC_1123_DATE_FORMAT, Locale.US);
+          expires = parser.parse(value).getTime();
+          calculateCacheSeconds();
+        }
+        catch (ParseException e) {
+          log.warn("ignoring date for header \"Expires\" in invalid format: {}", value);
+        }
+        return;
+      }
+
+      if (name.equalsIgnoreCase(Headers.HEADER_LAST_MODIFIED)) {
+        if (value == null) {
+          if (overwrite)
+            lastModified = AcceleratorFilter.this.lastModified;
+          return;
+        }
+        try {
+          SimpleDateFormat parser = new SimpleDateFormat(Headers.RFC_1123_DATE_FORMAT, Locale.US);
+          lastModified = parser.parse(value).getTime();
+        }
+        catch (ParseException e) {
+          log.warn("ignoring date for header \"Last-Modified\" in invalid format: {}", value);
+        }
+        return;
+      }
+
+      if (name.equalsIgnoreCase(Headers.HEADER_ETAG)) {
+        if (value == null) {
+          if (overwrite) {
+            eTag = AcceleratorFilter.this.eTag;
+            weak = AcceleratorFilter.this.weak;
+          }
+          return;
+        }
+        value = value.trim();
+        int start = 0;
+        int end = value.length();
+        if (value.startsWith("W/")) {
+          weak = true;
+          start = 2;
+        }
+        else {
+          weak = false;
+        }
+        if (value.charAt(start) == '"')
+          start++;
+        else
+          log.warn("Quote at the beginning ov ETag is missing: {}", value);
+        if (value.charAt(end -1) == '"')
+          end--;
+        else
+          log.warn("Quote at the end of ETag is missing: {}", value);
+        eTag = value.substring(start, end);
+        String filtered = eTag.replaceAll("[^\\x00-\\x21\\x23-\\x7F]+","");
+        if (filtered.length() < eTag.length()) {
+          log.warn("filtering out illegal characters in ETag: \"{}\" -> \"{}\"", eTag, filtered);
+          eTag = filtered;
+        }
+      }
+
+      if (name.equalsIgnoreCase(Headers.HEADER_CACHE_CONTROL)) {
+        if (overwrite)
+          cacheParams.clear();
+        if (value == null)
+          return;
+        for (String param : value.split(",")) {
+          param = param.trim();
+          int pos = param.indexOf("=");
+          if (pos < 0) {
+            cacheParams.put(param, null);
+          }
+          else {
+            String paramName = param.substring(0, pos).trim();
+            if (paramName.equalsIgnoreCase("max-age")) {
+              try {
+                cacheSeconds = Integer.parseInt(param.substring(pos + 1));
+                cacheSecondsSet = true;
+              }
+              catch (NumberFormatException e) {
+                log.warn("illegal value for Header \"Cache-Control\":", param);
+              }
+            }
+            else {
+              cacheParams.put(paramName, param.substring(pos + 1));
+            }
+          }
+        }
+        return;
+      }
+
+      if (name.equalsIgnoreCase(Headers.HEADER_PRAGMA)) {
+        if (value != null && value.trim().equalsIgnoreCase("no-cache"))
+          cacheSeconds = 0;
+        return;
+      }
+
+      /** Pass header through, if no value from intrest was found */
+      if (overwrite)
+        response.setHeader(name, value);
+      else
+        response.addHeader(name, value);
+    }
+
+    private void calculateCacheSeconds() {
+      if (!cacheSecondsSet && expires >= now) {
+        cacheSeconds = (int)(expires/1000 - now/1000);
+        log.debug("calculating cache-seconds from DATE and EXPIRES: {}", cacheSeconds);
+      }
+    }
+
+
+    private class AcceleratorServletOutputStream extends ServletOutputStream  {
+
+      private final ServletOutputStream sos;
+
+
+      private AcceleratorServletOutputStream() throws IOException {
+        bufferSize = defaultBufferSize;
+        buffer = new byte[bufferSize];
+        sos = AccelerationWrapper.this.response.getOutputStream();
+      }
+
+
+      private OutputStream out() throws IOException {
+        if (os == null)
+          os = zipped ? new GZIPOutputStream(sos) : sos;
+        return os;
+      }
+
+      @Override
+      public void write(int i) throws IOException {
+        if (pos == bufferSize) {
+          out().write(buffer);
+          committed = true;
+          /** Dekoration nur beim ersten Schreib-Schub anstoßen */
+          if (pos == size) {
+            if (!cacheControl.decorate(request, response)) {
+              zipped = false;
+              os = null;
+              pos = 0;
+              throw new NotModifiedException();
+            }
+          }
+          pos = 0;
+        }
+        buffer[pos++] = (byte) i;
+        size++;
+      }
+
+      @Override
+      public void flush() throws IOException {
+        if (pos == 0)
+          return;
+
+        committed = true;
+        /** Dekoration nur beim ersten Schreib-Schub anstoßen */
+        if (pos == size) {
+          if (!cacheControl.decorate(request, response)) {
+            zipped = false;
+            os = null;
+            pos = 0;
+            throw new NotModifiedException();
+          }
+        }
+        out().write(buffer, 0, pos);
+        out().flush();
+        pos = 0;
+      }
+
+      @Override
+      public void close() throws IOException {
+        if (size == 0) {
+          committed = true;
+          zipped = false;
+          if (!cacheControl.decorate(request, response))
+            throw new NotModifiedException();
+          sos.close();
+        }
+        else {
+          flush();
+          out().close();
+        }
+      }
+    }
+  }
+}
+
+class NotModifiedException extends IOException {}
\ No newline at end of file
diff --git a/accelerator/src/main/java/de/juplo/accelerator/CacheControl.java b/accelerator/src/main/java/de/juplo/accelerator/CacheControl.java
new file mode 100644 (file)
index 0000000..f119b60
--- /dev/null
@@ -0,0 +1,548 @@
+package de.juplo.accelerator;
+
+import de.juplo.accelerator.AcceleratorFilter.AccelerationWrapper;
+import de.juplo.accelerator.annotations.CacheSeconds;
+import de.juplo.accelerator.annotations.Accepts;
+import de.juplo.accelerator.annotations.AdditionalHeaders;
+import de.juplo.accelerator.annotations.LastModified;
+import de.juplo.accelerator.annotations.ETag;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.TreeMap;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Component;
+
+/**
+ *
+ * @author kai
+ */
+@Component
+public class CacheControl {
+  private final static Logger log = LoggerFactory.getLogger(CacheControl.class);
+
+  private static final ThreadLocal<CacheMethodHandle> tl = new ThreadLocal<CacheMethodHandle>();
+
+  @Autowired @Qualifier("cacheSeconds") private Integer defaultCacheSeconds;
+  @Autowired @Qualifier("lastModified") private Long defaultLastModified;
+
+
+  public void init(CacheMethodHandle handle) {
+    CacheControl.tl.set(handle);
+  }
+
+  void init(Object handler, AccelerationWrapper wrapper) throws NoSuchMethodException {
+    CacheControl.tl.set(new ReflectionCacheMethodHandle(handler, wrapper == null ? false : wrapper.zipped));
+  }
+
+  public boolean decorate(
+      HttpServletRequest request,
+      HttpServletResponse response
+      )
+  {
+    try {
+    CacheMethodHandle handle = CacheControl.tl.get();
+
+    /** Doppelte Ausführung verhindern... */
+    if (handle == null) {
+      /** Dekoration wurde bereits durchgeführt! */
+      return true;
+    }
+
+    /**
+     * Alle Antworten (insbesondere auch 304) sollen nach dem {@plainlink
+     * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.18 RFC 2616,
+     * Abschnitt 14.18} einen Date-Header enthalten
+     */
+    response.setDateHeader(Headers.HEADER_DATE, handle.getTimestamp());
+
+    /** Besondere Maßnahmen für besondere HTTP-Status-Codes ?!? */
+    int status = handle.accepts(request);
+    switch (status) {
+      case HttpServletResponse.SC_OK: // 200
+      case HttpServletResponse.SC_NO_CONTENT: // 204
+      case HttpServletResponse.SC_PARTIAL_CONTENT: // 206
+        /** Normale Antwort! Antwort dekorieren... */
+        break;
+      case HttpServletResponse.SC_MOVED_PERMANENTLY: // 301
+      case HttpServletResponse.SC_MOVED_TEMPORARILY: // 302
+      case HttpServletResponse.SC_SEE_OTHER: // 303
+      case HttpServletResponse.SC_NOT_MODIFIED: // 304
+      case HttpServletResponse.SC_USE_PROXY: // 305
+      case HttpServletResponse.SC_TEMPORARY_REDIRECT: // 307
+        /** Redirect-Antwort! Antwort dekodieren... */
+        // TODO: Kann das wirklich nicht zu Protokoll-Verletzungen führen?
+        break;
+      case HttpServletResponse.SC_BAD_REQUEST: // 400
+      case HttpServletResponse.SC_UNAUTHORIZED: // 401
+      case HttpServletResponse.SC_PAYMENT_REQUIRED: // 402
+      case HttpServletResponse.SC_FORBIDDEN: // 403
+      case HttpServletResponse.SC_NOT_FOUND: // 404
+      case HttpServletResponse.SC_METHOD_NOT_ALLOWED: // 405
+      case HttpServletResponse.SC_NOT_ACCEPTABLE: // 406
+      case HttpServletResponse.SC_PROXY_AUTHENTICATION_REQUIRED: // 407
+      case HttpServletResponse.SC_REQUEST_TIMEOUT: // 408
+      case HttpServletResponse.SC_CONFLICT: // 409
+      case HttpServletResponse.SC_GONE: // 410
+      case HttpServletResponse.SC_LENGTH_REQUIRED: // 411
+      case HttpServletResponse.SC_PRECONDITION_FAILED: // 412
+      case HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE: // 413
+      case HttpServletResponse.SC_REQUEST_URI_TOO_LONG: // 414
+      case HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE: // 415
+      case HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE: // 416
+      case HttpServletResponse.SC_INTERNAL_SERVER_ERROR: // 500
+      case HttpServletResponse.SC_NOT_IMPLEMENTED: // 501
+      case HttpServletResponse.SC_SERVICE_UNAVAILABLE: // 503
+      case HttpServletResponse.SC_HTTP_VERSION_NOT_SUPPORTED: // 505
+      default:
+        /**
+         * Es ist nicht klar, was der Handler noch machen wird/muss:
+         * Antwort nicht dekorieren und Kontroller an den Handler übergeben...
+         */
+        return true;
+    }
+
+    Map<String,String> headers = handle.getAdditionalHeaders(request);
+    for (String name : headers.keySet())
+      response.addHeader(name, headers.get(name));
+
+    String url = null;
+    if (log.isDebugEnabled()) {
+      if (request.getQueryString() == null) {
+        url = request.getRequestURI();
+      }
+      else {
+        StringBuilder builder = new StringBuilder();
+        builder.append(request.getRequestURI());
+        builder.append('?');
+        builder.append(request.getQueryString());
+        url = builder.toString();
+      }
+    }
+
+    int cacheSeconds = handle.getCacheSeconds(request);
+    if (cacheSeconds < 0) {
+      log.debug("{}: caching disabled!", url);
+      response.setDateHeader(Headers.HEADER_DATE, handle.getTimestamp());
+      response.setDateHeader(Headers.HEADER_EXPIRES, 0);
+      response.addHeader(Headers.HEADER_PRAGMA, "no-cache");
+      response.addHeader(Headers.HEADER_CACHE_CONTROL, "private");
+      response.addHeader(Headers.HEADER_CACHE_CONTROL, "no-cache");
+      response.addHeader(Headers.HEADER_CACHE_CONTROL, "no-store");
+      response.addHeader(Headers.HEADER_CACHE_CONTROL, "max-age=0");
+      response.addHeader(Headers.HEADER_CACHE_CONTROL, "s-max-age=0");
+      if (handle.isZipped())
+        response.addHeader(Headers.HEADER_CONTENT_ENCODING, "gzip");
+      return true;
+    }
+
+    long ifModifiedSince = -1;
+    try {
+      ifModifiedSince = request.getDateHeader(Headers.HEADER_IF_MODIFIED_SINCE);
+    }
+    catch (Exception e) {
+      log.error("Exception while fetching If-Modified-Since: {}", e);
+    }
+
+    long lastModified = handle.getLastModified(request);
+
+    /**
+     * Sicherstellen, dass der Wert keine Millisekunden enthält, da die
+     * Zeitangabe aus dem Modified-Since-Header keine Millisekunden enthalten
+     * kann und der Test unten dann stets fehlschlagen würde!
+     */
+    lastModified = lastModified - (lastModified % 1000);
+
+    String ifNoneMatch = request.getHeader(Headers.HEADER_IF_NONE_MATCH);
+    String eTag = handle.getETag(request);
+
+    /**
+     * 304-Antworten sollen nach dem {@plainlink
+     * http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5 RFC
+     * 2616, Abschnitt 10.3.5} einen ETag-Header enthalten, wenn auch die
+     * 200-Antwort einen enthalten hätte.
+     */
+    if (eTag != null) {
+      StringBuilder builder = new StringBuilder();
+      if (handle.isETagWeak())
+        builder.append("W/");
+      builder.append('"');
+      builder.append(eTag);
+      builder.append('"');
+      response.setHeader(Headers.HEADER_ETAG, builder.toString());
+    }
+
+
+    if (ifModifiedSince >= lastModified && lastModified > 0) {
+      /**
+       * request.getDateHeader liefert die Zeit als long, oder -1, wenn der
+       * Header nicht existiert. D.h., wenn "If-Modified-Since" nicht gesetzt
+       * ist, wird die komplette Seite ausgeliefert.
+       * Der zusätzliche Test, ob lastModified größer 0 ist, ist nötig, um
+       * Fehler auszuschließen, wenn die Implementierung von Cachable
+       * negative Werte für Last-Modified zurückliefert.
+       */
+      if (log.isDebugEnabled())
+        log.debug("{}: Not modified since {}", url, new Date(ifModifiedSince));
+
+      if (ifNoneMatch == null) {
+        /** Neue Anfrage oder HTTP/1.0 Client! */
+        log.debug("{}: ETag nicht gesetzt -> 304", url);
+        response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
+        return false;
+      }
+    }
+
+    if (ifNoneMatch != null) {
+      boolean weak = false;
+      if (ifNoneMatch.startsWith("W/")) {
+        weak = true;
+        ifNoneMatch = ifNoneMatch.substring(3, ifNoneMatch.length() - 1);
+      }
+      else {
+        ifNoneMatch = ifNoneMatch.substring(1, ifNoneMatch.length() - 1);
+      }
+
+      if (!weak || (request.getMethod().equals("GET") && request.getHeader(Headers.HEADER_RANGE) == null)) {
+        /**
+         * Die Gleichheit gilt nur, wenn die ETag's der Anfrage _und_ der
+         * Antwort stark sind (starke Gleichheit!), oder wenn die Antwort nur
+         * schwache Gleichheit fordert...
+         */
+        if (ifNoneMatch.equals(eTag) && (handle.isETagWeak() || !weak)) {
+          log.debug("{}: ETag {} not changed -> 304 ", url, ifNoneMatch);
+          response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
+          return false;
+        }
+      }
+      else {
+        log.warn("{}: ignoring weak ETag W/\"{}\", because the request was no GET-request or the Range-Header was present!", url, ifNoneMatch);
+      }
+    }
+
+
+    log.debug("{}: first up!", url);
+
+    if (handle.isZipped())
+      response.addHeader(Headers.HEADER_CONTENT_ENCODING, "gzip");
+
+    /** HTTP/1.1-Caching-Header richtig setzen!! */
+    response.setDateHeader(Headers.HEADER_LAST_MODIFIED, lastModified);
+
+    /** Cache-Control für HTTP/1.1-Clients generieren */
+    Map<String, String> cacheControl = new TreeMap<String, String>();
+
+    /**
+     * Wenn eins JSESSIONID in der URL enthalten ist, darf die Anfrage nur vom
+     * Browser gecached werden!
+     */
+    if (request.isRequestedSessionIdFromURL()) {
+      cacheControl.put("private", null);
+    }
+    else {
+      /**
+       * Hier muss nicht geprüft werden, ob cacheSeconds > 0 gilt, da in diesem
+       * Fall oben bereits No-Cache-Header generiert und <code>false</code>
+       * zurückgeliefert werden!
+       *
+       * Den Wert als <code>max-age</code> zu den Schlüssel-Wert-Paaren für den
+       * <code>Cache-Control</code>-Header hinzufügen und einen entsprechenden
+       * <code>Expires</code>-Header für HTTP/1.0-Clients setzen.
+       */
+      cacheControl.put("max-age", Integer.toString(cacheSeconds));
+      response.setDateHeader(Headers.HEADER_EXPIRES, (handle.getTimestamp() + (long) cacheSeconds * 1000));
+    }
+
+    /** Dem Handler die Gelegenheit geben, den Cache-Controll-Header anzupassen */
+    handle.cacheControl(request, cacheControl);
+
+
+    if (cacheControl.containsKey("private")) {
+      /**
+       * HTTP/1.0 Caches davon abhalten, die Ressource zu cachen (vgl.: RFC
+       * 2616, {@plainlink
+       * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.3
+       * Abschnitt 14.9.3} und {@plainlink
+       * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.32
+       * Abschnitt 14.32})
+       */
+      response.setDateHeader(Headers.HEADER_EXPIRES, 0l);
+      response.addHeader(Headers.HEADER_PRAGMA, "no-cache");
+    }
+
+    StringBuilder builder = new StringBuilder();
+    for (Entry<String, String> entry : cacheControl.entrySet()) {
+      builder.setLength(0);
+      builder.append(entry.getKey());
+      if (entry.getValue() != null) {
+        builder.append('=');
+        builder.append(entry.getValue());
+      }
+      response.addHeader(Headers.HEADER_CACHE_CONTROL, builder.toString());
+    }
+
+    return true;
+    }
+    finally {
+      /**
+       * Thread-Locale-Variable zurücksetzen, damit
+       * 1.) ein doppelter Aufruf dieser Methode pro Request erkannt werden kann
+       * 2.) der nächste Request nicht mit dem selben Handle weiterarbeitet
+       */
+      CacheControl.tl.set(null);
+    }
+  }
+
+  public void release() {
+    CacheControl.tl.set(null);
+  }
+
+
+  class ReflectionCacheMethodHandle implements CacheMethodHandle {
+
+    private Object handler;
+    private long now = System.currentTimeMillis();
+    private Integer cacheSeconds;
+    private Long lastModified;
+    private String eTag;
+    private Map<String,String> additionalHeaders;
+    private Method acceptsMethod;
+    private Method cacheSecondsMethod;
+    private Method lastModifiedMethod;
+    private Method eTagMethod;
+    private Method cacheControlMethod;
+    private Method additionalHeadersMethod;
+    private boolean isAcceptsMethodDefined;
+    private boolean isCacheSecondsMethodDefined;
+    private boolean isLastModifiedMethodDefined;
+    private boolean isETagMethodDefined;
+    private boolean isCacheControlMethodDefined;
+    private boolean isAdditionalHeadersMethodDefined;
+    private boolean weak;
+    private boolean zipped;
+
+
+    ReflectionCacheMethodHandle(Object handler, boolean zipped) throws NoSuchMethodException {
+
+      this.handler = handler;
+      this.zipped = zipped;
+
+      cacheSeconds = CacheControl.this.defaultCacheSeconds;
+      lastModified = CacheControl.this.defaultLastModified;
+
+      /** Class-Level-Annotations auslesen */
+      for (Annotation annotation : handler.getClass().getAnnotations()) {
+        if (annotation.annotationType().equals(CacheSeconds.class)) {
+          cacheSeconds = ((CacheSeconds)annotation).value();
+          isCacheSecondsMethodDefined = true;
+          continue;
+        }
+        if (annotation.annotationType().equals(LastModified.class)) {
+          lastModified = ((LastModified)annotation).value();
+          if (lastModified < 1) {
+            /**
+             * Ein Last-Modified-Header wurde angefordert, aber es wurde kein
+             * statischer Wert spezifiziert:
+             * globalen statischen Default-Wert benutzen!
+             */
+            lastModified = defaultLastModified;
+          }
+          isLastModifiedMethodDefined = true;
+          continue;
+        }
+        if (annotation.annotationType().equals(ETag.class)) {
+          ETag eTagAnnotation = (ETag)annotation;
+          eTag = eTagAnnotation.value();
+          weak = eTagAnnotation.weak();
+          isETagMethodDefined = true;
+          continue;
+        }
+        if (annotation.annotationType().equals(AdditionalHeaders.class)) {
+          AdditionalHeaders additionalHeadersAnnotation = (AdditionalHeaders)annotation;
+          additionalHeaders = new HashMap<String,String>();
+          for (String header : additionalHeadersAnnotation.value()) {
+            int i = header.indexOf(':');
+            if (i < 0) {
+              log.error("invalid header: [{}]", header);
+            }
+            else {
+              String name = header.substring(0,i).trim();
+              String value = header.substring(i+1,header.length()).trim();
+              additionalHeaders.put(name, value);
+            }
+          }
+          isAdditionalHeadersMethodDefined = true;
+          continue;
+        }
+      }
+
+      /** Method-Level-Annotations auslesen */
+      for (Method method : handler.getClass().getMethods()) {
+        for (Annotation annotation : method.getAnnotations()) {
+          if (annotation.annotationType().equals(Accepts.class)) {
+            if (isAcceptsMethodDefined)
+              throw new IllegalArgumentException("Die Annotation @Accept wurde in der Klasse " + handler.getClass().getSimpleName() + " mehrfach verwendet!");
+            acceptsMethod = method;
+            isAcceptsMethodDefined = true;
+            continue;
+          }
+          if (annotation.annotationType().equals(CacheSeconds.class)) {
+            if (isCacheSecondsMethodDefined)
+              throw new IllegalArgumentException("Die Annotation @CacheSeconds wurde in der Klasse " + handler.getClass().getSimpleName() + " mehrfach verwendet!");
+            cacheSecondsMethod = method;
+            isCacheSecondsMethodDefined = true;
+            continue;
+          }
+          if (annotation.annotationType().equals(LastModified.class)) {
+            if (isLastModifiedMethodDefined)
+              throw new IllegalArgumentException("Die Annotation @LastModified wurde in der Klasse " + handler.getClass().getSimpleName() + " mehrfach verwendet!");
+            lastModifiedMethod = method;
+            isLastModifiedMethodDefined = true;
+            continue;
+          }
+          if (annotation.annotationType().equals(ETag.class)) {
+            if (isETagMethodDefined)
+              throw new IllegalArgumentException("Die Annotation @ETag wurde in der Klasse " + handler.getClass().getSimpleName() + " mehrfach verwendet!");
+            eTagMethod = method;
+            weak = ((ETag)annotation).weak();
+            isETagMethodDefined = true;
+            continue;
+          }
+          if (annotation.annotationType().equals(de.juplo.accelerator.annotations.CacheControl.class)) {
+            if (isCacheControlMethodDefined)
+              throw new IllegalArgumentException("Die Annotation @CacheControl wurde in der Klasse " + handler.getClass().getSimpleName() + " mehrfach verwendet!");
+            cacheControlMethod = method;
+            isCacheControlMethodDefined = true;
+            continue;
+          }
+          if (annotation.annotationType().equals(AdditionalHeaders.class)) {
+            if (isAdditionalHeadersMethodDefined)
+              throw new IllegalArgumentException("Die Annotation @AdditionalHeaders wurde in der Klasse " + handler.getClass().getSimpleName() + " mehrfach verwendet!");
+            additionalHeadersMethod = method;
+            isAdditionalHeadersMethodDefined = true;
+            continue;
+          }
+        }
+      }
+
+      if (!isAdditionalHeadersMethodDefined)
+        additionalHeaders = new HashMap<String,String>();
+    }
+
+
+    @Override
+    public boolean isZipped() {
+      return zipped;
+    }
+
+    @Override
+    public long getTimestamp() {
+      return now;
+    }
+
+    @Override
+    public int accepts(HttpServletRequest request) throws IllegalArgumentException {
+      if (acceptsMethod == null) {
+        return HttpServletResponse.SC_OK;
+      }
+      else {
+        try {
+          return (Integer)acceptsMethod.invoke(handler, request);
+        }
+        catch (Exception e) {
+          throw new IllegalArgumentException(e);
+        }
+      }
+    }
+
+    @Override
+    public int getCacheSeconds(HttpServletRequest request) throws IllegalArgumentException {
+      if (cacheSecondsMethod == null) {
+        return cacheSeconds;
+      }
+      else {
+        try {
+          return (Integer)cacheSecondsMethod.invoke(handler, request);
+        }
+        catch (Exception e) {
+          throw new IllegalArgumentException(e);
+        }
+      }
+    }
+
+    @Override
+    public long getLastModified(HttpServletRequest request) throws IllegalArgumentException {
+      if (lastModifiedMethod == null) {
+        return lastModified;
+      }
+      else {
+        try {
+          return (Long)lastModifiedMethod.invoke(handler, request);
+        }
+        catch (Exception e) {
+          throw new IllegalArgumentException(e);
+        }
+      }
+    }
+
+    @Override
+    public String getETag(HttpServletRequest request) throws IllegalArgumentException {
+      if (eTagMethod == null) {
+        return eTag;
+      }
+      else {
+        try {
+          return (String)eTagMethod.invoke(handler, request);
+        }
+        catch (Exception e) {
+          throw new IllegalArgumentException(e);
+        }
+      }
+    }
+
+    @Override
+    public boolean isETagWeak() {
+      return weak;
+    }
+
+    @Override
+    public void cacheControl(
+        HttpServletRequest request,
+        Map<String, String> cacheControlMap
+        )
+        throws IllegalArgumentException
+    {
+      if (cacheControlMethod != null) {
+        try {
+          cacheControlMethod.invoke(handler, request, cacheControlMap);
+        }
+        catch (Exception e) {
+          throw new IllegalArgumentException(e);
+        }
+      }
+    }
+
+    @Override
+    public Map<String,String> getAdditionalHeaders(HttpServletRequest request) throws IllegalArgumentException {
+      if (additionalHeadersMethod == null) {
+        return additionalHeaders;
+      }
+      else {
+        try {
+          return (Map<String,String>)additionalHeadersMethod.invoke(handler, request);
+        }
+        catch (Exception e) {
+          throw new IllegalArgumentException(e);
+        }
+      }
+    }
+  }
+}
diff --git a/accelerator/src/main/java/de/juplo/accelerator/CacheControlInterceptor.java b/accelerator/src/main/java/de/juplo/accelerator/CacheControlInterceptor.java
new file mode 100644 (file)
index 0000000..10aaee1
--- /dev/null
@@ -0,0 +1,83 @@
+package de.juplo.accelerator;
+
+import de.juplo.accelerator.AcceleratorFilter.AccelerationWrapper;
+import de.juplo.accelerator.annotations.Cacheable;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.servlet.HandlerInterceptor;
+import org.springframework.web.servlet.ModelAndView;
+
+/**
+ *
+ * @author kai
+ */
+public class CacheControlInterceptor implements HandlerInterceptor {
+  private final static Logger log = LoggerFactory.getLogger(CacheControlInterceptor.class);
+
+
+  private CacheControl cacheControl;
+
+
+  @Override
+  public boolean preHandle(
+      HttpServletRequest request,
+      HttpServletResponse response,
+      Object handler
+      ) throws Exception
+  {
+    Cacheable cacheable = handler.getClass().getAnnotation(Cacheable.class);
+    if (cacheable == null) {
+      /** Der Handler ist nicht mit @Cacheable annotiert: keine Dekorationen anbringen! */
+      return true;
+    }
+
+    AccelerationWrapper wrapper = (AccelerationWrapper)request.getAttribute(AcceleratorFilter.RESPONSE_WRAPPER);
+    if (wrapper != null)
+      wrapper.supressGuessing();
+
+    /** CacheControll initialisieren (Handler nach annotierte Methoden scannen etc.) */
+    cacheControl.init(handler, wrapper);
+
+    if (cacheable.eager()) {
+      return cacheControl.decorate(request, response);
+    }
+    else {
+      return true;
+    }
+  }
+
+  @Override
+  public void postHandle(
+      HttpServletRequest request,
+      HttpServletResponse response,
+      Object handler,
+      ModelAndView modelAndView
+      ) throws Exception
+  {
+    /**
+     * Dekoration nur dann anstossen, wenn sie nicht bereits von dem
+     * AcceleratorFilter ausgelöst wird.
+     */
+    if (request.getAttribute(AcceleratorFilter.RESPONSE_WRAPPER) == null)
+      cacheControl.decorate(request, response);
+  }
+
+  @Override
+  public void afterCompletion(
+      HttpServletRequest request,
+      HttpServletResponse response,
+      Object handler, Exception ex
+      ) throws Exception
+  {
+    cacheControl.release();
+  }
+
+
+  @Autowired
+  public void setCacheControl(CacheControl cacheControl) {
+    this.cacheControl = cacheControl;
+  }
+}
diff --git a/accelerator/src/main/java/de/juplo/accelerator/CacheMethodHandle.java b/accelerator/src/main/java/de/juplo/accelerator/CacheMethodHandle.java
new file mode 100644 (file)
index 0000000..96f529b
--- /dev/null
@@ -0,0 +1,20 @@
+package de.juplo.accelerator;
+
+import java.util.Map;
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ *
+ * @author kai
+ */
+public interface CacheMethodHandle {
+  boolean isZipped();
+  long getTimestamp();
+  int accepts(HttpServletRequest request);
+  int getCacheSeconds(HttpServletRequest request);
+  long getLastModified(HttpServletRequest request);
+  String getETag(HttpServletRequest request);
+  boolean isETagWeak();
+  void cacheControl(HttpServletRequest request, Map<String, String> cacheControlMap);
+  Map<String,String> getAdditionalHeaders(HttpServletRequest request);
+}
diff --git a/accelerator/src/main/java/de/juplo/accelerator/Headers.java b/accelerator/src/main/java/de/juplo/accelerator/Headers.java
new file mode 100644 (file)
index 0000000..49f19fe
--- /dev/null
@@ -0,0 +1,24 @@
+package de.juplo.accelerator;
+
+/**
+ *
+ * @author kai
+ */
+public abstract class Headers {
+
+  public static final String RFC_1123_DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss z";
+
+  public static final String HEADER_ACCEPT_ENCODING = "Accept-Encoding";
+  public static final String HEADER_CACHE_CONTROL = "Cache-Control";
+  public static final String HEADER_CONTENT_ENCODING = "Content-Encoding";
+  public static final String HEADER_CONTENT_TYPE = "Content-Type";
+  public static final String HEADER_DATE = "Date";
+  public static final String HEADER_ETAG = "ETag";
+  public static final String HEADER_EXPIRES = "Expires";
+  public static final String HEADER_IF_MODIFIED_SINCE = "If-Modified-Since";
+  public static final String HEADER_IF_NONE_MATCH = "If-None-Match";
+  public static final String HEADER_LAST_MODIFIED = "Last-Modified";
+  public static final String HEADER_PRAGMA = "Pragma";
+  public static final String HEADER_RANGE = "Range";
+
+}
diff --git a/accelerator/src/main/java/de/juplo/accelerator/annotations/Accepts.java b/accelerator/src/main/java/de/juplo/accelerator/annotations/Accepts.java
new file mode 100644 (file)
index 0000000..2f44afc
--- /dev/null
@@ -0,0 +1,30 @@
+package de.juplo.accelerator.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Mit dieser Methode kann eine Methode annotiert werden, die Auskunft darüber
+ * erteilt, mit welchem HTTP-Status-Code der Handler die Anfrage beatnworten
+ * wird.
+ * <p>
+ * Die Methode muss eine Instanz von {@link HttpServletRequest} als (einziges!)
+ * Argument akzeptieren und einen Wert liefern, der sich nach
+ * <code>int</code> casten lässt.
+ * <p>
+ * Eine mit dieser Annotation markierte Methode wird nur benötigt, wenn die
+ * Caching-Dekoration im Modus <code>eager=true</code> ausgeführt wird. Sie
+ * wird in diesem Fall benötigt, weil die Entscheidungen zur Cache-Dekoration
+ * dann getroffen werden müssen, <em>bevor</em> die verarbeitende Klasse die
+ * Anfrage verarbeitet hat.
+ * Wenn die Cache-Dekoration im Modus <code>eager=true</code> betrieben wird
+ * und keine Methode mit dieser Annotation annotiert ist, geht {@link CacheControl}
+ * davon aus, dass die verarbeitende Klasse alle Anfragen annimmt.
+ *
+ * @author kai
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface Accepts {}
diff --git a/accelerator/src/main/java/de/juplo/accelerator/annotations/AdditionalHeaders.java b/accelerator/src/main/java/de/juplo/accelerator/annotations/AdditionalHeaders.java
new file mode 100644 (file)
index 0000000..2f37920
--- /dev/null
@@ -0,0 +1,17 @@
+package de.juplo.accelerator.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ *
+ * @author kai
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.TYPE, ElementType.METHOD })
+public @interface AdditionalHeaders {
+
+  String[] value() default {};
+}
diff --git a/accelerator/src/main/java/de/juplo/accelerator/annotations/CacheControl.java b/accelerator/src/main/java/de/juplo/accelerator/annotations/CacheControl.java
new file mode 100644 (file)
index 0000000..6f7705d
--- /dev/null
@@ -0,0 +1,30 @@
+package de.juplo.accelerator.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Mit dieser Annotation kann eine Methode markiert werden, die die von Juplo-
+ * CacheControl für den Header <code>Cache-Control</code> generierten
+ * Schlüssel/Wert-Kombinationen manipulieren oder ergänzen kann, bevor der
+ * Header an den Client ausgeliefert wird (s. {@plainlink
+ * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.3 RFC2616, Abschnitt 14.9.3}).
+ * <p>
+ * Die Methode muss zwei Parameter akzeptieren.
+ * Als ersten Parameter eine Instanz von {@link HttpServletRequest}.
+ * Als zweiten Parameter eine <code>Map<String,String></code>, die die von
+ * Juplo-CacheControl erzeugten Schlüssel/Wert-Paare enthält.
+ * <p>
+ * Diese Methode liefert eine Map mit Schlüssel-Wert-Paaren für den
+ * HTTP/1.1-Header <code>Cache-Control</code> (s. {@plainlink
+ * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.3 RFC2616,
+ * Abschnitt 14.9.3}).
+ *
+ * @author kai
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface CacheControl {
+}
diff --git a/accelerator/src/main/java/de/juplo/accelerator/annotations/CacheSeconds.java b/accelerator/src/main/java/de/juplo/accelerator/annotations/CacheSeconds.java
new file mode 100644 (file)
index 0000000..0403646
--- /dev/null
@@ -0,0 +1,52 @@
+package de.juplo.accelerator.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * Mit dieser Annotation können Klassen oder Methoden merkiert werden.
+ * <p>
+ * Wenn eine Methode markiert wird, muss diese eine Instanz von
+ * {@link HttpServletRequest} als (einziges!) Argument akzeptieren und einen
+ * Wert liefern, der sich nach <code>int</code> casten lässt.
+ * Die annotierte Methode ermöglicht eine einfache, zentrale aber Request-
+ * Abhängige Steuerung des Caching-Verhaltens.
+ * <p>
+ * Wenn eine Klasse annotiert wird, muss der Anotation die dann statisch für
+ * alle von der Klasse erzeugten Antworten gültige Cache-Zeit als Argument
+ * übergeben werden.
+ * Wird keine Cache-Zeit spezifiziert, wird der Wert <code>86400</code>
+ * (ein Tag) verwendet.
+ * <ul>
+ * <li>Wenn negativer Wert als Cache-Seconds festgelet wird, werden Cache-Header
+ * erzeugt, die das Cachen der Antwort für HTTP/1.0 und HTTP/1.1 vollständig
+ * untersagen.</li>
+ * <li>Wenn einen Wert größer oder gleich <code>0</code> festgelegt wird, wird
+ * für HTTP/1.0-Clients ein <code>Expires</code>-Header generiert und für
+ * HTTP/1.1-Clients ein <code>Cache-Control</code>-Header mit einem
+ * entsprechenden <code>max-age</code>-Eintrag. Dies reicht in Kombination mit
+ * der Annotation {@link LastModified} vollständig für ein einfaches aber
+ * effektives Caching aus.</li>
+ * </ul>
+ * <p>
+ * TODO
+ * <strong>Zu beachten:</strong> Wenn die Methode
+ * {@link #getCacheControl(javax.servlet.http.HttpServletRequest)} weitere
+ * Schlüssel-Wert-Paare für den <code>Cache-Control</code>-Header liefert,
+ * werden diese ergänzt. Wenn in der Rückgabe ein Wert für
+ * <code>max-age</code> enthalten ist, wir er allerdings von dem durch diese
+ * Methode vorgegebenen Wert überschrieben!
+ *
+ * @author kai
+ * @See Cacheable
+ * @See LastModified
+ * @See CacheControl
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.TYPE, ElementType.METHOD })
+public @interface CacheSeconds {
+  int value() default 86400; /** Default: 1 Tag */
+}
diff --git a/accelerator/src/main/java/de/juplo/accelerator/annotations/Cacheable.java b/accelerator/src/main/java/de/juplo/accelerator/annotations/Cacheable.java
new file mode 100644 (file)
index 0000000..a5960fc
--- /dev/null
@@ -0,0 +1,45 @@
+package de.juplo.accelerator.annotations;
+
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+
+/**
+ * Marker-Annotation für Handler (i.A. eine Impelementierung von
+ * {@link Controller}), deren Antworten vom {@link CachingInterceptor} mit
+ *  HTTP/1.1-Caching-Header nach
+ * {@linkplain http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html RFC 2616}
+ * dekoriert werden sollen.
+ * <p>
+ * Wenn der Parameter <code>eager</code> auf <code>true</code> gesetzt wird,
+ * ermittelt der {@link CachingInterceptor} die Cache-Parameter über die
+ * annotierten Methoden vorab.
+ * <strong>Achtung:</strong>
+ * Dies bedeutet, dass die annotierten Methoden aufgerufen werden <em>bevor</em>
+ * die eigentliche Verarbeitungs-Routine der markierten Klasse aufgerufen wird!
+ * Wenn sich dabei ergiebt, dass die Antwort nicht erneut ausgeliefert werden
+ * muss, wird die eigentliche Verarbeitungs-Routine <em>gar nicht aufgerufen</em>.
+ * <p>
+ * Wenn der Parameter <code>eager</code> nicht gesetzt ist (oder explizit auf
+ * <code>false</code> gesetzt wurde), kapselt der {@link CachingInterceptor}
+ * den Request und den Ausgabestrom für den Response-Body und trifft die
+ * Entscheidung über die zu ergänzenden Header, wenn der Status des
+ * {@link HttpServletResponse} gesetzt oder mit dem Schreiben des Response-Body
+ * begonnen wird.
+ *
+ * @see CacheControl
+ * @see Accepts
+ * @see CacheSeconds
+ * @see LastModified
+ * @see ETag
+ * @see CacheControl
+ * @author kai
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface Cacheable {
+  boolean eager() default false;
+}
diff --git a/accelerator/src/main/java/de/juplo/accelerator/annotations/ETag.java b/accelerator/src/main/java/de/juplo/accelerator/annotations/ETag.java
new file mode 100644 (file)
index 0000000..81b6d16
--- /dev/null
@@ -0,0 +1,84 @@
+package de.juplo.accelerator.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * Über diese Annotation kann der Inhalt des <code>ETag/code>-Headers
+ * gesteuert werden.
+ * Mit dieser Annotation können Klassen oder Methoden merkiert werden.
+ * <p>
+ * Wenn eine Methode annotiert wird, muss diese eine Instanz von
+ * {@link HttpServletRequest} als (einziges!) Argument akzeptieren und einen
+ * <code>String</code> liefern.
+ * <p>
+ * Wenn eine Klasse Annotiert wird, muss der Annotation der Wert für den
+ * <code>ETag</code>-Header übergeben werden.
+ * Da dieser Wert somit statisch ist, macht es nur Sinn, Klassen mit dieser
+ * Annotation zu markieren, die ausschließlich statische Ressourcen ausliefern,
+ * die sich nur mit der Neuinstallation der Webanwendung ändern.
+ * Wenn sich (z.B. nach einer Neuinstallation der Webanwendung) die statischen
+ * Ressourcen geändert haben, muss der übergebene statische ETag geändert
+ * werden, da Caches sonst weiterhin die alten Ressourcen ausliefern!
+ * </p>
+ * Frei wählbares ETag nach
+ * {@linkplain http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.19 RFC 2616, Abschnitt 14.19 ETag}.
+ * Der gelieferte Wert darf die vom RFC geforderten Anführungszeichen noch nicht
+ * enthalten, da er, wenn <code>vary</code> gesetzt ist, noch um je nach
+ * erfolgter Content-Negotiation varriierende Teile ergänzt wird.
+ * <p>
+ * Die erzeugten <code>ETag</code>'s können über die Annotations-Parameter
+ * <code>weak</code> und <code>vary</code> weiter gesteuert werden.
+ * <ul>
+ * <li>
+ * Wenn der Parameter <strong>weak</strong> auf den wert <code>true</code>
+ * gesetzt wird, wird ein schwaches <code>ETag</code> erezeugt und der
+ * Vergleichs-Algorithmus verhält sich entsprechend anders (siehe:
+ * {@linkplain http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.3.3 RFC 2616, Abschnitt 13.3.3 Weak and Strong Validators}).
+ * </li>
+ * <li>
+ * Über den Parameter <strong>vary</strong> kann Juplo-CacheControl damit
+ * beauftragt werden, die Nötigen Maßnahmen für korrektes Content-Negotiating
+ * zu ergreifen (siehe:
+ * {@linkplain http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.6 RFC 2616, Abschnitt 13.6 Caching Negotiated Responses}).
+ * Als Eingabe werden die Header-Namen erwertet, die zu unterschiedlichen
+ * Ergebnissen der Content-Negotiation führen können (hier können folgende
+ * Header angegeben werden: <code>Accept</code>, <code>Accept-Charset</code>,
+ * <code>Accept-Encoding</code> und <code>Accept-Language</code>).
+ * Juplo-CacheControl modifizert den übergebenen <code>ETag</code> dann so,
+ * dass unterschiedliche Resultate der Content-Negotiation unterschieden
+ * werden können.
+ * Außerdem wird der <code>Vary</code>-Header entsprechend gesetzt.
+ *</li>
+ * </ul>
+ * <strong>Zu beachten:</strong>
+ * Wenn zugleich die Annotation {@link CacheSeconds} verwendet wird, wird
+ * die mit dieser Annotation markierte Methode nur aufgerufen, wenn die mit
+ * der Annotation {@link CacheSeconds} markierte Methode einen Wert größer
+ * oder gleich <code>0</code> liefert, bzw. für die mit Annotation
+ * {@link CacheSeconds} markierte Klasse eine Cache-Zeit größer oder gleich
+ * <code>0</code> festgelegt wurde.
+ *
+ * @see #getCacheSeconds(javax.servlet.http.HttpServletRequest)
+ *
+ * @author kai
+ * @see Cacheable
+ * @see CacheSeconds
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.TYPE, ElementType.METHOD })
+public @interface ETag {
+
+  public final static String ACCEPT = "Accept";
+  public final static String ACCEPT_CHARSET = "Accept-Charset";
+  public final static String ACCEPT_ENCODING = "Accept-Encoding";
+  public final static String ACCEPT_LANGUAGE = "Accept-Language";
+
+
+  String value() default "X";
+  boolean weak() default false;
+  String[] vary() default {};
+}
diff --git a/accelerator/src/main/java/de/juplo/accelerator/annotations/LastModified.java b/accelerator/src/main/java/de/juplo/accelerator/annotations/LastModified.java
new file mode 100644 (file)
index 0000000..c5e86a3
--- /dev/null
@@ -0,0 +1,53 @@
+package de.juplo.accelerator.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * Über diese Annotation kann der Inhalt des <code>Last-Modified</code>-Headers
+ * gesteuert werden.
+ * Mit dieser Annotation können Klassen oder Methoden merkiert werden.
+ * <p>
+ * Wenn eine Methode annotiert wird, muss diese eine Instanz von
+ * {@link HttpServletRequest} als (einziges!) Argument akzeptieren und einen
+ * Wert liefern, der sich nach <code>long</code> casten lässt.
+ * Die Signatur der Methode entspricht der Methode
+ * {@link HttpServlet#getLastModified(javax.servlet.http.HttpServletRequest)}
+ * aus dem <code>HttpServlet</code>-Interface.
+ * Um das Cache-Verhalten ein existierendes Servlet, das diese Methode bereits
+ * implementiert, mit Juplo-CacheControll zu verbessern, kann als erste
+ * Maßnahme daher einfach diese Methode mit dieser Annotation markiert werden.
+ * <p>
+ * Wenn eine Klasse Annotiert wird, muss der Annotation der Wert für den
+ * <code>Last-Modified</code>-Header übergeben werden.
+ * Da dieser Wert somit statisch ist, macht es nur Sinn, Klassen mit dieser
+ * Annotation zu markieren, die ausschließlich statische Ressourcen ausliefern,
+ * die sich nur mit der Neuinstallation der Webanwendung ändern.
+ * </p>
+ * Über diese Annotation wird der Zeitpunkt gesteuert, zu dem die gelieferte
+ * Ressource zuletzt verändert wurde.
+ * Erwartet wird eine Zeitangabe in Millisekunden seit dem Unix-0-Zeitpunkt,
+ * die dann an {@link HttpServletResponse#setDateHeader(String, long)}
+ * weitergegeben wird.
+ * <p>
+ * <strong>Zu beachten:</strong>
+ * Wenn zugleich die Annotation {@link CacheSeconds} verwendet wird, wird
+ * die mit dieser Annotation markierte Methode nur aufgerufen, wenn die mit
+ * der Annotation {@link CacheSeconds} markierte Methode einen Wert größer
+ * oder gleich <code>0</code> liefert, bzw. für die mit Annotation
+ * {@link CacheSeconds} markierte Klasse eine Cache-Zeit größer oder gleich
+ * <code>0</code> festgelegt wurde.
+ *
+ * @author kai
+ * @see Cacheable
+ * @see CacheSeconds
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.TYPE, ElementType.METHOD })
+public @interface LastModified {
+  long value() default 0;
+}
diff --git a/accelerator/src/test/java/com/meterware/servletunit/ServletUnitHttpResponse.java b/accelerator/src/test/java/com/meterware/servletunit/ServletUnitHttpResponse.java
new file mode 100644 (file)
index 0000000..285d173
--- /dev/null
@@ -0,0 +1,621 @@
+package com.meterware.servletunit;
+/********************************************************************************************************************
+* $Id: ServletUnitHttpResponse.java 751 2006-03-24 19:59:12Z russgold $
+*
+* Copyright (c) 2000-2004,2006, Russell Gold
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
+* documentation files (the "Software"), to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
+* to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in all copies or substantial portions
+* of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
+* THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+* DEALINGS IN THE SOFTWARE.
+*
+*******************************************************************************************************************/
+import com.meterware.httpunit.HttpUnitUtils;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+
+import java.util.*;
+import java.text.SimpleDateFormat;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+
+
+class ServletUnitHttpResponse implements HttpServletResponse {
+
+    // rfc1123-date is "Sun, 06 Nov 1994 08:49:37 GMT"
+    private static final String RFC1123_DATE_SPEC = "EEE, dd MMM yyyy HH:mm:ss z";
+    private boolean _committed;
+    private Locale _locale = Locale.getDefault();
+
+    private static final Hashtable ENCODING_MAP = new Hashtable();
+
+    /**
+     * @deprecated Use encodeURL(String url)
+     */
+    public String encodeUrl( String url ) {
+        return encodeURL( url );
+    }
+
+
+    /**
+     * Adds the specified cookie to the response.  It can be called
+     * multiple times to set more than one cookie.
+     */
+    public void addCookie( Cookie cookie ) {
+        _cookies.addElement( cookie );
+    }
+
+
+    /**
+     * Checks whether the response message header has a field with
+     * the specified name.
+     */
+    public boolean containsHeader( String name ) {
+        return _headers.containsKey( name.toUpperCase() );
+    }
+
+
+    /**
+     * @deprecated Use encodeRedirectURL(String url)
+     **/
+    public String encodeRedirectUrl( String url ) {
+        return encodeRedirectURL( url );
+    }
+
+
+    /**
+     * Encodes the specified URL by including the session ID in it,
+     * or, if encoding is not needed, returns the URL unchanged.
+     * The implementation of this method should include the logic to
+     * determine whether the session ID needs to be encoded in the URL.
+     * For example, if the browser supports cookies, or session
+     * tracking is turned off, URL encoding is unnecessary.
+     **/
+    public String encodeURL( String url ) {
+        return url;
+    }
+
+
+    /**
+     * Encodes the specified URL for use in the
+     * <code>sendRedirect</code> method or, if encoding is not needed,
+     * returns the URL unchanged.  The implementation of this method
+     * should include the logic to determine whether the session ID
+     * needs to be encoded in the URL.  Because the rules for making
+     * this determination differ from those used to decide whether to
+     * encode a normal link, this method is seperate from the
+     * <code>encodeUrl</code> method.
+     **/
+    public String encodeRedirectURL( String url ) {
+        return url;
+    }
+
+
+    /**
+     * Sends a temporary redirect response to the client using the
+     * specified redirect location URL.  The URL must be absolute (for
+     * example, <code><em>https://hostname/path/file.html</em></code>).
+     * Relative URLs are not permitted here.
+     */
+    public void sendRedirect( String location ) throws IOException {
+        setStatus( HttpServletResponse.SC_MOVED_TEMPORARILY );
+        setHeader( "Location", location );
+    }
+
+
+    /**
+     * Sends an error response to the client using the specified status
+     * code and descriptive message.  If setStatus has previously been
+     * called, it is reset to the error status code.  The message is
+     * sent as the body of an HTML page, which is returned to the user
+     * to describe the problem.  The page is sent with a default HTML
+     * header; the message is enclosed in simple body tags
+     * (&lt;body&gt;&lt;/body&gt;).
+     **/
+    public void sendError( int sc ) throws IOException {
+        sendError( sc, "" );
+    }
+
+
+    /**
+     * Sends an error response to the client using the specified status
+     * code and descriptive message.  If setStatus has previously been
+     * called, it is reset to the error status code.  The message is
+     * sent as the body of an HTML page, which is returned to the user
+     * to describe the problem.  The page is sent with a default HTML
+     * header; the message is enclosed in simple body tags
+     * (&lt;body&gt;&lt;/body&gt;).
+     **/
+    public void sendError(int sc, String msg) throws IOException {
+        setStatus( sc );
+        _statusMessage = msg;
+
+        _writer = null;
+        _servletStream = null;
+
+        setContentType( "text/html" );
+        getWriter().println( "<html><head><title>" + msg + "</title></head><body>" + msg + "</body></html>" );
+    }
+
+
+    /**
+     * Sets the status code for this response.  This method is used to
+     * set the return status code when there is no error (for example,
+     * for the status codes SC_OK or SC_MOVED_TEMPORARILY).  If there
+     * is an error, the <code>sendError</code> method should be used
+     * instead.
+     **/
+    public void setStatus( int sc ) {
+        _status = sc;
+    }
+
+
+    /**
+     * @deprecated As of version 2.1, due to ambiguous meaning of the message parameter.
+     * To set a status code use setStatus(int), to send an error with a description
+     * use sendError(int, String). Sets the status code and message for this response.
+     **/
+    public void setStatus( int sc, String msg ) {
+        setStatus( sc );
+    }
+
+
+    /**
+     * Adds a field to the response header with the given name and value.
+     * If the field had already been set, the new value overwrites the
+     * previous one.  The <code>containsHeader</code> method can be
+     * used to test for the presence of a header before setting its
+     * value.
+     **/
+    public void setHeader( String name, String value ) {
+        ArrayList values = new ArrayList();
+        values.add( value );
+        synchronized (_headers) {
+            _headers.put( name.toUpperCase(), values );
+        }
+    }
+
+
+    /**
+     * Adds a field to the response header with the given name and
+     * integer value.  If the field had already been set, the new value
+     * overwrites the previous one.  The <code>containsHeader</code>
+     * method can be used to test for the presence of a header before
+     * setting its value.
+     **/
+    public void setIntHeader( String name, int value ) {
+        setHeader( name, asHeaderValue( value ) );
+    }
+
+
+    private String asHeaderValue( int value ) {
+        return Integer.toString( value );
+    }
+
+
+    /**
+     * Adds a field to the response header with the given name and
+     * date-valued field.  The date is specified in terms of
+     * milliseconds since the epoch.  If the date field had already
+     * been set, the new value overwrites the previous one.  The
+     * <code>containsHeader</code> method can be used to test for the
+     * presence of a header before setting its value.
+     **/
+    public void setDateHeader( String name, long date ) {
+        setHeader( name, asDateHeaderValue( date ) );
+    }
+
+
+    private String asDateHeaderValue( long date ) {
+        Date value = new Date( date );
+        SimpleDateFormat formatter = new SimpleDateFormat( RFC1123_DATE_SPEC, Locale.US );
+        formatter.setTimeZone( TimeZone.getTimeZone( "Greenwich Mean Time" ) );
+        return formatter.format( value );
+    }
+
+
+    /**
+     * Returns the name of the character set encoding used for
+     * the MIME body sent by this response.
+     **/
+    public String getCharacterEncoding() {
+        return _encoding == null ? HttpUnitUtils.DEFAULT_CHARACTER_SET : _encoding;
+    }
+
+
+    /**
+     * Sets the content type of the response the server sends to
+     * the client. The content type may include the type of character
+     * encoding used, for example, <code>text/html; charset=ISO-8859-4</code>.
+     *
+     * <p>You can only use this method once, and you should call it
+     * before you obtain a <code>PrintWriter</code> or
+     * {@link ServletOutputStream} object to return a response.
+     **/
+    public void setContentType( String type ) {
+        String[] typeAndEncoding = HttpUnitUtils.parseContentTypeHeader( type );
+
+        _contentType = typeAndEncoding[0];
+        if (typeAndEncoding[1] != null) _encoding = typeAndEncoding[1];
+    }
+
+
+    /**
+     * Returns a {@link ServletOutputStream} suitable for writing binary
+     * data in the response. The servlet engine does not encode the
+     * binary data.
+     *
+     * @exception IllegalStateException if you have already called the <code>getWriter</code> method
+     **/
+    public ServletOutputStream getOutputStream() throws IOException {
+        if (_writer != null) throw new IllegalStateException( "Tried to create output stream; writer already exists" );
+        if (_servletStream == null) {
+            _outputStream = new ByteArrayOutputStream();
+            _servletStream = new ServletUnitOutputStream( _outputStream );
+        }
+        return _servletStream;
+    }
+
+
+    /**
+     * Returns a <code>PrintWriter</code> object that you
+     * can use to send character text to the client.
+     * The character encoding used is the one specified
+     * in the <code>charset=</code> property of the
+     * {@link #setContentType} method, which you must call
+     * <i>before</i> you call this method.
+     *
+     * <p>If necessary, the MIME type of the response is
+     * modified to reflect the character encoding used.
+     *
+     * <p> You cannot use this method if you have already
+     * called {@link #getOutputStream} for this
+     * <code>ServletResponse</code> object.
+     *
+     * @exception UnsupportedEncodingException  if the character encoding specified in
+     *                                         <code>setContentType</code> cannot be
+     *                                         used
+     *
+     * @exception IllegalStateException        if the <code>getOutputStream</code>
+     *                                                 method has already been called for this
+     *                                         response object; in that case, you can't
+     *                                         use this method
+     *
+     **/
+    public PrintWriter getWriter() throws UnsupportedEncodingException {
+        if (_servletStream != null) throw new IllegalStateException( "Tried to create writer; output stream already exists" );
+        if (_writer == null) {
+            _outputStream = new ByteArrayOutputStream();
+            _writer = new PrintWriter( new OutputStreamWriter( _outputStream, getCharacterEncoding() ) );
+        }
+        return _writer;
+    }
+
+
+    /**
+     * Sets the length of the content the server returns
+     * to the client. In HTTP servlets, this method sets the
+     * HTTP Content-Length header.
+     **/
+    public void setContentLength( int len ) {
+        setIntHeader( "Content-Length", len );
+    }
+
+
+//------------------------------- the following methods are new in JSDK 2.2 ----------------------
+
+
+    /**
+     * Adds a response header with the given name and value. This method allows response headers to have multiple values.
+     **/
+    public void addHeader( String name, String value ) {
+        synchronized (_headers) {
+            String key = name.toUpperCase();
+            ArrayList values = (ArrayList) _headers.get( key );
+            if (values == null) {
+                values = new ArrayList();
+                _headers.put( key, values );
+            }
+            values.add( value );
+        }
+    }
+
+
+    /**
+     * Adds a response header with the given name and value. This method allows response headers to have multiple values.
+     **/
+    public void addIntHeader( String name, int value ) {
+        addHeader( name, asHeaderValue( value ) );
+    }
+
+
+    /**
+     * Adds a response header with the given name and value. This method allows response headers to have multiple values.
+     **/
+    public void addDateHeader( String name, long value ) {
+        addHeader( name, asDateHeaderValue( value ) );
+    }
+
+
+    /**
+     * Sets the preferred buffer size for the body of the response. The servlet container
+     * will use a buffer at least as large as the size requested. The actual buffer size
+     * used can be found using getBufferSize.
+     **/
+    public void setBufferSize( int size ) {
+        if (getContents().length != 0) throw new IllegalStateException( "May not set buffer size after data is written" );
+    }
+
+
+    /**
+     * Returns the actual buffer size used for the response. If no buffering is used, this method returns 0.
+     **/
+    public int getBufferSize() {
+        return 0;
+    }
+
+
+    /**
+     * Returns a boolean indicating if the response has been committed. A committed response has
+     * already had its status code and headers written.
+     **/
+    public boolean isCommitted() {
+        return _committed;
+    }
+
+
+    /**
+     * Forces any content in the buffer to be written to the client. A call to this method automatically
+     * commits the response, meaning the status code and headers will be written.
+     **/
+    public void flushBuffer() throws IOException {
+        _committed = true;
+    }
+
+
+    /**
+     * Clears any data that exists in the buffer as well as the status code and headers.
+     * If the response has been committed, this method throws an IllegalStateException.
+     **/
+    public void reset() {
+        resetBuffer();
+        _headers.clear();
+        _headersComplete = false;
+        _status = SC_OK;
+    }
+
+
+    /**
+     * Sets the locale of the response, setting the headers (including the Content-Type's charset)
+     * as appropriate. This method should be called before a call to getWriter().
+     * By default, the response locale is the default locale for the server.
+     **/
+    public void setLocale( Locale locale ) {
+        _locale = locale;
+        if (_encoding == null) {
+            for (Iterator it = ENCODING_MAP.entrySet().iterator(); it.hasNext();) {
+                Map.Entry entry = (Map.Entry) it.next();
+                String locales = (String) entry.getValue();
+                if (locales.indexOf( locale.getLanguage() ) >= 0 || locales.indexOf( locale.toString() ) >= 0) {
+                    _encoding = (String) entry.getKey();
+                    return;
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Returns the locale assigned to the response.
+     **/
+    public Locale getLocale() {
+        return _locale;
+    }
+
+
+//----------------------------- methods added to ServletResponse in JSDK 2.3 --------------------------------------
+
+
+    /**
+     * Clears the content of the underlying buffer in the response without clearing headers or status code.
+     * If the response has been committed, this method throws an IllegalStateException.
+     *
+     * @since 1.3
+     */
+    public void resetBuffer() {
+        if (_committed) throw new IllegalStateException( "May not resetBuffer after response is committed" );
+        _outputStream = null;
+        _servletStream = null;
+        _writer = null;
+    }
+
+
+//---------------------------------------------- package methods --------------------------------------------------
+
+    /**
+     * Returns the contents of this response.
+     **/
+    byte[] getContents() {
+        if (_outputStream == null) {
+            return new byte[0];
+        } else {
+            if (_writer != null) _writer.flush();
+            return _outputStream.toByteArray();
+        }
+    }
+
+
+    /**
+     * Returns the status of this response.
+     **/
+    int getStatus() {
+        return _status;
+    }
+
+
+    /**
+     * Returns the message associated with this response's status.
+     **/
+    String getMessage() {
+        return _statusMessage;
+    }
+
+
+    public String[] getHeaderFieldNames() {
+        if (!_headersComplete) completeHeaders();
+        Vector names = new Vector();
+        for (Enumeration e = _headers.keys(); e.hasMoreElements();) {
+            names.addElement( e.nextElement() );
+        }
+        String[] result = new String[ names.size() ];
+        names.copyInto( result );
+        return result;
+    }
+
+
+    /**
+     * Returns the headers defined for this response.
+     **/
+    String getHeaderField( String name ) {
+        if (!_headersComplete) completeHeaders();
+
+        ArrayList values;
+        synchronized (_headers) {
+            values = (ArrayList) _headers.get( name.toUpperCase() );
+        }
+
+        return values == null ? null : (String) values.get( 0 );
+     }
+
+
+     /**
+     * Return an array of all the header values associated with the
+     * specified header name, or an zero-length array if there are no such
+     * header values.
+     *
+     * @param name Header name to look up
+     */
+    public String[] getHeaderFields(String name) {
+        if (!_headersComplete) completeHeaders();
+        ArrayList values;
+        synchronized (_headers) {
+            values = (ArrayList) _headers.get(name.toUpperCase());
+        }
+        if (values == null)
+            return (new String[0]);
+        String results[] = new String[values.size()];
+        return ((String[]) values.toArray(results));
+
+    }
+
+//--------------------------------------- methods added to ServletRequest in Servlet API 2.4 ----------------------------
+
+    public void setCharacterEncoding(String string) {
+        _encoding = string;
+    }
+
+    /**
+     * Returns the content type defined for this response.
+     **/
+    public String getContentType() {
+        return _contentType;
+    }
+
+
+//------------------------------------------- private members ------------------------------------
+
+
+    private String _contentType = "text/plain";
+
+    private String _encoding;
+
+    private PrintWriter  _writer;
+
+    private ServletOutputStream _servletStream;
+
+    private ByteArrayOutputStream _outputStream;
+
+    private int _status = SC_OK;
+
+    private String _statusMessage = "OK";
+
+    private final Hashtable _headers = new Hashtable();
+
+    private boolean _headersComplete;
+
+    private Vector  _cookies = new Vector();
+
+
+    private void completeHeaders() {
+        if (_headersComplete) return;
+        addCookieHeader();
+        setHeader( "Content-Type", _contentType + "; charset=" + getCharacterEncoding() );
+        _headersComplete = true;
+    }
+
+
+    private void addCookieHeader() {
+        if (_cookies.isEmpty()) return;
+
+        StringBuffer sb = new StringBuffer();
+        for (Enumeration e = _cookies.elements(); e.hasMoreElements();) {
+            Cookie cookie = (Cookie) e.nextElement();
+            sb.append( cookie.getName() ).append( '=' ).append( cookie.getValue() );
+            if (cookie.getPath() != null) sb.append( ";path=" ).append( cookie.getPath() );
+            if (cookie.getDomain() != null) sb.append( ";domain=" ).append( cookie.getDomain() );
+            if (e.hasMoreElements()) sb.append( ',' );
+        }
+        setHeader( "Set-Cookie", sb.toString() );
+    }
+
+
+
+    static {
+        ENCODING_MAP.put( "iso-8859-1", "ca da de en es fi fr is it nl no pt sv " );
+        ENCODING_MAP.put( "iso-8859-2", "cs hr hu pl ro sh sk sl sq " );
+        ENCODING_MAP.put( "iso-8859-4", "et lt lv ");
+        ENCODING_MAP.put( "iso-8859-5", "be bg mk ru sr uk " );
+        ENCODING_MAP.put( "iso-8859-6", "ar " );
+        ENCODING_MAP.put( "iso-8859-7", "el " );
+        ENCODING_MAP.put( "iso-8859-8", "iw he " );
+        ENCODING_MAP.put( "iso-8859-9", "tr " );
+
+        ENCODING_MAP.put("Shift_JIS", "ja ");
+        ENCODING_MAP.put("EUC-KR",    "ko ");
+        ENCODING_MAP.put("TIS-620",   "th ");
+        ENCODING_MAP.put("GB2312",    "zh " );
+        ENCODING_MAP.put("Big5",      "zh_TW zh_HK " );
+    }
+
+}
+
+
+
+class ServletUnitOutputStream extends ServletOutputStream {
+
+    ServletUnitOutputStream( ByteArrayOutputStream stream ) {
+        _stream = stream;
+    }
+
+
+    public void write( int aByte ) throws IOException {
+        _stream.write( aByte );
+    }
+
+    private ByteArrayOutputStream _stream;
+}
diff --git a/accelerator/src/test/java/de/juplo/accelerator/ParameterGuessingTest.java b/accelerator/src/test/java/de/juplo/accelerator/ParameterGuessingTest.java
new file mode 100644 (file)
index 0000000..342a8f2
--- /dev/null
@@ -0,0 +1,575 @@
+package de.juplo.accelerator;
+
+import com.meterware.httpunit.WebResponse;
+import de.juplo.testingtools.HttpTestCase;
+import java.net.URLEncoder;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Set;
+import org.junit.Assert;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+
+
+/**
+ *
+ * @author kai
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(locations = {
+  "classpath:/config.xml"
+})
+public class ParameterGuessingTest extends HttpTestCase {
+  private final static Logger log = LoggerFactory.getLogger(ParameterGuessingTest.class);
+
+
+  public ParameterGuessingTest() {
+    super("src/test/resources/web.xml");
+  }
+
+
+  @Test
+  public void testNothingSet() throws Exception {
+
+    log.info("-------- Test: Servlet does not implement getLastModified() and sets no Headers...");
+
+    WebResponse response = executeRequest("http://localhost/parameter-guessing?n=16");
+    Assert.assertEquals("W/\"Hallo Welt!\"", response.getHeaderField(Headers.HEADER_ETAG));
+    Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
+    Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
+    Assert.assertEquals("max-age=3600", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
+    Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
+    Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
+    Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
+    SimpleDateFormat df = new SimpleDateFormat(Headers.RFC_1123_DATE_FORMAT, Locale.US);
+    long date = df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime();
+    long expires = df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime();
+    Assert.assertTrue("Expires-Header passt nicht zum Date-Header! Unterschied: " + (expires-date)/1000 + " Sekunden.", date + 3600000 == expires);
+  }
+
+  @Test
+  public void testSetUnfilteredHeaders() throws Exception {
+
+    log.info("-------- Test: Servlet sets unfiltered Headers...");
+
+    WebResponse response = executeRequest("http://localhost/parameter-guessing?n=16&X-Debug=bla&Age=34&Content-Language=de");
+    Assert.assertEquals("bla", response.getHeaderField("X-Debug"));
+    Assert.assertEquals("34", response.getHeaderField("Age"));
+    Assert.assertEquals("de", response.getHeaderField("Content-Language"));
+    Assert.assertEquals("W/\"Hallo Welt!\"", response.getHeaderField(Headers.HEADER_ETAG));
+    Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
+    Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
+    Assert.assertEquals("max-age=3600", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
+    Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
+    Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
+    Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
+    SimpleDateFormat df = new SimpleDateFormat(Headers.RFC_1123_DATE_FORMAT, Locale.US);
+    long date = df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime();
+    long expires = df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime();
+    Assert.assertTrue("Expires-Header passt nicht zum Date-Header! Unterschied: " + (expires-date)/1000 + " Sekunden.", date + 3600000 == expires);
+  }
+
+  @Test
+  public void testETagSet() throws Exception {
+
+    log.info("-------- Test: Servlet sets Header \"ETag\"");
+
+    SimpleDateFormat df = new SimpleDateFormat(Headers.RFC_1123_DATE_FORMAT, Locale.US);
+    WebResponse response;
+    long date, expires;
+
+    response = executeRequest("http://localhost/parameter-guessing?n=16&ETag=" + URLEncoder.encode("\"bla\"", "UTF-8"));
+    Assert.assertEquals("\"bla\"", response.getHeaderField(Headers.HEADER_ETAG));
+    Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
+    Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
+    Assert.assertEquals("max-age=3600", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
+    Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
+    Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
+    Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
+    date = df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime();
+    expires = df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime();
+    Assert.assertTrue("Expires-Header passt nicht zum Date-Header! Unterschied: " + (expires-date)/1000 + " Sekunden.", date + 3600000 == expires);
+
+    response = executeRequest("http://localhost/parameter-guessing?n=16&ETag=" + URLEncoder.encode("\"bÄl\"a\"", "UTF-8"));
+    Assert.assertEquals("\"bla\"", response.getHeaderField(Headers.HEADER_ETAG));
+    Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
+    Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
+    Assert.assertEquals("max-age=3600", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
+    Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
+    Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
+    Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
+    date = df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime();
+    expires = df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime();
+    Assert.assertTrue("Expires-Header passt nicht zum Date-Header! Unterschied: " + (expires-date)/1000 + " Sekunden.", date + 3600000 == expires);
+
+    response = executeRequest("http://localhost/parameter-guessing?n=16&ETag=" + URLEncoder.encode("bla", "UTF-8"));
+    Assert.assertEquals("\"bla\"", response.getHeaderField(Headers.HEADER_ETAG));
+    Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
+    Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
+    Assert.assertEquals("max-age=3600", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
+    Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
+    Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
+    Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
+    date = df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime();
+    expires = df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime();
+    Assert.assertTrue("Expires-Header passt nicht zum Date-Header! Unterschied: " + (expires-date)/1000 + " Sekunden.", date + 3600000 == expires);
+
+    response = executeRequest("http://localhost/parameter-guessing?n=16&ETag=" + URLEncoder.encode("bÄl\"a", "UTF-8"));
+    Assert.assertEquals("\"bla\"", response.getHeaderField(Headers.HEADER_ETAG));
+    Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
+    Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
+    Assert.assertEquals("max-age=3600", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
+    Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
+    Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
+    Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
+    date = df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime();
+    expires = df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime();
+    Assert.assertTrue("Expires-Header passt nicht zum Date-Header! Unterschied: " + (expires-date)/1000 + " Sekunden.", date + 3600000 == expires);
+
+    response = executeRequest("http://localhost/parameter-guessing?n=16&ETag=" + URLEncoder.encode("W/\"blub\"", "UTF-8"));
+    Assert.assertEquals("W/\"blub\"", response.getHeaderField(Headers.HEADER_ETAG));
+    Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
+    Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
+    Assert.assertEquals("max-age=3600", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
+    Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
+    Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
+    Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
+    date = df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime();
+    expires = df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime();
+    Assert.assertTrue("Expires-Header passt nicht zum Date-Header! Unterschied: " + (expires-date)/1000 + " Sekunden.", date + 3600000 == expires);
+
+    response = executeRequest("http://localhost/parameter-guessing?n=16&ETag=" + URLEncoder.encode("W/\"bÄl\"ub\"", "UTF-8"));
+    Assert.assertEquals("W/\"blub\"", response.getHeaderField(Headers.HEADER_ETAG));
+    Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
+    Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
+    Assert.assertEquals("max-age=3600", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
+    Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
+    Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
+    Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
+    date = df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime();
+    expires = df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime();
+    Assert.assertTrue("Expires-Header passt nicht zum Date-Header! Unterschied: " + (expires-date)/1000 + " Sekunden.", date + 3600000 == expires);
+
+    response = executeRequest("http://localhost/parameter-guessing?n=16&ETag=" + URLEncoder.encode("W/blub", "UTF-8"));
+    Assert.assertEquals("W/\"blub\"", response.getHeaderField(Headers.HEADER_ETAG));
+    Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
+    Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
+    Assert.assertEquals("max-age=3600", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
+    Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
+    Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
+    Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
+    date = df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime();
+    expires = df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime();
+    Assert.assertTrue("Expires-Header passt nicht zum Date-Header! Unterschied: " + (expires-date)/1000 + " Sekunden.", date + 3600000 == expires);
+
+    response = executeRequest("http://localhost/parameter-guessing?n=16&ETag=" + URLEncoder.encode("W/bÄl\"ub", "UTF-8"));
+    Assert.assertEquals("W/\"blub\"", response.getHeaderField(Headers.HEADER_ETAG));
+    Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
+    Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
+    Assert.assertEquals("max-age=3600", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
+    Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
+    Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
+    Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
+    date = df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime();
+    expires = df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime();
+    Assert.assertTrue("Expires-Header passt nicht zum Date-Header! Unterschied: " + (expires-date)/1000 + " Sekunden.", date + 3600000 == expires);
+  }
+
+  @Test
+  public void testLastModifiedImplemented() throws Exception {
+
+    log.info("-------- Test: Servlet implements getLastModified()");
+
+    WebResponse response = executeRequest("http://localhost/parameter-guessing?n=16&l=1324162929861");
+    Assert.assertEquals("W/\"Hallo Welt!\"", response.getHeaderField(Headers.HEADER_ETAG));
+    Assert.assertEquals("Sat, 17 Dec 2011 23:02:09 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
+    Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
+    Assert.assertEquals("max-age=3600", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
+    Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
+    Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
+    Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
+    SimpleDateFormat df = new SimpleDateFormat(Headers.RFC_1123_DATE_FORMAT, Locale.US);
+    long date = df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime();
+    long expires = df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime();
+    Assert.assertTrue("Expires-Header passt nicht zum Date-Header! Unterschied: " + (expires-date)/1000 + " Sekunden.", date + 3600000 == expires);
+  }
+
+  @Test
+  public void testCacheControlSet() throws Exception {
+
+    log.info("-------- Test: Servlet sets Header \"Cache-Control\"");
+
+    SimpleDateFormat df = new SimpleDateFormat(Headers.RFC_1123_DATE_FORMAT, Locale.US);
+    StringBuilder uri;
+    WebResponse response;
+    Date date;
+    long expires;
+    Set<String> params;
+    Calendar calendar = Calendar.getInstance();
+    calendar.set(Calendar.MILLISECOND, 0);
+
+    /** max-age=120 */
+    uri = new StringBuilder();
+    uri.append("http://localhost/parameter-guessing");
+    uri.append("?n=16");
+    uri.append("&Cache-Control=");
+    uri.append(URLEncoder.encode("max-age=120", "UTF-8"));
+    response = executeRequest(uri.toString());
+    Assert.assertEquals("W/\"Hallo Welt!\"", response.getHeaderField(Headers.HEADER_ETAG));
+    Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
+    Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
+    Assert.assertEquals("max-age=120", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
+    Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
+    Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
+    date = df.parse(response.getHeaderField(Headers.HEADER_DATE));
+    expires = (date.getTime()/1000l + 120l) * 1000l;
+    Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
+    Assert.assertEquals("Unerwartetr Wert für den Expires-Header!", expires, df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime());
+
+    /** max-age=120, s-max-age=60, private, must-revalidate  */
+    uri = new StringBuilder();
+    uri.append("http://localhost/parameter-guessing");
+    uri.append("?n=16");
+    uri.append("&Cache-Control=");
+    uri.append(URLEncoder.encode("max-age=120, s-max-age=60, must-revalidate", "UTF-8"));
+    response = executeRequest(uri.toString());
+    Assert.assertEquals("W/\"Hallo Welt!\"", response.getHeaderField(Headers.HEADER_ETAG));
+    Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
+    Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
+    params = new HashSet<String>();
+    for (String param : response.getHeaderFields(Headers.HEADER_CACHE_CONTROL))
+      for (String part : param.split(","))
+        params.add(part.trim());
+    Assert.assertTrue(response.getHeaderField(Headers.HEADER_CACHE_CONTROL) + " enthält \"max-age=120\" nicht!", params.contains("max-age=120"));
+    Assert.assertTrue(response.getHeaderField(Headers.HEADER_CACHE_CONTROL) + " enthält \"s-max-age=60\" nicht!", params.contains("s-max-age=60"));
+    Assert.assertTrue(response.getHeaderField(Headers.HEADER_CACHE_CONTROL) + " enthält \"must-revalidate\" nicht!", params.contains("must-revalidate"));
+    Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
+    Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
+    Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
+    date = df.parse(response.getHeaderField(Headers.HEADER_DATE));
+    expires = (date.getTime()/1000l + 120l) * 1000l;
+    Assert.assertEquals("Unerwartetr Wert für den Expires-Header!", expires, df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime());
+
+    /** max-age=120, s-max-age=60, private, must-revalidate, BUT: several other values are set before  */
+    uri = new StringBuilder();
+    uri.append("http://localhost/parameter-guessing");
+    uri.append("?n=16");
+    uri.append("&Cache-Control=");
+    uri.append(URLEncoder.encode("no-store", "UTF-8"));
+    uri.append("&Cache-Control=");
+    uri.append(URLEncoder.encode("max-age=360, s-max-age=600, private", "UTF-8"));
+    uri.append("&Cache-Control=");
+    uri.append(URLEncoder.encode("public", "UTF-8"));
+    uri.append("&Cache-Control=");
+    uri.append(URLEncoder.encode("max-age=120, s-max-age=60, must-revalidate", "UTF-8"));
+    response = executeRequest(uri.toString());
+    Assert.assertEquals("W/\"Hallo Welt!\"", response.getHeaderField(Headers.HEADER_ETAG));
+    Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
+    Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
+    params = new HashSet<String>();
+    for (String param : response.getHeaderFields(Headers.HEADER_CACHE_CONTROL))
+      for (String part : param.split(","))
+        params.add(part.trim());
+    Assert.assertTrue(response.getHeaderField(Headers.HEADER_CACHE_CONTROL) + " enthält \"max-age=120\" nicht!", params.contains("max-age=120"));
+    Assert.assertTrue(response.getHeaderField(Headers.HEADER_CACHE_CONTROL) + " enthält \"s-max-age=60\" nicht!", params.contains("s-max-age=60"));
+    Assert.assertTrue(response.getHeaderField(Headers.HEADER_CACHE_CONTROL) + " enthält \"must-revalidate\" nicht!", params.contains("must-revalidate"));
+    Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
+    Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
+    Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
+    date = df.parse(response.getHeaderField(Headers.HEADER_DATE));
+    expires = (date.getTime()/1000l + 120l) * 1000l;
+    Assert.assertEquals("Unerwartetr Wert für den Expires-Header!", expires, df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime());
+  }
+
+  @Test
+  public void testDateSet() throws Exception {
+
+    log.info("-------- Test: Servlet sets Header \"Date\"");
+
+    SimpleDateFormat df = new SimpleDateFormat(Headers.RFC_1123_DATE_FORMAT, Locale.US);
+    StringBuilder uri;
+    WebResponse response;
+    Date date, expires;
+    Calendar calendar = Calendar.getInstance();
+    calendar.set(Calendar.MILLISECOND, 0);
+
+    /** Date ca NOW -1m */
+    calendar.add(Calendar.MINUTE, -1);
+    date = calendar.getTime();
+    calendar.add(Calendar.MINUTE, 60); /** default max-age=3600 yields 60m! */
+    expires = calendar.getTime();
+    uri = new StringBuilder();
+    uri.append("http://localhost/parameter-guessing");
+    uri.append("?n=16");
+    uri.append("&Date=");
+    uri.append(URLEncoder.encode(df.format(date), "UTF-8"));
+    response = executeRequest(uri.toString());
+    Assert.assertEquals("W/\"Hallo Welt!\"", response.getHeaderField(Headers.HEADER_ETAG));
+    Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
+    Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
+    Assert.assertEquals("max-age=3600", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
+    Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
+    Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
+    Assert.assertEquals("Unerwartetr Wert für den Date-Header!", date.getTime(), df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime());
+    Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
+    Assert.assertEquals("Unerwartetr Wert für den Expires-Header!", expires.getTime(), df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime());
+
+    /** Date ca NOW -1m, BUT: is set to some garbage values before */
+    calendar.add(Calendar.MINUTE, -1);
+    date = calendar.getTime();
+    calendar.add(Calendar.MINUTE, 60); /** default max-age=3600 yields 60m! */
+    expires = calendar.getTime();
+    uri = new StringBuilder();
+    uri.append("http://localhost/parameter-guessing");
+    uri.append("?n=16");
+    uri.append("&Date=");
+    calendar.add(Calendar.MINUTE, 10);
+    uri.append(URLEncoder.encode(df.format(calendar.getTime()), "UTF-8"));
+    uri.append("&Date=");
+    calendar.add(Calendar.HOUR, -2);
+    uri.append(URLEncoder.encode(df.format(calendar.getTime()), "UTF-8"));
+    uri.append("&Date=");
+    calendar.add(Calendar.DATE, 1);
+    uri.append(URLEncoder.encode(df.format(calendar.getTime()), "UTF-8"));
+    uri.append("&Date=");
+    uri.append(URLEncoder.encode(df.format(date), "UTF-8"));
+    response = executeRequest(uri.toString());
+    Assert.assertEquals("W/\"Hallo Welt!\"", response.getHeaderField(Headers.HEADER_ETAG));
+    Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
+    Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
+    Assert.assertEquals("max-age=3600", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
+    Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
+    Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
+    Assert.assertEquals("Unerwartetr Wert für den Date-Header!", date.getTime(), df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime());
+    Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
+    Assert.assertEquals("Unerwartetr Wert für den Expires-Header!", expires.getTime(), df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime());
+  }
+
+  @Test
+  public void testExpiresSet() throws Exception {
+
+    log.info("-------- Test: Servlet sets Header \"Expires\"");
+
+    SimpleDateFormat df = new SimpleDateFormat(Headers.RFC_1123_DATE_FORMAT, Locale.US);
+    StringBuilder uri;
+    WebResponse response;
+    Date date, expires;
+    long age;
+    Calendar calendar = Calendar.getInstance();
+    calendar.set(Calendar.MILLISECOND, 0);
+
+    /** Expires ca. NOW + 10m */
+    calendar.add(Calendar.MINUTE, 10);
+    expires = calendar.getTime();
+    uri = new StringBuilder();
+    uri.append("http://localhost/parameter-guessing");
+    uri.append("?n=16");
+    uri.append("&Expires=");
+    uri.append(URLEncoder.encode(df.format(expires), "UTF-8"));
+    response = executeRequest(uri.toString());
+    Assert.assertEquals("W/\"Hallo Welt!\"", response.getHeaderField(Headers.HEADER_ETAG));
+    Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
+    Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
+    Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
+    Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
+    date = df.parse(response.getHeaderField(Headers.HEADER_DATE));
+    age = (expires.getTime() - date.getTime())/1000l;
+    Assert.assertEquals("max-age=" + age, response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
+    Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
+    Assert.assertEquals("Unerwartetr Wert für den Expires-Header!", expires.getTime(), df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime());
+
+    /** Expires ca. NOW + 10m, BUT: is set to some garbage values before */
+    calendar.add(Calendar.MINUTE, 10);
+    uri = new StringBuilder();
+    uri.append("http://localhost/parameter-guessing");
+    uri.append("?n=16");
+    uri.append("&Expires=");
+    calendar.add(Calendar.MINUTE, 10);
+    uri.append(URLEncoder.encode(df.format(calendar.getTime()), "UTF-8"));
+    uri.append("&Expires=");
+    calendar.add(Calendar.HOUR, -2);
+    uri.append(URLEncoder.encode(df.format(calendar.getTime()), "UTF-8"));
+    uri.append("&Expires=");
+    calendar.add(Calendar.DATE, 1);
+    uri.append(URLEncoder.encode(df.format(calendar.getTime()), "UTF-8"));
+    uri.append("&Expires=");
+    uri.append(URLEncoder.encode(df.format(expires), "UTF-8"));
+    response = executeRequest(uri.toString());
+    Assert.assertEquals("W/\"Hallo Welt!\"", response.getHeaderField(Headers.HEADER_ETAG));
+    Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
+    Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
+    Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
+    Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
+    date = df.parse(response.getHeaderField(Headers.HEADER_DATE));
+    age = (expires.getTime() - date.getTime())/1000l;
+    Assert.assertEquals("max-age=" + age, response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
+    Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
+    Assert.assertEquals("Unerwartetr Wert für den Expires-Header!", expires.getTime(), df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime());
+  }
+
+  @Test
+  public void testDateAndExpiresSet() throws Exception {
+
+    log.info("-------- Test: Servlet sets Header's \"Date\" and \"Expires\"");
+
+    SimpleDateFormat df = new SimpleDateFormat(Headers.RFC_1123_DATE_FORMAT, Locale.US);
+    StringBuilder uri;
+    WebResponse response;
+    Date date, expires, garbage;
+    Calendar calendar = Calendar.getInstance();
+    calendar.set(Calendar.MILLISECOND, 0);
+
+    /** Expires = Date + 30m */
+    date = calendar.getTime();
+    calendar.add(Calendar.MINUTE, 30);
+    expires = calendar.getTime();
+    uri = new StringBuilder();
+    uri.append("http://localhost/parameter-guessing");
+    uri.append("?n=16");
+    uri.append("&Date=");
+    uri.append(URLEncoder.encode(df.format(date), "UTF-8"));
+    uri.append("&Expires=");
+    uri.append(URLEncoder.encode(df.format(expires), "UTF-8"));
+    response = executeRequest(uri.toString());
+    Assert.assertEquals("W/\"Hallo Welt!\"", response.getHeaderField(Headers.HEADER_ETAG));
+    Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
+    Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
+    Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
+    Assert.assertEquals("max-age=1800", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
+    Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
+    Assert.assertEquals("Unerwartetr Wert für den Date-Header!", date.getTime(), df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime());
+    Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
+    Assert.assertEquals("Unerwartetr Wert für den Expires-Header!", expires.getTime(), df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime());
+
+    /** Expires = Date + 30m, BUT: Date is set to Date - 2h first and Expires to Date */
+    date = calendar.getTime();
+    calendar.add(Calendar.MINUTE, 30);
+    expires = calendar.getTime();
+    calendar.add(Calendar.HOUR, -2);
+    garbage = calendar.getTime();
+    uri = new StringBuilder();
+    uri.append("http://localhost/parameter-guessing");
+    uri.append("?n=16");
+    uri.append("&Date=");
+    uri.append(URLEncoder.encode(df.format(garbage), "UTF-8"));
+    uri.append("&Expires=");
+    uri.append(URLEncoder.encode(df.format(date), "UTF-8"));
+    uri.append("&Date=");
+    uri.append(URLEncoder.encode(df.format(date), "UTF-8"));
+    uri.append("&Expires=");
+    uri.append(URLEncoder.encode(df.format(expires), "UTF-8"));
+    response = executeRequest(uri.toString());
+    Assert.assertEquals("W/\"Hallo Welt!\"", response.getHeaderField(Headers.HEADER_ETAG));
+    Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
+    Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
+    Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
+    Assert.assertEquals("max-age=1800", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
+    Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
+    Assert.assertEquals("Unerwartetr Wert für den Date-Header!", date.getTime(), df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime());
+    Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
+    Assert.assertEquals("Unerwartetr Wert für den Expires-Header!", expires.getTime(), df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime());
+
+    /** Expires = Date - 1h --> will be ignored! */
+    date = calendar.getTime();
+    calendar.add(Calendar.MINUTE, -60);
+    garbage = calendar.getTime();
+    calendar.setTime(date);
+    calendar.add(Calendar.MINUTE, 60); /** default max-age=3600 yields 60m! */
+    expires = calendar.getTime();
+    uri = new StringBuilder();
+    uri.append("http://localhost/parameter-guessing");
+    uri.append("?n=16");
+    uri.append("&Date=");
+    uri.append(URLEncoder.encode(df.format(date), "UTF-8"));
+    uri.append("&Expires=");
+    uri.append(URLEncoder.encode(df.format(garbage), "UTF-8"));
+    response = executeRequest(uri.toString());
+    Assert.assertEquals("W/\"Hallo Welt!\"", response.getHeaderField(Headers.HEADER_ETAG));
+    Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
+    Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
+    Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
+    Assert.assertEquals("max-age=3600", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
+    Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
+    Assert.assertEquals("Unerwartetr Wert für den Date-Header!", date.getTime(), df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime());
+    Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
+    Assert.assertEquals("Unerwartetr Wert für den Expires-Header!", expires.getTime(), df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime());
+  }
+
+  @Test
+  public void testCacheControlDateAndExpiresSet() throws Exception {
+
+    log.info("-------- Test: Servlet sets Header's \"Cache-Control\", \"Date\" and \"Expires\"");
+
+    SimpleDateFormat df = new SimpleDateFormat(Headers.RFC_1123_DATE_FORMAT, Locale.US);
+    StringBuilder uri;
+    WebResponse response;
+    Date date, expires, expected;
+    Set<String> params;
+    Calendar calendar = Calendar.getInstance();
+    calendar.set(Calendar.MILLISECOND, 0);
+
+    /** Expires = Date + 30m, Cache-Control: must-revalidate, no-store */
+    date = calendar.getTime();
+    calendar.add(Calendar.MINUTE, 30);
+    expires = calendar.getTime();
+    uri = new StringBuilder();
+    uri.append("http://localhost/parameter-guessing");
+    uri.append("?n=16");
+    uri.append("&Cache-Control=");
+    uri.append(URLEncoder.encode("must-revalidate, no-store", "UTF-8"));
+    uri.append("&Date=");
+    uri.append(URLEncoder.encode(df.format(date), "UTF-8"));
+    uri.append("&Expires=");
+    uri.append(URLEncoder.encode(df.format(expires), "UTF-8"));
+    response = executeRequest(uri.toString());
+    Assert.assertEquals("W/\"Hallo Welt!\"", response.getHeaderField(Headers.HEADER_ETAG));
+    Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
+    Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
+    Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
+    params = new HashSet<String>();
+    for (String param : response.getHeaderFields(Headers.HEADER_CACHE_CONTROL))
+      for (String part : param.split(","))
+        params.add(part.trim());
+    Assert.assertTrue(response.getHeaderField(Headers.HEADER_CACHE_CONTROL) + " enthält \"max-age=1800\" nicht!", params.contains("max-age=1800"));
+    Assert.assertTrue(response.getHeaderField(Headers.HEADER_CACHE_CONTROL) + " enthält \"must-revalidate\" nicht!", params.contains("must-revalidate"));
+    Assert.assertTrue(response.getHeaderField(Headers.HEADER_CACHE_CONTROL) + " enthält \"no-store\" nicht!", params.contains("no-store"));
+    Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
+    Assert.assertEquals("Unerwartetr Wert für den Date-Header!", date.getTime(), df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime());
+    Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
+    Assert.assertEquals("Unerwartetr Wert für den Expires-Header!", expires.getTime(), df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime());
+
+    /** Expires = Date + 30m, BUT: max-age is set to 600s */
+    date = calendar.getTime();
+    calendar.add(Calendar.MINUTE, 10);
+    expected = calendar.getTime();
+    calendar.add(Calendar.MINUTE, 20);
+    expires = calendar.getTime();
+    uri = new StringBuilder();
+    uri.append("http://localhost/parameter-guessing");
+    uri.append("?n=16");
+    uri.append("&Date=");
+    uri.append(URLEncoder.encode(df.format(date), "UTF-8"));
+    uri.append("&Expires=");
+    uri.append(URLEncoder.encode(df.format(expires), "UTF-8"));
+    uri.append("&Cache-Control=");
+    uri.append(URLEncoder.encode("max-age=600", "UTF-8"));
+    response = executeRequest(uri.toString());
+    Assert.assertEquals("W/\"Hallo Welt!\"", response.getHeaderField(Headers.HEADER_ETAG));
+    Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
+    Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
+    Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
+    Assert.assertEquals("max-age=600", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
+    Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
+    Assert.assertEquals("Unerwartetr Wert für den Date-Header!", date.getTime(), df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime());
+    Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
+    Assert.assertEquals("Unerwartetr Wert für den Expires-Header!", expected.getTime(), df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime());
+  }
+}
diff --git a/accelerator/src/test/java/de/juplo/accelerator/RequestSizeTest.java b/accelerator/src/test/java/de/juplo/accelerator/RequestSizeTest.java
new file mode 100644 (file)
index 0000000..6b94d2b
--- /dev/null
@@ -0,0 +1,237 @@
+package de.juplo.accelerator;
+
+import com.meterware.httpunit.WebResponse;
+import de.juplo.testingtools.HttpTestCase;
+import de.juplo.testingtools.LoggingHttpServletResponseWrapper;
+import java.net.URLEncoder;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import java.text.SimpleDateFormat;
+import java.util.Locale;
+import org.junit.Assert;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+
+
+/**
+ *
+ * @author kai
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(locations = {
+  "classpath:/config.xml"
+})
+public class RequestSizeTest extends HttpTestCase {
+  private final static Logger log = LoggerFactory.getLogger(RequestSizeTest.class);
+
+
+  public RequestSizeTest() {
+    super("src/test/resources/web.xml");
+  }
+
+
+  @Test
+  public void testSimpleRequestWithGzip() throws Exception {
+
+    log.info("-------- Test: gzipped simple request");
+
+    client.getClientProperties().setAcceptGzip(true);
+
+    for (int i=0; i<33; i++) {
+      /** 33 requests ranging from 0 B to 4 KB - response ist buffered up to 1 KB */
+      WebResponse response = executeRequest("http://localhost/request-size?n=" + i*128);
+      Assert.assertEquals("W/\"Hallo Welt!\"", response.getHeaderField(Headers.HEADER_ETAG));
+      Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
+      if (i==0)
+        Assert.assertNull(response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
+      else
+        Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
+      Assert.assertEquals("max-age=3600", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
+      Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
+      Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
+      Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
+      SimpleDateFormat df = new SimpleDateFormat(Headers.RFC_1123_DATE_FORMAT, Locale.US);
+      long date = df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime();
+      long expires = df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime();
+      Assert.assertTrue("Expires-Header passt nicht zum Date-Header! Unterschied: " + (expires-date)/1000 + " Sekunden.", date + 3600000 == expires);
+    }
+  }
+
+  @Test
+  public void testSimpleRequestWithoutGzip() throws Exception {
+
+    log.info("-------- Test: uncompressed simple request");
+
+    client.getClientProperties().setAcceptGzip(false);
+
+    for (int i=0; i<33; i++) {
+      /** 33 requests ranging from 0 B to 4 KB - response ist buffered up to 1 KB */
+      WebResponse response = executeRequest("http://localhost/request-size?n=" + i*128);
+      Assert.assertEquals("W/\"Hallo Welt!\"", response.getHeaderField(Headers.HEADER_ETAG));
+      Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
+      Assert.assertNull(response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
+      Assert.assertEquals("max-age=3600", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
+      Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
+      Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
+      Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
+      SimpleDateFormat df = new SimpleDateFormat(Headers.RFC_1123_DATE_FORMAT, Locale.US);
+      long date = df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime();
+      long expires = df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime();
+      Assert.assertTrue("Expires-Header passt nicht zum Date-Header! Unterschied: " + (expires-date)/1000 + " Sekunden.", date + 3600000 == expires);
+    }
+  }
+
+  @Test
+  public void testForwardWithGzip() throws Exception {
+
+    log.info("-------- Test: gzipped request with forward");
+
+    client.getClientProperties().setAcceptGzip(true);
+
+    /**
+     * Auf den Fehler bei einem Forward nach Überschreitung der Puffer-Größe
+     * des ursprünglichen Requests wird hier nicht geprüft, weil der Puffer
+     * durch die Komprimierung bei den hier gewählten Test-Parametern nie
+     * vollgeschrieben wird, so dass er stets ohne Fehler zurückgesetzt
+     * werden kann...
+     *
+     * Dafür wird hier zusätzlich geprüft, ob die Komprimierung korrekt nur
+     * dann unterdrückt wird, wenn die gesamte Antwort leer ist (und nicht nur
+     * der initiale Request, der geforwarded wird).
+     */
+    for (int i=0; i<33; i++) {
+      /** 33 requests ranging from 0 B to 4 KB - response ist buffered up to 1 KB */
+      StringBuilder uri = new StringBuilder();
+      uri.append("http://localhost/request-size");
+      uri.append("?n=");
+      uri.append(i%7*128);
+      uri.append("&f=");
+      uri.append(URLEncoder.encode("/forwarded?n=" + i*128, "UTF-8"));
+      WebResponse response = executeRequest(uri.toString());
+      Assert.assertEquals("W/\"Hallo Welt!\"", response.getHeaderField(Headers.HEADER_ETAG));
+      Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
+      if (i==0)
+        Assert.assertNull(response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
+      else
+        Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
+      Assert.assertEquals("max-age=3600", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
+      Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
+      Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
+      Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
+      SimpleDateFormat df = new SimpleDateFormat(Headers.RFC_1123_DATE_FORMAT, Locale.US);
+      long date = df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime();
+      long expires = df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime();
+      Assert.assertTrue("Expires-Header passt nicht zum Date-Header! Unterschied: " + (expires-date)/1000 + " Sekunden.", date + 3600000 == expires);
+    }
+  }
+
+  @Test
+  public void testForwardWithoutGzip() throws Exception {
+
+    log.info("-------- Test: uncompressed request with forward");
+
+    client.getClientProperties().setAcceptGzip(false);
+
+    for (int i=0; i<33; i++) {
+      /** 33 requests ranging from 0 B to 4 KB - response ist buffered up to 1 KB */
+      StringBuilder uri = new StringBuilder();
+      uri.append("http://localhost/request-size");
+      uri.append("?n=");
+      uri.append(i*128);
+      uri.append("&f=");
+      uri.append(URLEncoder.encode("/forwarded?n=" + i*128, "UTF-8"));
+      try {
+        WebResponse response = executeRequest(uri.toString());
+        if (i*128 > LoggingHttpServletResponseWrapper.DEFAULT_BUFFER_SIZE)
+          Assert.fail("Error expected while forwarding after " + i*128 + " bytes written!");
+        Assert.assertEquals("W/\"Hallo Welt!\"", response.getHeaderField(Headers.HEADER_ETAG));
+        Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
+        Assert.assertNull(response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
+        Assert.assertEquals("max-age=3600", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
+        Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
+        Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
+        Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
+        SimpleDateFormat df = new SimpleDateFormat(Headers.RFC_1123_DATE_FORMAT, Locale.US);
+        long date = df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime();
+        long expires = df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime();
+        Assert.assertTrue("Expires-Header passt nicht zum Date-Header! Unterschied: " + (expires-date)/1000 + " Sekunden.", date + 3600000 == expires);
+      }
+      catch (IllegalStateException e) {
+        if (i*128 > LoggingHttpServletResponseWrapper.DEFAULT_BUFFER_SIZE)
+          log.debug("Expected error while forwarding after {} bytes written: {}", i*128, e.getMessage());
+        else
+          Assert.fail("Unexpected error while forwarding after " + i*128 + " bytes written: " + e.getMessage());
+      }
+    }
+  }
+
+  @Test
+  public void testIncludeWithGzip() throws Exception {
+
+    log.info("-------- Test: gzipped request with includes");
+
+    client.getClientProperties().setAcceptGzip(true);
+
+    for (int i=0; i<33; i++) {
+      /** 33 requests ranging from 0 B to 4 KB - response ist buffered up to 1 KB */
+      StringBuilder uri = new StringBuilder();
+      uri.append("http://localhost/request-size");
+      uri.append("?n=");
+      uri.append(i%7*128);
+      for (int j=0; j < i%4+1; j++) {
+        uri.append("&i=");
+        uri.append(URLEncoder.encode("/included?n=" + i*32*(4-j), "UTF-8"));
+      }
+      WebResponse response = executeRequest(uri.toString());
+      Assert.assertEquals("W/\"Hallo Welt!\"", response.getHeaderField(Headers.HEADER_ETAG));
+      Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
+      if (i==0)
+        Assert.assertNull(response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
+      else
+        Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
+      Assert.assertEquals("max-age=3600", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
+      Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
+      Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
+      Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
+      SimpleDateFormat df = new SimpleDateFormat(Headers.RFC_1123_DATE_FORMAT, Locale.US);
+      long date = df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime();
+      long expires = df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime();
+      Assert.assertTrue("Expires-Header passt nicht zum Date-Header! Unterschied: " + (expires-date)/1000 + " Sekunden.", date + 3600000 == expires);
+    }
+  }
+
+  @Test
+  public void testIncludeWithoutGzip() throws Exception {
+
+    log.info("-------- Test: uncompressed request with includes");
+
+    client.getClientProperties().setAcceptGzip(false);
+
+    for (int i=0; i<33; i++) {
+      /** 33 requests ranging from 0 B to 4 KB - response ist buffered up to 1 KB */
+      StringBuilder uri = new StringBuilder();
+      uri.append("http://localhost/request-size");
+      uri.append("?n=");
+      uri.append(i%7*128);
+      for (int j=0; j < i%4+1; j++) {
+        uri.append("&i=");
+        uri.append(URLEncoder.encode("/included?n=" + i*32*(4-j), "UTF-8"));
+      }
+      WebResponse response = executeRequest(uri.toString());
+      Assert.assertEquals("W/\"Hallo Welt!\"", response.getHeaderField(Headers.HEADER_ETAG));
+      Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
+      Assert.assertNull(response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
+      Assert.assertEquals("max-age=3600", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
+      Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
+      Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
+      Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
+      SimpleDateFormat df = new SimpleDateFormat(Headers.RFC_1123_DATE_FORMAT, Locale.US);
+      long date = df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime();
+      long expires = df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime();
+      Assert.assertTrue("Expires-Header passt nicht zum Date-Header! Unterschied: " + (expires-date)/1000 + " Sekunden.", date + 3600000 == expires);
+    }
+  }
+}
diff --git a/accelerator/src/test/java/de/juplo/accelerator/TestServlet.java b/accelerator/src/test/java/de/juplo/accelerator/TestServlet.java
new file mode 100644 (file)
index 0000000..769269c
--- /dev/null
@@ -0,0 +1,81 @@
+package de.juplo.accelerator;
+
+import java.io.IOException;
+import java.util.Map;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.junit.Ignore;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ *
+ * @author kai
+ */
+@Ignore
+public class TestServlet extends HttpServlet {
+  private final static Logger log = LoggerFactory.getLogger(TestServlet.class);
+
+  private static final String FORWARDED = TestServlet.class.getName() + ".FORWARDED";
+  private static final String INCLUDED = TestServlet.class.getName() + ".INCLUDED";
+
+  @Override
+  protected long getLastModified(HttpServletRequest req) {
+    try {
+      /** Der Reqeust-Parameter "lm" wird als Wert für Last-Modified zurückgegeben */
+      return Long.parseLong(req.getParameter("l"));
+    }
+    catch (Exception e) {
+      return -1l;
+    }
+  }
+
+  @Override
+  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+    /** Angeforderte Header setzen */
+    Map<String, String[]> map = request.getParameterMap();
+    for (String param : map.keySet()) {
+      if (param.equals("n") || param.equals("l") || param.equals("f") || param.equals("i"))
+        continue;
+      /**
+       * Alle Request-Parameter außer die Sonder-Parameter "n" und "lm"
+       * werden als Schlüssel/Wert-Paare für die Antwort-Header interpretiert!
+       */
+      for (String value : map.get(param))
+        response.setHeader(param, value);
+    }
+
+    int n = 0;
+    try {
+      /**
+       * Wenn der Parameter n gesetzt ist, wird ein Antwort-Body erzeugt, der
+       * exakt die Anzahl der geforderten Bytes enthält.
+       */
+      n = Integer.parseInt(request.getParameter("n"));
+    }
+    catch(Exception e) {}
+    log.debug("GET {} bytes: {}", n, request.getRequestURI());
+    ServletOutputStream out = response.getOutputStream();
+    for (int i=0; i<n; i++)
+      out.write(i%2 + 48); /** ASCII-Codes für "0" und "1" */
+
+    if (request.getParameter("i") != null && request.getAttribute(INCLUDED) == null) {
+      for (String include : request.getParameterValues("i")) {
+        RequestDispatcher dispatcher = request.getRequestDispatcher(include);
+        request.setAttribute(INCLUDED, include);
+        dispatcher.include(request, response);
+      }
+    }
+
+    if (request.getParameter("f") != null && request.getAttribute(FORWARDED) == null) {
+      String forward = request.getParameter("f");
+      request.setAttribute(FORWARDED, forward);
+      RequestDispatcher dispatcher = request.getRequestDispatcher(forward);
+      dispatcher.forward(request, response);
+    }
+  }
+}
diff --git a/accelerator/src/test/resources/config.xml b/accelerator/src/test/resources/config.xml
new file mode 100644 (file)
index 0000000..0b4ebb5
--- /dev/null
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:context="http://www.springframework.org/schema/context"
+       xsi:schemaLocation="
+           http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
+           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
+
+  <!-- Activates the AspectJ-Weaver -->
+  <context:component-scan base-package="de.juplo"/>
+  <context:spring-configured/>
+  <!--<context:load-time-weaver/>-->
+
+  <bean id="eTag" class="java.lang.String">
+    <constructor-arg value="Hallo Welt!"/>
+  </bean>
+
+  <bean id="weak" class="java.lang.Boolean">
+    <constructor-arg value="true"/>
+  </bean>
+
+  <bean id="lastModified" class="java.lang.Long">
+    <constructor-arg value="0"/>
+  </bean>
+
+  <bean id="cacheSeconds" class="java.lang.Integer">
+    <constructor-arg value="3600"/>
+  </bean>
+
+</beans>
diff --git a/accelerator/src/test/resources/log4j.xml b/accelerator/src/test/resources/log4j.xml
new file mode 100644 (file)
index 0000000..16e81f0
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+  
+  <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
+    <layout class="org.apache.log4j.PatternLayout">
+      <param name="ConversionPattern"
+                value="%p - %C{1}.%M(%L) | %m%n"/>
+    </layout>
+  </appender>
+  
+  <logger name="de.juplo">
+    <level value="debug" />
+  </logger>
+
+  <root>
+    <level value="INFO"/>
+    <appender-ref ref="CONSOLE"/>
+  </root>
+  
+</log4j:configuration>
diff --git a/accelerator/src/test/resources/web.xml b/accelerator/src/test/resources/web.xml
new file mode 100644 (file)
index 0000000..01ce9b7
--- /dev/null
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+
+  <filter>
+    <filter-name>accelerator</filter-name>
+    <filter-class>de.juplo.accelerator.AcceleratorFilter</filter-class>
+  </filter>
+
+  <filter-mapping>
+    <filter-name>accelerator</filter-name>
+    <url-pattern>/*</url-pattern>
+  </filter-mapping>
+
+  <servlet>
+    <servlet-name>test-servlet</servlet-name>
+    <servlet-class>de.juplo.accelerator.TestServlet</servlet-class>
+  </servlet>
+
+  <servlet-mapping>
+    <servlet-name>test-servlet</servlet-name>
+    <url-pattern>/*</url-pattern>
+  </servlet-mapping>
+
+</web-app>
diff --git a/cachecontrol/pom.xml b/cachecontrol/pom.xml
deleted file mode 100644 (file)
index 686432e..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-
-  <modelVersion>4.0.0</modelVersion>
-
-  <parent>
-    <groupId>de.halbekunst</groupId>
-    <artifactId>juplo</artifactId>
-    <version>2.0-SNAPSHOT</version>
-  </parent>
-
-  <artifactId>${pom.parent.artifactId}-cachecontrol</artifactId>
-  <name>Juplo - CacheControl</name>
-
-  <dependencies>
-    <dependency>
-      <groupId>org.springframework</groupId>
-      <artifactId>spring-webmvc</artifactId>
-      <version>${springframework.version}</version>
-      <scope>provided</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.springframework</groupId>
-      <artifactId>spring-aspects</artifactId>
-      <version>${springframework.version}</version>
-      <scope>provided</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.springframework</groupId>
-      <artifactId>spring-tx</artifactId>
-      <version>${springframework.version}</version>
-      <scope>provided</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.aspectj</groupId>
-      <artifactId>aspectjrt</artifactId>
-      <version>${aspectj.version}</version>
-      <scope>provided</scope>
-    </dependency>
-    <dependency>
-      <groupId>javax.servlet</groupId>
-      <artifactId>servlet-api</artifactId>
-      <version>${servlet-api.version}</version>
-      <scope>provided</scope>
-    </dependency>
-    <dependency>
-      <groupId>javax.persistence</groupId>
-      <artifactId>persistence-api</artifactId>
-      <version>${jpa.version}</version>
-      <scope>provided</scope>
-    </dependency>
-    <dependency>
-      <groupId>${pom.parent.groupId}</groupId>
-      <artifactId>${pom.parent.artifactId}-test</artifactId>
-      <version>${pom.parent.version}</version>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.slf4j</groupId>
-      <artifactId>${slf4j.binding}</artifactId>
-      <version>${slf4j.version}</version>
-      <scope>test</scope>
-    </dependency>
-  </dependencies>
-
-  <build>
-    <plugins>
-      <plugin>
-        <groupId>org.codehaus.mojo</groupId>
-        <artifactId>aspectj-maven-plugin</artifactId>
-        <configuration>
-          <complianceLevel>1.6</complianceLevel>
-          <aspectLibraries>
-            <aspectLibrary>
-              <groupId>org.springframework</groupId>
-              <artifactId>spring-aspects</artifactId>
-            </aspectLibrary>
-          </aspectLibraries>
-        </configuration>
-        <executions>
-          <execution>
-            <goals>
-              <goal>compile</goal>
-            </goals>
-          </execution>
-        </executions>
-      </plugin>
-    </plugins>
-  </build>
-
-</project>
diff --git a/cachecontrol/src/main/java/de/halbekunst/juplo/cachecontrol/AcceleratorFilter.java b/cachecontrol/src/main/java/de/halbekunst/juplo/cachecontrol/AcceleratorFilter.java
deleted file mode 100644 (file)
index 8e63222..0000000
+++ /dev/null
@@ -1,724 +0,0 @@
-package de.halbekunst.juplo.cachecontrol;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
-import java.util.zip.GZIPOutputStream;
-import javax.servlet.Filter;
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletException;
-import javax.servlet.ServletOutputStream;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.Cookie;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowire;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Configurable;
-import org.springframework.beans.factory.annotation.Qualifier;
-
-
-
-/**
- *
- * @author kai
- */
-@Configurable(autowire=Autowire.BY_NAME)
-public class AcceleratorFilter implements Filter {
-  private final static Logger log = LoggerFactory.getLogger(AcceleratorFilter.class);
-
-  private final static Map<String,String> EMPTY = Collections.unmodifiableMap(new HashMap<String,String>());
-
-  public final static Integer DEFAULT_BUFFER_SIZE = 1024;
-  public final static String REQUEST_URI_ATTRIBUTE = "javax.servlet.include.request_uri";
-  public final static String RESPONSE_WRAPPER = AcceleratorFilter.class.getName() + ".RESPONSE_WRAPPER";
-
-
-  @Autowired CacheControl cacheControl;
-  @Autowired(required=false) @Qualifier("defaultBufferSize") Integer defaultBufferSize = DEFAULT_BUFFER_SIZE;
-  @Autowired String eTag;
-  @Autowired Boolean weak;
-  @Autowired Long lastModified;
-  @Autowired Integer cacheSeconds;
-
-
-  @Override
-  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
-    if (!(request instanceof HttpServletRequest)) {
-      log.error("AcceleratorFilter can only handle HTTP-requests");
-      chain.doFilter(request, response);
-      return;
-    }
-
-    HttpServletRequest httpRequest = (HttpServletRequest)request;
-    HttpServletResponse httpResponse = (HttpServletResponse)response;
-
-    AccelerationWrapper wrapper;
-
-    wrapper = (AccelerationWrapper)request.getAttribute(AcceleratorFilter.RESPONSE_WRAPPER);
-    if (wrapper != null) {
-      if (wrapper.getFilter() == this) {
-        /** Ignore multiple mappings of the same filter-instance */
-        log.warn("Ignoring multiple mappings on same URL: {}", httpRequest.getRequestURI());
-        chain.doFilter(request, response);
-        return;
-      }
-      else {
-        log.error("Only one instance of AcceleratorFilter must be mapped to any URL: {}", httpRequest.getRequestURI());
-        httpResponse.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Only one instance of AcceleratorFilter must be mapped to any URL!");
-        return;
-      }
-    }
-
-    wrapper = new AccelerationWrapper(httpRequest, httpResponse);
-    httpRequest.setAttribute(RESPONSE_WRAPPER, wrapper);
-    cacheControl.init(wrapper);
-    try {
-      chain.doFilter(request, wrapper);
-      wrapper.finish();
-    }
-    catch (NotModifiedException nm) {
-      log.trace("Not modified: {}", httpRequest.getRequestURI());
-    }
-  }
-
-  @Override
-  public void init(FilterConfig filterConfig) throws ServletException {
-  }
-
-  @Override
-  public void destroy() {
-  }
-
-
-  class AccelerationWrapper implements HttpServletResponse, CacheMethodHandle {
-
-    private final HttpServletRequest request;
-    private final HttpServletResponse response;
-
-    private final AcceleratorServletOutputStream out;
-
-    private ServletOutputStream stream;
-    private PrintWriter writer;
-
-    private boolean guessing = true;
-    protected boolean zipped = false; // << CacheControll greift direkt auf diese Variable zu!
-
-    private long now;
-    private int status;
-    private int cacheSeconds;
-    private boolean cacheSecondsSet = false;
-    private long lastModified, expires = 0l;
-    private String eTag;
-    private boolean weak;
-    private Map<String,String> cacheParams;
-
-    /** Für den AcceleratorOutputStream */
-    private boolean committed = false;
-    private OutputStream os = null;
-    private int bufferSize;
-    private byte[] buffer;
-    private int pos = 0;
-    private int size = 0;
-
-
-
-    AccelerationWrapper(HttpServletRequest request, HttpServletResponse response) throws IOException {
-
-      this.request = request;
-      this.response = response;
-
-      now = System.currentTimeMillis();
-      status = HttpServletResponse.SC_OK;
-      cacheSeconds = AcceleratorFilter.this.cacheSeconds;
-      lastModified = AcceleratorFilter.this.lastModified;
-      eTag = AcceleratorFilter.this.eTag;
-      weak = AcceleratorFilter.this.weak;
-      cacheParams = new HashMap<String,String>();
-
-      Enumeration values = request.getHeaders(Headers.HEADER_ACCEPT_ENCODING);
-      while (values.hasMoreElements()) {
-        String value = (String) values.nextElement();
-        if (value.indexOf("gzip") != -1) {
-          zipped = true;
-          break;
-        }
-      }
-
-      out = new AcceleratorServletOutputStream();
-    }
-
-
-    private AcceleratorFilter getFilter() {
-      return AcceleratorFilter.this;
-    }
-
-    private void finish() throws IOException {
-      flushBuffer();
-      out.close();
-    }
-
-    @Override
-    public void setStatus(int sc) {
-      response.setStatus(sc);
-      status = sc;
-    }
-
-    @Override
-    public void setStatus(int sc, String sm) {
-      response.setStatus(sc,sm);
-      status = sc;
-    }
-
-    @Override
-    public void addDateHeader(String name, long value) {
-
-      if (!guessing) {
-        response.addDateHeader(name, value);
-        return;
-      }
-
-      if (Headers.HEADER_DATE.equalsIgnoreCase(name)) {
-        now = value;
-        calculateCacheSeconds();
-        return;
-      }
-
-      if (Headers.HEADER_EXPIRES.equalsIgnoreCase(name)) {
-        expires = value;
-        calculateCacheSeconds();
-        return;
-      }
-
-      if (Headers.HEADER_LAST_MODIFIED.equalsIgnoreCase(name)) {
-        lastModified = value;
-        return;
-      }
-
-      /** Unknown header: pass throug! */
-      response.addDateHeader(name, value);
-    }
-
-    @Override
-    public void addHeader(String name, String value) {
-
-      if (!guessing) {
-        response.addHeader(name, value);
-        return;
-      }
-
-      if (value == null)
-        return;
-      analyzeHeader(name, value, false);
-    }
-
-    @Override
-    public void addIntHeader(String name, int value) {
-
-      if (!guessing) {
-        response.addIntHeader(name, value);
-        return;
-      }
-
-      analyzeHeader(name, Integer.toString(value), false);
-    }
-
-    @Override
-    public void setDateHeader(String name, long value) {
-
-      if (!guessing) {
-        response.setDateHeader(name, value);
-        return;
-      }
-
-      if (Headers.HEADER_DATE.equalsIgnoreCase(name)) {
-        now = value;
-        calculateCacheSeconds();
-        return;
-      }
-
-      if (Headers.HEADER_EXPIRES.equalsIgnoreCase(name)) {
-        expires = value;
-        calculateCacheSeconds();
-        return;
-      }
-
-      if (Headers.HEADER_LAST_MODIFIED.equalsIgnoreCase(name)) {
-        lastModified = value;
-        return;
-      }
-
-      /** Unknown header: pass throug! */
-      response.setDateHeader(name, value);
-    }
-
-    @Override
-    public void setHeader(String name, String value) {
-
-      if (!guessing) {
-        response.setHeader(name, value);
-        return;
-      }
-
-      analyzeHeader(name, value, true);
-    }
-
-    @Override
-    public void setIntHeader(String name, int value) {
-
-      if (!guessing) {
-        response.setIntHeader(name, value);
-        return;
-      }
-
-      analyzeHeader(name, Integer.toString(value), true);
-    }
-
-    @Override
-    public ServletOutputStream getOutputStream() throws IOException {
-
-      if (writer != null)
-        throw new IllegalStateException("ServletOutputStream and PrintWriter cannot be requested both!");
-
-      if (stream == null) {
-        stream = out;
-      }
-
-      return out;
-    }
-
-    @Override
-    public PrintWriter getWriter() throws IOException {
-
-      if (stream != null)
-        throw new IllegalStateException("ServletOutputStream and PrintWriter cannot be requested both!");
-
-      if (writer == null) {
-        writer = new PrintWriter(new OutputStreamWriter(out, response.getCharacterEncoding()));
-      }
-
-      return writer;
-    }
-
-    @Override
-    public void setContentLength(int len) {
-      if (zipped)
-        log.info("Supressing explicit content-length {} for request {}, because content will be zipped!", len, request.getRequestURI());
-      else
-        response.setContentLength(len);
-    }
-
-    @Override
-    public int getBufferSize() {
-      return bufferSize;
-    }
-
-    @Override
-    public void setBufferSize(int size) {
-      if (this.size > 0)
-        throw new IllegalStateException("cannot change buffer size, because content was already written!");
-      bufferSize = size;
-      buffer = new byte[size];
-    }
-
-    @Override
-    public void resetBuffer() {
-      if (committed)
-        throw new IllegalStateException("cannot reset buffer, because response is already commited!");
-      pos = 0;
-      stream = null;
-      writer = null;
-    }
-
-    @Override
-    public void reset() {
-      if (committed)
-        throw new IllegalStateException("cannot reset response, because response is already commited!");
-      /**
-       * Da committed==false gilt, wurde die Dekoration noch nicht angestßen
-       * und muss entsprechend auch nicht rückgängig gemacht werden!
-       */
-      response.reset();
-      pos = 0;
-      size = 0;
-      stream = null;
-      writer = null;
-    }
-
-    @Override
-    public void flushBuffer() throws IOException {
-      if (writer != null)
-        writer.flush();
-      else if (stream != null)
-        stream.flush();
-    }
-
-    @Override
-    public void addCookie(Cookie cookie) {
-      // TODO: Je nach Vary-Einstellung ETag anpassen?
-      response.addCookie(cookie);
-    }
-
-    @Override
-    public boolean containsHeader(String name) {
-      return response.containsHeader(name);
-    }
-
-    @Override
-    public String encodeURL(String url) {
-      return response.encodeURL(url);
-    }
-
-    @Override
-    public String encodeRedirectURL(String url) {
-      return response.encodeRedirectURL(url);
-    }
-
-    @Override
-    public String encodeUrl(String url) {
-      return response.encodeUrl(url);
-    }
-
-    @Override
-    public String encodeRedirectUrl(String url) {
-      return response.encodeRedirectUrl(url);
-    }
-
-    @Override
-    public void sendError(int sc, String msg) throws IOException {
-      response.sendError(sc,msg);
-    }
-
-    @Override
-    public void sendError(int sc) throws IOException {
-      response.sendError(sc);
-    }
-
-    @Override
-    public void sendRedirect(String location) throws IOException {
-      response.sendRedirect(location);
-    }
-
-    @Override
-    public String getCharacterEncoding() {
-      return response.getCharacterEncoding();
-    }
-
-    @Override
-    public String getContentType() {
-      return response.getContentType();
-    }
-
-    @Override
-    public void setCharacterEncoding(String charset) {
-      // TODO: Je nach Vary-Einstellung ETag anpassen?
-      response.setCharacterEncoding(charset);
-    }
-
-    @Override
-    public void setContentType(String type) {
-      // TODO: Je nach Vary-Einstellung ETag anpassen?
-      response.setContentType(type);
-    }
-
-    @Override
-    public boolean isCommitted() {
-      return committed;
-    }
-
-    @Override
-    public void setLocale(Locale loc) {
-      // TODO: Je nach Vary-Einstellung ETag anpassen?
-      response.setLocale(loc);
-    }
-
-    @Override
-    public Locale getLocale() {
-      return getLocale();
-    }
-
-
-
-    @Override
-    public boolean isZipped() {
-      return zipped;
-    }
-
-    @Override
-    public long getTimestamp() {
-      return now;
-    }
-
-    @Override
-    public int accepts(HttpServletRequest request) {
-      return status;
-    }
-
-    @Override
-    public int getCacheSeconds(HttpServletRequest request) {
-      return cacheSeconds;
-    }
-
-    @Override
-    public long getLastModified(HttpServletRequest request) {
-      return lastModified;
-    }
-
-    @Override
-    public String getETag(HttpServletRequest request) {
-      return eTag;
-    }
-
-    @Override
-    public boolean isETagWeak() {
-      return weak;
-    }
-
-    @Override
-    public void cacheControl(HttpServletRequest request, Map<String, String> cacheControlMap) {
-      cacheControlMap.putAll(cacheParams);
-    }
-
-    @Override
-    public Map<String,String> getAdditionalHeaders(HttpServletRequest request) {
-      return EMPTY;
-    }
-
-    public void supressGuessing() {
-      guessing = false;
-    }
-
-
-    private void analyzeHeader(String name, String value, boolean overwrite) {
-      if (name == null)
-        return;
-      name = name.trim();
-
-      if (name.equalsIgnoreCase(Headers.HEADER_DATE)) {
-        if (value == null) {
-          if (overwrite) {
-            now = System.currentTimeMillis();
-            cacheSeconds = AcceleratorFilter.this.cacheSeconds;
-          }
-          return;
-        }
-        try {
-          SimpleDateFormat parser = new SimpleDateFormat(Headers.RFC_1123_DATE_FORMAT, Locale.US);
-          now = parser.parse(value).getTime();
-          calculateCacheSeconds();
-        }
-        catch (ParseException e) {
-          log.warn("ignoring date for header \"Date\" in invalid format: {}", value);
-        }
-        return;
-      }
-
-      if (name.equalsIgnoreCase(Headers.HEADER_EXPIRES)) {
-        if (value == null) {
-          if (overwrite) {
-            expires = 0;
-            cacheSeconds = AcceleratorFilter.this.cacheSeconds;
-          }
-          return;
-        }
-        try {
-          SimpleDateFormat parser = new SimpleDateFormat(Headers.RFC_1123_DATE_FORMAT, Locale.US);
-          expires = parser.parse(value).getTime();
-          calculateCacheSeconds();
-        }
-        catch (ParseException e) {
-          log.warn("ignoring date for header \"Expires\" in invalid format: {}", value);
-        }
-        return;
-      }
-
-      if (name.equalsIgnoreCase(Headers.HEADER_LAST_MODIFIED)) {
-        if (value == null) {
-          if (overwrite)
-            lastModified = AcceleratorFilter.this.lastModified;
-          return;
-        }
-        try {
-          SimpleDateFormat parser = new SimpleDateFormat(Headers.RFC_1123_DATE_FORMAT, Locale.US);
-          lastModified = parser.parse(value).getTime();
-        }
-        catch (ParseException e) {
-          log.warn("ignoring date for header \"Last-Modified\" in invalid format: {}", value);
-        }
-        return;
-      }
-
-      if (name.equalsIgnoreCase(Headers.HEADER_ETAG)) {
-        if (value == null) {
-          if (overwrite) {
-            eTag = AcceleratorFilter.this.eTag;
-            weak = AcceleratorFilter.this.weak;
-          }
-          return;
-        }
-        value = value.trim();
-        int start = 0;
-        int end = value.length();
-        if (value.startsWith("W/")) {
-          weak = true;
-          start = 2;
-        }
-        else {
-          weak = false;
-        }
-        if (value.charAt(start) == '"')
-          start++;
-        else
-          log.warn("Quote at the beginning ov ETag is missing: {}", value);
-        if (value.charAt(end -1) == '"')
-          end--;
-        else
-          log.warn("Quote at the end of ETag is missing: {}", value);
-        eTag = value.substring(start, end);
-        String filtered = eTag.replaceAll("[^\\x00-\\x21\\x23-\\x7F]+","");
-        if (filtered.length() < eTag.length()) {
-          log.warn("filtering out illegal characters in ETag: \"{}\" -> \"{}\"", eTag, filtered);
-          eTag = filtered;
-        }
-      }
-
-      if (name.equalsIgnoreCase(Headers.HEADER_CACHE_CONTROL)) {
-        if (overwrite)
-          cacheParams.clear();
-        if (value == null)
-          return;
-        for (String param : value.split(",")) {
-          param = param.trim();
-          int pos = param.indexOf("=");
-          if (pos < 0) {
-            cacheParams.put(param, null);
-          }
-          else {
-            String paramName = param.substring(0, pos).trim();
-            if (paramName.equalsIgnoreCase("max-age")) {
-              try {
-                cacheSeconds = Integer.parseInt(param.substring(pos + 1));
-                cacheSecondsSet = true;
-              }
-              catch (NumberFormatException e) {
-                log.warn("illegal value for Header \"Cache-Control\":", param);
-              }
-            }
-            else {
-              cacheParams.put(paramName, param.substring(pos + 1));
-            }
-          }
-        }
-        return;
-      }
-
-      if (name.equalsIgnoreCase(Headers.HEADER_PRAGMA)) {
-        if (value != null && value.trim().equalsIgnoreCase("no-cache"))
-          cacheSeconds = 0;
-        return;
-      }
-
-      /** Pass header through, if no value from intrest was found */
-      if (overwrite)
-        response.setHeader(name, value);
-      else
-        response.addHeader(name, value);
-    }
-
-    private void calculateCacheSeconds() {
-      if (!cacheSecondsSet && expires >= now) {
-        cacheSeconds = (int)(expires/1000 - now/1000);
-        log.debug("calculating cache-seconds from DATE and EXPIRES: {}", cacheSeconds);
-      }
-    }
-
-
-    private class AcceleratorServletOutputStream extends ServletOutputStream  {
-
-      private final ServletOutputStream sos;
-
-
-      private AcceleratorServletOutputStream() throws IOException {
-        bufferSize = defaultBufferSize;
-        buffer = new byte[bufferSize];
-        sos = AccelerationWrapper.this.response.getOutputStream();
-      }
-
-
-      private OutputStream out() throws IOException {
-        if (os == null)
-          os = zipped ? new GZIPOutputStream(sos) : sos;
-        return os;
-      }
-
-      @Override
-      public void write(int i) throws IOException {
-        if (pos == bufferSize) {
-          out().write(buffer);
-          committed = true;
-          /** Dekoration nur beim ersten Schreib-Schub anstoßen */
-          if (pos == size) {
-            if (!cacheControl.decorate(request, response)) {
-              zipped = false;
-              os = null;
-              pos = 0;
-              throw new NotModifiedException();
-            }
-          }
-          pos = 0;
-        }
-        buffer[pos++] = (byte) i;
-        size++;
-      }
-
-      @Override
-      public void flush() throws IOException {
-        if (pos == 0)
-          return;
-
-        committed = true;
-        /** Dekoration nur beim ersten Schreib-Schub anstoßen */
-        if (pos == size) {
-          if (!cacheControl.decorate(request, response)) {
-            zipped = false;
-            os = null;
-            pos = 0;
-            throw new NotModifiedException();
-          }
-        }
-        out().write(buffer, 0, pos);
-        out().flush();
-        pos = 0;
-      }
-
-      @Override
-      public void close() throws IOException {
-        if (size == 0) {
-          committed = true;
-          zipped = false;
-          if (!cacheControl.decorate(request, response))
-            throw new NotModifiedException();
-          sos.close();
-        }
-        else {
-          flush();
-          out().close();
-        }
-      }
-    }
-  }
-}
-
-class NotModifiedException extends IOException {}
\ No newline at end of file
diff --git a/cachecontrol/src/main/java/de/halbekunst/juplo/cachecontrol/CacheControl.java b/cachecontrol/src/main/java/de/halbekunst/juplo/cachecontrol/CacheControl.java
deleted file mode 100644 (file)
index 458979d..0000000
+++ /dev/null
@@ -1,548 +0,0 @@
-package de.halbekunst.juplo.cachecontrol;
-
-import de.halbekunst.juplo.cachecontrol.AcceleratorFilter.AccelerationWrapper;
-import de.halbekunst.juplo.cachecontrol.annotations.CacheSeconds;
-import de.halbekunst.juplo.cachecontrol.annotations.Accepts;
-import de.halbekunst.juplo.cachecontrol.annotations.AdditionalHeaders;
-import de.halbekunst.juplo.cachecontrol.annotations.LastModified;
-import de.halbekunst.juplo.cachecontrol.annotations.ETag;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Method;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.TreeMap;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.stereotype.Component;
-
-/**
- *
- * @author kai
- */
-@Component
-public class CacheControl {
-  private final static Logger log = LoggerFactory.getLogger(CacheControl.class);
-
-  private static final ThreadLocal<CacheMethodHandle> tl = new ThreadLocal<CacheMethodHandle>();
-
-  @Autowired @Qualifier("cacheSeconds") private Integer defaultCacheSeconds;
-  @Autowired @Qualifier("lastModified") private Long defaultLastModified;
-
-
-  public void init(CacheMethodHandle handle) {
-    CacheControl.tl.set(handle);
-  }
-
-  void init(Object handler, AccelerationWrapper wrapper) throws NoSuchMethodException {
-    CacheControl.tl.set(new ReflectionCacheMethodHandle(handler, wrapper == null ? false : wrapper.zipped));
-  }
-
-  public boolean decorate(
-      HttpServletRequest request,
-      HttpServletResponse response
-      )
-  {
-    try {
-    CacheMethodHandle handle = CacheControl.tl.get();
-
-    /** Doppelte Ausführung verhindern... */
-    if (handle == null) {
-      /** Dekoration wurde bereits durchgeführt! */
-      return true;
-    }
-
-    /**
-     * Alle Antworten (insbesondere auch 304) sollen nach dem {@plainlink
-     * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.18 RFC 2616,
-     * Abschnitt 14.18} einen Date-Header enthalten
-     */
-    response.setDateHeader(Headers.HEADER_DATE, handle.getTimestamp());
-
-    /** Besondere Maßnahmen für besondere HTTP-Status-Codes ?!? */
-    int status = handle.accepts(request);
-    switch (status) {
-      case HttpServletResponse.SC_OK: // 200
-      case HttpServletResponse.SC_NO_CONTENT: // 204
-      case HttpServletResponse.SC_PARTIAL_CONTENT: // 206
-        /** Normale Antwort! Antwort dekorieren... */
-        break;
-      case HttpServletResponse.SC_MOVED_PERMANENTLY: // 301
-      case HttpServletResponse.SC_MOVED_TEMPORARILY: // 302
-      case HttpServletResponse.SC_SEE_OTHER: // 303
-      case HttpServletResponse.SC_NOT_MODIFIED: // 304
-      case HttpServletResponse.SC_USE_PROXY: // 305
-      case HttpServletResponse.SC_TEMPORARY_REDIRECT: // 307
-        /** Redirect-Antwort! Antwort dekodieren... */
-        // TODO: Kann das wirklich nicht zu Protokoll-Verletzungen führen?
-        break;
-      case HttpServletResponse.SC_BAD_REQUEST: // 400
-      case HttpServletResponse.SC_UNAUTHORIZED: // 401
-      case HttpServletResponse.SC_PAYMENT_REQUIRED: // 402
-      case HttpServletResponse.SC_FORBIDDEN: // 403
-      case HttpServletResponse.SC_NOT_FOUND: // 404
-      case HttpServletResponse.SC_METHOD_NOT_ALLOWED: // 405
-      case HttpServletResponse.SC_NOT_ACCEPTABLE: // 406
-      case HttpServletResponse.SC_PROXY_AUTHENTICATION_REQUIRED: // 407
-      case HttpServletResponse.SC_REQUEST_TIMEOUT: // 408
-      case HttpServletResponse.SC_CONFLICT: // 409
-      case HttpServletResponse.SC_GONE: // 410
-      case HttpServletResponse.SC_LENGTH_REQUIRED: // 411
-      case HttpServletResponse.SC_PRECONDITION_FAILED: // 412
-      case HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE: // 413
-      case HttpServletResponse.SC_REQUEST_URI_TOO_LONG: // 414
-      case HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE: // 415
-      case HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE: // 416
-      case HttpServletResponse.SC_INTERNAL_SERVER_ERROR: // 500
-      case HttpServletResponse.SC_NOT_IMPLEMENTED: // 501
-      case HttpServletResponse.SC_SERVICE_UNAVAILABLE: // 503
-      case HttpServletResponse.SC_HTTP_VERSION_NOT_SUPPORTED: // 505
-      default:
-        /**
-         * Es ist nicht klar, was der Handler noch machen wird/muss:
-         * Antwort nicht dekorieren und Kontroller an den Handler übergeben...
-         */
-        return true;
-    }
-
-    Map<String,String> headers = handle.getAdditionalHeaders(request);
-    for (String name : headers.keySet())
-      response.addHeader(name, headers.get(name));
-
-    String url = null;
-    if (log.isDebugEnabled()) {
-      if (request.getQueryString() == null) {
-        url = request.getRequestURI();
-      }
-      else {
-        StringBuilder builder = new StringBuilder();
-        builder.append(request.getRequestURI());
-        builder.append('?');
-        builder.append(request.getQueryString());
-        url = builder.toString();
-      }
-    }
-
-    int cacheSeconds = handle.getCacheSeconds(request);
-    if (cacheSeconds < 0) {
-      log.debug("{}: caching disabled!", url);
-      response.setDateHeader(Headers.HEADER_DATE, handle.getTimestamp());
-      response.setDateHeader(Headers.HEADER_EXPIRES, 0);
-      response.addHeader(Headers.HEADER_PRAGMA, "no-cache");
-      response.addHeader(Headers.HEADER_CACHE_CONTROL, "private");
-      response.addHeader(Headers.HEADER_CACHE_CONTROL, "no-cache");
-      response.addHeader(Headers.HEADER_CACHE_CONTROL, "no-store");
-      response.addHeader(Headers.HEADER_CACHE_CONTROL, "max-age=0");
-      response.addHeader(Headers.HEADER_CACHE_CONTROL, "s-max-age=0");
-      if (handle.isZipped())
-        response.addHeader(Headers.HEADER_CONTENT_ENCODING, "gzip");
-      return true;
-    }
-
-    long ifModifiedSince = -1;
-    try {
-      ifModifiedSince = request.getDateHeader(Headers.HEADER_IF_MODIFIED_SINCE);
-    }
-    catch (Exception e) {
-      log.error("Exception while fetching If-Modified-Since: {}", e);
-    }
-
-    long lastModified = handle.getLastModified(request);
-
-    /**
-     * Sicherstellen, dass der Wert keine Millisekunden enthält, da die
-     * Zeitangabe aus dem Modified-Since-Header keine Millisekunden enthalten
-     * kann und der Test unten dann stets fehlschlagen würde!
-     */
-    lastModified = lastModified - (lastModified % 1000);
-
-    String ifNoneMatch = request.getHeader(Headers.HEADER_IF_NONE_MATCH);
-    String eTag = handle.getETag(request);
-
-    /**
-     * 304-Antworten sollen nach dem {@plainlink
-     * http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5 RFC
-     * 2616, Abschnitt 10.3.5} einen ETag-Header enthalten, wenn auch die
-     * 200-Antwort einen enthalten hätte.
-     */
-    if (eTag != null) {
-      StringBuilder builder = new StringBuilder();
-      if (handle.isETagWeak())
-        builder.append("W/");
-      builder.append('"');
-      builder.append(eTag);
-      builder.append('"');
-      response.setHeader(Headers.HEADER_ETAG, builder.toString());
-    }
-
-
-    if (ifModifiedSince >= lastModified && lastModified > 0) {
-      /**
-       * request.getDateHeader liefert die Zeit als long, oder -1, wenn der
-       * Header nicht existiert. D.h., wenn "If-Modified-Since" nicht gesetzt
-       * ist, wird die komplette Seite ausgeliefert.
-       * Der zusätzliche Test, ob lastModified größer 0 ist, ist nötig, um
-       * Fehler auszuschließen, wenn die Implementierung von Cachable
-       * negative Werte für Last-Modified zurückliefert.
-       */
-      if (log.isDebugEnabled())
-        log.debug("{}: Not modified since {}", url, new Date(ifModifiedSince));
-
-      if (ifNoneMatch == null) {
-        /** Neue Anfrage oder HTTP/1.0 Client! */
-        log.debug("{}: ETag nicht gesetzt -> 304", url);
-        response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
-        return false;
-      }
-    }
-
-    if (ifNoneMatch != null) {
-      boolean weak = false;
-      if (ifNoneMatch.startsWith("W/")) {
-        weak = true;
-        ifNoneMatch = ifNoneMatch.substring(3, ifNoneMatch.length() - 1);
-      }
-      else {
-        ifNoneMatch = ifNoneMatch.substring(1, ifNoneMatch.length() - 1);
-      }
-
-      if (!weak || (request.getMethod().equals("GET") && request.getHeader(Headers.HEADER_RANGE) == null)) {
-        /**
-         * Die Gleichheit gilt nur, wenn die ETag's der Anfrage _und_ der
-         * Antwort stark sind (starke Gleichheit!), oder wenn die Antwort nur
-         * schwache Gleichheit fordert...
-         */
-        if (ifNoneMatch.equals(eTag) && (handle.isETagWeak() || !weak)) {
-          log.debug("{}: ETag {} not changed -> 304 ", url, ifNoneMatch);
-          response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
-          return false;
-        }
-      }
-      else {
-        log.warn("{}: ignoring weak ETag W/\"{}\", because the request was no GET-request or the Range-Header was present!", url, ifNoneMatch);
-      }
-    }
-
-
-    log.debug("{}: first up!", url);
-
-    if (handle.isZipped())
-      response.addHeader(Headers.HEADER_CONTENT_ENCODING, "gzip");
-
-    /** HTTP/1.1-Caching-Header richtig setzen!! */
-    response.setDateHeader(Headers.HEADER_LAST_MODIFIED, lastModified);
-
-    /** Cache-Control für HTTP/1.1-Clients generieren */
-    Map<String, String> cacheControl = new TreeMap<String, String>();
-
-    /**
-     * Wenn eins JSESSIONID in der URL enthalten ist, darf die Anfrage nur vom
-     * Browser gecached werden!
-     */
-    if (request.isRequestedSessionIdFromURL()) {
-      cacheControl.put("private", null);
-    }
-    else {
-      /**
-       * Hier muss nicht geprüft werden, ob cacheSeconds > 0 gilt, da in diesem
-       * Fall oben bereits No-Cache-Header generiert und <code>false</code>
-       * zurückgeliefert werden!
-       *
-       * Den Wert als <code>max-age</code> zu den Schlüssel-Wert-Paaren für den
-       * <code>Cache-Control</code>-Header hinzufügen und einen entsprechenden
-       * <code>Expires</code>-Header für HTTP/1.0-Clients setzen.
-       */
-      cacheControl.put("max-age", Integer.toString(cacheSeconds));
-      response.setDateHeader(Headers.HEADER_EXPIRES, (handle.getTimestamp() + (long) cacheSeconds * 1000));
-    }
-
-    /** Dem Handler die Gelegenheit geben, den Cache-Controll-Header anzupassen */
-    handle.cacheControl(request, cacheControl);
-
-
-    if (cacheControl.containsKey("private")) {
-      /**
-       * HTTP/1.0 Caches davon abhalten, die Ressource zu cachen (vgl.: RFC
-       * 2616, {@plainlink
-       * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.3
-       * Abschnitt 14.9.3} und {@plainlink
-       * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.32
-       * Abschnitt 14.32})
-       */
-      response.setDateHeader(Headers.HEADER_EXPIRES, 0l);
-      response.addHeader(Headers.HEADER_PRAGMA, "no-cache");
-    }
-
-    StringBuilder builder = new StringBuilder();
-    for (Entry<String, String> entry : cacheControl.entrySet()) {
-      builder.setLength(0);
-      builder.append(entry.getKey());
-      if (entry.getValue() != null) {
-        builder.append('=');
-        builder.append(entry.getValue());
-      }
-      response.addHeader(Headers.HEADER_CACHE_CONTROL, builder.toString());
-    }
-
-    return true;
-    }
-    finally {
-      /**
-       * Thread-Locale-Variable zurücksetzen, damit
-       * 1.) ein doppelter Aufruf dieser Methode pro Request erkannt werden kann
-       * 2.) der nächste Request nicht mit dem selben Handle weiterarbeitet
-       */
-      CacheControl.tl.set(null);
-    }
-  }
-
-  public void release() {
-    CacheControl.tl.set(null);
-  }
-
-
-  class ReflectionCacheMethodHandle implements CacheMethodHandle {
-
-    private Object handler;
-    private long now = System.currentTimeMillis();
-    private Integer cacheSeconds;
-    private Long lastModified;
-    private String eTag;
-    private Map<String,String> additionalHeaders;
-    private Method acceptsMethod;
-    private Method cacheSecondsMethod;
-    private Method lastModifiedMethod;
-    private Method eTagMethod;
-    private Method cacheControlMethod;
-    private Method additionalHeadersMethod;
-    private boolean isAcceptsMethodDefined;
-    private boolean isCacheSecondsMethodDefined;
-    private boolean isLastModifiedMethodDefined;
-    private boolean isETagMethodDefined;
-    private boolean isCacheControlMethodDefined;
-    private boolean isAdditionalHeadersMethodDefined;
-    private boolean weak;
-    private boolean zipped;
-
-
-    ReflectionCacheMethodHandle(Object handler, boolean zipped) throws NoSuchMethodException {
-
-      this.handler = handler;
-      this.zipped = zipped;
-
-      cacheSeconds = CacheControl.this.defaultCacheSeconds;
-      lastModified = CacheControl.this.defaultLastModified;
-
-      /** Class-Level-Annotations auslesen */
-      for (Annotation annotation : handler.getClass().getAnnotations()) {
-        if (annotation.annotationType().equals(CacheSeconds.class)) {
-          cacheSeconds = ((CacheSeconds)annotation).value();
-          isCacheSecondsMethodDefined = true;
-          continue;
-        }
-        if (annotation.annotationType().equals(LastModified.class)) {
-          lastModified = ((LastModified)annotation).value();
-          if (lastModified < 1) {
-            /**
-             * Ein Last-Modified-Header wurde angefordert, aber es wurde kein
-             * statischer Wert spezifiziert:
-             * globalen statischen Default-Wert benutzen!
-             */
-            lastModified = defaultLastModified;
-          }
-          isLastModifiedMethodDefined = true;
-          continue;
-        }
-        if (annotation.annotationType().equals(ETag.class)) {
-          ETag eTagAnnotation = (ETag)annotation;
-          eTag = eTagAnnotation.value();
-          weak = eTagAnnotation.weak();
-          isETagMethodDefined = true;
-          continue;
-        }
-        if (annotation.annotationType().equals(AdditionalHeaders.class)) {
-          AdditionalHeaders additionalHeadersAnnotation = (AdditionalHeaders)annotation;
-          additionalHeaders = new HashMap<String,String>();
-          for (String header : additionalHeadersAnnotation.value()) {
-            int i = header.indexOf(':');
-            if (i < 0) {
-              log.error("invalid header: [{}]", header);
-            }
-            else {
-              String name = header.substring(0,i).trim();
-              String value = header.substring(i+1,header.length()).trim();
-              additionalHeaders.put(name, value);
-            }
-          }
-          isAdditionalHeadersMethodDefined = true;
-          continue;
-        }
-      }
-
-      /** Method-Level-Annotations auslesen */
-      for (Method method : handler.getClass().getMethods()) {
-        for (Annotation annotation : method.getAnnotations()) {
-          if (annotation.annotationType().equals(Accepts.class)) {
-            if (isAcceptsMethodDefined)
-              throw new IllegalArgumentException("Die Annotation @Accept wurde in der Klasse " + handler.getClass().getSimpleName() + " mehrfach verwendet!");
-            acceptsMethod = method;
-            isAcceptsMethodDefined = true;
-            continue;
-          }
-          if (annotation.annotationType().equals(CacheSeconds.class)) {
-            if (isCacheSecondsMethodDefined)
-              throw new IllegalArgumentException("Die Annotation @CacheSeconds wurde in der Klasse " + handler.getClass().getSimpleName() + " mehrfach verwendet!");
-            cacheSecondsMethod = method;
-            isCacheSecondsMethodDefined = true;
-            continue;
-          }
-          if (annotation.annotationType().equals(LastModified.class)) {
-            if (isLastModifiedMethodDefined)
-              throw new IllegalArgumentException("Die Annotation @LastModified wurde in der Klasse " + handler.getClass().getSimpleName() + " mehrfach verwendet!");
-            lastModifiedMethod = method;
-            isLastModifiedMethodDefined = true;
-            continue;
-          }
-          if (annotation.annotationType().equals(ETag.class)) {
-            if (isETagMethodDefined)
-              throw new IllegalArgumentException("Die Annotation @ETag wurde in der Klasse " + handler.getClass().getSimpleName() + " mehrfach verwendet!");
-            eTagMethod = method;
-            weak = ((ETag)annotation).weak();
-            isETagMethodDefined = true;
-            continue;
-          }
-          if (annotation.annotationType().equals(de.halbekunst.juplo.cachecontrol.annotations.CacheControl.class)) {
-            if (isCacheControlMethodDefined)
-              throw new IllegalArgumentException("Die Annotation @CacheControl wurde in der Klasse " + handler.getClass().getSimpleName() + " mehrfach verwendet!");
-            cacheControlMethod = method;
-            isCacheControlMethodDefined = true;
-            continue;
-          }
-          if (annotation.annotationType().equals(AdditionalHeaders.class)) {
-            if (isAdditionalHeadersMethodDefined)
-              throw new IllegalArgumentException("Die Annotation @AdditionalHeaders wurde in der Klasse " + handler.getClass().getSimpleName() + " mehrfach verwendet!");
-            additionalHeadersMethod = method;
-            isAdditionalHeadersMethodDefined = true;
-            continue;
-          }
-        }
-      }
-
-      if (!isAdditionalHeadersMethodDefined)
-        additionalHeaders = new HashMap<String,String>();
-    }
-
-
-    @Override
-    public boolean isZipped() {
-      return zipped;
-    }
-
-    @Override
-    public long getTimestamp() {
-      return now;
-    }
-
-    @Override
-    public int accepts(HttpServletRequest request) throws IllegalArgumentException {
-      if (acceptsMethod == null) {
-        return HttpServletResponse.SC_OK;
-      }
-      else {
-        try {
-          return (Integer)acceptsMethod.invoke(handler, request);
-        }
-        catch (Exception e) {
-          throw new IllegalArgumentException(e);
-        }
-      }
-    }
-
-    @Override
-    public int getCacheSeconds(HttpServletRequest request) throws IllegalArgumentException {
-      if (cacheSecondsMethod == null) {
-        return cacheSeconds;
-      }
-      else {
-        try {
-          return (Integer)cacheSecondsMethod.invoke(handler, request);
-        }
-        catch (Exception e) {
-          throw new IllegalArgumentException(e);
-        }
-      }
-    }
-
-    @Override
-    public long getLastModified(HttpServletRequest request) throws IllegalArgumentException {
-      if (lastModifiedMethod == null) {
-        return lastModified;
-      }
-      else {
-        try {
-          return (Long)lastModifiedMethod.invoke(handler, request);
-        }
-        catch (Exception e) {
-          throw new IllegalArgumentException(e);
-        }
-      }
-    }
-
-    @Override
-    public String getETag(HttpServletRequest request) throws IllegalArgumentException {
-      if (eTagMethod == null) {
-        return eTag;
-      }
-      else {
-        try {
-          return (String)eTagMethod.invoke(handler, request);
-        }
-        catch (Exception e) {
-          throw new IllegalArgumentException(e);
-        }
-      }
-    }
-
-    @Override
-    public boolean isETagWeak() {
-      return weak;
-    }
-
-    @Override
-    public void cacheControl(
-        HttpServletRequest request,
-        Map<String, String> cacheControlMap
-        )
-        throws IllegalArgumentException
-    {
-      if (cacheControlMethod != null) {
-        try {
-          cacheControlMethod.invoke(handler, request, cacheControlMap);
-        }
-        catch (Exception e) {
-          throw new IllegalArgumentException(e);
-        }
-      }
-    }
-
-    @Override
-    public Map<String,String> getAdditionalHeaders(HttpServletRequest request) throws IllegalArgumentException {
-      if (additionalHeadersMethod == null) {
-        return additionalHeaders;
-      }
-      else {
-        try {
-          return (Map<String,String>)additionalHeadersMethod.invoke(handler, request);
-        }
-        catch (Exception e) {
-          throw new IllegalArgumentException(e);
-        }
-      }
-    }
-  }
-}
diff --git a/cachecontrol/src/main/java/de/halbekunst/juplo/cachecontrol/CacheControlInterceptor.java b/cachecontrol/src/main/java/de/halbekunst/juplo/cachecontrol/CacheControlInterceptor.java
deleted file mode 100644 (file)
index c32183f..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-package de.halbekunst.juplo.cachecontrol;
-
-import de.halbekunst.juplo.cachecontrol.AcceleratorFilter.AccelerationWrapper;
-import de.halbekunst.juplo.cachecontrol.annotations.Cacheable;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.servlet.HandlerInterceptor;
-import org.springframework.web.servlet.ModelAndView;
-
-/**
- *
- * @author kai
- */
-public class CacheControlInterceptor implements HandlerInterceptor {
-  private final static Logger log = LoggerFactory.getLogger(CacheControlInterceptor.class);
-
-
-  private CacheControl cacheControl;
-
-
-  @Override
-  public boolean preHandle(
-      HttpServletRequest request,
-      HttpServletResponse response,
-      Object handler
-      ) throws Exception
-  {
-    Cacheable cacheable = handler.getClass().getAnnotation(Cacheable.class);
-    if (cacheable == null) {
-      /** Der Handler ist nicht mit @Cacheable annotiert: keine Dekorationen anbringen! */
-      return true;
-    }
-
-    AccelerationWrapper wrapper = (AccelerationWrapper)request.getAttribute(AcceleratorFilter.RESPONSE_WRAPPER);
-    if (wrapper != null)
-      wrapper.supressGuessing();
-
-    /** CacheControll initialisieren (Handler nach annotierte Methoden scannen etc.) */
-    cacheControl.init(handler, wrapper);
-
-    if (cacheable.eager()) {
-      return cacheControl.decorate(request, response);
-    }
-    else {
-      return true;
-    }
-  }
-
-  @Override
-  public void postHandle(
-      HttpServletRequest request,
-      HttpServletResponse response,
-      Object handler,
-      ModelAndView modelAndView
-      ) throws Exception
-  {
-    /**
-     * Dekoration nur dann anstossen, wenn sie nicht bereits von dem
-     * AcceleratorFilter ausgelöst wird.
-     */
-    if (request.getAttribute(AcceleratorFilter.RESPONSE_WRAPPER) == null)
-      cacheControl.decorate(request, response);
-  }
-
-  @Override
-  public void afterCompletion(
-      HttpServletRequest request,
-      HttpServletResponse response,
-      Object handler, Exception ex
-      ) throws Exception
-  {
-    cacheControl.release();
-  }
-
-
-  @Autowired
-  public void setCacheControl(CacheControl cacheControl) {
-    this.cacheControl = cacheControl;
-  }
-}
diff --git a/cachecontrol/src/main/java/de/halbekunst/juplo/cachecontrol/CacheMethodHandle.java b/cachecontrol/src/main/java/de/halbekunst/juplo/cachecontrol/CacheMethodHandle.java
deleted file mode 100644 (file)
index a36196e..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-package de.halbekunst.juplo.cachecontrol;
-
-import java.util.Map;
-import javax.servlet.http.HttpServletRequest;
-
-/**
- *
- * @author kai
- */
-public interface CacheMethodHandle {
-  boolean isZipped();
-  long getTimestamp();
-  int accepts(HttpServletRequest request);
-  int getCacheSeconds(HttpServletRequest request);
-  long getLastModified(HttpServletRequest request);
-  String getETag(HttpServletRequest request);
-  boolean isETagWeak();
-  void cacheControl(HttpServletRequest request, Map<String, String> cacheControlMap);
-  Map<String,String> getAdditionalHeaders(HttpServletRequest request);
-}
diff --git a/cachecontrol/src/main/java/de/halbekunst/juplo/cachecontrol/Headers.java b/cachecontrol/src/main/java/de/halbekunst/juplo/cachecontrol/Headers.java
deleted file mode 100644 (file)
index 6f7579a..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-package de.halbekunst.juplo.cachecontrol;
-
-/**
- *
- * @author kai
- */
-public abstract class Headers {
-
-  public static final String RFC_1123_DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss z";
-
-  public static final String HEADER_ACCEPT_ENCODING = "Accept-Encoding";
-  public static final String HEADER_CACHE_CONTROL = "Cache-Control";
-  public static final String HEADER_CONTENT_ENCODING = "Content-Encoding";
-  public static final String HEADER_CONTENT_TYPE = "Content-Type";
-  public static final String HEADER_DATE = "Date";
-  public static final String HEADER_ETAG = "ETag";
-  public static final String HEADER_EXPIRES = "Expires";
-  public static final String HEADER_IF_MODIFIED_SINCE = "If-Modified-Since";
-  public static final String HEADER_IF_NONE_MATCH = "If-None-Match";
-  public static final String HEADER_LAST_MODIFIED = "Last-Modified";
-  public static final String HEADER_PRAGMA = "Pragma";
-  public static final String HEADER_RANGE = "Range";
-
-}
diff --git a/cachecontrol/src/main/java/de/halbekunst/juplo/cachecontrol/annotations/Accepts.java b/cachecontrol/src/main/java/de/halbekunst/juplo/cachecontrol/annotations/Accepts.java
deleted file mode 100644 (file)
index 879cfd3..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-package de.halbekunst.juplo.cachecontrol.annotations;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Mit dieser Methode kann eine Methode annotiert werden, die Auskunft darüber
- * erteilt, mit welchem HTTP-Status-Code der Handler die Anfrage beatnworten
- * wird.
- * <p>
- * Die Methode muss eine Instanz von {@link HttpServletRequest} als (einziges!)
- * Argument akzeptieren und einen Wert liefern, der sich nach
- * <code>int</code> casten lässt.
- * <p>
- * Eine mit dieser Annotation markierte Methode wird nur benötigt, wenn die
- * Caching-Dekoration im Modus <code>eager=true</code> ausgeführt wird. Sie
- * wird in diesem Fall benötigt, weil die Entscheidungen zur Cache-Dekoration
- * dann getroffen werden müssen, <em>bevor</em> die verarbeitende Klasse die
- * Anfrage verarbeitet hat.
- * Wenn die Cache-Dekoration im Modus <code>eager=true</code> betrieben wird
- * und keine Methode mit dieser Annotation annotiert ist, geht {@link CacheControl}
- * davon aus, dass die verarbeitende Klasse alle Anfragen annimmt.
- *
- * @author kai
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.METHOD)
-public @interface Accepts {}
diff --git a/cachecontrol/src/main/java/de/halbekunst/juplo/cachecontrol/annotations/AdditionalHeaders.java b/cachecontrol/src/main/java/de/halbekunst/juplo/cachecontrol/annotations/AdditionalHeaders.java
deleted file mode 100644 (file)
index f8b9266..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-package de.halbekunst.juplo.cachecontrol.annotations;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- *
- * @author kai
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target({ ElementType.TYPE, ElementType.METHOD })
-public @interface AdditionalHeaders {
-
-  String[] value() default {};
-}
diff --git a/cachecontrol/src/main/java/de/halbekunst/juplo/cachecontrol/annotations/CacheControl.java b/cachecontrol/src/main/java/de/halbekunst/juplo/cachecontrol/annotations/CacheControl.java
deleted file mode 100644 (file)
index ab5c4a3..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-package de.halbekunst.juplo.cachecontrol.annotations;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Mit dieser Annotation kann eine Methode markiert werden, die die von Juplo-
- * CacheControl für den Header <code>Cache-Control</code> generierten
- * Schlüssel/Wert-Kombinationen manipulieren oder ergänzen kann, bevor der
- * Header an den Client ausgeliefert wird (s. {@plainlink
- * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.3 RFC2616, Abschnitt 14.9.3}).
- * <p>
- * Die Methode muss zwei Parameter akzeptieren.
- * Als ersten Parameter eine Instanz von {@link HttpServletRequest}.
- * Als zweiten Parameter eine <code>Map<String,String></code>, die die von
- * Juplo-CacheControl erzeugten Schlüssel/Wert-Paare enthält.
- * <p>
- * Diese Methode liefert eine Map mit Schlüssel-Wert-Paaren für den
- * HTTP/1.1-Header <code>Cache-Control</code> (s. {@plainlink
- * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.3 RFC2616,
- * Abschnitt 14.9.3}).
- *
- * @author kai
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.METHOD)
-public @interface CacheControl {
-}
diff --git a/cachecontrol/src/main/java/de/halbekunst/juplo/cachecontrol/annotations/CacheSeconds.java b/cachecontrol/src/main/java/de/halbekunst/juplo/cachecontrol/annotations/CacheSeconds.java
deleted file mode 100644 (file)
index e076e08..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-package de.halbekunst.juplo.cachecontrol.annotations;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-import javax.servlet.http.HttpServletRequest;
-
-/**
- * Mit dieser Annotation können Klassen oder Methoden merkiert werden.
- * <p>
- * Wenn eine Methode markiert wird, muss diese eine Instanz von
- * {@link HttpServletRequest} als (einziges!) Argument akzeptieren und einen
- * Wert liefern, der sich nach <code>int</code> casten lässt.
- * Die annotierte Methode ermöglicht eine einfache, zentrale aber Request-
- * Abhängige Steuerung des Caching-Verhaltens.
- * <p>
- * Wenn eine Klasse annotiert wird, muss der Anotation die dann statisch für
- * alle von der Klasse erzeugten Antworten gültige Cache-Zeit als Argument
- * übergeben werden.
- * Wird keine Cache-Zeit spezifiziert, wird der Wert <code>86400</code>
- * (ein Tag) verwendet.
- * <ul>
- * <li>Wenn negativer Wert als Cache-Seconds festgelet wird, werden Cache-Header
- * erzeugt, die das Cachen der Antwort für HTTP/1.0 und HTTP/1.1 vollständig
- * untersagen.</li>
- * <li>Wenn einen Wert größer oder gleich <code>0</code> festgelegt wird, wird
- * für HTTP/1.0-Clients ein <code>Expires</code>-Header generiert und für
- * HTTP/1.1-Clients ein <code>Cache-Control</code>-Header mit einem
- * entsprechenden <code>max-age</code>-Eintrag. Dies reicht in Kombination mit
- * der Annotation {@link LastModified} vollständig für ein einfaches aber
- * effektives Caching aus.</li>
- * </ul>
- * <p>
- * TODO
- * <strong>Zu beachten:</strong> Wenn die Methode
- * {@link #getCacheControl(javax.servlet.http.HttpServletRequest)} weitere
- * Schlüssel-Wert-Paare für den <code>Cache-Control</code>-Header liefert,
- * werden diese ergänzt. Wenn in der Rückgabe ein Wert für
- * <code>max-age</code> enthalten ist, wir er allerdings von dem durch diese
- * Methode vorgegebenen Wert überschrieben!
- *
- * @author kai
- * @See Cacheable
- * @See LastModified
- * @See CacheControl
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target({ ElementType.TYPE, ElementType.METHOD })
-public @interface CacheSeconds {
-  int value() default 86400; /** Default: 1 Tag */
-}
diff --git a/cachecontrol/src/main/java/de/halbekunst/juplo/cachecontrol/annotations/Cacheable.java b/cachecontrol/src/main/java/de/halbekunst/juplo/cachecontrol/annotations/Cacheable.java
deleted file mode 100644 (file)
index 7a1be26..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-package de.halbekunst.juplo.cachecontrol.annotations;
-
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-
-/**
- * Marker-Annotation für Handler (i.A. eine Impelementierung von
- * {@link Controller}), deren Antworten vom {@link CachingInterceptor} mit
- *  HTTP/1.1-Caching-Header nach
- * {@linkplain http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html RFC 2616}
- * dekoriert werden sollen.
- * <p>
- * Wenn der Parameter <code>eager</code> auf <code>true</code> gesetzt wird,
- * ermittelt der {@link CachingInterceptor} die Cache-Parameter über die
- * annotierten Methoden vorab.
- * <strong>Achtung:</strong>
- * Dies bedeutet, dass die annotierten Methoden aufgerufen werden <em>bevor</em>
- * die eigentliche Verarbeitungs-Routine der markierten Klasse aufgerufen wird!
- * Wenn sich dabei ergiebt, dass die Antwort nicht erneut ausgeliefert werden
- * muss, wird die eigentliche Verarbeitungs-Routine <em>gar nicht aufgerufen</em>.
- * <p>
- * Wenn der Parameter <code>eager</code> nicht gesetzt ist (oder explizit auf
- * <code>false</code> gesetzt wurde), kapselt der {@link CachingInterceptor}
- * den Request und den Ausgabestrom für den Response-Body und trifft die
- * Entscheidung über die zu ergänzenden Header, wenn der Status des
- * {@link HttpServletResponse} gesetzt oder mit dem Schreiben des Response-Body
- * begonnen wird.
- *
- * @see CacheControl
- * @see Accepts
- * @see CacheSeconds
- * @see LastModified
- * @see ETag
- * @see CacheControl
- * @author kai
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.TYPE)
-public @interface Cacheable {
-  boolean eager() default false;
-}
diff --git a/cachecontrol/src/main/java/de/halbekunst/juplo/cachecontrol/annotations/ETag.java b/cachecontrol/src/main/java/de/halbekunst/juplo/cachecontrol/annotations/ETag.java
deleted file mode 100644 (file)
index d2542dc..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-package de.halbekunst.juplo.cachecontrol.annotations;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-import javax.servlet.http.HttpServletRequest;
-
-/**
- * Über diese Annotation kann der Inhalt des <code>ETag/code>-Headers
- * gesteuert werden.
- * Mit dieser Annotation können Klassen oder Methoden merkiert werden.
- * <p>
- * Wenn eine Methode annotiert wird, muss diese eine Instanz von
- * {@link HttpServletRequest} als (einziges!) Argument akzeptieren und einen
- * <code>String</code> liefern.
- * <p>
- * Wenn eine Klasse Annotiert wird, muss der Annotation der Wert für den
- * <code>ETag</code>-Header übergeben werden.
- * Da dieser Wert somit statisch ist, macht es nur Sinn, Klassen mit dieser
- * Annotation zu markieren, die ausschließlich statische Ressourcen ausliefern,
- * die sich nur mit der Neuinstallation der Webanwendung ändern.
- * Wenn sich (z.B. nach einer Neuinstallation der Webanwendung) die statischen
- * Ressourcen geändert haben, muss der übergebene statische ETag geändert
- * werden, da Caches sonst weiterhin die alten Ressourcen ausliefern!
- * </p>
- * Frei wählbares ETag nach
- * {@linkplain http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.19 RFC 2616, Abschnitt 14.19 ETag}.
- * Der gelieferte Wert darf die vom RFC geforderten Anführungszeichen noch nicht
- * enthalten, da er, wenn <code>vary</code> gesetzt ist, noch um je nach
- * erfolgter Content-Negotiation varriierende Teile ergänzt wird.
- * <p>
- * Die erzeugten <code>ETag</code>'s können über die Annotations-Parameter
- * <code>weak</code> und <code>vary</code> weiter gesteuert werden.
- * <ul>
- * <li>
- * Wenn der Parameter <strong>weak</strong> auf den wert <code>true</code>
- * gesetzt wird, wird ein schwaches <code>ETag</code> erezeugt und der
- * Vergleichs-Algorithmus verhält sich entsprechend anders (siehe:
- * {@linkplain http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.3.3 RFC 2616, Abschnitt 13.3.3 Weak and Strong Validators}).
- * </li>
- * <li>
- * Über den Parameter <strong>vary</strong> kann Juplo-CacheControl damit
- * beauftragt werden, die Nötigen Maßnahmen für korrektes Content-Negotiating
- * zu ergreifen (siehe:
- * {@linkplain http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.6 RFC 2616, Abschnitt 13.6 Caching Negotiated Responses}).
- * Als Eingabe werden die Header-Namen erwertet, die zu unterschiedlichen
- * Ergebnissen der Content-Negotiation führen können (hier können folgende
- * Header angegeben werden: <code>Accept</code>, <code>Accept-Charset</code>,
- * <code>Accept-Encoding</code> und <code>Accept-Language</code>).
- * Juplo-CacheControl modifizert den übergebenen <code>ETag</code> dann so,
- * dass unterschiedliche Resultate der Content-Negotiation unterschieden
- * werden können.
- * Außerdem wird der <code>Vary</code>-Header entsprechend gesetzt.
- *</li>
- * </ul>
- * <strong>Zu beachten:</strong>
- * Wenn zugleich die Annotation {@link CacheSeconds} verwendet wird, wird
- * die mit dieser Annotation markierte Methode nur aufgerufen, wenn die mit
- * der Annotation {@link CacheSeconds} markierte Methode einen Wert größer
- * oder gleich <code>0</code> liefert, bzw. für die mit Annotation
- * {@link CacheSeconds} markierte Klasse eine Cache-Zeit größer oder gleich
- * <code>0</code> festgelegt wurde.
- *
- * @see #getCacheSeconds(javax.servlet.http.HttpServletRequest)
- *
- * @author kai
- * @see Cacheable
- * @see CacheSeconds
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target({ ElementType.TYPE, ElementType.METHOD })
-public @interface ETag {
-
-  public final static String ACCEPT = "Accept";
-  public final static String ACCEPT_CHARSET = "Accept-Charset";
-  public final static String ACCEPT_ENCODING = "Accept-Encoding";
-  public final static String ACCEPT_LANGUAGE = "Accept-Language";
-
-
-  String value() default "X";
-  boolean weak() default false;
-  String[] vary() default {};
-}
diff --git a/cachecontrol/src/main/java/de/halbekunst/juplo/cachecontrol/annotations/LastModified.java b/cachecontrol/src/main/java/de/halbekunst/juplo/cachecontrol/annotations/LastModified.java
deleted file mode 100644 (file)
index be28e0e..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-package de.halbekunst.juplo.cachecontrol.annotations;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-
-/**
- * Über diese Annotation kann der Inhalt des <code>Last-Modified</code>-Headers
- * gesteuert werden.
- * Mit dieser Annotation können Klassen oder Methoden merkiert werden.
- * <p>
- * Wenn eine Methode annotiert wird, muss diese eine Instanz von
- * {@link HttpServletRequest} als (einziges!) Argument akzeptieren und einen
- * Wert liefern, der sich nach <code>long</code> casten lässt.
- * Die Signatur der Methode entspricht der Methode
- * {@link HttpServlet#getLastModified(javax.servlet.http.HttpServletRequest)}
- * aus dem <code>HttpServlet</code>-Interface.
- * Um das Cache-Verhalten ein existierendes Servlet, das diese Methode bereits
- * implementiert, mit Juplo-CacheControll zu verbessern, kann als erste
- * Maßnahme daher einfach diese Methode mit dieser Annotation markiert werden.
- * <p>
- * Wenn eine Klasse Annotiert wird, muss der Annotation der Wert für den
- * <code>Last-Modified</code>-Header übergeben werden.
- * Da dieser Wert somit statisch ist, macht es nur Sinn, Klassen mit dieser
- * Annotation zu markieren, die ausschließlich statische Ressourcen ausliefern,
- * die sich nur mit der Neuinstallation der Webanwendung ändern.
- * </p>
- * Über diese Annotation wird der Zeitpunkt gesteuert, zu dem die gelieferte
- * Ressource zuletzt verändert wurde.
- * Erwartet wird eine Zeitangabe in Millisekunden seit dem Unix-0-Zeitpunkt,
- * die dann an {@link HttpServletResponse#setDateHeader(String, long)}
- * weitergegeben wird.
- * <p>
- * <strong>Zu beachten:</strong>
- * Wenn zugleich die Annotation {@link CacheSeconds} verwendet wird, wird
- * die mit dieser Annotation markierte Methode nur aufgerufen, wenn die mit
- * der Annotation {@link CacheSeconds} markierte Methode einen Wert größer
- * oder gleich <code>0</code> liefert, bzw. für die mit Annotation
- * {@link CacheSeconds} markierte Klasse eine Cache-Zeit größer oder gleich
- * <code>0</code> festgelegt wurde.
- *
- * @author kai
- * @see Cacheable
- * @see CacheSeconds
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target({ ElementType.TYPE, ElementType.METHOD })
-public @interface LastModified {
-  long value() default 0;
-}
diff --git a/cachecontrol/src/test/java/com/meterware/servletunit/ServletUnitHttpResponse.java b/cachecontrol/src/test/java/com/meterware/servletunit/ServletUnitHttpResponse.java
deleted file mode 100644 (file)
index 285d173..0000000
+++ /dev/null
@@ -1,621 +0,0 @@
-package com.meterware.servletunit;
-/********************************************************************************************************************
-* $Id: ServletUnitHttpResponse.java 751 2006-03-24 19:59:12Z russgold $
-*
-* Copyright (c) 2000-2004,2006, Russell Gold
-*
-* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
-* documentation files (the "Software"), to deal in the Software without restriction, including without limitation
-* the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
-* to permit persons to whom the Software is furnished to do so, subject to the following conditions:
-*
-* The above copyright notice and this permission notice shall be included in all copies or substantial portions
-* of the Software.
-*
-* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
-* THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
-* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-* DEALINGS IN THE SOFTWARE.
-*
-*******************************************************************************************************************/
-import com.meterware.httpunit.HttpUnitUtils;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.io.UnsupportedEncodingException;
-
-import java.util.*;
-import java.text.SimpleDateFormat;
-
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.Cookie;
-import javax.servlet.http.HttpServletResponse;
-
-
-class ServletUnitHttpResponse implements HttpServletResponse {
-
-    // rfc1123-date is "Sun, 06 Nov 1994 08:49:37 GMT"
-    private static final String RFC1123_DATE_SPEC = "EEE, dd MMM yyyy HH:mm:ss z";
-    private boolean _committed;
-    private Locale _locale = Locale.getDefault();
-
-    private static final Hashtable ENCODING_MAP = new Hashtable();
-
-    /**
-     * @deprecated Use encodeURL(String url)
-     */
-    public String encodeUrl( String url ) {
-        return encodeURL( url );
-    }
-
-
-    /**
-     * Adds the specified cookie to the response.  It can be called
-     * multiple times to set more than one cookie.
-     */
-    public void addCookie( Cookie cookie ) {
-        _cookies.addElement( cookie );
-    }
-
-
-    /**
-     * Checks whether the response message header has a field with
-     * the specified name.
-     */
-    public boolean containsHeader( String name ) {
-        return _headers.containsKey( name.toUpperCase() );
-    }
-
-
-    /**
-     * @deprecated Use encodeRedirectURL(String url)
-     **/
-    public String encodeRedirectUrl( String url ) {
-        return encodeRedirectURL( url );
-    }
-
-
-    /**
-     * Encodes the specified URL by including the session ID in it,
-     * or, if encoding is not needed, returns the URL unchanged.
-     * The implementation of this method should include the logic to
-     * determine whether the session ID needs to be encoded in the URL.
-     * For example, if the browser supports cookies, or session
-     * tracking is turned off, URL encoding is unnecessary.
-     **/
-    public String encodeURL( String url ) {
-        return url;
-    }
-
-
-    /**
-     * Encodes the specified URL for use in the
-     * <code>sendRedirect</code> method or, if encoding is not needed,
-     * returns the URL unchanged.  The implementation of this method
-     * should include the logic to determine whether the session ID
-     * needs to be encoded in the URL.  Because the rules for making
-     * this determination differ from those used to decide whether to
-     * encode a normal link, this method is seperate from the
-     * <code>encodeUrl</code> method.
-     **/
-    public String encodeRedirectURL( String url ) {
-        return url;
-    }
-
-
-    /**
-     * Sends a temporary redirect response to the client using the
-     * specified redirect location URL.  The URL must be absolute (for
-     * example, <code><em>https://hostname/path/file.html</em></code>).
-     * Relative URLs are not permitted here.
-     */
-    public void sendRedirect( String location ) throws IOException {
-        setStatus( HttpServletResponse.SC_MOVED_TEMPORARILY );
-        setHeader( "Location", location );
-    }
-
-
-    /**
-     * Sends an error response to the client using the specified status
-     * code and descriptive message.  If setStatus has previously been
-     * called, it is reset to the error status code.  The message is
-     * sent as the body of an HTML page, which is returned to the user
-     * to describe the problem.  The page is sent with a default HTML
-     * header; the message is enclosed in simple body tags
-     * (&lt;body&gt;&lt;/body&gt;).
-     **/
-    public void sendError( int sc ) throws IOException {
-        sendError( sc, "" );
-    }
-
-
-    /**
-     * Sends an error response to the client using the specified status
-     * code and descriptive message.  If setStatus has previously been
-     * called, it is reset to the error status code.  The message is
-     * sent as the body of an HTML page, which is returned to the user
-     * to describe the problem.  The page is sent with a default HTML
-     * header; the message is enclosed in simple body tags
-     * (&lt;body&gt;&lt;/body&gt;).
-     **/
-    public void sendError(int sc, String msg) throws IOException {
-        setStatus( sc );
-        _statusMessage = msg;
-
-        _writer = null;
-        _servletStream = null;
-
-        setContentType( "text/html" );
-        getWriter().println( "<html><head><title>" + msg + "</title></head><body>" + msg + "</body></html>" );
-    }
-
-
-    /**
-     * Sets the status code for this response.  This method is used to
-     * set the return status code when there is no error (for example,
-     * for the status codes SC_OK or SC_MOVED_TEMPORARILY).  If there
-     * is an error, the <code>sendError</code> method should be used
-     * instead.
-     **/
-    public void setStatus( int sc ) {
-        _status = sc;
-    }
-
-
-    /**
-     * @deprecated As of version 2.1, due to ambiguous meaning of the message parameter.
-     * To set a status code use setStatus(int), to send an error with a description
-     * use sendError(int, String). Sets the status code and message for this response.
-     **/
-    public void setStatus( int sc, String msg ) {
-        setStatus( sc );
-    }
-
-
-    /**
-     * Adds a field to the response header with the given name and value.
-     * If the field had already been set, the new value overwrites the
-     * previous one.  The <code>containsHeader</code> method can be
-     * used to test for the presence of a header before setting its
-     * value.
-     **/
-    public void setHeader( String name, String value ) {
-        ArrayList values = new ArrayList();
-        values.add( value );
-        synchronized (_headers) {
-            _headers.put( name.toUpperCase(), values );
-        }
-    }
-
-
-    /**
-     * Adds a field to the response header with the given name and
-     * integer value.  If the field had already been set, the new value
-     * overwrites the previous one.  The <code>containsHeader</code>
-     * method can be used to test for the presence of a header before
-     * setting its value.
-     **/
-    public void setIntHeader( String name, int value ) {
-        setHeader( name, asHeaderValue( value ) );
-    }
-
-
-    private String asHeaderValue( int value ) {
-        return Integer.toString( value );
-    }
-
-
-    /**
-     * Adds a field to the response header with the given name and
-     * date-valued field.  The date is specified in terms of
-     * milliseconds since the epoch.  If the date field had already
-     * been set, the new value overwrites the previous one.  The
-     * <code>containsHeader</code> method can be used to test for the
-     * presence of a header before setting its value.
-     **/
-    public void setDateHeader( String name, long date ) {
-        setHeader( name, asDateHeaderValue( date ) );
-    }
-
-
-    private String asDateHeaderValue( long date ) {
-        Date value = new Date( date );
-        SimpleDateFormat formatter = new SimpleDateFormat( RFC1123_DATE_SPEC, Locale.US );
-        formatter.setTimeZone( TimeZone.getTimeZone( "Greenwich Mean Time" ) );
-        return formatter.format( value );
-    }
-
-
-    /**
-     * Returns the name of the character set encoding used for
-     * the MIME body sent by this response.
-     **/
-    public String getCharacterEncoding() {
-        return _encoding == null ? HttpUnitUtils.DEFAULT_CHARACTER_SET : _encoding;
-    }
-
-
-    /**
-     * Sets the content type of the response the server sends to
-     * the client. The content type may include the type of character
-     * encoding used, for example, <code>text/html; charset=ISO-8859-4</code>.
-     *
-     * <p>You can only use this method once, and you should call it
-     * before you obtain a <code>PrintWriter</code> or
-     * {@link ServletOutputStream} object to return a response.
-     **/
-    public void setContentType( String type ) {
-        String[] typeAndEncoding = HttpUnitUtils.parseContentTypeHeader( type );
-
-        _contentType = typeAndEncoding[0];
-        if (typeAndEncoding[1] != null) _encoding = typeAndEncoding[1];
-    }
-
-
-    /**
-     * Returns a {@link ServletOutputStream} suitable for writing binary
-     * data in the response. The servlet engine does not encode the
-     * binary data.
-     *
-     * @exception IllegalStateException if you have already called the <code>getWriter</code> method
-     **/
-    public ServletOutputStream getOutputStream() throws IOException {
-        if (_writer != null) throw new IllegalStateException( "Tried to create output stream; writer already exists" );
-        if (_servletStream == null) {
-            _outputStream = new ByteArrayOutputStream();
-            _servletStream = new ServletUnitOutputStream( _outputStream );
-        }
-        return _servletStream;
-    }
-
-
-    /**
-     * Returns a <code>PrintWriter</code> object that you
-     * can use to send character text to the client.
-     * The character encoding used is the one specified
-     * in the <code>charset=</code> property of the
-     * {@link #setContentType} method, which you must call
-     * <i>before</i> you call this method.
-     *
-     * <p>If necessary, the MIME type of the response is
-     * modified to reflect the character encoding used.
-     *
-     * <p> You cannot use this method if you have already
-     * called {@link #getOutputStream} for this
-     * <code>ServletResponse</code> object.
-     *
-     * @exception UnsupportedEncodingException  if the character encoding specified in
-     *                                         <code>setContentType</code> cannot be
-     *                                         used
-     *
-     * @exception IllegalStateException        if the <code>getOutputStream</code>
-     *                                                 method has already been called for this
-     *                                         response object; in that case, you can't
-     *                                         use this method
-     *
-     **/
-    public PrintWriter getWriter() throws UnsupportedEncodingException {
-        if (_servletStream != null) throw new IllegalStateException( "Tried to create writer; output stream already exists" );
-        if (_writer == null) {
-            _outputStream = new ByteArrayOutputStream();
-            _writer = new PrintWriter( new OutputStreamWriter( _outputStream, getCharacterEncoding() ) );
-        }
-        return _writer;
-    }
-
-
-    /**
-     * Sets the length of the content the server returns
-     * to the client. In HTTP servlets, this method sets the
-     * HTTP Content-Length header.
-     **/
-    public void setContentLength( int len ) {
-        setIntHeader( "Content-Length", len );
-    }
-
-
-//------------------------------- the following methods are new in JSDK 2.2 ----------------------
-
-
-    /**
-     * Adds a response header with the given name and value. This method allows response headers to have multiple values.
-     **/
-    public void addHeader( String name, String value ) {
-        synchronized (_headers) {
-            String key = name.toUpperCase();
-            ArrayList values = (ArrayList) _headers.get( key );
-            if (values == null) {
-                values = new ArrayList();
-                _headers.put( key, values );
-            }
-            values.add( value );
-        }
-    }
-
-
-    /**
-     * Adds a response header with the given name and value. This method allows response headers to have multiple values.
-     **/
-    public void addIntHeader( String name, int value ) {
-        addHeader( name, asHeaderValue( value ) );
-    }
-
-
-    /**
-     * Adds a response header with the given name and value. This method allows response headers to have multiple values.
-     **/
-    public void addDateHeader( String name, long value ) {
-        addHeader( name, asDateHeaderValue( value ) );
-    }
-
-
-    /**
-     * Sets the preferred buffer size for the body of the response. The servlet container
-     * will use a buffer at least as large as the size requested. The actual buffer size
-     * used can be found using getBufferSize.
-     **/
-    public void setBufferSize( int size ) {
-        if (getContents().length != 0) throw new IllegalStateException( "May not set buffer size after data is written" );
-    }
-
-
-    /**
-     * Returns the actual buffer size used for the response. If no buffering is used, this method returns 0.
-     **/
-    public int getBufferSize() {
-        return 0;
-    }
-
-
-    /**
-     * Returns a boolean indicating if the response has been committed. A committed response has
-     * already had its status code and headers written.
-     **/
-    public boolean isCommitted() {
-        return _committed;
-    }
-
-
-    /**
-     * Forces any content in the buffer to be written to the client. A call to this method automatically
-     * commits the response, meaning the status code and headers will be written.
-     **/
-    public void flushBuffer() throws IOException {
-        _committed = true;
-    }
-
-
-    /**
-     * Clears any data that exists in the buffer as well as the status code and headers.
-     * If the response has been committed, this method throws an IllegalStateException.
-     **/
-    public void reset() {
-        resetBuffer();
-        _headers.clear();
-        _headersComplete = false;
-        _status = SC_OK;
-    }
-
-
-    /**
-     * Sets the locale of the response, setting the headers (including the Content-Type's charset)
-     * as appropriate. This method should be called before a call to getWriter().
-     * By default, the response locale is the default locale for the server.
-     **/
-    public void setLocale( Locale locale ) {
-        _locale = locale;
-        if (_encoding == null) {
-            for (Iterator it = ENCODING_MAP.entrySet().iterator(); it.hasNext();) {
-                Map.Entry entry = (Map.Entry) it.next();
-                String locales = (String) entry.getValue();
-                if (locales.indexOf( locale.getLanguage() ) >= 0 || locales.indexOf( locale.toString() ) >= 0) {
-                    _encoding = (String) entry.getKey();
-                    return;
-                }
-            }
-        }
-    }
-
-
-    /**
-     * Returns the locale assigned to the response.
-     **/
-    public Locale getLocale() {
-        return _locale;
-    }
-
-
-//----------------------------- methods added to ServletResponse in JSDK 2.3 --------------------------------------
-
-
-    /**
-     * Clears the content of the underlying buffer in the response without clearing headers or status code.
-     * If the response has been committed, this method throws an IllegalStateException.
-     *
-     * @since 1.3
-     */
-    public void resetBuffer() {
-        if (_committed) throw new IllegalStateException( "May not resetBuffer after response is committed" );
-        _outputStream = null;
-        _servletStream = null;
-        _writer = null;
-    }
-
-
-//---------------------------------------------- package methods --------------------------------------------------
-
-    /**
-     * Returns the contents of this response.
-     **/
-    byte[] getContents() {
-        if (_outputStream == null) {
-            return new byte[0];
-        } else {
-            if (_writer != null) _writer.flush();
-            return _outputStream.toByteArray();
-        }
-    }
-
-
-    /**
-     * Returns the status of this response.
-     **/
-    int getStatus() {
-        return _status;
-    }
-
-
-    /**
-     * Returns the message associated with this response's status.
-     **/
-    String getMessage() {
-        return _statusMessage;
-    }
-
-
-    public String[] getHeaderFieldNames() {
-        if (!_headersComplete) completeHeaders();
-        Vector names = new Vector();
-        for (Enumeration e = _headers.keys(); e.hasMoreElements();) {
-            names.addElement( e.nextElement() );
-        }
-        String[] result = new String[ names.size() ];
-        names.copyInto( result );
-        return result;
-    }
-
-
-    /**
-     * Returns the headers defined for this response.
-     **/
-    String getHeaderField( String name ) {
-        if (!_headersComplete) completeHeaders();
-
-        ArrayList values;
-        synchronized (_headers) {
-            values = (ArrayList) _headers.get( name.toUpperCase() );
-        }
-
-        return values == null ? null : (String) values.get( 0 );
-     }
-
-
-     /**
-     * Return an array of all the header values associated with the
-     * specified header name, or an zero-length array if there are no such
-     * header values.
-     *
-     * @param name Header name to look up
-     */
-    public String[] getHeaderFields(String name) {
-        if (!_headersComplete) completeHeaders();
-        ArrayList values;
-        synchronized (_headers) {
-            values = (ArrayList) _headers.get(name.toUpperCase());
-        }
-        if (values == null)
-            return (new String[0]);
-        String results[] = new String[values.size()];
-        return ((String[]) values.toArray(results));
-
-    }
-
-//--------------------------------------- methods added to ServletRequest in Servlet API 2.4 ----------------------------
-
-    public void setCharacterEncoding(String string) {
-        _encoding = string;
-    }
-
-    /**
-     * Returns the content type defined for this response.
-     **/
-    public String getContentType() {
-        return _contentType;
-    }
-
-
-//------------------------------------------- private members ------------------------------------
-
-
-    private String _contentType = "text/plain";
-
-    private String _encoding;
-
-    private PrintWriter  _writer;
-
-    private ServletOutputStream _servletStream;
-
-    private ByteArrayOutputStream _outputStream;
-
-    private int _status = SC_OK;
-
-    private String _statusMessage = "OK";
-
-    private final Hashtable _headers = new Hashtable();
-
-    private boolean _headersComplete;
-
-    private Vector  _cookies = new Vector();
-
-
-    private void completeHeaders() {
-        if (_headersComplete) return;
-        addCookieHeader();
-        setHeader( "Content-Type", _contentType + "; charset=" + getCharacterEncoding() );
-        _headersComplete = true;
-    }
-
-
-    private void addCookieHeader() {
-        if (_cookies.isEmpty()) return;
-
-        StringBuffer sb = new StringBuffer();
-        for (Enumeration e = _cookies.elements(); e.hasMoreElements();) {
-            Cookie cookie = (Cookie) e.nextElement();
-            sb.append( cookie.getName() ).append( '=' ).append( cookie.getValue() );
-            if (cookie.getPath() != null) sb.append( ";path=" ).append( cookie.getPath() );
-            if (cookie.getDomain() != null) sb.append( ";domain=" ).append( cookie.getDomain() );
-            if (e.hasMoreElements()) sb.append( ',' );
-        }
-        setHeader( "Set-Cookie", sb.toString() );
-    }
-
-
-
-    static {
-        ENCODING_MAP.put( "iso-8859-1", "ca da de en es fi fr is it nl no pt sv " );
-        ENCODING_MAP.put( "iso-8859-2", "cs hr hu pl ro sh sk sl sq " );
-        ENCODING_MAP.put( "iso-8859-4", "et lt lv ");
-        ENCODING_MAP.put( "iso-8859-5", "be bg mk ru sr uk " );
-        ENCODING_MAP.put( "iso-8859-6", "ar " );
-        ENCODING_MAP.put( "iso-8859-7", "el " );
-        ENCODING_MAP.put( "iso-8859-8", "iw he " );
-        ENCODING_MAP.put( "iso-8859-9", "tr " );
-
-        ENCODING_MAP.put("Shift_JIS", "ja ");
-        ENCODING_MAP.put("EUC-KR",    "ko ");
-        ENCODING_MAP.put("TIS-620",   "th ");
-        ENCODING_MAP.put("GB2312",    "zh " );
-        ENCODING_MAP.put("Big5",      "zh_TW zh_HK " );
-    }
-
-}
-
-
-
-class ServletUnitOutputStream extends ServletOutputStream {
-
-    ServletUnitOutputStream( ByteArrayOutputStream stream ) {
-        _stream = stream;
-    }
-
-
-    public void write( int aByte ) throws IOException {
-        _stream.write( aByte );
-    }
-
-    private ByteArrayOutputStream _stream;
-}
diff --git a/cachecontrol/src/test/java/de/halbekunst/juplo/cachecontrol/ParameterGuessingTest.java b/cachecontrol/src/test/java/de/halbekunst/juplo/cachecontrol/ParameterGuessingTest.java
deleted file mode 100644 (file)
index e7b64f6..0000000
+++ /dev/null
@@ -1,575 +0,0 @@
-package de.halbekunst.juplo.cachecontrol;
-
-import com.meterware.httpunit.WebResponse;
-import de.halbekunst.juplo.test.HttpTestCase;
-import java.net.URLEncoder;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import java.text.SimpleDateFormat;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.Locale;
-import java.util.Set;
-import org.junit.Assert;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.test.context.ContextConfiguration;
-import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
-
-
-
-/**
- *
- * @author kai
- */
-@RunWith(SpringJUnit4ClassRunner.class)
-@ContextConfiguration(locations = {
-  "classpath:/config.xml"
-})
-public class ParameterGuessingTest extends HttpTestCase {
-  private final static Logger log = LoggerFactory.getLogger(ParameterGuessingTest.class);
-
-
-  public ParameterGuessingTest() {
-    super("src/test/resources/web.xml");
-  }
-
-
-  @Test
-  public void testNothingSet() throws Exception {
-
-    log.info("-------- Test: Servlet does not implement getLastModified() and sets no Headers...");
-
-    WebResponse response = executeRequest("http://localhost/parameter-guessing?n=16");
-    Assert.assertEquals("W/\"Hallo Welt!\"", response.getHeaderField(Headers.HEADER_ETAG));
-    Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
-    Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
-    Assert.assertEquals("max-age=3600", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
-    Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
-    Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
-    Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
-    SimpleDateFormat df = new SimpleDateFormat(Headers.RFC_1123_DATE_FORMAT, Locale.US);
-    long date = df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime();
-    long expires = df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime();
-    Assert.assertTrue("Expires-Header passt nicht zum Date-Header! Unterschied: " + (expires-date)/1000 + " Sekunden.", date + 3600000 == expires);
-  }
-
-  @Test
-  public void testSetUnfilteredHeaders() throws Exception {
-
-    log.info("-------- Test: Servlet sets unfiltered Headers...");
-
-    WebResponse response = executeRequest("http://localhost/parameter-guessing?n=16&X-Debug=bla&Age=34&Content-Language=de");
-    Assert.assertEquals("bla", response.getHeaderField("X-Debug"));
-    Assert.assertEquals("34", response.getHeaderField("Age"));
-    Assert.assertEquals("de", response.getHeaderField("Content-Language"));
-    Assert.assertEquals("W/\"Hallo Welt!\"", response.getHeaderField(Headers.HEADER_ETAG));
-    Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
-    Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
-    Assert.assertEquals("max-age=3600", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
-    Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
-    Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
-    Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
-    SimpleDateFormat df = new SimpleDateFormat(Headers.RFC_1123_DATE_FORMAT, Locale.US);
-    long date = df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime();
-    long expires = df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime();
-    Assert.assertTrue("Expires-Header passt nicht zum Date-Header! Unterschied: " + (expires-date)/1000 + " Sekunden.", date + 3600000 == expires);
-  }
-
-  @Test
-  public void testETagSet() throws Exception {
-
-    log.info("-------- Test: Servlet sets Header \"ETag\"");
-
-    SimpleDateFormat df = new SimpleDateFormat(Headers.RFC_1123_DATE_FORMAT, Locale.US);
-    WebResponse response;
-    long date, expires;
-
-    response = executeRequest("http://localhost/parameter-guessing?n=16&ETag=" + URLEncoder.encode("\"bla\"", "UTF-8"));
-    Assert.assertEquals("\"bla\"", response.getHeaderField(Headers.HEADER_ETAG));
-    Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
-    Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
-    Assert.assertEquals("max-age=3600", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
-    Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
-    Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
-    Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
-    date = df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime();
-    expires = df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime();
-    Assert.assertTrue("Expires-Header passt nicht zum Date-Header! Unterschied: " + (expires-date)/1000 + " Sekunden.", date + 3600000 == expires);
-
-    response = executeRequest("http://localhost/parameter-guessing?n=16&ETag=" + URLEncoder.encode("\"bÄl\"a\"", "UTF-8"));
-    Assert.assertEquals("\"bla\"", response.getHeaderField(Headers.HEADER_ETAG));
-    Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
-    Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
-    Assert.assertEquals("max-age=3600", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
-    Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
-    Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
-    Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
-    date = df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime();
-    expires = df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime();
-    Assert.assertTrue("Expires-Header passt nicht zum Date-Header! Unterschied: " + (expires-date)/1000 + " Sekunden.", date + 3600000 == expires);
-
-    response = executeRequest("http://localhost/parameter-guessing?n=16&ETag=" + URLEncoder.encode("bla", "UTF-8"));
-    Assert.assertEquals("\"bla\"", response.getHeaderField(Headers.HEADER_ETAG));
-    Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
-    Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
-    Assert.assertEquals("max-age=3600", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
-    Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
-    Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
-    Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
-    date = df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime();
-    expires = df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime();
-    Assert.assertTrue("Expires-Header passt nicht zum Date-Header! Unterschied: " + (expires-date)/1000 + " Sekunden.", date + 3600000 == expires);
-
-    response = executeRequest("http://localhost/parameter-guessing?n=16&ETag=" + URLEncoder.encode("bÄl\"a", "UTF-8"));
-    Assert.assertEquals("\"bla\"", response.getHeaderField(Headers.HEADER_ETAG));
-    Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
-    Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
-    Assert.assertEquals("max-age=3600", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
-    Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
-    Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
-    Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
-    date = df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime();
-    expires = df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime();
-    Assert.assertTrue("Expires-Header passt nicht zum Date-Header! Unterschied: " + (expires-date)/1000 + " Sekunden.", date + 3600000 == expires);
-
-    response = executeRequest("http://localhost/parameter-guessing?n=16&ETag=" + URLEncoder.encode("W/\"blub\"", "UTF-8"));
-    Assert.assertEquals("W/\"blub\"", response.getHeaderField(Headers.HEADER_ETAG));
-    Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
-    Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
-    Assert.assertEquals("max-age=3600", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
-    Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
-    Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
-    Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
-    date = df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime();
-    expires = df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime();
-    Assert.assertTrue("Expires-Header passt nicht zum Date-Header! Unterschied: " + (expires-date)/1000 + " Sekunden.", date + 3600000 == expires);
-
-    response = executeRequest("http://localhost/parameter-guessing?n=16&ETag=" + URLEncoder.encode("W/\"bÄl\"ub\"", "UTF-8"));
-    Assert.assertEquals("W/\"blub\"", response.getHeaderField(Headers.HEADER_ETAG));
-    Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
-    Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
-    Assert.assertEquals("max-age=3600", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
-    Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
-    Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
-    Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
-    date = df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime();
-    expires = df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime();
-    Assert.assertTrue("Expires-Header passt nicht zum Date-Header! Unterschied: " + (expires-date)/1000 + " Sekunden.", date + 3600000 == expires);
-
-    response = executeRequest("http://localhost/parameter-guessing?n=16&ETag=" + URLEncoder.encode("W/blub", "UTF-8"));
-    Assert.assertEquals("W/\"blub\"", response.getHeaderField(Headers.HEADER_ETAG));
-    Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
-    Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
-    Assert.assertEquals("max-age=3600", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
-    Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
-    Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
-    Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
-    date = df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime();
-    expires = df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime();
-    Assert.assertTrue("Expires-Header passt nicht zum Date-Header! Unterschied: " + (expires-date)/1000 + " Sekunden.", date + 3600000 == expires);
-
-    response = executeRequest("http://localhost/parameter-guessing?n=16&ETag=" + URLEncoder.encode("W/bÄl\"ub", "UTF-8"));
-    Assert.assertEquals("W/\"blub\"", response.getHeaderField(Headers.HEADER_ETAG));
-    Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
-    Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
-    Assert.assertEquals("max-age=3600", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
-    Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
-    Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
-    Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
-    date = df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime();
-    expires = df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime();
-    Assert.assertTrue("Expires-Header passt nicht zum Date-Header! Unterschied: " + (expires-date)/1000 + " Sekunden.", date + 3600000 == expires);
-  }
-
-  @Test
-  public void testLastModifiedImplemented() throws Exception {
-
-    log.info("-------- Test: Servlet implements getLastModified()");
-
-    WebResponse response = executeRequest("http://localhost/parameter-guessing?n=16&l=1324162929861");
-    Assert.assertEquals("W/\"Hallo Welt!\"", response.getHeaderField(Headers.HEADER_ETAG));
-    Assert.assertEquals("Sat, 17 Dec 2011 23:02:09 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
-    Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
-    Assert.assertEquals("max-age=3600", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
-    Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
-    Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
-    Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
-    SimpleDateFormat df = new SimpleDateFormat(Headers.RFC_1123_DATE_FORMAT, Locale.US);
-    long date = df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime();
-    long expires = df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime();
-    Assert.assertTrue("Expires-Header passt nicht zum Date-Header! Unterschied: " + (expires-date)/1000 + " Sekunden.", date + 3600000 == expires);
-  }
-
-  @Test
-  public void testCacheControlSet() throws Exception {
-
-    log.info("-------- Test: Servlet sets Header \"Cache-Control\"");
-
-    SimpleDateFormat df = new SimpleDateFormat(Headers.RFC_1123_DATE_FORMAT, Locale.US);
-    StringBuilder uri;
-    WebResponse response;
-    Date date;
-    long expires;
-    Set<String> params;
-    Calendar calendar = Calendar.getInstance();
-    calendar.set(Calendar.MILLISECOND, 0);
-
-    /** max-age=120 */
-    uri = new StringBuilder();
-    uri.append("http://localhost/parameter-guessing");
-    uri.append("?n=16");
-    uri.append("&Cache-Control=");
-    uri.append(URLEncoder.encode("max-age=120", "UTF-8"));
-    response = executeRequest(uri.toString());
-    Assert.assertEquals("W/\"Hallo Welt!\"", response.getHeaderField(Headers.HEADER_ETAG));
-    Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
-    Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
-    Assert.assertEquals("max-age=120", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
-    Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
-    Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
-    date = df.parse(response.getHeaderField(Headers.HEADER_DATE));
-    expires = (date.getTime()/1000l + 120l) * 1000l;
-    Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
-    Assert.assertEquals("Unerwartetr Wert für den Expires-Header!", expires, df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime());
-
-    /** max-age=120, s-max-age=60, private, must-revalidate  */
-    uri = new StringBuilder();
-    uri.append("http://localhost/parameter-guessing");
-    uri.append("?n=16");
-    uri.append("&Cache-Control=");
-    uri.append(URLEncoder.encode("max-age=120, s-max-age=60, must-revalidate", "UTF-8"));
-    response = executeRequest(uri.toString());
-    Assert.assertEquals("W/\"Hallo Welt!\"", response.getHeaderField(Headers.HEADER_ETAG));
-    Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
-    Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
-    params = new HashSet<String>();
-    for (String param : response.getHeaderFields(Headers.HEADER_CACHE_CONTROL))
-      for (String part : param.split(","))
-        params.add(part.trim());
-    Assert.assertTrue(response.getHeaderField(Headers.HEADER_CACHE_CONTROL) + " enthält \"max-age=120\" nicht!", params.contains("max-age=120"));
-    Assert.assertTrue(response.getHeaderField(Headers.HEADER_CACHE_CONTROL) + " enthält \"s-max-age=60\" nicht!", params.contains("s-max-age=60"));
-    Assert.assertTrue(response.getHeaderField(Headers.HEADER_CACHE_CONTROL) + " enthält \"must-revalidate\" nicht!", params.contains("must-revalidate"));
-    Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
-    Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
-    Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
-    date = df.parse(response.getHeaderField(Headers.HEADER_DATE));
-    expires = (date.getTime()/1000l + 120l) * 1000l;
-    Assert.assertEquals("Unerwartetr Wert für den Expires-Header!", expires, df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime());
-
-    /** max-age=120, s-max-age=60, private, must-revalidate, BUT: several other values are set before  */
-    uri = new StringBuilder();
-    uri.append("http://localhost/parameter-guessing");
-    uri.append("?n=16");
-    uri.append("&Cache-Control=");
-    uri.append(URLEncoder.encode("no-store", "UTF-8"));
-    uri.append("&Cache-Control=");
-    uri.append(URLEncoder.encode("max-age=360, s-max-age=600, private", "UTF-8"));
-    uri.append("&Cache-Control=");
-    uri.append(URLEncoder.encode("public", "UTF-8"));
-    uri.append("&Cache-Control=");
-    uri.append(URLEncoder.encode("max-age=120, s-max-age=60, must-revalidate", "UTF-8"));
-    response = executeRequest(uri.toString());
-    Assert.assertEquals("W/\"Hallo Welt!\"", response.getHeaderField(Headers.HEADER_ETAG));
-    Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
-    Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
-    params = new HashSet<String>();
-    for (String param : response.getHeaderFields(Headers.HEADER_CACHE_CONTROL))
-      for (String part : param.split(","))
-        params.add(part.trim());
-    Assert.assertTrue(response.getHeaderField(Headers.HEADER_CACHE_CONTROL) + " enthält \"max-age=120\" nicht!", params.contains("max-age=120"));
-    Assert.assertTrue(response.getHeaderField(Headers.HEADER_CACHE_CONTROL) + " enthält \"s-max-age=60\" nicht!", params.contains("s-max-age=60"));
-    Assert.assertTrue(response.getHeaderField(Headers.HEADER_CACHE_CONTROL) + " enthält \"must-revalidate\" nicht!", params.contains("must-revalidate"));
-    Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
-    Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
-    Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
-    date = df.parse(response.getHeaderField(Headers.HEADER_DATE));
-    expires = (date.getTime()/1000l + 120l) * 1000l;
-    Assert.assertEquals("Unerwartetr Wert für den Expires-Header!", expires, df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime());
-  }
-
-  @Test
-  public void testDateSet() throws Exception {
-
-    log.info("-------- Test: Servlet sets Header \"Date\"");
-
-    SimpleDateFormat df = new SimpleDateFormat(Headers.RFC_1123_DATE_FORMAT, Locale.US);
-    StringBuilder uri;
-    WebResponse response;
-    Date date, expires;
-    Calendar calendar = Calendar.getInstance();
-    calendar.set(Calendar.MILLISECOND, 0);
-
-    /** Date ca NOW -1m */
-    calendar.add(Calendar.MINUTE, -1);
-    date = calendar.getTime();
-    calendar.add(Calendar.MINUTE, 60); /** default max-age=3600 yields 60m! */
-    expires = calendar.getTime();
-    uri = new StringBuilder();
-    uri.append("http://localhost/parameter-guessing");
-    uri.append("?n=16");
-    uri.append("&Date=");
-    uri.append(URLEncoder.encode(df.format(date), "UTF-8"));
-    response = executeRequest(uri.toString());
-    Assert.assertEquals("W/\"Hallo Welt!\"", response.getHeaderField(Headers.HEADER_ETAG));
-    Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
-    Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
-    Assert.assertEquals("max-age=3600", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
-    Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
-    Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
-    Assert.assertEquals("Unerwartetr Wert für den Date-Header!", date.getTime(), df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime());
-    Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
-    Assert.assertEquals("Unerwartetr Wert für den Expires-Header!", expires.getTime(), df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime());
-
-    /** Date ca NOW -1m, BUT: is set to some garbage values before */
-    calendar.add(Calendar.MINUTE, -1);
-    date = calendar.getTime();
-    calendar.add(Calendar.MINUTE, 60); /** default max-age=3600 yields 60m! */
-    expires = calendar.getTime();
-    uri = new StringBuilder();
-    uri.append("http://localhost/parameter-guessing");
-    uri.append("?n=16");
-    uri.append("&Date=");
-    calendar.add(Calendar.MINUTE, 10);
-    uri.append(URLEncoder.encode(df.format(calendar.getTime()), "UTF-8"));
-    uri.append("&Date=");
-    calendar.add(Calendar.HOUR, -2);
-    uri.append(URLEncoder.encode(df.format(calendar.getTime()), "UTF-8"));
-    uri.append("&Date=");
-    calendar.add(Calendar.DATE, 1);
-    uri.append(URLEncoder.encode(df.format(calendar.getTime()), "UTF-8"));
-    uri.append("&Date=");
-    uri.append(URLEncoder.encode(df.format(date), "UTF-8"));
-    response = executeRequest(uri.toString());
-    Assert.assertEquals("W/\"Hallo Welt!\"", response.getHeaderField(Headers.HEADER_ETAG));
-    Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
-    Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
-    Assert.assertEquals("max-age=3600", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
-    Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
-    Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
-    Assert.assertEquals("Unerwartetr Wert für den Date-Header!", date.getTime(), df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime());
-    Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
-    Assert.assertEquals("Unerwartetr Wert für den Expires-Header!", expires.getTime(), df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime());
-  }
-
-  @Test
-  public void testExpiresSet() throws Exception {
-
-    log.info("-------- Test: Servlet sets Header \"Expires\"");
-
-    SimpleDateFormat df = new SimpleDateFormat(Headers.RFC_1123_DATE_FORMAT, Locale.US);
-    StringBuilder uri;
-    WebResponse response;
-    Date date, expires;
-    long age;
-    Calendar calendar = Calendar.getInstance();
-    calendar.set(Calendar.MILLISECOND, 0);
-
-    /** Expires ca. NOW + 10m */
-    calendar.add(Calendar.MINUTE, 10);
-    expires = calendar.getTime();
-    uri = new StringBuilder();
-    uri.append("http://localhost/parameter-guessing");
-    uri.append("?n=16");
-    uri.append("&Expires=");
-    uri.append(URLEncoder.encode(df.format(expires), "UTF-8"));
-    response = executeRequest(uri.toString());
-    Assert.assertEquals("W/\"Hallo Welt!\"", response.getHeaderField(Headers.HEADER_ETAG));
-    Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
-    Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
-    Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
-    Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
-    date = df.parse(response.getHeaderField(Headers.HEADER_DATE));
-    age = (expires.getTime() - date.getTime())/1000l;
-    Assert.assertEquals("max-age=" + age, response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
-    Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
-    Assert.assertEquals("Unerwartetr Wert für den Expires-Header!", expires.getTime(), df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime());
-
-    /** Expires ca. NOW + 10m, BUT: is set to some garbage values before */
-    calendar.add(Calendar.MINUTE, 10);
-    uri = new StringBuilder();
-    uri.append("http://localhost/parameter-guessing");
-    uri.append("?n=16");
-    uri.append("&Expires=");
-    calendar.add(Calendar.MINUTE, 10);
-    uri.append(URLEncoder.encode(df.format(calendar.getTime()), "UTF-8"));
-    uri.append("&Expires=");
-    calendar.add(Calendar.HOUR, -2);
-    uri.append(URLEncoder.encode(df.format(calendar.getTime()), "UTF-8"));
-    uri.append("&Expires=");
-    calendar.add(Calendar.DATE, 1);
-    uri.append(URLEncoder.encode(df.format(calendar.getTime()), "UTF-8"));
-    uri.append("&Expires=");
-    uri.append(URLEncoder.encode(df.format(expires), "UTF-8"));
-    response = executeRequest(uri.toString());
-    Assert.assertEquals("W/\"Hallo Welt!\"", response.getHeaderField(Headers.HEADER_ETAG));
-    Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
-    Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
-    Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
-    Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
-    date = df.parse(response.getHeaderField(Headers.HEADER_DATE));
-    age = (expires.getTime() - date.getTime())/1000l;
-    Assert.assertEquals("max-age=" + age, response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
-    Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
-    Assert.assertEquals("Unerwartetr Wert für den Expires-Header!", expires.getTime(), df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime());
-  }
-
-  @Test
-  public void testDateAndExpiresSet() throws Exception {
-
-    log.info("-------- Test: Servlet sets Header's \"Date\" and \"Expires\"");
-
-    SimpleDateFormat df = new SimpleDateFormat(Headers.RFC_1123_DATE_FORMAT, Locale.US);
-    StringBuilder uri;
-    WebResponse response;
-    Date date, expires, garbage;
-    Calendar calendar = Calendar.getInstance();
-    calendar.set(Calendar.MILLISECOND, 0);
-
-    /** Expires = Date + 30m */
-    date = calendar.getTime();
-    calendar.add(Calendar.MINUTE, 30);
-    expires = calendar.getTime();
-    uri = new StringBuilder();
-    uri.append("http://localhost/parameter-guessing");
-    uri.append("?n=16");
-    uri.append("&Date=");
-    uri.append(URLEncoder.encode(df.format(date), "UTF-8"));
-    uri.append("&Expires=");
-    uri.append(URLEncoder.encode(df.format(expires), "UTF-8"));
-    response = executeRequest(uri.toString());
-    Assert.assertEquals("W/\"Hallo Welt!\"", response.getHeaderField(Headers.HEADER_ETAG));
-    Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
-    Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
-    Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
-    Assert.assertEquals("max-age=1800", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
-    Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
-    Assert.assertEquals("Unerwartetr Wert für den Date-Header!", date.getTime(), df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime());
-    Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
-    Assert.assertEquals("Unerwartetr Wert für den Expires-Header!", expires.getTime(), df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime());
-
-    /** Expires = Date + 30m, BUT: Date is set to Date - 2h first and Expires to Date */
-    date = calendar.getTime();
-    calendar.add(Calendar.MINUTE, 30);
-    expires = calendar.getTime();
-    calendar.add(Calendar.HOUR, -2);
-    garbage = calendar.getTime();
-    uri = new StringBuilder();
-    uri.append("http://localhost/parameter-guessing");
-    uri.append("?n=16");
-    uri.append("&Date=");
-    uri.append(URLEncoder.encode(df.format(garbage), "UTF-8"));
-    uri.append("&Expires=");
-    uri.append(URLEncoder.encode(df.format(date), "UTF-8"));
-    uri.append("&Date=");
-    uri.append(URLEncoder.encode(df.format(date), "UTF-8"));
-    uri.append("&Expires=");
-    uri.append(URLEncoder.encode(df.format(expires), "UTF-8"));
-    response = executeRequest(uri.toString());
-    Assert.assertEquals("W/\"Hallo Welt!\"", response.getHeaderField(Headers.HEADER_ETAG));
-    Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
-    Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
-    Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
-    Assert.assertEquals("max-age=1800", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
-    Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
-    Assert.assertEquals("Unerwartetr Wert für den Date-Header!", date.getTime(), df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime());
-    Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
-    Assert.assertEquals("Unerwartetr Wert für den Expires-Header!", expires.getTime(), df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime());
-
-    /** Expires = Date - 1h --> will be ignored! */
-    date = calendar.getTime();
-    calendar.add(Calendar.MINUTE, -60);
-    garbage = calendar.getTime();
-    calendar.setTime(date);
-    calendar.add(Calendar.MINUTE, 60); /** default max-age=3600 yields 60m! */
-    expires = calendar.getTime();
-    uri = new StringBuilder();
-    uri.append("http://localhost/parameter-guessing");
-    uri.append("?n=16");
-    uri.append("&Date=");
-    uri.append(URLEncoder.encode(df.format(date), "UTF-8"));
-    uri.append("&Expires=");
-    uri.append(URLEncoder.encode(df.format(garbage), "UTF-8"));
-    response = executeRequest(uri.toString());
-    Assert.assertEquals("W/\"Hallo Welt!\"", response.getHeaderField(Headers.HEADER_ETAG));
-    Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
-    Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
-    Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
-    Assert.assertEquals("max-age=3600", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
-    Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
-    Assert.assertEquals("Unerwartetr Wert für den Date-Header!", date.getTime(), df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime());
-    Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
-    Assert.assertEquals("Unerwartetr Wert für den Expires-Header!", expires.getTime(), df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime());
-  }
-
-  @Test
-  public void testCacheControlDateAndExpiresSet() throws Exception {
-
-    log.info("-------- Test: Servlet sets Header's \"Cache-Control\", \"Date\" and \"Expires\"");
-
-    SimpleDateFormat df = new SimpleDateFormat(Headers.RFC_1123_DATE_FORMAT, Locale.US);
-    StringBuilder uri;
-    WebResponse response;
-    Date date, expires, expected;
-    Set<String> params;
-    Calendar calendar = Calendar.getInstance();
-    calendar.set(Calendar.MILLISECOND, 0);
-
-    /** Expires = Date + 30m, Cache-Control: must-revalidate, no-store */
-    date = calendar.getTime();
-    calendar.add(Calendar.MINUTE, 30);
-    expires = calendar.getTime();
-    uri = new StringBuilder();
-    uri.append("http://localhost/parameter-guessing");
-    uri.append("?n=16");
-    uri.append("&Cache-Control=");
-    uri.append(URLEncoder.encode("must-revalidate, no-store", "UTF-8"));
-    uri.append("&Date=");
-    uri.append(URLEncoder.encode(df.format(date), "UTF-8"));
-    uri.append("&Expires=");
-    uri.append(URLEncoder.encode(df.format(expires), "UTF-8"));
-    response = executeRequest(uri.toString());
-    Assert.assertEquals("W/\"Hallo Welt!\"", response.getHeaderField(Headers.HEADER_ETAG));
-    Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
-    Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
-    Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
-    params = new HashSet<String>();
-    for (String param : response.getHeaderFields(Headers.HEADER_CACHE_CONTROL))
-      for (String part : param.split(","))
-        params.add(part.trim());
-    Assert.assertTrue(response.getHeaderField(Headers.HEADER_CACHE_CONTROL) + " enthält \"max-age=1800\" nicht!", params.contains("max-age=1800"));
-    Assert.assertTrue(response.getHeaderField(Headers.HEADER_CACHE_CONTROL) + " enthält \"must-revalidate\" nicht!", params.contains("must-revalidate"));
-    Assert.assertTrue(response.getHeaderField(Headers.HEADER_CACHE_CONTROL) + " enthält \"no-store\" nicht!", params.contains("no-store"));
-    Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
-    Assert.assertEquals("Unerwartetr Wert für den Date-Header!", date.getTime(), df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime());
-    Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
-    Assert.assertEquals("Unerwartetr Wert für den Expires-Header!", expires.getTime(), df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime());
-
-    /** Expires = Date + 30m, BUT: max-age is set to 600s */
-    date = calendar.getTime();
-    calendar.add(Calendar.MINUTE, 10);
-    expected = calendar.getTime();
-    calendar.add(Calendar.MINUTE, 20);
-    expires = calendar.getTime();
-    uri = new StringBuilder();
-    uri.append("http://localhost/parameter-guessing");
-    uri.append("?n=16");
-    uri.append("&Date=");
-    uri.append(URLEncoder.encode(df.format(date), "UTF-8"));
-    uri.append("&Expires=");
-    uri.append(URLEncoder.encode(df.format(expires), "UTF-8"));
-    uri.append("&Cache-Control=");
-    uri.append(URLEncoder.encode("max-age=600", "UTF-8"));
-    response = executeRequest(uri.toString());
-    Assert.assertEquals("W/\"Hallo Welt!\"", response.getHeaderField(Headers.HEADER_ETAG));
-    Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
-    Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
-    Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
-    Assert.assertEquals("max-age=600", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
-    Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
-    Assert.assertEquals("Unerwartetr Wert für den Date-Header!", date.getTime(), df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime());
-    Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
-    Assert.assertEquals("Unerwartetr Wert für den Expires-Header!", expected.getTime(), df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime());
-  }
-}
diff --git a/cachecontrol/src/test/java/de/halbekunst/juplo/cachecontrol/RequestSizeTest.java b/cachecontrol/src/test/java/de/halbekunst/juplo/cachecontrol/RequestSizeTest.java
deleted file mode 100644 (file)
index 2b3fec8..0000000
+++ /dev/null
@@ -1,237 +0,0 @@
-package de.halbekunst.juplo.cachecontrol;
-
-import com.meterware.httpunit.WebResponse;
-import de.halbekunst.juplo.test.HttpTestCase;
-import de.halbekunst.juplo.test.LoggingHttpServletResponseWrapper;
-import java.net.URLEncoder;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import java.text.SimpleDateFormat;
-import java.util.Locale;
-import org.junit.Assert;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.test.context.ContextConfiguration;
-import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
-
-
-
-/**
- *
- * @author kai
- */
-@RunWith(SpringJUnit4ClassRunner.class)
-@ContextConfiguration(locations = {
-  "classpath:/config.xml"
-})
-public class RequestSizeTest extends HttpTestCase {
-  private final static Logger log = LoggerFactory.getLogger(RequestSizeTest.class);
-
-
-  public RequestSizeTest() {
-    super("src/test/resources/web.xml");
-  }
-
-
-  @Test
-  public void testSimpleRequestWithGzip() throws Exception {
-
-    log.info("-------- Test: gzipped simple request");
-
-    client.getClientProperties().setAcceptGzip(true);
-
-    for (int i=0; i<33; i++) {
-      /** 33 requests ranging from 0 B to 4 KB - response ist buffered up to 1 KB */
-      WebResponse response = executeRequest("http://localhost/request-size?n=" + i*128);
-      Assert.assertEquals("W/\"Hallo Welt!\"", response.getHeaderField(Headers.HEADER_ETAG));
-      Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
-      if (i==0)
-        Assert.assertNull(response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
-      else
-        Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
-      Assert.assertEquals("max-age=3600", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
-      Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
-      Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
-      Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
-      SimpleDateFormat df = new SimpleDateFormat(Headers.RFC_1123_DATE_FORMAT, Locale.US);
-      long date = df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime();
-      long expires = df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime();
-      Assert.assertTrue("Expires-Header passt nicht zum Date-Header! Unterschied: " + (expires-date)/1000 + " Sekunden.", date + 3600000 == expires);
-    }
-  }
-
-  @Test
-  public void testSimpleRequestWithoutGzip() throws Exception {
-
-    log.info("-------- Test: uncompressed simple request");
-
-    client.getClientProperties().setAcceptGzip(false);
-
-    for (int i=0; i<33; i++) {
-      /** 33 requests ranging from 0 B to 4 KB - response ist buffered up to 1 KB */
-      WebResponse response = executeRequest("http://localhost/request-size?n=" + i*128);
-      Assert.assertEquals("W/\"Hallo Welt!\"", response.getHeaderField(Headers.HEADER_ETAG));
-      Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
-      Assert.assertNull(response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
-      Assert.assertEquals("max-age=3600", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
-      Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
-      Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
-      Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
-      SimpleDateFormat df = new SimpleDateFormat(Headers.RFC_1123_DATE_FORMAT, Locale.US);
-      long date = df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime();
-      long expires = df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime();
-      Assert.assertTrue("Expires-Header passt nicht zum Date-Header! Unterschied: " + (expires-date)/1000 + " Sekunden.", date + 3600000 == expires);
-    }
-  }
-
-  @Test
-  public void testForwardWithGzip() throws Exception {
-
-    log.info("-------- Test: gzipped request with forward");
-
-    client.getClientProperties().setAcceptGzip(true);
-
-    /**
-     * Auf den Fehler bei einem Forward nach Überschreitung der Puffer-Größe
-     * des ursprünglichen Requests wird hier nicht geprüft, weil der Puffer
-     * durch die Komprimierung bei den hier gewählten Test-Parametern nie
-     * vollgeschrieben wird, so dass er stets ohne Fehler zurückgesetzt
-     * werden kann...
-     *
-     * Dafür wird hier zusätzlich geprüft, ob die Komprimierung korrekt nur
-     * dann unterdrückt wird, wenn die gesamte Antwort leer ist (und nicht nur
-     * der initiale Request, der geforwarded wird).
-     */
-    for (int i=0; i<33; i++) {
-      /** 33 requests ranging from 0 B to 4 KB - response ist buffered up to 1 KB */
-      StringBuilder uri = new StringBuilder();
-      uri.append("http://localhost/request-size");
-      uri.append("?n=");
-      uri.append(i%7*128);
-      uri.append("&f=");
-      uri.append(URLEncoder.encode("/forwarded?n=" + i*128, "UTF-8"));
-      WebResponse response = executeRequest(uri.toString());
-      Assert.assertEquals("W/\"Hallo Welt!\"", response.getHeaderField(Headers.HEADER_ETAG));
-      Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
-      if (i==0)
-        Assert.assertNull(response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
-      else
-        Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
-      Assert.assertEquals("max-age=3600", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
-      Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
-      Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
-      Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
-      SimpleDateFormat df = new SimpleDateFormat(Headers.RFC_1123_DATE_FORMAT, Locale.US);
-      long date = df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime();
-      long expires = df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime();
-      Assert.assertTrue("Expires-Header passt nicht zum Date-Header! Unterschied: " + (expires-date)/1000 + " Sekunden.", date + 3600000 == expires);
-    }
-  }
-
-  @Test
-  public void testForwardWithoutGzip() throws Exception {
-
-    log.info("-------- Test: uncompressed request with forward");
-
-    client.getClientProperties().setAcceptGzip(false);
-
-    for (int i=0; i<33; i++) {
-      /** 33 requests ranging from 0 B to 4 KB - response ist buffered up to 1 KB */
-      StringBuilder uri = new StringBuilder();
-      uri.append("http://localhost/request-size");
-      uri.append("?n=");
-      uri.append(i*128);
-      uri.append("&f=");
-      uri.append(URLEncoder.encode("/forwarded?n=" + i*128, "UTF-8"));
-      try {
-        WebResponse response = executeRequest(uri.toString());
-        if (i*128 > LoggingHttpServletResponseWrapper.DEFAULT_BUFFER_SIZE)
-          Assert.fail("Error expected while forwarding after " + i*128 + " bytes written!");
-        Assert.assertEquals("W/\"Hallo Welt!\"", response.getHeaderField(Headers.HEADER_ETAG));
-        Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
-        Assert.assertNull(response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
-        Assert.assertEquals("max-age=3600", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
-        Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
-        Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
-        Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
-        SimpleDateFormat df = new SimpleDateFormat(Headers.RFC_1123_DATE_FORMAT, Locale.US);
-        long date = df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime();
-        long expires = df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime();
-        Assert.assertTrue("Expires-Header passt nicht zum Date-Header! Unterschied: " + (expires-date)/1000 + " Sekunden.", date + 3600000 == expires);
-      }
-      catch (IllegalStateException e) {
-        if (i*128 > LoggingHttpServletResponseWrapper.DEFAULT_BUFFER_SIZE)
-          log.debug("Expected error while forwarding after {} bytes written: {}", i*128, e.getMessage());
-        else
-          Assert.fail("Unexpected error while forwarding after " + i*128 + " bytes written: " + e.getMessage());
-      }
-    }
-  }
-
-  @Test
-  public void testIncludeWithGzip() throws Exception {
-
-    log.info("-------- Test: gzipped request with includes");
-
-    client.getClientProperties().setAcceptGzip(true);
-
-    for (int i=0; i<33; i++) {
-      /** 33 requests ranging from 0 B to 4 KB - response ist buffered up to 1 KB */
-      StringBuilder uri = new StringBuilder();
-      uri.append("http://localhost/request-size");
-      uri.append("?n=");
-      uri.append(i%7*128);
-      for (int j=0; j < i%4+1; j++) {
-        uri.append("&i=");
-        uri.append(URLEncoder.encode("/included?n=" + i*32*(4-j), "UTF-8"));
-      }
-      WebResponse response = executeRequest(uri.toString());
-      Assert.assertEquals("W/\"Hallo Welt!\"", response.getHeaderField(Headers.HEADER_ETAG));
-      Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
-      if (i==0)
-        Assert.assertNull(response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
-      else
-        Assert.assertEquals("gzip", response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
-      Assert.assertEquals("max-age=3600", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
-      Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
-      Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
-      Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
-      SimpleDateFormat df = new SimpleDateFormat(Headers.RFC_1123_DATE_FORMAT, Locale.US);
-      long date = df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime();
-      long expires = df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime();
-      Assert.assertTrue("Expires-Header passt nicht zum Date-Header! Unterschied: " + (expires-date)/1000 + " Sekunden.", date + 3600000 == expires);
-    }
-  }
-
-  @Test
-  public void testIncludeWithoutGzip() throws Exception {
-
-    log.info("-------- Test: uncompressed request with includes");
-
-    client.getClientProperties().setAcceptGzip(false);
-
-    for (int i=0; i<33; i++) {
-      /** 33 requests ranging from 0 B to 4 KB - response ist buffered up to 1 KB */
-      StringBuilder uri = new StringBuilder();
-      uri.append("http://localhost/request-size");
-      uri.append("?n=");
-      uri.append(i%7*128);
-      for (int j=0; j < i%4+1; j++) {
-        uri.append("&i=");
-        uri.append(URLEncoder.encode("/included?n=" + i*32*(4-j), "UTF-8"));
-      }
-      WebResponse response = executeRequest(uri.toString());
-      Assert.assertEquals("W/\"Hallo Welt!\"", response.getHeaderField(Headers.HEADER_ETAG));
-      Assert.assertEquals("Thu, 01 Jan 1970 00:00:00 GMT", response.getHeaderField(Headers.HEADER_LAST_MODIFIED));
-      Assert.assertNull(response.getHeaderField(Headers.HEADER_CONTENT_ENCODING));
-      Assert.assertEquals("max-age=3600", response.getHeaderField(Headers.HEADER_CACHE_CONTROL));
-      Assert.assertEquals("text/plain; charset=iso-8859-1", response.getHeaderField(Headers.HEADER_CONTENT_TYPE));
-      Assert.assertNotNull("Date-Header was not set!", response.getHeaderField(Headers.HEADER_DATE));
-      Assert.assertNotNull("Expires-Header was not set!", response.getHeaderField(Headers.HEADER_EXPIRES));
-      SimpleDateFormat df = new SimpleDateFormat(Headers.RFC_1123_DATE_FORMAT, Locale.US);
-      long date = df.parse(response.getHeaderField(Headers.HEADER_DATE)).getTime();
-      long expires = df.parse(response.getHeaderField(Headers.HEADER_EXPIRES)).getTime();
-      Assert.assertTrue("Expires-Header passt nicht zum Date-Header! Unterschied: " + (expires-date)/1000 + " Sekunden.", date + 3600000 == expires);
-    }
-  }
-}
diff --git a/cachecontrol/src/test/resources/config.xml b/cachecontrol/src/test/resources/config.xml
deleted file mode 100644 (file)
index f9e3e0f..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<beans xmlns="http://www.springframework.org/schema/beans"
-       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-       xmlns:context="http://www.springframework.org/schema/context"
-       xsi:schemaLocation="
-           http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
-           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
-
-  <!-- Activates the AspectJ-Weaver -->
-  <context:component-scan base-package="de.halbekunst"/>
-  <context:spring-configured/>
-  <!--<context:load-time-weaver/>-->
-
-  <bean id="eTag" class="java.lang.String">
-    <constructor-arg value="Hallo Welt!"/>
-  </bean>
-
-  <bean id="weak" class="java.lang.Boolean">
-    <constructor-arg value="true"/>
-  </bean>
-
-  <bean id="lastModified" class="java.lang.Long">
-    <constructor-arg value="0"/>
-  </bean>
-
-  <bean id="cacheSeconds" class="java.lang.Integer">
-    <constructor-arg value="3600"/>
-  </bean>
-
-</beans>
diff --git a/cachecontrol/src/test/resources/log4j.xml b/cachecontrol/src/test/resources/log4j.xml
deleted file mode 100644 (file)
index 833d85c..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
-
-<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
-  
-  <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
-    <layout class="org.apache.log4j.PatternLayout">
-      <param name="ConversionPattern"
-                value="%p - %C{1}.%M(%L) | %m%n"/>
-    </layout>
-  </appender>
-  
-  <logger name="de.halbekunst">
-    <level value="debug" />
-  </logger>
-
-  <root>
-    <level value="INFO"/>
-    <appender-ref ref="CONSOLE"/>
-  </root>
-  
-</log4j:configuration>
diff --git a/cachecontrol/src/test/resources/web.xml b/cachecontrol/src/test/resources/web.xml
deleted file mode 100644 (file)
index 4838a1a..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
-         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
-
-  <filter>
-    <filter-name>accelerator</filter-name>
-    <filter-class>de.halbekunst.juplo.cachecontrol.AcceleratorFilter</filter-class>
-  </filter>
-
-  <filter-mapping>
-    <filter-name>accelerator</filter-name>
-    <url-pattern>/*</url-pattern>
-  </filter-mapping>
-
-  <servlet>
-    <servlet-name>test-servlet</servlet-name>
-    <servlet-class>de.halbekunst.juplo.test.TestServlet</servlet-class>
-  </servlet>
-
-  <servlet-mapping>
-    <servlet-name>test-servlet</servlet-name>
-    <url-pattern>/*</url-pattern>
-  </servlet-mapping>
-
-</web-app>
diff --git a/examples/jsp/catalog.xml b/examples/jsp/catalog.xml
deleted file mode 100644 (file)
index 94e8090..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog" prefer="system">
-    <nextCatalog catalog="../.netbeans/6.9/var/cache/mavencachedirs/586500746/retriever/catalog.xml"/>
-    <nextCatalog catalog="../../.netbeans/6.9/var/cache/mavencachedirs/207239483/retriever/catalog.xml"/>
-</catalog>
\ No newline at end of file
diff --git a/examples/jsp/jetty.sh b/examples/jsp/jetty.sh
deleted file mode 100755 (executable)
index 4950796..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/bash
-#
-
-# OutOfMemoryException beim "mvn jetty:run" umgehen und
-# Parameter zum nachträglichen anhängen eines Debuggers
-# setzen
-export MAVEN_OPTS="-Xmx512m -XX:MaxPermSize=256m -XX:-UseGCOverheadLimit -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=4000,server=y,suspend=n"
-
-rm -v src/main/webapp/WEB-INF/lib/juplo*
-
-mvn jetty:run
diff --git a/examples/jsp/pom.xml b/examples/jsp/pom.xml
deleted file mode 100644 (file)
index 7eaed0b..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-
-  <modelVersion>4.0.0</modelVersion>
-
-  <parent>
-    <groupId>de.halbekunst</groupId>
-    <artifactId>juplo-examples</artifactId>
-    <version>2.0-SNAPSHOT</version>
-  </parent>
-
-  <artifactId>juplo-examples-jsp</artifactId>
-  <packaging>war</packaging>
-  <name>Juplo - Examples: JSP-Pages</name>
-
-</project>
diff --git a/examples/jsp/src/main/resources/config.xml b/examples/jsp/src/main/resources/config.xml
deleted file mode 100644 (file)
index e414ce9..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<beans xmlns="http://www.springframework.org/schema/beans"
-       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-       xmlns:context="http://www.springframework.org/schema/context"
-       xsi:schemaLocation="
-           http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
-           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
-
-  <!-- Activates the AspectJ-Weaver -->
-  <context:component-scan base-package="de.halbekunst"/>
-  <context:spring-configured/>
-
-  <bean id="eTag" class="java.lang.String">
-    <constructor-arg value="Hallo Welt!"/>
-  </bean>
-
-  <bean id="weak" class="java.lang.Boolean">
-    <constructor-arg value="true"/>
-  </bean>
-
-  <bean id="lastModified" class="java.lang.Long">
-    <constructor-arg value="1338593731"/>
-  </bean>
-
-  <bean id="cacheSeconds" class="java.lang.Integer">
-    <constructor-arg value="3600"/>
-  </bean>
-
-</beans>
diff --git a/examples/jsp/src/main/resources/log4j.xml b/examples/jsp/src/main/resources/log4j.xml
deleted file mode 100644 (file)
index d3414bd..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
-
-<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
-
-  <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
-    <layout class="org.apache.log4j.PatternLayout">
-      <param name="ConversionPattern" value="%p - %C{1}.%M(%L) | %m%n"/>
-    </layout>
-  </appender>
-
-  <logger name="de.halbekunst">
-   <level value="trace"/>
-  </logger>
-
-  <root>
-    <level value="info"/>
-    <appender-ref ref="CONSOLE"/>
-  </root>
-
-</log4j:configuration>
diff --git a/examples/jsp/src/main/webapp/WEB-INF/c.tld b/examples/jsp/src/main/webapp/WEB-INF/c.tld
deleted file mode 100644 (file)
index 4b85b44..0000000
+++ /dev/null
@@ -1,578 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-
-<!--
-Workaround for a bug in ServletUnit:
-The TLD in jstl.jar is not found when the webapp is running in this
-environment. Because of that, the TLD must be copied here and this
-copy must be referenced explicitly in all JSP's
--->
-<taglib xmlns="http://java.sun.com/xml/ns/javaee"
-    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
-    version="2.1">
-    
-  <description>JSTL 1.1 core library</description>
-  <display-name>JSTL core</display-name>
-  <tlib-version>1.1</tlib-version>
-  <short-name>c</short-name>
-  <uri>http://java.sun.com/jsp/jstl/core</uri>
-
-  <validator>
-    <description>
-        Provides core validation features for JSTL tags.
-    </description>
-    <validator-class>
-        org.apache.taglibs.standard.tlv.JstlCoreTLV
-    </validator-class>
-  </validator>
-
-  <tag>
-    <description>
-        Catches any Throwable that occurs in its body and optionally
-        exposes it.
-    </description>
-    <name>catch</name>
-    <tag-class>org.apache.taglibs.standard.tag.common.core.CatchTag</tag-class>
-    <body-content>JSP</body-content>
-    <attribute>
-        <description>
-Name of the exported scoped variable for the
-exception thrown from a nested action. The type of the
-scoped variable is the type of the exception thrown.
-        </description>
-        <name>var</name>
-        <required>false</required>
-        <rtexprvalue>false</rtexprvalue>
-    </attribute>
-  </tag>
-
-  <tag>
-    <description>
-       Simple conditional tag that establishes a context for
-       mutually exclusive conditional operations, marked by
-       &lt;when&gt; and &lt;otherwise&gt;
-    </description>
-    <name>choose</name>
-    <tag-class>org.apache.taglibs.standard.tag.common.core.ChooseTag</tag-class>
-    <body-content>JSP</body-content>
-  </tag>
-
-  <tag>
-    <description>
-       Simple conditional tag, which evalutes its body if the
-       supplied condition is true and optionally exposes a Boolean
-       scripting variable representing the evaluation of this condition
-    </description>
-    <name>if</name>
-    <tag-class>org.apache.taglibs.standard.tag.rt.core.IfTag</tag-class>
-    <body-content>JSP</body-content>
-    <attribute>
-        <description>
-The test condition that determines whether or
-not the body content should be processed.
-        </description>
-        <name>test</name>
-        <required>true</required>
-        <rtexprvalue>true</rtexprvalue>
-       <type>boolean</type>
-    </attribute>
-    <attribute>
-        <description>
-Name of the exported scoped variable for the
-resulting value of the test condition. The type
-of the scoped variable is Boolean.        
-        </description>
-        <name>var</name>
-        <required>false</required>
-        <rtexprvalue>false</rtexprvalue>
-    </attribute>
-    <attribute>
-        <description>
-Scope for var.
-        </description>
-        <name>scope</name>
-        <required>false</required>
-        <rtexprvalue>false</rtexprvalue>
-    </attribute>
-  </tag>
-
-  <tag>
-    <description>
-        Retrieves an absolute or relative URL and exposes its contents
-        to either the page, a String in 'var', or a Reader in 'varReader'.
-    </description>
-    <name>import</name>
-    <tag-class>org.apache.taglibs.standard.tag.rt.core.ImportTag</tag-class>
-    <tei-class>org.apache.taglibs.standard.tei.ImportTEI</tei-class>
-    <body-content>JSP</body-content>
-    <attribute>
-        <description>
-The URL of the resource to import.
-        </description>
-        <name>url</name>
-        <required>true</required>
-        <rtexprvalue>true</rtexprvalue>
-    </attribute>
-    <attribute>
-        <description>
-Name of the exported scoped variable for the
-resource's content. The type of the scoped
-variable is String.
-        </description>
-        <name>var</name>
-        <required>false</required>
-        <rtexprvalue>false</rtexprvalue>
-    </attribute>
-    <attribute>
-        <description>
-Scope for var.
-        </description>
-        <name>scope</name>
-        <required>false</required>
-        <rtexprvalue>false</rtexprvalue>
-    </attribute>
-    <attribute>
-        <description>
-Name of the exported scoped variable for the
-resource's content. The type of the scoped
-variable is Reader.
-        </description>
-        <name>varReader</name>
-        <required>false</required>
-        <rtexprvalue>false</rtexprvalue>
-    </attribute>
-    <attribute>
-        <description>
-Name of the context when accessing a relative
-URL resource that belongs to a foreign
-context.
-        </description>
-        <name>context</name>
-        <required>false</required>
-        <rtexprvalue>true</rtexprvalue>
-    </attribute>
-    <attribute>
-        <description>
-Character encoding of the content at the input
-resource.
-        </description>
-        <name>charEncoding</name>
-        <required>false</required>
-        <rtexprvalue>true</rtexprvalue>
-    </attribute>
-  </tag>
-
-  <tag>
-    <description>
-       The basic iteration tag, accepting many different
-        collection types and supporting subsetting and other
-        functionality
-    </description>
-    <name>forEach</name>
-    <tag-class>org.apache.taglibs.standard.tag.rt.core.ForEachTag</tag-class>
-    <tei-class>org.apache.taglibs.standard.tei.ForEachTEI</tei-class>
-    <body-content>JSP</body-content>
-    <attribute>
-        <description>
-Collection of items to iterate over.
-        </description>
-       <name>items</name>
-       <required>false</required>
-       <rtexprvalue>true</rtexprvalue>
-       <type>java.lang.Object</type>
-        <deferred-value>
-           <type>java.lang.Object</type>
-        </deferred-value>
-    </attribute>
-    <attribute>
-        <description>
-If items specified:
-Iteration begins at the item located at the
-specified index. First item of the collection has
-index 0.
-If items not specified:
-Iteration begins with index set at the value
-specified.
-        </description>
-       <name>begin</name>
-       <required>false</required>
-       <rtexprvalue>true</rtexprvalue>
-       <type>int</type>
-    </attribute>
-    <attribute>
-        <description>
-If items specified:
-Iteration ends at the item located at the
-specified index (inclusive).
-If items not specified:
-Iteration ends when index reaches the value
-specified.
-        </description>
-       <name>end</name>
-       <required>false</required>
-       <rtexprvalue>true</rtexprvalue>
-       <type>int</type>
-    </attribute>
-    <attribute>
-        <description>
-Iteration will only process every step items of
-the collection, starting with the first one.
-        </description>
-       <name>step</name>
-       <required>false</required>
-       <rtexprvalue>true</rtexprvalue>
-       <type>int</type>
-    </attribute>
-    <attribute>
-        <description>
-Name of the exported scoped variable for the
-current item of the iteration. This scoped
-variable has nested visibility. Its type depends
-on the object of the underlying collection.
-        </description>
-       <name>var</name>
-       <required>false</required>
-       <rtexprvalue>false</rtexprvalue>
-    </attribute>
-    <attribute>
-        <description>
-Name of the exported scoped variable for the
-status of the iteration. Object exported is of type
-javax.servlet.jsp.jstl.core.LoopTagStatus. This scoped variable has nested
-visibility.
-        </description>
-       <name>varStatus</name>
-       <required>false</required>
-       <rtexprvalue>false</rtexprvalue>
-    </attribute>
-  </tag>
-
-  <tag>
-    <description>
-       Iterates over tokens, separated by the supplied delimeters
-    </description>
-    <name>forTokens</name>
-    <tag-class>org.apache.taglibs.standard.tag.rt.core.ForTokensTag</tag-class>
-    <body-content>JSP</body-content>
-    <attribute>
-        <description>
-String of tokens to iterate over.
-        </description>
-       <name>items</name>
-       <required>true</required>
-       <rtexprvalue>true</rtexprvalue>
-       <type>java.lang.String</type>
-        <deferred-value>
-           <type>java.lang.String</type>
-        </deferred-value>
-    </attribute>
-    <attribute>
-        <description>
-The set of delimiters (the characters that
-separate the tokens in the string).
-        </description>
-       <name>delims</name>
-       <required>true</required>
-       <rtexprvalue>true</rtexprvalue>
-       <type>java.lang.String</type>
-    </attribute>
-    <attribute>
-        <description>
-Iteration begins at the token located at the
-specified index. First token has index 0.
-        </description>
-       <name>begin</name>
-       <required>false</required>
-       <rtexprvalue>true</rtexprvalue>
-       <type>int</type>
-    </attribute>
-    <attribute>
-        <description>
-Iteration ends at the token located at the
-specified index (inclusive).
-        </description>
-       <name>end</name>
-       <required>false</required>
-       <rtexprvalue>true</rtexprvalue>
-       <type>int</type>
-    </attribute>
-    <attribute>
-        <description>
-Iteration will only process every step tokens
-of the string, starting with the first one.
-        </description>
-       <name>step</name>
-       <required>false</required>
-       <rtexprvalue>true</rtexprvalue>
-       <type>int</type>
-    </attribute>
-    <attribute>
-        <description>
-Name of the exported scoped variable for the
-current item of the iteration. This scoped
-variable has nested visibility.
-        </description>
-       <name>var</name>
-       <required>false</required>
-       <rtexprvalue>false</rtexprvalue>
-    </attribute>
-    <attribute>
-        <description>
-Name of the exported scoped variable for the
-status of the iteration. Object exported is of
-type
-javax.servlet.jsp.jstl.core.LoopTag
-Status. This scoped variable has nested
-visibility.
-        </description>
-       <name>varStatus</name>
-       <required>false</required>
-       <rtexprvalue>false</rtexprvalue>
-    </attribute>
-  </tag>
-
-  <tag>
-    <description>
-        Like &lt;%= ... &gt;, but for expressions.
-    </description> 
-    <name>out</name>
-    <tag-class>org.apache.taglibs.standard.tag.rt.core.OutTag</tag-class>
-    <body-content>JSP</body-content>
-    <attribute>
-        <description>
-Expression to be evaluated.
-        </description>
-        <name>value</name>
-        <required>true</required>
-        <rtexprvalue>true</rtexprvalue>
-    </attribute>
-    <attribute>
-        <description>
-Default value if the resulting value is null.
-        </description>
-        <name>default</name>
-        <required>false</required>
-        <rtexprvalue>true</rtexprvalue>
-    </attribute>
-    <attribute>
-        <description>
-Determines whether characters &lt;,&gt;,&amp;,'," in the
-resulting string should be converted to their
-corresponding character entity codes. Default value is
-true.
-        </description>
-        <name>escapeXml</name>
-        <required>false</required>
-        <rtexprvalue>true</rtexprvalue>
-    </attribute>
-  </tag>
-
-
-  <tag>
-    <description>
-        Subtag of &lt;choose&gt; that follows &lt;when&gt; tags
-        and runs only if all of the prior conditions evaluated to
-        'false'
-    </description>
-    <name>otherwise</name>
-    <tag-class>org.apache.taglibs.standard.tag.common.core.OtherwiseTag</tag-class>
-    <body-content>JSP</body-content>
-  </tag>
-
-  <tag>
-    <description>
-        Adds a parameter to a containing 'import' tag's URL.
-    </description>
-    <name>param</name>
-    <tag-class>org.apache.taglibs.standard.tag.rt.core.ParamTag</tag-class>
-    <body-content>JSP</body-content>
-    <attribute>
-        <description>
-Name of the query string parameter.
-        </description>
-        <name>name</name>
-        <required>true</required>
-        <rtexprvalue>true</rtexprvalue>
-    </attribute>
-    <attribute>
-        <description>
-Value of the parameter.
-        </description>
-        <name>value</name>
-        <required>false</required>
-        <rtexprvalue>true</rtexprvalue>
-    </attribute>
-  </tag>
-
-  <tag>
-    <description>
-        Redirects to a new URL.
-    </description>
-    <name>redirect</name>
-    <tag-class>org.apache.taglibs.standard.tag.rt.core.RedirectTag</tag-class>
-    <body-content>JSP</body-content>
-    <attribute>
-        <description>
-The URL of the resource to redirect to.
-        </description>
-        <name>url</name>
-        <required>false</required>
-        <rtexprvalue>true</rtexprvalue>
-    </attribute>
-    <attribute>
-        <description>
-Name of the context when redirecting to a relative URL
-resource that belongs to a foreign context.
-        </description>
-        <name>context</name>
-        <required>false</required>
-        <rtexprvalue>true</rtexprvalue>
-    </attribute>
-  </tag>
-
-  <tag>
-    <description>
-        Removes a scoped variable (from a particular scope, if specified).
-    </description>
-    <name>remove</name>
-    <tag-class>org.apache.taglibs.standard.tag.common.core.RemoveTag</tag-class>
-    <body-content>empty</body-content>
-    <attribute>
-        <description>
-Name of the scoped variable to be removed.
-        </description>
-        <name>var</name>
-        <required>true</required>
-        <rtexprvalue>false</rtexprvalue>
-    </attribute>
-    <attribute>
-        <description>
-Scope for var.
-        </description>
-        <name>scope</name>
-        <required>false</required>
-        <rtexprvalue>false</rtexprvalue>
-    </attribute>
-  </tag>
-
- <tag>
-    <description>
-        Sets the result of an expression evaluation in a 'scope'
-    </description>
-    <name>set</name>
-    <tag-class>org.apache.taglibs.standard.tag.rt.core.SetTag</tag-class>
-    <body-content>JSP</body-content>
-    <attribute>
-        <description>
-Name of the exported scoped variable to hold the value
-specified in the action. The type of the scoped variable is
-whatever type the value expression evaluates to.
-        </description>
-        <name>var</name>
-        <required>false</required>
-        <rtexprvalue>false</rtexprvalue>
-    </attribute>
-    <attribute>
-        <description>
-Expression to be evaluated.
-        </description>
-        <name>value</name>
-        <required>false</required>
-        <rtexprvalue>true</rtexprvalue>
-        <deferred-value>
-           <type>java.lang.Object</type>
-        </deferred-value>
-    </attribute>
-    <attribute>
-        <description>
-Target object whose property will be set. Must evaluate to
-a JavaBeans object with setter property property, or to a
-java.util.Map object.
-        </description>
-        <name>target</name>
-        <required>false</required>
-        <rtexprvalue>true</rtexprvalue>
-    </attribute>
-    <attribute>
-        <description>
-Name of the property to be set in the target object.
-        </description>
-        <name>property</name>
-        <required>false</required>
-        <rtexprvalue>true</rtexprvalue>
-    </attribute>
-    <attribute>
-        <description>
-Scope for var.
-        </description>
-        <name>scope</name>
-        <required>false</required>
-        <rtexprvalue>false</rtexprvalue>
-    </attribute>
-  </tag>
-
-  <tag>
-    <description>
-        Creates a URL with optional query parameters.
-    </description>
-    <name>url</name>
-    <tag-class>org.apache.taglibs.standard.tag.rt.core.UrlTag</tag-class>
-    <body-content>JSP</body-content>
-    <attribute>
-        <description>
-Name of the exported scoped variable for the
-processed url. The type of the scoped variable is
-String.
-        </description>
-        <name>var</name>
-        <required>false</required>
-        <rtexprvalue>false</rtexprvalue>
-    </attribute>
-    <attribute>
-        <description>
-Scope for var.
-        </description>
-        <name>scope</name>
-        <required>false</required>
-        <rtexprvalue>false</rtexprvalue>
-    </attribute>
-    <attribute>
-        <description>
-URL to be processed.
-        </description>
-        <name>value</name>
-        <required>false</required>
-        <rtexprvalue>true</rtexprvalue>
-    </attribute>
-    <attribute>
-        <description>
-Name of the context when specifying a relative URL
-resource that belongs to a foreign context.
-        </description>
-        <name>context</name>
-        <required>false</required>
-        <rtexprvalue>true</rtexprvalue>
-    </attribute>
-  </tag>
-
-  <tag>
-    <description>
-       Subtag of &lt;choose&gt; that includes its body if its
-       condition evalutes to 'true'
-    </description>
-    <name>when</name>
-    <tag-class>org.apache.taglibs.standard.tag.rt.core.WhenTag</tag-class>
-    <body-content>JSP</body-content>
-    <attribute>
-        <description>
-The test condition that determines whether or not the
-body content should be processed.
-        </description>
-        <name>test</name>
-        <required>true</required>
-        <rtexprvalue>true</rtexprvalue>
-       <type>boolean</type>
-    </attribute>
-  </tag>
-
-</taglib>
diff --git a/examples/jsp/src/main/webapp/WEB-INF/included.jsp b/examples/jsp/src/main/webapp/WEB-INF/included.jsp
deleted file mode 100644 (file)
index 1931d89..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-<%@page contentType="text/html" pageEncoding="UTF-8" session="false"%>
-<p>Hello World, again...</p>
diff --git a/examples/jsp/src/main/webapp/WEB-INF/web.xml b/examples/jsp/src/main/webapp/WEB-INF/web.xml
deleted file mode 100644 (file)
index c5661f8..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
-         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
-
-  <!-- Context Configuration locations for Spring XML files -->
-
-  <context-param>
-    <param-name>contextConfigLocation</param-name>
-    <param-value>classpath:/config.xml</param-value>
-  </context-param>
-
-
-  <!-- Listener-Definitions -->
-
-  <listener>
-    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
-  </listener>
-
-
-  <!-- Filter-Definitions -->
-
-  <filter>
-    <filter-name>accelerator</filter-name>
-    <filter-class>de.halbekunst.juplo.cachecontrol.AcceleratorFilter</filter-class>
-  </filter>
-
-  <filter>
-    <filter-name>logger</filter-name>
-    <filter-class>de.halbekunst.juplo.test.LoggingHttpServletResponseFilter</filter-class>
-  </filter>
-
-
-  <!-- Filter-Mappings -->
-
-  <filter-mapping>
-    <filter-name>logger</filter-name>
-    <url-pattern>*.jsp</url-pattern>
-  </filter-mapping>
-
-  <filter-mapping>
-    <filter-name>accelerator</filter-name>
-    <url-pattern>*.jsp</url-pattern>
-  </filter-mapping>
-
-</web-app>
diff --git a/examples/jsp/src/main/webapp/faulty-page.jsp b/examples/jsp/src/main/webapp/faulty-page.jsp
deleted file mode 100644 (file)
index e40c213..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-<%@page contentType="text/html" pageEncoding="UTF-8" session="false" buffer="1kb" %>
-<%@taglib uri="/WEB-INF/c.tld" prefix="c"%>
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-
-<html>
-  <head>
-    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
-    <title>Faulty Page</title>
-  </head>
-  <body>
-    <h1>Faulty Page</h1>
-    <p>
-      This page will raise an error<c:forEach begin="1" end="${param['n']}" step="1">.</c:forEach>
-      after a while!
-    </p>
-    <p>
-      <strong>Ecactly, NOW:</strong>
-      <% if (true) throw new RuntimeException("Oh no!"); %>
-    </p>
-  </body>
-</html>
diff --git a/examples/jsp/src/main/webapp/index.html b/examples/jsp/src/main/webapp/index.html
deleted file mode 100644 (file)
index 2246263..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-
-<html>
-  <head>
-    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
-    <title>JSP Examples</title>
-  </head>
-  <body>
-    <h1>JSP Examples</h1>
-    <ul>
-      <li><a href="/simple-page.jsp">A really simple JSP-page</a></li>
-      <li><a href="/page-with-include.jsp">A JSP-page with several includes</a></li>
-      <li><a href="/page-with-forward.jsp">A JSP-page with a forward to /simple-page.jsp</a></li>
-      <li><a href="/faulty-page.jsp?n=8822">A JSP-page with raises an error</a></li>
-    </ul>
-  </body>
-</html>
diff --git a/examples/jsp/src/main/webapp/page-with-forward.jsp b/examples/jsp/src/main/webapp/page-with-forward.jsp
deleted file mode 100644 (file)
index 35e4905..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-<%@page contentType="text/html" pageEncoding="UTF-8" session="false" buffer="1kb" %>
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-
-<html>
-  <head>
-    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
-    <title>Page with forward</title>
-  </head>
-  <body>
-    <h1>Hello World!</h1>
-    <p>This should not be seen, because the page is forwardes to /simple-page.jsp</p>
-    <jsp:forward page="/simple-page.jsp" />
-  </body>
-</html>
diff --git a/examples/jsp/src/main/webapp/page-with-include.jsp b/examples/jsp/src/main/webapp/page-with-include.jsp
deleted file mode 100644 (file)
index 3347af7..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-<%@page contentType="text/html" pageEncoding="UTF-8" session="false" buffer="1kb" %>
-<%@taglib uri="/WEB-INF/c.tld" prefix="c"%>
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-
-<html>
-  <head>
-    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
-    <title>Page with include</title>
-  </head>
-  <body>
-    <h1>Hello World!</h1>
-    <c:forEach var="i" begin="1" end="100" step="1">${i}:<jsp:include page="/WEB-INF/included.jsp" /></c:forEach>
-  </body>
-</html>
diff --git a/examples/jsp/src/main/webapp/simple-page.jsp b/examples/jsp/src/main/webapp/simple-page.jsp
deleted file mode 100644 (file)
index 99d92d7..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-<%@page contentType="text/html" pageEncoding="UTF-8" session="false" buffer="1kb" %>
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-
-<html>
-  <head>
-    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
-    <title>Simple Page</title>
-  </head>
-  <body>
-    <h1>Simple Page</h1>
-    <p>This page is a simple jsp-page</p>
-  </body>
-</html>
diff --git a/examples/jsp/src/test/java/de/halbekunst/cachecontrol/examples/JspTest.java b/examples/jsp/src/test/java/de/halbekunst/cachecontrol/examples/JspTest.java
deleted file mode 100644 (file)
index a7331b6..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-package de.halbekunst.cachecontrol.examples;
-
-import de.halbekunst.juplo.test.HttpTestCase;
-import com.meterware.httpunit.WebResponse;
-import org.junit.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- *
- * @author kai
- */
-public class JspTest extends HttpTestCase {
-
-  private static final Logger log = LoggerFactory.getLogger(JspTest.class);
-
-
-  public JspTest() {
-    super("src/main/webapp/WEB-INF/web.xml");
-  }
-
-
-  @Test
-  public void testSimplePage() throws Exception {
-    WebResponse response = executeRequest("http://localhost:8080/simple-page.jsp");
-    log.info("Title:\t\t{}", response.getTitle());
-    log.debug("Text:\t\t{}", response.getText());
-  }
-
-  @Test
-  public void testPageWithInclude() throws Exception {
-    WebResponse response = executeRequest("http://localhost:8080/page-with-include.jsp");
-    log.info("Title:\t\t{}", response.getTitle());
-    log.debug("Text:\t\t{}", response.getText());
-  }
-
-  @Test
-  public void testPageWithForward() throws Exception {
-    WebResponse response = executeRequest("http://localhost:8080/page-with-forward.jsp");
-    log.info("Title:\t\t{}", response.getTitle());
-    log.debug("Text:\t\t{}", response.getText());
-  }
-}
diff --git a/examples/jsp/tomcat.sh b/examples/jsp/tomcat.sh
deleted file mode 100755 (executable)
index 4378a12..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/bash
-#
-
-# OutOfMemoryException beim "mvn jetty:run" umgehen und
-# Parameter zum nachträglichen anhängen eines Debuggers
-# setzen
-export MAVEN_OPTS="-Xmx512m -XX:MaxPermSize=256m -XX:-UseGCOverheadLimit -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=4000,server=y,suspend=n"
-
-rm -v src/main/webapp/WEB-INF/lib/juplo*
-
-mvn tomcat:run-war
diff --git a/examples/pom.xml b/examples/pom.xml
deleted file mode 100644 (file)
index 1905f52..0000000
+++ /dev/null
@@ -1,138 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-
-  <modelVersion>4.0.0</modelVersion>
-
-  <parent>
-    <groupId>de.halbekunst</groupId>
-    <artifactId>juplo</artifactId>
-    <version>2.0-SNAPSHOT</version>
-  </parent>
-
-  <artifactId>juplo-examples</artifactId>
-  <packaging>pom</packaging>
-  <name>Juplo - Examples</name>
-
-  <modules>
-    <module>jsp</module>
-    <module>static</module>
-    <module>servlet</module>
-    <module>spring</module>
-  </modules>
-
-  <dependencies>
-
-    <!-- Juplo -->
-    <dependency>
-      <groupId>${pom.parent.groupId}</groupId>
-      <artifactId>juplo-cachecontrol</artifactId>
-      <version>${pom.parent.version}</version>
-    </dependency>
-
-    <!-- JSP-Stuff... -->
-    <dependency>
-      <groupId>javax.servlet</groupId>
-      <artifactId>jstl</artifactId>
-      <version>${jstl.version}</version>
-      <scope>runtime</scope>
-    </dependency>
-
-    <!--  Spring -->
-    <dependency>
-      <groupId>org.springframework</groupId>
-      <artifactId>spring-webmvc</artifactId>
-      <version>${springframework.version}</version>
-      <scope>runtime</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.springframework</groupId>
-      <artifactId>spring-context</artifactId>
-      <version>${springframework.version}</version>
-      <scope>runtime</scope>
-      <exclusions>
-        <!-- Exclude Commons Logging in favor of SLF4j -->
-        <exclusion>
-          <groupId>commons-logging</groupId>
-          <artifactId>commons-logging</artifactId>
-        </exclusion>
-      </exclusions>
-    </dependency>
-    <dependency>
-      <groupId>org.springframework</groupId>
-      <artifactId>spring-aspects</artifactId>
-      <version>${springframework.version}</version>
-      <scope>runtime</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.aspectj</groupId>
-      <artifactId>aspectjrt</artifactId>
-      <version>${aspectj.version}</version>
-      <scope>runtime</scope>
-    </dependency>
-
-    <!-- Logging -->
-    <dependency>
-      <groupId>org.slf4j</groupId>
-      <artifactId>${slf4j.binding}</artifactId>
-      <version>${slf4j.version}</version>
-      <scope>runtime</scope>
-    </dependency>
-
-    <!-- Test -->
-    <dependency>
-      <groupId>${pom.parent.groupId}</groupId>
-      <artifactId>juplo-test</artifactId>
-      <version>${pom.parent.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.tomcat</groupId>
-      <artifactId>jasper</artifactId>
-      <version>${jasper.version}</version>
-      <scope>test</scope>
-    </dependency>
-
-  </dependencies>
-
-  <build>
-    <plugins>
-      <plugin>
-        <groupId>org.mortbay.jetty</groupId>
-        <artifactId>jetty-maven-plugin</artifactId>
-        <version>${jetty.version}</version>
-        <configuration>
-          <connectors>
-            <connector implementation="org.eclipse.jetty.server.nio.BlockingChannelConnector">
-              <host>0.0.0.0</host>
-              <port>8080</port>
-              <acceptors>2</acceptors>
-            </connector>
-          </connectors>
-          <scanIntervalSeconds>0</scanIntervalSeconds>
-          <scanTargetPatterns>
-            <scanTargetPattern>
-              <directory>${project.basedir}/src/main/webapp/WEB-INF</directory>
-              <excludes>
-                <exclude>**/*.jsp</exclude>
-              </excludes>
-              <includes>
-                <include>**/*.properties</include>
-                <include>**/*.xml</include>
-              </includes>
-            </scanTargetPattern>
-          </scanTargetPatterns>
-        </configuration>
-      </plugin>
-      <plugin>
-        <groupId>org.codehaus.mojo</groupId>
-        <artifactId>tomcat-maven-plugin</artifactId>
-        <version>1.1</version>
-        <configuration>
-          <path>/</path>
-          <uriEncoding>UTF-8</uriEncoding>
-        </configuration>
-      </plugin>
-    </plugins>
-  </build>
-
-</project>
diff --git a/examples/servlet/jetty.sh b/examples/servlet/jetty.sh
deleted file mode 100755 (executable)
index 4950796..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/bash
-#
-
-# OutOfMemoryException beim "mvn jetty:run" umgehen und
-# Parameter zum nachträglichen anhängen eines Debuggers
-# setzen
-export MAVEN_OPTS="-Xmx512m -XX:MaxPermSize=256m -XX:-UseGCOverheadLimit -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=4000,server=y,suspend=n"
-
-rm -v src/main/webapp/WEB-INF/lib/juplo*
-
-mvn jetty:run
diff --git a/examples/servlet/pom.xml b/examples/servlet/pom.xml
deleted file mode 100644 (file)
index 313fe7c..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-
-  <modelVersion>4.0.0</modelVersion>
-
-  <parent>
-    <groupId>de.halbekunst</groupId>
-    <artifactId>juplo-examples</artifactId>
-    <version>2.0-SNAPSHOT</version>
-  </parent>
-
-  <artifactId>juplo-examples-servlet</artifactId>
-  <packaging>war</packaging>
-  <name>Juplo - Examples: Servlet</name>
-
-</project>
diff --git a/examples/servlet/src/main/java/de/halbekunst/cachecontrol/examples/FaultyServlet.java b/examples/servlet/src/main/java/de/halbekunst/cachecontrol/examples/FaultyServlet.java
deleted file mode 100644 (file)
index bc382db..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-package de.halbekunst.cachecontrol.examples;
-
-import java.io.IOException;
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- *
- * @author kai
- */
-public class FaultyServlet extends HttpServlet {
-  private final static Logger log = LoggerFactory.getLogger(FaultyServlet.class);
-  private final static long lastModified = System.currentTimeMillis();
-
-
-  @Override
-  protected long getLastModified(HttpServletRequest req) {
-    return lastModified;
-  }
-
-  @Override
-  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
-    int n = 0;
-    try {
-      /**
-       * Wenn der Parameter n gesetzt ist, wird ein Antwort-Body erzeugt, der
-       * exakt die Anzahl der geforderten Bytes enthält.
-       */
-      n = Integer.parseInt(request.getParameter("n"));
-    }
-    catch(Exception e) {}
-    log.debug("Error will be risen after {} bytes: {}", n, request.getRequestURI());
-    ServletOutputStream out = response.getOutputStream();
-    for (int i=0; i<n; i++)
-      out.write(i%2 + 48); /** ASCII-Codes für "0" und "1" */
-    log.debug("Failing.... NOW:");
-    throw new RuntimeException("Oh, no!");
-  }
-}
diff --git a/examples/servlet/src/main/resources/config.xml b/examples/servlet/src/main/resources/config.xml
deleted file mode 100644 (file)
index b7a4892..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<beans xmlns="http://www.springframework.org/schema/beans"
-       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-       xmlns:context="http://www.springframework.org/schema/context"
-       xsi:schemaLocation="
-           http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
-           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
-
-  <!-- Activates the AspectJ-Weaver -->
-  <context:component-scan base-package="de.halbekunst"/>
-  <context:spring-configured/>
-
-  <bean id="eTag" class="java.lang.String">
-    <constructor-arg value="Hallo Welt!"/>
-  </bean>
-
-  <bean id="weak" class="java.lang.Boolean">
-    <constructor-arg value="true"/>
-  </bean>
-
-  <bean id="lastModified" class="java.lang.Long">
-    <constructor-arg value="1338593731"/>
-  </bean>
-
-  <bean id="cacheSeconds" class="java.lang.Integer">
-    <constructor-arg value="3600"/>
-  </bean>
-
-  <bean id="simpleUrlHandlerMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
-    <property name="defaultHandler" value="urlFilenameViewController"/>
-  </bean>
-
-  <bean id="urlFilenameViewController" class="org.springframework.web.servlet.mvc.UrlFilenameViewController"/>
-
-  <!-- Resolves view names to protected .jsp resources within the /WEB-INF/views directory -->
-  <bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
-    <property name="prefix" value="/WEB-INF/views/"/>
-    <property name="suffix" value=".jsp"/>
-  </bean>
-
-</beans>
diff --git a/examples/servlet/src/main/resources/log4j.xml b/examples/servlet/src/main/resources/log4j.xml
deleted file mode 100644 (file)
index d3414bd..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
-
-<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
-
-  <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
-    <layout class="org.apache.log4j.PatternLayout">
-      <param name="ConversionPattern" value="%p - %C{1}.%M(%L) | %m%n"/>
-    </layout>
-  </appender>
-
-  <logger name="de.halbekunst">
-   <level value="trace"/>
-  </logger>
-
-  <root>
-    <level value="info"/>
-    <appender-ref ref="CONSOLE"/>
-  </root>
-
-</log4j:configuration>
diff --git a/examples/servlet/src/main/webapp/WEB-INF/web.xml b/examples/servlet/src/main/webapp/WEB-INF/web.xml
deleted file mode 100644 (file)
index dc503d0..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
-         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
-
-  <!-- Context Configuration locations for Spring XML files -->
-
-  <context-param>
-    <param-name>contextConfigLocation</param-name>
-    <param-value>classpath:/config.xml</param-value>
-  </context-param>
-
-
-  <!-- Listener-Definitions -->
-
-  <listener>
-    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
-  </listener>
-
-
-  <!-- Filter-Definitions -->
-
-  <filter>
-    <filter-name>accelerator</filter-name>
-    <filter-class>de.halbekunst.juplo.cachecontrol.AcceleratorFilter</filter-class>
-  </filter>
-
-  <filter>
-    <filter-name>logger</filter-name>
-    <filter-class>de.halbekunst.juplo.test.LoggingHttpServletResponseFilter</filter-class>
-  </filter>
-
-
-  <!-- Filter-Mappings -->
-
-  <filter-mapping>
-    <filter-name>logger</filter-name>
-    <url-pattern>/test-servlet</url-pattern>
-  </filter-mapping>
-  <filter-mapping>
-    <filter-name>logger</filter-name>
-    <url-pattern>/faulty-servlet</url-pattern>
-  </filter-mapping>
-
-  <filter-mapping>
-    <filter-name>accelerator</filter-name>
-    <url-pattern>/test-servlet</url-pattern>
-  </filter-mapping>
-  <filter-mapping>
-    <filter-name>accelerator</filter-name>
-    <url-pattern>/faulty-servlet</url-pattern>
-  </filter-mapping>
-
-
-  <!-- Servlet-Definitions -->
-
-  <servlet>
-    <servlet-name>test-servlet</servlet-name>
-    <servlet-class>de.halbekunst.juplo.test.TestServlet</servlet-class>
-  </servlet>
-  <servlet>
-    <servlet-name>faulty-servlet</servlet-name>
-    <servlet-class>de.halbekunst.cachecontrol.examples.FaultyServlet</servlet-class>
-  </servlet>
-
-
-  <!-- Servlet-Mappings -->
-
-  <servlet-mapping>
-    <servlet-name>test-servlet</servlet-name>
-    <url-pattern>/test-servlet</url-pattern>
-  </servlet-mapping>
-  <servlet-mapping>
-    <servlet-name>faulty-servlet</servlet-name>
-    <url-pattern>/faulty-servlet</url-pattern>
-  </servlet-mapping>
-
-</web-app>
diff --git a/examples/servlet/src/main/webapp/index.html b/examples/servlet/src/main/webapp/index.html
deleted file mode 100644 (file)
index c08ba7c..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-
-<html>
-  <head>
-    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
-    <title>Servlet Examples</title>
-  </head>
-  <body>
-    <h1>Servlet Examples</h1>
-    <p>This page is a static HTML-page</p>
-    <ul>
-      <li><a href="/test-servlet">Empty Answer</a></li>
-      <li><a href="/test-servlet?n=16">16-Bytes-Answer</a></li>
-      <li><a href="/test-servlet?n=32">32-Bytes-Answer</a></li>
-      <li><a href="/test-servlet?n=64">64-Bytes-Answer</a></li>
-      <li><a href="/test-servlet?n=128">128-Bytes-Answer</a></li>
-      <li><a href="/test-servlet?n=256">256-Bytes-Answer</a></li>
-      <li><a href="/test-servlet?n=512">512-Bytes-Answer</a></li>
-      <li><a href="/test-servlet?n=1024">1024-Bytes-Answer</a></li>
-      <li><a href="/test-servlet?n=2048">2048-Bytes-Answer</a></li>
-      <li><a href="/test-servlet?n=4096">4096-Bytes-Answer</a></li>
-      <li><a href="/test-servlet?n=8192">8192-Bytes-Answer</a></li>
-      <li><a href="/test-servlet?n=16384">16384-Bytes-Answer</a></li>
-    </ul>
-    <ul>
-      <li><a href="/faulty-servlet">Empty Faulty Answer</a></li>
-      <li><a href="/faulty-servlet?n=16">Error after 16 Bytes</a></li>
-      <li><a href="/faulty-servlet?n=32">Error after 32 Bytes</a></li>
-      <li><a href="/faulty-servlet?n=64">Error after 64 Bytes</a></li>
-      <li><a href="/faulty-servlet?n=128">Error after 128 Bytes</a></li>
-      <li><a href="/faulty-servlet?n=256">Error after 256 Bytes</a></li>
-      <li><a href="/faulty-servlet?n=512">Error after 512 Bytes</a></li>
-      <li><a href="/faulty-servlet?n=1024">Error after 1024 Bytes</a></li>
-      <li><a href="/faulty-servlet?n=2048">Error after 2048 Bytes</a></li>
-      <li><a href="/faulty-servlet?n=4096">Error after 4096 Bytes</a></li>
-      <li><a href="/faulty-servlet?n=8192">Error after 8192 Bytes</a></li>
-      <li><a href="/faulty-servlet?n=16384">Error after 16384 Bytes</a></li>
-    </ul>
-  </body>
-</html>
diff --git a/examples/servlet/src/test/java/de/halbekunst/cachecontrol/examples/ServletTest.java b/examples/servlet/src/test/java/de/halbekunst/cachecontrol/examples/ServletTest.java
deleted file mode 100644 (file)
index 32b13e1..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-package de.halbekunst.cachecontrol.examples;
-
-import de.halbekunst.juplo.test.HttpTestCase;
-import org.junit.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- *
- * @author kai
- */
-public class ServletTest extends HttpTestCase {
-
-  private static final Logger log = LoggerFactory.getLogger(ServletTest.class);
-
-
-  public ServletTest() {
-    super("src/main/webapp/WEB-INF/web.xml");
-  }
-
-
-  @Test
-  public void test() throws Exception {
-//    WebResponse response = executeRequest("http://localhost:8080/simple-page.jsp");
-//    log.info("Title:\t\t{}", response.getTitle());
-//    log.debug("Text:\t\t{}", response.getText());
-  }
-}
diff --git a/examples/servlet/tomcat.sh b/examples/servlet/tomcat.sh
deleted file mode 100755 (executable)
index 4378a12..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/bash
-#
-
-# OutOfMemoryException beim "mvn jetty:run" umgehen und
-# Parameter zum nachträglichen anhängen eines Debuggers
-# setzen
-export MAVEN_OPTS="-Xmx512m -XX:MaxPermSize=256m -XX:-UseGCOverheadLimit -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=4000,server=y,suspend=n"
-
-rm -v src/main/webapp/WEB-INF/lib/juplo*
-
-mvn tomcat:run-war
diff --git a/examples/spring/jetty.sh b/examples/spring/jetty.sh
deleted file mode 100755 (executable)
index 4950796..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/bash
-#
-
-# OutOfMemoryException beim "mvn jetty:run" umgehen und
-# Parameter zum nachträglichen anhängen eines Debuggers
-# setzen
-export MAVEN_OPTS="-Xmx512m -XX:MaxPermSize=256m -XX:-UseGCOverheadLimit -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=4000,server=y,suspend=n"
-
-rm -v src/main/webapp/WEB-INF/lib/juplo*
-
-mvn jetty:run
diff --git a/examples/spring/pom.xml b/examples/spring/pom.xml
deleted file mode 100644 (file)
index b6924e4..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-
-  <modelVersion>4.0.0</modelVersion>
-
-  <parent>
-    <groupId>de.halbekunst</groupId>
-    <artifactId>juplo-examples</artifactId>
-    <version>2.0-SNAPSHOT</version>
-  </parent>
-
-  <artifactId>juplo-examples-spring</artifactId>
-  <packaging>war</packaging>
-  <name>Juplo - Examples: Spring-MVC</name>
-
-  <dependencies>
-
-    <!--  Spring -->
-    <dependency>
-      <groupId>org.springframework</groupId>
-      <artifactId>spring-context</artifactId>
-      <version>${springframework.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>org.springframework</groupId>
-      <artifactId>spring-web</artifactId>
-      <version>${springframework.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>org.springframework</groupId>
-      <artifactId>spring-webmvc</artifactId>
-      <version>${springframework.version}</version>
-    </dependency>
-
-  </dependencies>
-
-</project>
diff --git a/examples/spring/src/main/java/de/halbekunst/juplo/examples/spring/FaultyController.java b/examples/spring/src/main/java/de/halbekunst/juplo/examples/spring/FaultyController.java
deleted file mode 100644 (file)
index 21ec823..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-package de.halbekunst.juplo.examples.spring;
-
-import de.halbekunst.juplo.cachecontrol.annotations.CacheSeconds;
-import de.halbekunst.juplo.cachecontrol.annotations.Cacheable;
-import de.halbekunst.juplo.cachecontrol.annotations.LastModified;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.TreeMap;
-import javax.servlet.http.HttpServletRequest;
-import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.servlet.ModelAndView;
-
-
-/**
- * Simple Spring-MVC Controller
- * @author kai
- */
-@Controller
-@Cacheable(eager=true)
-public class FaultyController
-{
-  public static final String ACCESS_TIME = FaultyController.class.getCanonicalName() + ".ACCESS_TIME";
-  public static final Integer DEFAULT_MAX_AGE = 60;
-
-  private final static long lastModified = System.currentTimeMillis();
-
-  @CacheSeconds
-  public int cacheSeconds(HttpServletRequest request) {
-    return DEFAULT_MAX_AGE;
-  }
-
-  @LastModified
-  public long lastModified(HttpServletRequest request) {
-    return lastModified;
-  }
-
-  @RequestMapping("/faulty-controller.html")
-  public ModelAndView process(HttpServletRequest request)
-  {
-    throw new RuntimeException("Oh, no!");
-  }
-}
\ No newline at end of file
diff --git a/examples/spring/src/main/java/de/halbekunst/juplo/examples/spring/SpringController.java b/examples/spring/src/main/java/de/halbekunst/juplo/examples/spring/SpringController.java
deleted file mode 100644 (file)
index 2ce8f4e..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-package de.halbekunst.juplo.examples.spring;
-
-import de.halbekunst.juplo.cachecontrol.annotations.CacheSeconds;
-import de.halbekunst.juplo.cachecontrol.annotations.Cacheable;
-import de.halbekunst.juplo.cachecontrol.annotations.LastModified;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.TreeMap;
-import javax.servlet.http.HttpServletRequest;
-import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.servlet.ModelAndView;
-
-
-/**
- * Simple Spring-MVC Controller
- * @author kai
- */
-@Controller
-@Cacheable(eager=true)
-public class SpringController
-{
-  public static final String ACCESS_TIME = SpringController.class.getCanonicalName() + ".ACCESS_TIME";
-  public static final Integer DEFAULT_MAX_AGE = 60;
-
-  private Date lastModified = new Date();
-  private Map<Date,String> requests = new TreeMap<Date,String>();
-  private Map<String,Date> accessTimes = new HashMap<String,Date>();
-
-
-  @CacheSeconds
-  public int cacheSeconds(HttpServletRequest request) {
-    String maxAge = request.getParameter("max-age");
-    if (maxAge != null && maxAge.matches("^\\s*\\d+\\s*$"))
-      return Integer.parseInt(maxAge);
-    else
-      return DEFAULT_MAX_AGE;
-  }
-
-  @LastModified
-  public long lastModified(HttpServletRequest request) {
-    String query = request.getQueryString();
-    query = query == null ? "" : query;
-    Date accessTime = accessTimes.get(query);
-    if (accessTime == null || !accessTime.equals(lastModified))
-      /** Neuer Zugriff! */
-      accessTime = new Date();
-    request.setAttribute(ACCESS_TIME, accessTime);
-    return accessTime.getTime();
-  }
-
-  @RequestMapping("/spring-controller.html")
-  public ModelAndView process(HttpServletRequest request)
-  {
-    lastModified = (Date)request.getAttribute(ACCESS_TIME);
-    String query = request.getQueryString();
-    query = query == null ? "" : query;
-    requests.put(lastModified, query);
-    accessTimes.put(query, lastModified);
-    ModelAndView mav = new ModelAndView("controller-view");
-    mav.addObject("requests", requests);
-    return mav;
-  }
-}
\ No newline at end of file
diff --git a/examples/spring/src/main/resources/config.xml b/examples/spring/src/main/resources/config.xml
deleted file mode 100644 (file)
index f77073c..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<beans xmlns="http://www.springframework.org/schema/beans"
-       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-       xmlns:context="http://www.springframework.org/schema/context"
-       xmlns:mvc="http://www.springframework.org/schema/mvc"
-       xsi:schemaLocation="
-           http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
-           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
-           http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
-
-  <!-- Activates the AspectJ-Weaver -->
-  <context:component-scan base-package="de.halbekunst"/>
-  <context:spring-configured/>
-
-  <bean id="eTag" class="java.lang.String">
-    <constructor-arg value="Hallo Welt!"/>
-  </bean>
-
-  <bean id="weak" class="java.lang.Boolean">
-    <constructor-arg value="true"/>
-  </bean>
-
-  <bean id="lastModified" class="java.lang.Long">
-    <constructor-arg value="1338593731"/>
-  </bean>
-
-  <bean id="cacheSeconds" class="java.lang.Integer">
-    <constructor-arg value="3600"/>
-  </bean>
-
-  <!-- Configures the CacheControlInterceptor -->
-  <mvc:interceptors>
-    <bean class="de.halbekunst.juplo.cachecontrol.CacheControlInterceptor"/>
-  </mvc:interceptors>
-
-  <bean id="defaultAnnotationlHandlerMapping" class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
-    <property name="defaultHandler" value="urlFilenameViewController"/>
-  </bean>
-
-  <bean id="urlFilenameViewController" class="org.springframework.web.servlet.mvc.UrlFilenameViewController"/>
-
-  <!-- Resolves view names to protected .jsp resources within the /WEB-INF/views directory -->
-  <bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
-    <property name="prefix" value="/WEB-INF/views/"/>
-    <property name="suffix" value=".jsp"/>
-  </bean>
-
-</beans>
diff --git a/examples/spring/src/main/resources/log4j.xml b/examples/spring/src/main/resources/log4j.xml
deleted file mode 100644 (file)
index 13ec197..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
-
-<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
-
-  <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
-    <layout class="org.apache.log4j.PatternLayout">
-      <param name="ConversionPattern" value="%p - %C{1}.%M(%L) | %m%n"/>
-    </layout>
-  </appender>
-
-  <logger name="de.halbekunst">
-   <level value="trace"/>
-  </logger>
-
-  <logger name="org.springframework">
-    <level value="debug" />
-  </logger>
-
-  <root>
-    <level value="info"/>
-    <appender-ref ref="CONSOLE"/>
-  </root>
-
-</log4j:configuration>
diff --git a/examples/spring/src/main/webapp/WEB-INF/views/controller-view.jsp b/examples/spring/src/main/webapp/WEB-INF/views/controller-view.jsp
deleted file mode 100644 (file)
index 26abdbd..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-<%@page contentType="text/html" pageEncoding="UTF-8" session="false" buffer="1kb" %>
-<%@page import="java.util.Map" %>
-<%@page import="java.util.Date"%>
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-
-<html>
-  <head>
-    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
-    <title>Page, that is handled by a controller</title>
-  </head>
-  <body>
-    <% Map<Date,String> requests = (Map<Date,String>)request.getAttribute("requests");
-       if (requests == null) { %>
-    <h1 style="color: red">This page associated with a controller and should be viewed as such!</h1>
-    <p style="color: red">Go to: <a href="/spring-controller.html">/spring-controller.html</a></p>
-    <% } else { %>
-    <h1>Controlled Page</h1>
-    <form action="/spring-controller.html">
-      <label for="max-age">Max Age:</label>
-      <input type="text" name="max-age" value="<% if (request.getParameter("max-age") != null) { %><%= request.getParameter("max-age") %><% } %>" />
-      <br />
-    </form>
-    <p>(Uncached) requests so far:</p>
-    <ol>
-      <% for(Map.Entry<Date,String> entry : requests.entrySet()) { %>
-      <li>
-        <%= entry.getKey() %>:
-        <% if (entry.getValue().equals("")) { %>
-          <a href="/spring-controller.html">No parameters...</a>
-        <% } else { %>
-          <a href="/spring-controller.html?<%= entry.getValue() %>">
-            <% for (String parameter : entry.getValue().split("&")) { %>
-            <strong><%= parameter %></strong>
-            <% } %>
-          </a>
-        <% } %>
-      </li>
-      <% } %>
-    </ol>
-    <% } %>
-    <p>This page was delivered via SPRING!</p>
-  </body>
-</html>
diff --git a/examples/spring/src/main/webapp/WEB-INF/views/faulty-page.jsp b/examples/spring/src/main/webapp/WEB-INF/views/faulty-page.jsp
deleted file mode 100644 (file)
index 8718f54..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-<%@page contentType="text/html" pageEncoding="UTF-8" session="false" buffer="1kb" %>
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-
-<html>
-  <head>
-    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
-    <title>Simple Page</title>
-  </head>
-  <body>
-    <h1>Faulty Page</h1>
-    <p>This page will raise an error!</p>
-    <p>
-      <strong>Ecactly, NOW:</strong>
-      <% if (true) throw new RuntimeException("Oh no!"); %>
-    </p>
-  </body>
-</html>
diff --git a/examples/spring/src/main/webapp/WEB-INF/views/index.jsp b/examples/spring/src/main/webapp/WEB-INF/views/index.jsp
deleted file mode 100644 (file)
index a329acd..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-
-<html>
-  <head>
-    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
-    <title>Simple Spring-MVC Examples</title>
-  </head>
-  <body>
-    <h1>Simple Spring-MVC Examples</h1>
-    <ul>
-      <li><a href="/spring-page.html">Simple Spring-View</a></li>
-      <li><a href="/spring-controller.html">Simple Spring-Controller</a></li>
-      <li><a href="/faulty-page.html">Spring-View, which will raise an error</a></li>
-      <li><a href="/faulty-controller.html">Spring-Controller, which will raise an error</a></li>
-    </ul>
-    <p>This page was delivered via SPRING!</p>
-    <h2>Note:</h2>
-    <p>
-      Since the <code>org.springframework.web.servlet.DispatcherServlet</code>
-      ist configured to handle all <code>*.html</code>-requests, the path
-      <code>/index.html</code> points to a view.
-    </p>
-    <p>
-      Therefore, this page must be stored under
-      <code>/WEB-INF/views/index.jsp</code> in order to be served as
-      <code>/index.html</code>
-    </p>
-    <p>
-      Additionatly, a file <code>/index.jsp</code> in the root-directory of
-      the webappliction is needed, to forward unqualified requests
-      (like <code>http://HOSTNAME/</code>) to the welcome-page served by
-      the Spring-Dispatcher-Servlet.
-    </p>
-  </body>
-</html>
diff --git a/examples/spring/src/main/webapp/WEB-INF/views/spring-page.jsp b/examples/spring/src/main/webapp/WEB-INF/views/spring-page.jsp
deleted file mode 100644 (file)
index 892bf1a..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-<%@page contentType="text/html" pageEncoding="UTF-8" session="false" buffer="1kb" %>
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-
-<html>
-  <head>
-    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
-    <title>Simple Page</title>
-  </head>
-  <body>
-    <h1>Hello World!</h1>
-    <p>This is a really simple page...</p>
-    <p>This page was delivered via SPRING!</p>
-  </body>
-</html>
diff --git a/examples/spring/src/main/webapp/WEB-INF/web.xml b/examples/spring/src/main/webapp/WEB-INF/web.xml
deleted file mode 100644 (file)
index 4f26f01..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
-         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
-
-  <!-- Context Configuration locations for Spring XML files -->
-
-  <context-param>
-    <param-name>contextConfigLocation</param-name>
-    <param-value>classpath:/config.xml</param-value>
-  </context-param>
-
-
-  <!-- Listener-Definitions -->
-
-  <listener>
-    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
-  </listener>
-
-
-  <!-- Filter-Definitions -->
-
-  <filter>
-    <filter-name>accelerator</filter-name>
-    <filter-class>de.halbekunst.juplo.cachecontrol.AcceleratorFilter</filter-class>
-  </filter>
-
-  <filter>
-    <filter-name>logger</filter-name>
-    <filter-class>de.halbekunst.juplo.test.LoggingHttpServletResponseFilter</filter-class>
-  </filter>
-
-
-  <!-- Filter-Mappings -->
-
-  <filter-mapping>
-    <filter-name>logger</filter-name>
-    <url-pattern>*.html</url-pattern>
-  </filter-mapping>
-
-  <filter-mapping>
-    <filter-name>accelerator</filter-name>
-    <url-pattern>*.html</url-pattern>
-  </filter-mapping>
-
-
-  <!-- Servlet-Definitions -->
-
-  <servlet>
-    <servlet-name>dispatcher-servlet</servlet-name>
-    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
-    <init-param>
-      <param-name>contextConfigLocation</param-name>
-      <param-value>
-      </param-value>
-    </init-param>
-    <load-on-startup>1</load-on-startup>
-  </servlet>
-
-
-  <!-- Servlet-Mappings -->
-
-  <servlet-mapping>
-    <servlet-name>dispatcher-servlet</servlet-name>
-    <url-pattern>*.html</url-pattern>
-  </servlet-mapping>
-
-</web-app>
diff --git a/examples/spring/src/main/webapp/index.jsp b/examples/spring/src/main/webapp/index.jsp
deleted file mode 100644 (file)
index 9082000..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-<%@page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" session="false" %>
-<jsp:forward page="/index.html"/>
-<%--
-
-Unfortionatly, the welcome-mechanism in web.xml does not work with a page
-served by a servlet...
-
---%>
diff --git a/examples/spring/src/test/java/de/halbekunst/cachecontrol/examples/SpringMVCTest.java b/examples/spring/src/test/java/de/halbekunst/cachecontrol/examples/SpringMVCTest.java
deleted file mode 100644 (file)
index f90d16e..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-package de.halbekunst.cachecontrol.examples;
-
-import de.halbekunst.juplo.test.HttpTestCase;
-import com.meterware.httpunit.WebResponse;
-import org.junit.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- *
- * @author kai
- */
-public class SpringMVCTest extends HttpTestCase {
-
-  private static final Logger log = LoggerFactory.getLogger(SpringMVCTest.class);
-
-
-  public SpringMVCTest() {
-    super("src/main/webapp/WEB-INF/web.xml");
-  }
-
-
-  @Test
-  public void testSpringPage() throws Exception {
-    WebResponse response = executeRequest("http://localhost:8080/spring-page.html");
-    log.info("Title:\t\t{}", response.getTitle());
-    log.debug("Text:\t\t{}", response.getText());
-  }
-}
diff --git a/examples/spring/tomcat.sh b/examples/spring/tomcat.sh
deleted file mode 100755 (executable)
index 4378a12..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/bash
-#
-
-# OutOfMemoryException beim "mvn jetty:run" umgehen und
-# Parameter zum nachträglichen anhängen eines Debuggers
-# setzen
-export MAVEN_OPTS="-Xmx512m -XX:MaxPermSize=256m -XX:-UseGCOverheadLimit -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=4000,server=y,suspend=n"
-
-rm -v src/main/webapp/WEB-INF/lib/juplo*
-
-mvn tomcat:run-war
diff --git a/examples/static/jetty.sh b/examples/static/jetty.sh
deleted file mode 100755 (executable)
index 4950796..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/bash
-#
-
-# OutOfMemoryException beim "mvn jetty:run" umgehen und
-# Parameter zum nachträglichen anhängen eines Debuggers
-# setzen
-export MAVEN_OPTS="-Xmx512m -XX:MaxPermSize=256m -XX:-UseGCOverheadLimit -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=4000,server=y,suspend=n"
-
-rm -v src/main/webapp/WEB-INF/lib/juplo*
-
-mvn jetty:run
diff --git a/examples/static/pom.xml b/examples/static/pom.xml
deleted file mode 100644 (file)
index 5cf0634..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-
-  <modelVersion>4.0.0</modelVersion>
-
-  <parent>
-    <groupId>de.halbekunst</groupId>
-    <artifactId>juplo-examples</artifactId>
-    <version>2.0-SNAPSHOT</version>
-  </parent>
-
-  <artifactId>juplo-examples-static</artifactId>
-  <packaging>war</packaging>
-  <name>Juplo - Examples: Static Content</name>
-
-  <dependencies>
-    <dependency>
-      <groupId>org.apache.commons</groupId>
-      <artifactId>commons-io</artifactId>
-      <version>${commons-io.version}</version>
-      <scope>test</scope>
-    </dependency>
-  </dependencies>
-
-</project>
diff --git a/examples/static/src/main/resources/config.xml b/examples/static/src/main/resources/config.xml
deleted file mode 100644 (file)
index e414ce9..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<beans xmlns="http://www.springframework.org/schema/beans"
-       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-       xmlns:context="http://www.springframework.org/schema/context"
-       xsi:schemaLocation="
-           http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
-           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
-
-  <!-- Activates the AspectJ-Weaver -->
-  <context:component-scan base-package="de.halbekunst"/>
-  <context:spring-configured/>
-
-  <bean id="eTag" class="java.lang.String">
-    <constructor-arg value="Hallo Welt!"/>
-  </bean>
-
-  <bean id="weak" class="java.lang.Boolean">
-    <constructor-arg value="true"/>
-  </bean>
-
-  <bean id="lastModified" class="java.lang.Long">
-    <constructor-arg value="1338593731"/>
-  </bean>
-
-  <bean id="cacheSeconds" class="java.lang.Integer">
-    <constructor-arg value="3600"/>
-  </bean>
-
-</beans>
diff --git a/examples/static/src/main/resources/log4j.xml b/examples/static/src/main/resources/log4j.xml
deleted file mode 100644 (file)
index d3414bd..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
-
-<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
-
-  <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
-    <layout class="org.apache.log4j.PatternLayout">
-      <param name="ConversionPattern" value="%p - %C{1}.%M(%L) | %m%n"/>
-    </layout>
-  </appender>
-
-  <logger name="de.halbekunst">
-   <level value="trace"/>
-  </logger>
-
-  <root>
-    <level value="info"/>
-    <appender-ref ref="CONSOLE"/>
-  </root>
-
-</log4j:configuration>
diff --git a/examples/static/src/main/webapp/WEB-INF/web.xml b/examples/static/src/main/webapp/WEB-INF/web.xml
deleted file mode 100644 (file)
index 2df85a5..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
-         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
-
-  <!-- Context Configuration locations for Spring XML files -->
-
-  <context-param>
-    <param-name>contextConfigLocation</param-name>
-    <param-value>classpath:/config.xml</param-value>
-  </context-param>
-
-
-  <!-- Listener-Definitions -->
-
-  <listener>
-    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
-  </listener>
-
-
-  <!-- Filter-Definitions -->
-
-  <filter>
-    <filter-name>accelerator</filter-name>
-    <filter-class>de.halbekunst.juplo.cachecontrol.AcceleratorFilter</filter-class>
-  </filter>
-
-  <filter>
-    <filter-name>logger</filter-name>
-    <filter-class>de.halbekunst.juplo.test.LoggingHttpServletResponseFilter</filter-class>
-  </filter>
-
-
-  <!-- Filter-Mappings -->
-
-  <filter-mapping>
-    <filter-name>logger</filter-name>
-    <url-pattern>/static/*</url-pattern>
-  </filter-mapping>
-
-  <filter-mapping>
-    <filter-name>accelerator</filter-name>
-    <url-pattern>/static/*</url-pattern>
-  </filter-mapping>
-
-
-  <!-- Servlet-Definitions -->
-
-  <servlet>
-    <servlet-name>default</servlet-name>
-    <servlet-class>org.eclipse.jetty.servlet.DefaultServlet</servlet-class>
-  </servlet>
-
-
-  <!-- Servlet-Mappings -->
-
-  <servlet-mapping>
-    <servlet-name>default</servlet-name>
-    <url-pattern>/</url-pattern>
-  </servlet-mapping>
-
-
-</web-app>
diff --git a/examples/static/src/main/webapp/index.html b/examples/static/src/main/webapp/index.html
deleted file mode 100644 (file)
index 22c61d0..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-
-<html>
-  <head>
-    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
-    <title>Examples for Static Content</title>
-  </head>
-  <body>
-    <h1>Examples for Static Content</h1>
-    <ul>
-      <li><a href="/static/page.html">A plain static HTML-file</a></li>
-      <li><a href="/static/stylesheets.css">A plain static CSS-file</a></li>
-    </ul>
-  </body>
-</html>
diff --git a/examples/static/src/main/webapp/static/page.html b/examples/static/src/main/webapp/static/page.html
deleted file mode 100644 (file)
index f851795..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-
-<html>
-  <head>
-    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
-    <title>A Static Page</title>
-    <link rel="stylesheet" type="text/css" media="all" href="/static/stylesheets.css">
-  </head>
-  <body>
-    <h1>A Static Page</h1>
-    <p>This page is a static HTML-page</p>
-  </body>
-</html>
diff --git a/examples/static/src/main/webapp/static/stylesheets.css b/examples/static/src/main/webapp/static/stylesheets.css
deleted file mode 100644 (file)
index e04bba2..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-body {
-  background-color: #ccc;
-  color: #444;
-}
-h1,h2,h3,h4 {
-  color: #000;
-}
\ No newline at end of file
diff --git a/examples/static/src/test/java/de/halbekunst/cachecontrol/examples/StaticTest.java b/examples/static/src/test/java/de/halbekunst/cachecontrol/examples/StaticTest.java
deleted file mode 100644 (file)
index e66a508..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-package de.halbekunst.cachecontrol.examples;
-
-import de.halbekunst.juplo.test.HttpTestCase;
-import com.meterware.httpunit.WebResponse;
-import org.junit.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- *
- * @author kai
- */
-public class StaticTest extends HttpTestCase {
-
-  private static final Logger log = LoggerFactory.getLogger(StaticTest.class);
-
-
-  public StaticTest() {
-    super("src/main/webapp/WEB-INF/web.xml");
-  }
-
-  @Test
-  public void testStaticContent() throws Exception {
-    WebResponse response = executeRequest("http://localhost:8080/static/page.html");
-    log.info("Title:\t\t{}", response.getTitle());
-    log.debug("Text:\t\t{}", response.getText());
-  }
-}
diff --git a/examples/static/src/test/java/org/eclipse/jetty/servlet/DefaultServlet.java b/examples/static/src/test/java/org/eclipse/jetty/servlet/DefaultServlet.java
deleted file mode 100644 (file)
index 0173a1f..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-package org.eclipse.jetty.servlet;
-
-import java.io.FileInputStream;
-import java.io.IOException;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.apache.commons.io.IOUtils;
-
-/**
- * Möglichst simple Fake-Implementierung für die Ausführung des Testfalls
- *
- * @author kai
- */
-public class DefaultServlet extends HttpServlet {
-
-  @Override
-  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
-    String path = getServletContext().getRealPath(request.getRequestURI());
-    if (path == null) {
-      response.sendError(HttpServletResponse.SC_NOT_FOUND);
-      return;
-    }
-    response.setContentType("text/html");
-    IOUtils.copy(new FileInputStream(path), response.getOutputStream());
-  }
-}
diff --git a/maven-plugins/hibernate4/pom.xml b/maven-plugins/hibernate4/pom.xml
new file mode 100644 (file)
index 0000000..a5f7e01
--- /dev/null
@@ -0,0 +1,172 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <groupId>de.juplo</groupId>
+  <artifactId>hibernate4-maven-plugin</artifactId>
+  <name>Juplo - Maven-Plugins: Hibernate 4 Maven Mojo</name>
+  <version>1.0</version>
+  <packaging>maven-plugin</packaging>
+  <url>http://www.juplo.de/maven-plugins/hibernate4</url>
+
+  <prerequisites>
+    <maven>2.0.6</maven>
+  </prerequisites>
+
+  <developers>
+    <developer>
+      <id>kai</id>
+      <name>Kai Moritz</name>
+      <email>kai@juplo.de</email>
+    </developer>
+  </developers>
+
+  <properties>
+    <!-- Zeichensatz -->
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <!-- Verwendete Versionen -->
+    <hibernate-core.version>4.1.5.SP1</hibernate-core.version>
+    <maven.version>3.0.4</maven.version>
+    <maven-plugin-log4j.version>1.0.1</maven-plugin-log4j.version>
+    <scannotation.version>1.0.2</scannotation.version>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-core</artifactId>
+      <version>${maven.version}</version>
+      <exclusions>
+        <exclusion>
+          <groupId>org.codehaus.plexus</groupId>
+          <artifactId>plexus-utils</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-plugin-api</artifactId>
+      <version>${maven.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.hibernate</groupId>
+      <artifactId>hibernate-core</artifactId>
+      <version>${hibernate-core.version}</version>
+      <exclusions>
+        <exclusion>
+          <groupId>org.jboss.logging</groupId>
+          <artifactId>jboss-logging</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+    <dependency>
+      <groupId>net.sf.scannotation</groupId>
+      <artifactId>scannotation</artifactId>
+      <version>${scannotation.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.pyx4j</groupId>
+      <artifactId>maven-plugin-log4j</artifactId>
+      <version>${maven-plugin-log4j.version}</version>
+      <exclusions>
+        <exclusion>
+          <groupId>org.apache.maven</groupId>
+          <artifactId>maven-artifact</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>org.apache.maven</groupId>
+          <artifactId>maven-plugin-api</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+  </dependencies>
+
+  <distributionManagement>
+    <repository>
+      <id>juplo.internal</id>
+      <name>Internal Release Repository</name>
+      <url>http://juplo.de/archiva/repository/internal/</url>
+    </repository>
+    <snapshotRepository>
+      <id>juplo.snapshots</id>
+      <name>Internal Snapshot Repository</name>
+      <url>http://juplo.de/archiva/repository/snapshots/</url>
+    </snapshotRepository>
+  </distributionManagement>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <configuration>
+          <source>1.6</source>
+          <target>1.6</target>
+          <encoding>utf8</encoding>
+          <showWarnings>true</showWarnings>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-dependency-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>install</id>
+            <phase>install</phase>
+            <goals>
+              <goal>sources</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-source-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>attach-sources</id>
+            <phase>verify</phase>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+  <reporting>
+    <plugins>
+      <plugin>
+        <artifactId>maven-changes-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>cobertura-maven-plugin</artifactId>
+        <version>2.0</version>
+      </plugin>
+      <plugin>
+        <artifactId>maven-javadoc-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <artifactId>maven-jxr-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <artifactId>maven-pmd-plugin</artifactId>
+        <configuration>
+          <linkXref>true</linkXref>
+          <targetJdk>1.5</targetJdk>
+        </configuration>
+      </plugin>
+      <plugin>
+        <artifactId>maven-surefire-report-plugin</artifactId>
+        <version>2.7.2</version>
+      </plugin>
+    </plugins>
+  </reporting>
+
+</project>
diff --git a/maven-plugins/hibernate4/src/main/java/de/juplo/plugins/hibernate4/Hbm2DdlMojo.java b/maven-plugins/hibernate4/src/main/java/de/juplo/plugins/hibernate4/Hbm2DdlMojo.java
new file mode 100644 (file)
index 0000000..f2fd232
--- /dev/null
@@ -0,0 +1,433 @@
+package de.juplo.plugins.hibernate4;
+
+/*
+ * Copyright 2001-2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import com.pyx4j.log4j.MavenLogAppender;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.sql.Connection;
+import java.sql.Driver;
+import java.sql.DriverManager;
+import java.sql.DriverPropertyInfo;
+import java.sql.SQLException;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.Properties;
+import java.util.Set;
+import javax.persistence.Entity;
+import javax.persistence.Embeddable;
+import javax.persistence.MappedSuperclass;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.project.MavenProject;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.tool.hbm2ddl.SchemaExport;
+import org.hibernate.tool.hbm2ddl.SchemaExport.Type;
+import org.hibernate.tool.hbm2ddl.Target;
+import org.scannotation.AnnotationDB;
+
+
+/**
+ * Goal which extracts the hibernate-mapping-configuration and
+ * exports an according SQL-database-schema.
+ *
+ * @goal export
+ * @phase process-classes
+ * @threadSafe
+ * @requiresDependencyResolution runtime
+ */
+public class Hbm2DdlMojo extends AbstractMojo
+{
+  /**
+   * The project whose project files to create.
+   *
+   * @parameter expression="${project}"
+   * @required
+   * @readonly
+   */
+  private MavenProject project;
+
+  /**
+   * Directories to scan.
+   *
+   * @parameter expression="${project.build.outputDirectory}"
+   */
+  private String outputDirectory;
+
+  /**
+   * Skip execution
+   *
+   * @parameter default-value="false"
+   */
+  private boolean skip;
+
+  /**
+   * SQL-Driver name.
+   *
+   * @parameter expression="${hibernate.connection.driver_class}
+   */
+  private String driverClassName;
+
+  /**
+   * Database URL.
+   *
+   * @parameter expression="${hibernate.connection.url}"
+   */
+  private String url;
+
+  /**
+   * Database username
+   *
+   * @parameter expression="${hibernate.connection.username}"
+   */
+  private String username;
+
+  /**
+   * Database password
+   *
+   * @parameter expression="${hibernate.connection.password}"
+   */
+  private String password;
+
+  /**
+   * Hibernate dialect.
+   *
+   * @parameter expression="${hibernate.dialect}"
+   */
+  private String hibernateDialect;
+
+  /**
+   * Hibernate configuration file.
+   *
+   * @parameter default-value="${project.build.outputDirectory}/hibernate.properties"
+   */
+  private String hibernateProperties;
+
+  /**
+   * Target of execution:
+   * <ul>
+   *   <li><strong>NONE</strong> do nothing - just validate the configuration</li>
+   *   <li><strong>EXPORT</strong> create database <strong>(DEFAULT!)</strong></li>
+   *   <li><strong>SCRIPT</strong> export schema to SQL-script</li>
+   *   <li><strong>BOTH</strong></li>
+   * </ul>
+   * @parameter default-value="EXPORT"
+   */
+  private String target;
+
+  /**
+   * Type of export.
+   * <ul>
+   *   <li><strong>NONE</strong> do nothing - just validate the configuration</li>
+   *   <li><strong>CREATE</strong> create database-schema</li>
+   *   <li><strong>DROP</strong> drop database-schema</li>
+   *   <li><strong>BOTH</strong> <strong>(DEFAULT!)</strong></li>
+   * </ul>
+   * @parameter default-value="BOTH"
+   */
+  private String type;
+
+  /**
+   * Output file.
+   *
+   * @parameter default-value="${project.build.outputDirectory}/schema.sql"
+   */
+  private String outputFile;
+
+  /**
+   * Delimiter in output-file.
+   *
+   * @parameter default-value=";"
+   */
+  private String delimiter;
+
+  /**
+   * Format output-file.
+   *
+   * @parameter default-value="true"
+   */
+  private boolean format;
+
+
+  @Override
+  public void execute()
+      throws
+        MojoFailureException,
+        MojoExecutionException
+  {
+    if (skip)
+      return;
+
+    File dir = new File(outputDirectory);
+    if (!dir.exists())
+      throw new MojoExecutionException("Cannot scan for annotated classes in " + outputDirectory + ": directory does not exist!");
+
+
+    Set<String> classes = new HashSet<String>();
+    URL dirUrl = null;
+    try {
+      AnnotationDB db = new AnnotationDB();
+      getLog().info("Scanning directory " + outputDirectory + " for annotated classes...");
+      dirUrl = dir.toURI().toURL();
+      db.scanArchives(dirUrl);
+      if (db.getAnnotationIndex().containsKey(Entity.class.getName()))
+        classes.addAll(db.getAnnotationIndex().get(Entity.class.getName()));
+      if (db.getAnnotationIndex().containsKey(MappedSuperclass.class.getName()))
+        classes.addAll(db.getAnnotationIndex().get(MappedSuperclass.class.getName()));
+      if (db.getAnnotationIndex().containsKey(Embeddable.class.getName()))
+        classes.addAll(db.getAnnotationIndex().get(Embeddable.class.getName()));
+    }
+    catch (IOException e) {
+      getLog().error("Error while scanning!", e);
+      throw new MojoFailureException(e.getMessage());
+    }
+    if (classes.isEmpty())
+      throw new MojoFailureException("No annotated classes found in directory " + outputDirectory);
+
+    Properties properties = new Properties();
+
+    /** Try to read configuration from properties-file */
+    try {
+      File file = new File(hibernateProperties);
+      if (file.exists()) {
+        getLog().info("Reading properties from file " + hibernateProperties + "...");
+        properties.load(new FileInputStream(file));
+      }
+      else
+        getLog().info("Ignoring nonexistent properties-file " + hibernateProperties + "!");
+    }
+    catch (IOException e) {
+      getLog().error("Error while reading properties!", e);
+      throw new MojoExecutionException(e.getMessage());
+    }
+
+    /** Overwrite values from propertie-file or set  if given */
+    if (driverClassName != null)
+      properties.setProperty("hibernate.connection.driver_class", driverClassName);
+    if (url != null)
+      properties.setProperty("hibernate.connection.url", url);
+    if (username != null)
+      properties.setProperty("hibernate.connection.username", username);
+    if (password != null)
+      properties.setProperty("hibernate.connection.password", password);
+    if (hibernateDialect != null)
+      properties.setProperty("hibernate.dialect", hibernateDialect);
+
+    if (properties.isEmpty())
+      getLog().warn("No properties set!");
+    for (Entry<Object,Object> entry : properties.entrySet())
+      getLog().debug(entry.getKey() + " = " + entry.getValue());
+
+    ClassLoader classLoader = null;
+    try {
+      getLog().debug("Creating ClassLoader for project-dependencies...");
+      List<String> classpathFiles = project.getCompileClasspathElements();
+      URL[] urls = new URL[classpathFiles.size()];
+      for (int i = 0; i < classpathFiles.size(); ++i) {
+        getLog().debug("Dependency: " + classpathFiles.get(i));
+        urls[i] = new File(classpathFiles.get(i)).toURI().toURL();
+      }
+      classLoader = new URLClassLoader(urls, getClass().getClassLoader());
+    }
+    catch (Exception e) {
+      getLog().error("Error while creating ClassLoader!", e);
+      throw new MojoExecutionException(e.getMessage());
+    }
+
+    Configuration config = new Configuration();
+    config.setProperties(properties);
+    try {
+      getLog().debug("Adding annotated classes to hibernate-mapping-configuration...");
+      for (String annotatedClass : classes) {
+        getLog().debug("Class " + annotatedClass);
+        config.addAnnotatedClass(classLoader.loadClass(annotatedClass));
+      }
+    }
+    catch (ClassNotFoundException e) {
+      getLog().error("Error while adding annotated classes!", e);
+      throw new MojoExecutionException(e.getMessage());
+    }
+
+    Target target = null;
+    try {
+      target = Target.valueOf(this.target);
+    }
+    catch (IllegalArgumentException e) {
+      getLog().error("Invalid value for configuration-option \"target\": " + this.target);
+      getLog().error("Valid values are: NONE, SCRIPT, EXPORT, BOTH");
+      throw new MojoExecutionException("Invalid value for configuration-option \"target\"");
+    }
+    Type type = null;
+    try {
+      type = Type.valueOf(this.type);
+    }
+    catch (IllegalArgumentException e) {
+      getLog().error("Invalid value for configuration-option \"type\": " + this.type);
+      getLog().error("Valid values are: NONE, CREATE, DROP, BOTH");
+      throw new MojoExecutionException("Invalid value for configuration-option \"type\"");
+    }
+
+    Connection connection = null;
+    try {
+      /**
+       * The connection must be established outside of hibernate, because
+       * hibernate does not use the context-classloader of the current
+       * thread and, hence, would not be able to resolve the driver-class!
+       */
+      switch (target) {
+        case EXPORT:
+        case BOTH:
+          switch (type) {
+            case CREATE:
+            case DROP:
+            case BOTH:
+              Class driverClass = classLoader.loadClass(driverClassName);
+              getLog().debug("Registering JDBC-driver " + driverClass.getName());
+              DriverManager.registerDriver(new DriverProxy((Driver)driverClass.newInstance()));
+              getLog().debug("Opening JDBC-connection to " + url + " as " + username + " with password " + password);
+              connection = DriverManager.getConnection(url, username, password);
+          }
+      }
+    }
+    catch (ClassNotFoundException e) {
+      getLog().error("Dependency for driver-class " + driverClassName + " is missing!");
+      throw new MojoExecutionException(e.getMessage());
+    }
+    catch (Exception e) {
+      getLog().error("Cannot establish connection to database!");
+      Enumeration<Driver> drivers = DriverManager.getDrivers();
+      if (!drivers.hasMoreElements())
+        getLog().error("No drivers registered!");
+      while (drivers.hasMoreElements())
+        getLog().debug("Driver: " + drivers.nextElement());
+      throw new MojoExecutionException(e.getMessage());
+    }
+
+    ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+    MavenLogAppender.startPluginLog(this);
+    try {
+      /**
+       * Change class-loader of current thread, so that hibernate can
+       * see all dependencies!
+       */
+      Thread.currentThread().setContextClassLoader(classLoader);
+
+      SchemaExport export = new SchemaExport(config, connection);
+      export.setOutputFile(outputFile);
+      export.setDelimiter(delimiter);
+      export.setFormat(format);
+      export.execute(target, type);
+
+      for (Object exception : export.getExceptions())
+        getLog().debug(exception.toString());
+    }
+    finally {
+      /** Stop Log-Capturing */
+      MavenLogAppender.endPluginLog(this);
+
+      /** Restore the old class-loader (TODO: is this really necessary?) */
+      Thread.currentThread().setContextClassLoader(contextClassLoader);
+
+      /** Close the connection */
+      try {
+        connection.close();
+      }
+      catch (SQLException e) {
+        getLog().error("Error while closing connection: " + e.getMessage());
+      }
+    }
+  }
+
+  /**
+   * Needed, because DriverManager won't pick up drivers, that were not
+   * loaded by the system-classloader!
+   * See:
+   * http://stackoverflow.com/questions/288828/how-to-use-a-jdbc-driver-from-an-arbitrary-location
+   */
+  static final class DriverProxy implements Driver {
+
+    private final Driver target;
+
+    DriverProxy(Driver target) {
+      if (target == null) {
+        throw new NullPointerException();
+      }
+      this.target = target;
+    }
+
+    public java.sql.Driver getTarget() {
+      return target;
+    }
+
+    @Override
+    public boolean acceptsURL(String url) throws SQLException {
+      return target.acceptsURL(url);
+    }
+
+    @Override
+    public java.sql.Connection connect(
+        String url, java.util.Properties info) throws SQLException {
+      return target.connect(url, info);
+    }
+
+    @Override
+    public int getMajorVersion() {
+      return target.getMajorVersion();
+    }
+
+    @Override
+    public int getMinorVersion() {
+      return target.getMinorVersion();
+    }
+
+    @Override
+    public DriverPropertyInfo[] getPropertyInfo(
+        String url, Properties info) throws SQLException {
+      return target.getPropertyInfo(url, info);
+    }
+
+    @Override
+    public boolean jdbcCompliant() {
+      return target.jdbcCompliant();
+    }
+
+    @Override
+    public String toString() {
+      return "Proxy: " + target;
+    }
+
+    @Override
+    public int hashCode() {
+      return target.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      if (!(obj instanceof DriverProxy)) {
+        return false;
+      }
+      DriverProxy other = (DriverProxy) obj;
+      return this.target.equals(other.target);
+    }
+}
+}
diff --git a/maven-plugins/pom.xml b/maven-plugins/pom.xml
new file mode 100644 (file)
index 0000000..8484172
--- /dev/null
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>de.juplo</groupId>
+    <artifactId>juplo</artifactId>
+    <version>1.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>maven-plugins</artifactId>
+  <packaging>pom</packaging>
+  <name>Juplo - Maven-Plugins</name>
+
+  <modules>
+    <module>hibernate4</module>
+  </modules>
+
+</project>
diff --git a/maven/hibernate4-maven-plugin/pom.xml b/maven/hibernate4-maven-plugin/pom.xml
deleted file mode 100644 (file)
index 2c480b9..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-
-  <modelVersion>4.0.0</modelVersion>
-
-  <parent>
-    <groupId>de.halbekunst</groupId>
-    <artifactId>juplo-maven</artifactId>
-    <version>2.0-SNAPSHOT</version>
-  </parent>
-
-  <artifactId>hibernate4-maven-plugin</artifactId>
-  <packaging>maven-plugin</packaging>
-  <name>Juplo - Maven-Plugins: Hibernate 4 Maven Mojo</name>
-  <url>http://www.halbekunst.de</url>
-
-  <dependencies>
-    <dependency>
-      <groupId>org.apache.maven</groupId>
-      <artifactId>maven-core</artifactId>
-      <version>3.0.4</version>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.maven</groupId>
-      <artifactId>maven-plugin-api</artifactId>
-      <version>3.0.4</version>
-    </dependency>
-    <dependency>
-      <groupId>org.hibernate</groupId>
-      <artifactId>hibernate-core</artifactId>
-      <version>4.1.5.SP1</version>
-    </dependency>
-    <dependency>
-      <groupId>net.sf.scannotation</groupId>
-      <artifactId>scannotation</artifactId>
-      <version>1.0.2</version>
-    </dependency>
-    <dependency>
-      <groupId>com.pyx4j</groupId>
-      <artifactId>maven-plugin-log4j</artifactId>
-      <version>1.0.1</version>
-    </dependency>
-  </dependencies>
-
-</project>
diff --git a/maven/hibernate4-maven-plugin/src/main/java/de/halbekunst/Hbm2DdlMojo.java b/maven/hibernate4-maven-plugin/src/main/java/de/halbekunst/Hbm2DdlMojo.java
deleted file mode 100644 (file)
index 5844ee5..0000000
+++ /dev/null
@@ -1,433 +0,0 @@
-package de.halbekunst;
-
-/*
- * Copyright 2001-2005 The Apache Software Foundation.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import com.pyx4j.log4j.MavenLogAppender;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.sql.Connection;
-import java.sql.Driver;
-import java.sql.DriverManager;
-import java.sql.DriverPropertyInfo;
-import java.sql.SQLException;
-import java.util.Enumeration;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map.Entry;
-import java.util.Properties;
-import java.util.Set;
-import javax.persistence.Entity;
-import javax.persistence.Embeddable;
-import javax.persistence.MappedSuperclass;
-import org.apache.maven.plugin.AbstractMojo;
-import org.apache.maven.plugin.MojoExecutionException;
-import org.apache.maven.plugin.MojoFailureException;
-import org.apache.maven.project.MavenProject;
-import org.hibernate.cfg.Configuration;
-import org.hibernate.tool.hbm2ddl.SchemaExport;
-import org.hibernate.tool.hbm2ddl.SchemaExport.Type;
-import org.hibernate.tool.hbm2ddl.Target;
-import org.scannotation.AnnotationDB;
-
-
-/**
- * Goal which extracts the hibernate-mapping-configuration and
- * exports an according SQL-database-schema.
- *
- * @goal export
- * @phase process-classes
- * @threadSafe
- * @requiresDependencyResolution runtime
- */
-public class Hbm2DdlMojo extends AbstractMojo
-{
-  /**
-   * The project whose project files to create.
-   *
-   * @parameter expression="${project}"
-   * @required
-   * @readonly
-   */
-  private MavenProject project;
-
-  /**
-   * Directories to scan.
-   *
-   * @parameter expression="${project.build.outputDirectory}"
-   */
-  private String outputDirectory;
-
-  /**
-   * Skip execution
-   *
-   * @parameter default-value="false"
-   */
-  private boolean skip;
-
-  /**
-   * SQL-Driver name.
-   *
-   * @parameter expression="${hibernate.connection.driver_class}
-   */
-  private String driverClassName;
-
-  /**
-   * Database URL.
-   *
-   * @parameter expression="${hibernate.connection.url}"
-   */
-  private String url;
-
-  /**
-   * Database username
-   *
-   * @parameter expression="${hibernate.connection.username}"
-   */
-  private String username;
-
-  /**
-   * Database password
-   *
-   * @parameter expression="${hibernate.connection.password}"
-   */
-  private String password;
-
-  /**
-   * Hibernate dialect.
-   *
-   * @parameter expression="${hibernate.dialect}"
-   */
-  private String hibernateDialect;
-
-  /**
-   * Hibernate configuration file.
-   *
-   * @parameter default-value="${project.build.outputDirectory}/hibernate.properties"
-   */
-  private String hibernateProperties;
-
-  /**
-   * Target of execution:
-   * <ul>
-   *   <li><strong>NONE</strong> do nothing - just validate the configuration</li>
-   *   <li><strong>EXPORT</strong> create database <strong>(DEFAULT!)</strong></li>
-   *   <li><strong>SCRIPT</strong> export schema to SQL-script</li>
-   *   <li><strong>BOTH</strong></li>
-   * </ul>
-   * @parameter default-value="EXPORT"
-   */
-  private String target;
-
-  /**
-   * Type of export.
-   * <ul>
-   *   <li><strong>NONE</strong> do nothing - just validate the configuration</li>
-   *   <li><strong>CREATE</strong> create database-schema</li>
-   *   <li><strong>DROP</strong> drop database-schema</li>
-   *   <li><strong>BOTH</strong> <strong>(DEFAULT!)</strong></li>
-   * </ul>
-   * @parameter default-value="BOTH"
-   */
-  private String type;
-
-  /**
-   * Output file.
-   *
-   * @parameter default-value="${project.build.outputDirectory}/schema.sql"
-   */
-  private String outputFile;
-
-  /**
-   * Delimiter in output-file.
-   *
-   * @parameter default-value=";"
-   */
-  private String delimiter;
-
-  /**
-   * Format output-file.
-   *
-   * @parameter default-value="true"
-   */
-  private boolean format;
-
-
-  @Override
-  public void execute()
-      throws
-        MojoFailureException,
-        MojoExecutionException
-  {
-    if (skip)
-      return;
-
-    File dir = new File(outputDirectory);
-    if (!dir.exists())
-      throw new MojoExecutionException("Cannot scan for annotated classes in " + outputDirectory + ": directory does not exist!");
-
-
-    Set<String> classes = new HashSet<String>();
-    URL dirUrl = null;
-    try {
-      AnnotationDB db = new AnnotationDB();
-      getLog().info("Scanning directory " + outputDirectory + " for annotated classes...");
-      dirUrl = dir.toURI().toURL();
-      db.scanArchives(dirUrl);
-      if (db.getAnnotationIndex().containsKey(Entity.class.getName()))
-        classes.addAll(db.getAnnotationIndex().get(Entity.class.getName()));
-      if (db.getAnnotationIndex().containsKey(MappedSuperclass.class.getName()))
-        classes.addAll(db.getAnnotationIndex().get(MappedSuperclass.class.getName()));
-      if (db.getAnnotationIndex().containsKey(Embeddable.class.getName()))
-        classes.addAll(db.getAnnotationIndex().get(Embeddable.class.getName()));
-    }
-    catch (IOException e) {
-      getLog().error("Error while scanning!", e);
-      throw new MojoFailureException(e.getMessage());
-    }
-    if (classes.isEmpty())
-      throw new MojoFailureException("No annotated classes found in directory " + outputDirectory);
-
-    Properties properties = new Properties();
-
-    /** Try to read configuration from properties-file */
-    try {
-      File file = new File(hibernateProperties);
-      if (file.exists()) {
-        getLog().info("Reading properties from file " + hibernateProperties + "...");
-        properties.load(new FileInputStream(file));
-      }
-      else
-        getLog().info("Ignoring nonexistent properties-file " + hibernateProperties + "!");
-    }
-    catch (IOException e) {
-      getLog().error("Error while reading properties!", e);
-      throw new MojoExecutionException(e.getMessage());
-    }
-
-    /** Overwrite values from propertie-file or set  if given */
-    if (driverClassName != null)
-      properties.setProperty("hibernate.connection.driver_class", driverClassName);
-    if (url != null)
-      properties.setProperty("hibernate.connection.url", url);
-    if (username != null)
-      properties.setProperty("hibernate.connection.username", username);
-    if (password != null)
-      properties.setProperty("hibernate.connection.password", password);
-    if (hibernateDialect != null)
-      properties.setProperty("hibernate.dialect", hibernateDialect);
-
-    if (properties.isEmpty())
-      getLog().warn("No properties set!");
-    for (Entry<Object,Object> entry : properties.entrySet())
-      getLog().debug(entry.getKey() + " = " + entry.getValue());
-
-    ClassLoader classLoader = null;
-    try {
-      getLog().debug("Creating ClassLoader for project-dependencies...");
-      List<String> classpathFiles = project.getCompileClasspathElements();
-      URL[] urls = new URL[classpathFiles.size()];
-      for (int i = 0; i < classpathFiles.size(); ++i) {
-        getLog().debug("Dependency: " + classpathFiles.get(i));
-        urls[i] = new File(classpathFiles.get(i)).toURI().toURL();
-      }
-      classLoader = new URLClassLoader(urls, getClass().getClassLoader());
-    }
-    catch (Exception e) {
-      getLog().error("Error while creating ClassLoader!", e);
-      throw new MojoExecutionException(e.getMessage());
-    }
-
-    Configuration config = new Configuration();
-    config.setProperties(properties);
-    try {
-      getLog().debug("Adding annotated classes to hibernate-mapping-configuration...");
-      for (String annotatedClass : classes) {
-        getLog().debug("Class " + annotatedClass);
-        config.addAnnotatedClass(classLoader.loadClass(annotatedClass));
-      }
-    }
-    catch (ClassNotFoundException e) {
-      getLog().error("Error while adding annotated classes!", e);
-      throw new MojoExecutionException(e.getMessage());
-    }
-
-    Target target = null;
-    try {
-      target = Target.valueOf(this.target);
-    }
-    catch (IllegalArgumentException e) {
-      getLog().error("Invalid value for configuration-option \"target\": " + this.target);
-      getLog().error("Valid values are: NONE, SCRIPT, EXPORT, BOTH");
-      throw new MojoExecutionException("Invalid value for configuration-option \"target\"");
-    }
-    Type type = null;
-    try {
-      type = Type.valueOf(this.type);
-    }
-    catch (IllegalArgumentException e) {
-      getLog().error("Invalid value for configuration-option \"type\": " + this.type);
-      getLog().error("Valid values are: NONE, CREATE, DROP, BOTH");
-      throw new MojoExecutionException("Invalid value for configuration-option \"type\"");
-    }
-
-    Connection connection = null;
-    try {
-      /**
-       * The connection must be established outside of hibernate, because
-       * hibernate does not use the context-classloader of the current
-       * thread and, hence, would not be able to resolve the driver-class!
-       */
-      switch (target) {
-        case EXPORT:
-        case BOTH:
-          switch (type) {
-            case CREATE:
-            case DROP:
-            case BOTH:
-              Class driverClass = classLoader.loadClass(driverClassName);
-              getLog().debug("Registering JDBC-driver " + driverClass.getName());
-              DriverManager.registerDriver(new DriverProxy((Driver)driverClass.newInstance()));
-              getLog().debug("Opening JDBC-connection to " + url + " as " + username + " with password " + password);
-              connection = DriverManager.getConnection(url, username, password);
-          }
-      }
-    }
-    catch (ClassNotFoundException e) {
-      getLog().error("Dependency for driver-class " + driverClassName + " is missing!");
-      throw new MojoExecutionException(e.getMessage());
-    }
-    catch (Exception e) {
-      getLog().error("Cannot establish connection to database!");
-      Enumeration<Driver> drivers = DriverManager.getDrivers();
-      if (!drivers.hasMoreElements())
-        getLog().error("No drivers registered!");
-      while (drivers.hasMoreElements())
-        getLog().debug("Driver: " + drivers.nextElement());
-      throw new MojoExecutionException(e.getMessage());
-    }
-
-    ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
-    MavenLogAppender.startPluginLog(this);
-    try {
-      /**
-       * Change class-loader of current thread, so that hibernate can
-       * see all dependencies!
-       */
-      Thread.currentThread().setContextClassLoader(classLoader);
-
-      SchemaExport export = new SchemaExport(config, connection);
-      export.setOutputFile(outputFile);
-      export.setDelimiter(delimiter);
-      export.setFormat(format);
-      export.execute(target, type);
-
-      for (Object exception : export.getExceptions())
-        getLog().debug(exception.toString());
-    }
-    finally {
-      /** Stop Log-Capturing */
-      MavenLogAppender.endPluginLog(this);
-
-      /** Restore the old class-loader (TODO: is this really necessary?) */
-      Thread.currentThread().setContextClassLoader(contextClassLoader);
-
-      /** Close the connection */
-      try {
-        connection.close();
-      }
-      catch (SQLException e) {
-        getLog().error("Error while closing connection: " + e.getMessage());
-      }
-    }
-  }
-
-  /**
-   * Needed, because DriverManager won't pick up drivers, that were not
-   * loaded by the system-classloader!
-   * See:
-   * http://stackoverflow.com/questions/288828/how-to-use-a-jdbc-driver-from-an-arbitrary-location
-   */
-  static final class DriverProxy implements Driver {
-
-    private final Driver target;
-
-    DriverProxy(Driver target) {
-      if (target == null) {
-        throw new NullPointerException();
-      }
-      this.target = target;
-    }
-
-    public java.sql.Driver getTarget() {
-      return target;
-    }
-
-    @Override
-    public boolean acceptsURL(String url) throws SQLException {
-      return target.acceptsURL(url);
-    }
-
-    @Override
-    public java.sql.Connection connect(
-        String url, java.util.Properties info) throws SQLException {
-      return target.connect(url, info);
-    }
-
-    @Override
-    public int getMajorVersion() {
-      return target.getMajorVersion();
-    }
-
-    @Override
-    public int getMinorVersion() {
-      return target.getMinorVersion();
-    }
-
-    @Override
-    public DriverPropertyInfo[] getPropertyInfo(
-        String url, Properties info) throws SQLException {
-      return target.getPropertyInfo(url, info);
-    }
-
-    @Override
-    public boolean jdbcCompliant() {
-      return target.jdbcCompliant();
-    }
-
-    @Override
-    public String toString() {
-      return "Proxy: " + target;
-    }
-
-    @Override
-    public int hashCode() {
-      return target.hashCode();
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-      if (!(obj instanceof DriverProxy)) {
-        return false;
-      }
-      DriverProxy other = (DriverProxy) obj;
-      return this.target.equals(other.target);
-    }
-}
-}
diff --git a/maven/pom.xml b/maven/pom.xml
deleted file mode 100644 (file)
index 76b05d6..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-
-  <modelVersion>4.0.0</modelVersion>
-
-  <parent>
-    <groupId>de.halbekunst</groupId>
-    <artifactId>juplo</artifactId>
-    <version>2.0-SNAPSHOT</version>
-  </parent>
-
-  <artifactId>${pom.parent.artifactId}-maven</artifactId>
-  <packaging>pom</packaging>
-  <name>Juplo - Maven-Plugins</name>
-
-  <modules>
-    <module>hibernate4-maven-plugin</module>
-  </modules>
-</project>
\ No newline at end of file
diff --git a/percentcodec/pom.xml b/percentcodec/pom.xml
new file mode 100644 (file)
index 0000000..a0d7e56
--- /dev/null
@@ -0,0 +1,166 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <groupId>de.juplo</groupId>
+  <artifactId>juplo-percentcodec</artifactId>
+  <name>Juplo - Percent-Codec</name>
+  <version>1.0.1</version>
+  <url>http://www.juplo.de/percentcodec</url>
+
+  <prerequisites>
+    <maven>2.0.6</maven>
+  </prerequisites>
+
+  <developers>
+    <developer>
+      <id>kai</id>
+      <name>Kai Moritz</name>
+      <email>kai@juplo.de</email>
+    </developer>
+  </developers>
+
+  <properties>
+    <!-- Zeichensatz -->
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <!-- Verwendete Versionen -->
+    <junit.version>4.8.1</junit.version>
+    <log4j.version>1.2.16</log4j.version>
+    <slf4j.version>1.6.1</slf4j.version>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>${junit.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-log4j12</artifactId>
+      <version>${slf4j.version}</version>
+      <exclusions>
+        <exclusion>
+          <groupId>log4j</groupId>
+          <artifactId>log4j</artifactId>
+        </exclusion>
+      </exclusions>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>log4j</groupId>
+      <artifactId>log4j</artifactId>
+      <version>${log4j.version}</version>
+      <exclusions>
+        <exclusion>
+          <groupId>javax.mail</groupId>
+          <artifactId>mail</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>javax.jms</groupId>
+          <artifactId>jms</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>com.sun.jdmk</groupId>
+          <artifactId>jmxtools</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>com.sun.jmx</groupId>
+          <artifactId>jmxri</artifactId>
+        </exclusion>
+      </exclusions>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <distributionManagement>
+    <repository>
+      <id>juplo.internal</id>
+      <name>Internal Release Repository</name>
+      <url>http://juplo.de/archiva/repository/internal/</url>
+    </repository>
+    <snapshotRepository>
+      <id>juplo.snapshots</id>
+      <name>Internal Snapshot Repository</name>
+      <url>http://juplo.de/archiva/repository/snapshots/</url>
+    </snapshotRepository>
+  </distributionManagement>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <configuration>
+          <source>1.6</source>
+          <target>1.6</target>
+          <encoding>utf8</encoding>
+          <showWarnings>true</showWarnings>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-dependency-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>install</id>
+            <phase>install</phase>
+            <goals>
+              <goal>sources</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-source-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>attach-sources</id>
+            <phase>verify</phase>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+  <reporting>
+    <plugins>
+      <plugin>
+        <artifactId>maven-changes-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>cobertura-maven-plugin</artifactId>
+        <version>2.0</version>
+      </plugin>
+      <plugin>
+        <artifactId>maven-javadoc-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <artifactId>maven-jxr-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <artifactId>maven-pmd-plugin</artifactId>
+        <configuration>
+          <linkXref>true</linkXref>
+          <targetJdk>1.5</targetJdk>
+        </configuration>
+      </plugin>
+      <plugin>
+        <artifactId>maven-surefire-report-plugin</artifactId>
+        <version>2.7.2</version>
+      </plugin>
+    </plugins>
+  </reporting>
+
+</project>
diff --git a/percentcodec/src/main/java/de/juplo/percentcodec/PercentCodec.java b/percentcodec/src/main/java/de/juplo/percentcodec/PercentCodec.java
new file mode 100644 (file)
index 0000000..1a23c09
--- /dev/null
@@ -0,0 +1,198 @@
+package de.juplo.percentcodec;
+
+import java.nio.charset.Charset;
+
+/**
+ * This class performes percent-encoding/-decoding like described in RFC 3986.
+ * <p>
+ * Complete URI's are not handled by this implementation.
+ * That is done best with the original {@linkplain java.net.URI}-class from core Java.
+ * The purpose of this class is to have a simple tool to encode/decode the
+ * inner parts of an URI, like a segment of the URI-path (the part between two
+ * forward slashes) or a name or value segment of the query, where all reserved
+ * characters must be encoded/decoded.
+ *
+ * @author kai
+ */
+public class PercentCodec {
+  private final Charset charset;
+
+
+  public PercentCodec(String encoding) {
+    charset = Charset.forName(encoding);
+  }
+
+
+  public String encode(CharSequence in) {
+    StringBuilder out = new StringBuilder();
+    int i = 0;
+    int length = in.length();
+    while (i < length) {
+      int codePoint = Character.codePointAt(in, i);
+      i += Character.charCount(codePoint);
+      switch (codePoint) {
+        case 'a':
+        case 'A':
+        case 'b':
+        case 'B':
+        case 'c':
+        case 'C':
+        case 'd':
+        case 'D':
+        case 'e':
+        case 'E':
+        case 'f':
+        case 'F':
+        case 'g':
+        case 'G':
+        case 'h':
+        case 'H':
+        case 'i':
+        case 'I':
+        case 'j':
+        case 'J':
+        case 'k':
+        case 'K':
+        case 'l':
+        case 'L':
+        case 'm':
+        case 'M':
+        case 'n':
+        case 'N':
+        case 'o':
+        case 'O':
+        case 'p':
+        case 'P':
+        case 'q':
+        case 'Q':
+        case 'r':
+        case 'R':
+        case 's':
+        case 'S':
+        case 't':
+        case 'T':
+        case 'u':
+        case 'U':
+        case 'v':
+        case 'V':
+        case 'w':
+        case 'W':
+        case 'x':
+        case 'X':
+        case 'y':
+        case 'Y':
+        case 'z':
+        case 'Z':
+        case '0':
+        case '1':
+        case '2':
+        case '3':
+        case '4':
+        case '5':
+        case '6':
+        case '7':
+        case '8':
+        case '9':
+        case '-':
+        case '_':
+        case '.':
+        case '~':
+          /**
+           * Unreserved characters can (and should!) stay unchanged!
+           * (See {@link http://en.wikipedia.org/wiki/Percent-encoding#Types_of_URI_characters})
+           */
+          out.append(Character.toChars(codePoint));
+          break;
+        default:
+          /**
+           * All other characters are reserved or special characters and,
+           * hence, must be encoded!
+           */
+          String encoded = new String(Character.toChars(codePoint));
+          byte[] bytes = encoded.getBytes(charset);
+          for (int j = 0; j < bytes.length; j++) {
+            out.append('%');
+            out.append(Character.forDigit((bytes[j] >> 4) & 0xF, 16));
+            out.append(Character.forDigit((bytes[j]) & 0xF, 16));
+          }
+      }
+    }
+    return out.toString();
+  }
+
+  public String decode(CharSequence in) {
+    StringBuilder out = new StringBuilder();
+    int i = 0;
+    int length = in.length();
+    while (i < length) {
+      char c = in.charAt(i);
+      if (c != '%') {
+        out.append(c);
+        i++;
+      }
+      else {
+        byte[] bytes = new byte[length-i/3];
+        int pos = 0;
+        while (i+2 < length && in.charAt(i) == '%' ) {
+          int b = 0;
+          switch (in.charAt(i+1)) {
+            case '0': break;
+            case '1': b = 16*1; break;
+            case '2': b = 16*2; break;
+            case '3': b = 16*3; break;
+            case '4': b = 16*4; break;
+            case '5': b = 16*5; break;
+            case '6': b = 16*6; break;
+            case '7': b = 16*7; break;
+            case '8': b = 16*8; break;
+            case '9': b = 16*9; break;
+            case 'a':
+            case 'A': b = 16*10; break;
+            case 'b':
+            case 'B': b = 16*11; break;
+            case 'c':
+            case 'C': b = 16*12; break;
+            case 'd':
+            case 'D': b = 16*13; break;
+            case 'e':
+            case 'E': b = 16*14; break;
+            case 'f':
+            case 'F': b = 16*15; break;
+            default: throw new IllegalArgumentException("Illegal escape-sequence: %" + in.subSequence(i, i+3));
+          }
+          switch (in.charAt(i+2)) {
+            case '0': break;
+            case '1': b += 1; break;
+            case '2': b += 2; break;
+            case '3': b += 3; break;
+            case '4': b += 4; break;
+            case '5': b += 5; break;
+            case '6': b += 6; break;
+            case '7': b += 7; break;
+            case '8': b += 8; break;
+            case '9': b += 9; break;
+            case 'a':
+            case 'A': b += 10; break;
+            case 'b':
+            case 'B': b += 11; break;
+            case 'c':
+            case 'C': b += 12; break;
+            case 'd':
+            case 'D': b += 13; break;
+            case 'e':
+            case 'E': b += 14; break;
+            case 'f':
+            case 'F': b += 15; break;
+            default: throw new IllegalArgumentException("Illegal escape-sequence: %" + in.subSequence(i, i+3));
+          }
+          bytes[pos++] = (byte)b;
+          i += 3;
+        }
+        out.append(new String(bytes, 0, pos, charset));
+        if (i < length && in.charAt(i) == '%')
+          throw  new IllegalArgumentException("Incomplete escape-sequence: %" + in.subSequence(i, length));
+      }
+    }
+    return out.toString();
+  }
+}
diff --git a/percentcodec/src/test/java/de/juplo/percentcodec/PercentCodecTest.java b/percentcodec/src/test/java/de/juplo/percentcodec/PercentCodecTest.java
new file mode 100644 (file)
index 0000000..bec35fa
--- /dev/null
@@ -0,0 +1,115 @@
+package de.juplo.percentcodec;
+
+import de.juplo.percentcodec.PercentCodec;
+import junit.framework.Assert;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public class PercentCodecTest {
+  private final static Logger log = LoggerFactory.getLogger(PercentCodecTest.class);
+
+  public final static char[] decoded = { ' ', '+', 'q', 's', '8', '0', 'x', 'ä', 'ß', 'à', '€', '¢', '@', '/', '?', '#', ';','.', '&', '%' };
+  public final static String[] encoded_latin1 = { "%20", "%2b", "q", "s", "8", "0", "x", "%e4", "%df", "%e0", "%3f", "%a2", "%40", "%2f", "%3f", "%23", "%3b",".", "%26", "%25" };
+  public final static String[] encoded_utf8 = { "%20", "%2b", "q", "s", "8", "0", "x", "%c3%a4", "%c3%9f", "%c3%a0", "%e2%82%ac", "%c2%a2", "%40", "%2f", "%3f", "%23", "%3b",".", "%26", "%25" };
+
+
+  @Test
+  public void testEncodeLatin1() throws Exception {
+    PercentCodec codec = new PercentCodec("latin1");
+
+    for (int a = 0; a < decoded.length; a++) {
+      for (int b = 0; b < decoded.length; b++) {
+        for (int c = 0; c < decoded.length; c++) {
+          /** Das Zeichen '€' existiert in Latin1 nicht! */
+          if (a == 10 || b == 10 || c == 10)
+            continue;
+          StringBuilder input = new StringBuilder();
+          input.append(decoded[a]);
+          input.append(decoded[b]);
+          input.append(decoded[c]);
+          StringBuilder expected = new StringBuilder();
+          expected.append(encoded_latin1[a]);
+          expected.append(encoded_latin1[b]);
+          expected.append(encoded_latin1[c]);
+          String output = codec.encode(input);
+          log.debug("{}\t-> {}", input, output);
+          Assert.assertEquals("\"" + input + "\" was encoded falsely", expected.toString(), output);
+        }
+      }
+    }
+  }
+
+  @Test
+  public void testDecodeLatin1() throws Exception {
+    PercentCodec codec = new PercentCodec("latin1");
+
+    for (int a = 0; a < decoded.length; a++) {
+      for (int b = 0; b < decoded.length; b++) {
+        for (int c = 0; c < decoded.length; c++) {
+          /** Das Zeichen '€' existiert in Latin1 nicht! */
+          if (a == 10 || b == 10 || c == 10)
+            continue;
+          StringBuilder input = new StringBuilder();
+          input.append(encoded_latin1[a]);
+          input.append(encoded_latin1[b]);
+          input.append(encoded_latin1[c]);
+          StringBuilder expected = new StringBuilder();
+          expected.append(decoded[a]);
+          expected.append(decoded[b]);
+          expected.append(decoded[c]);
+          String output = codec.decode(input);
+          log.debug("{}\t-> {}", input, output);
+          Assert.assertEquals("\"" + input + "\" was decoded falsely", expected.toString(), output);
+        }
+      }
+    }
+  }
+
+  @Test
+  public void testEncodeUtf8() throws Exception {
+    PercentCodec codec = new PercentCodec("UTF-8");
+
+    for (int a = 0; a < decoded.length; a++) {
+      for (int b = 0; b < decoded.length; b++) {
+        for (int c = 0; c < decoded.length; c++) {
+          StringBuilder input = new StringBuilder();
+          input.append(decoded[a]);
+          input.append(decoded[b]);
+          input.append(decoded[c]);
+          StringBuilder expected = new StringBuilder();
+          expected.append(encoded_utf8[a]);
+          expected.append(encoded_utf8[b]);
+          expected.append(encoded_utf8[c]);
+          String output = codec.encode(input);
+          log.debug("{}\t-> {}", input, output);
+          Assert.assertEquals("\"" + input + "\" was encoded falsely", expected.toString(), output);
+        }
+      }
+    }
+  }
+
+  @Test
+  public void testDecodeUtf8() throws Exception {
+    PercentCodec codec = new PercentCodec("UTF-8");
+
+    for (int a = 0; a < decoded.length; a++) {
+      for (int b = 0; b < decoded.length; b++) {
+        for (int c = 0; c < decoded.length; c++) {
+          StringBuilder input = new StringBuilder();
+          input.append(encoded_utf8[a]);
+          input.append(encoded_utf8[b]);
+          input.append(encoded_utf8[c]);
+          StringBuilder expected = new StringBuilder();
+          expected.append(decoded[a]);
+          expected.append(decoded[b]);
+          expected.append(decoded[c]);
+          String output = codec.decode(input);
+          log.debug("{}\t-> {}", input, output);
+          Assert.assertEquals("\"" + input + "\" was decoded falsely", expected.toString(), output);
+        }
+      }
+    }
+  }
+}
diff --git a/percentcodec/src/test/resources/log4j.xml b/percentcodec/src/test/resources/log4j.xml
new file mode 100644 (file)
index 0000000..18822a5
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE log4j:configuration PUBLIC "-//LOGGER" "log4j.dtd">
+
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+
+       <!-- Appenders -->
+       <appender name="console" class="org.apache.log4j.ConsoleAppender">
+               <param name="Target" value="System.out" />
+               <layout class="org.apache.log4j.PatternLayout">
+                       <param name="ConversionPattern" value="%-5p: %c - %m%n" />
+               </layout>
+       </appender>
+
+       <root>
+               <priority value="debug" />
+               <appender-ref ref="console" />
+       </root>
+
+</log4j:configuration>
diff --git a/pom.xml b/pom.xml
index 8c63f41..ec6d830 100644 (file)
--- a/pom.xml
+++ b/pom.xml
@@ -4,12 +4,12 @@
 
   <modelVersion>4.0.0</modelVersion>
 
 
   <modelVersion>4.0.0</modelVersion>
 
-  <groupId>de.halbekunst</groupId>
+  <groupId>de.juplo</groupId>
   <artifactId>juplo</artifactId>
   <artifactId>juplo</artifactId>
-  <version>2.0-SNAPSHOT</version>
+  <version>1.0-SNAPSHOT</version>
   <name>Juplo</name>
   <packaging>pom</packaging>
   <name>Juplo</name>
   <packaging>pom</packaging>
-  <url>http://www.halbekunst.de</url>
+  <url>http://www.juplo.de</url>
 
   <prerequisites>
     <maven>2.0.6</maven>
 
   <prerequisites>
     <maven>2.0.6</maven>
     <developer>
       <id>kai</id>
       <name>Kai Moritz</name>
     <developer>
       <id>kai</id>
       <name>Kai Moritz</name>
-      <email>kai@ich-geh-kaputt.de</email>
+      <email>kai@juplo.de</email>
     </developer>
   </developers>
 
   <modules>
     </developer>
   </developers>
 
   <modules>
-    <module>test</module>
-    <module>cachecontrol</module>
-    <module>examples</module>
+    <module>testingtools</module>
+    <module>percentcodec</module>
+    <module>maven-plugins</module>
+    <module>accelerator</module>
+    <module>accelerator-examples</module>
   </modules>
 
   <properties>
   </modules>
 
   <properties>
-
     <!-- Zeichensatz -->
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     <!-- Zeichensatz -->
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-
-    <!-- Verwendete Versionen -->
-    <aspectj.version>1.6.11</aspectj.version>
-    <commons-io.version>1.3.2</commons-io.version>
-    <httpunit.version>1.7.1</httpunit.version>
-    <jasper.version>6.0.29</jasper.version>
-    <jetty.version>8.1.4.v20120524</jetty.version>
-    <jpa.version>1.0</jpa.version>
-    <jstl.version>1.2</jstl.version>
-    <junit.version>4.8.1</junit.version>
-    <log4j.version>1.2.16</log4j.version>
-    <servlet-api.version>2.5</servlet-api.version>
-    <slf4j.binding>slf4j-log4j12</slf4j.binding>
-    <slf4j.version>1.6.1</slf4j.version>
-    <springframework.version>3.0.6.RELEASE</springframework.version>
-
   </properties>
 
   </properties>
 
-  <dependencies>
-
-    <!-- Logging -->
-    <dependency>
-      <groupId>org.slf4j</groupId>
-      <artifactId>slf4j-api</artifactId>
-      <version>${slf4j.version}</version>
-    </dependency>
-
-    <!-- Test -->
-    <dependency>
-      <groupId>junit</groupId>
-      <artifactId>junit</artifactId>
-      <version>${junit.version}</version>
-      <scope>test</scope>
-    </dependency>
-
-  </dependencies>
-
-  <repositories>
-    <!-- For JSR 303, Hibernate Validator and other Hibernate-Stuff - Encourage JBoss to publish these artifacts to Maven Central! -->
-    <repository>
-      <id>org.jboss.repository.maven</id>
-      <url>http://repository.jboss.org/maven2</url>
-      <snapshots><enabled>false</enabled></snapshots>
-    </repository>
-    <repository>
-      <id>halbekunst.internal</id>
-      <name>Internal Release Repository</name>
-      <url>http://halbekunst.de/archiva/repository/internal/</url>
-      <snapshots><enabled>false</enabled></snapshots>
-    </repository>
-  </repositories>
-
   <distributionManagement>
     <repository>
   <distributionManagement>
     <repository>
-      <id>halbekunst.internal</id>
+      <id>juplo.internal</id>
       <name>Internal Release Repository</name>
       <name>Internal Release Repository</name>
-      <url>http://halbekunst.de/archiva/repository/internal/</url>
+      <url>http://juplo.de/archiva/repository/internal/</url>
     </repository>
     <snapshotRepository>
     </repository>
     <snapshotRepository>
-      <id>halbekunst.snapshots</id>
+      <id>juplo.snapshots</id>
       <name>Internal Snapshot Repository</name>
       <name>Internal Snapshot Repository</name>
-      <url>http://halbekunst.de/archiva/repository/snapshots/</url>
+      <url>http://juplo.de/archiva/repository/snapshots/</url>
     </snapshotRepository>
   </distributionManagement>
 
     </snapshotRepository>
   </distributionManagement>
 
-  <build>
-    <plugins>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-compiler-plugin</artifactId>
-        <configuration>
-          <source>1.6</source>
-          <target>1.6</target>
-          <encoding>utf8</encoding>
-          <showWarnings>true</showWarnings>
-        </configuration>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-dependency-plugin</artifactId>
-        <executions>
-          <execution>
-            <id>install</id>
-            <phase>install</phase>
-            <goals>
-              <goal>sources</goal>
-            </goals>
-          </execution>
-        </executions>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-source-plugin</artifactId>
-        <executions>
-          <execution>
-            <id>attach-sources</id>
-            <phase>verify</phase>
-            <goals>
-              <goal>jar</goal>
-            </goals>
-          </execution>
-        </executions>
-      </plugin>
-    </plugins>
-  </build>
-
   <reporting>
     <plugins>
       <plugin>
   <reporting>
     <plugins>
       <plugin>
       <plugin>
         <artifactId>maven-checkstyle-plugin</artifactId>
       </plugin>
       <plugin>
         <artifactId>maven-checkstyle-plugin</artifactId>
       </plugin>
-      <plugin>
-        <groupId>org.codehaus.mojo</groupId>
-        <artifactId>cobertura-maven-plugin</artifactId>
-        <version>2.0</version>
-      </plugin>
-      <plugin>
-        <artifactId>maven-javadoc-plugin</artifactId>
-      </plugin>
-      <plugin>
-        <artifactId>maven-jxr-plugin</artifactId>
-      </plugin>
-      <plugin>
-        <artifactId>maven-pmd-plugin</artifactId>
-        <configuration>
-          <linkXref>true</linkXref>
-          <targetJdk>1.5</targetJdk>
-        </configuration>
-      </plugin>
-      <plugin>
-        <artifactId>maven-surefire-report-plugin</artifactId>
-      </plugin>
     </plugins>
   </reporting>
 
     </plugins>
   </reporting>
 
diff --git a/test/pom.xml b/test/pom.xml
deleted file mode 100644 (file)
index 7c3a4b4..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-
-  <modelVersion>4.0.0</modelVersion>
-
-  <parent>
-    <groupId>de.halbekunst</groupId>
-    <artifactId>juplo</artifactId>
-    <version>2.0-SNAPSHOT</version>
-  </parent>
-
-  <artifactId>${pom.parent.artifactId}-test</artifactId>
-  <name>Juplo - Test</name>
-
-  <dependencies>
-
-    <!-- public dependencies -->
-    <dependency>
-      <groupId>httpunit</groupId>
-      <artifactId>httpunit</artifactId>
-      <version>${httpunit.version}</version>
-      <exclusions>
-        <exclusion>
-          <groupId>junit</groupId>
-          <artifactId>junit</artifactId>
-        </exclusion>
-        <exclusion>
-          <groupId>javax.servlet</groupId>
-          <artifactId>servlet-api</artifactId>
-        </exclusion>
-      </exclusions>
-    </dependency>
-    <dependency>
-      <groupId>junit</groupId>
-      <artifactId>junit</artifactId>
-      <version>${junit.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>org.springframework</groupId>
-      <artifactId>spring-test</artifactId>
-      <version>${springframework.version}</version>
-      <scope>provided</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.eclipse.jetty</groupId>
-      <artifactId>jetty-servlet</artifactId>
-      <version>${jetty.version}</version>
-    </dependency>
-
-    <!-- hidden dependencies -->
-    <dependency>
-      <groupId>org.springframework</groupId>
-      <artifactId>spring-beans</artifactId>
-      <version>${springframework.version}</version>
-      <scope>provided</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.springframework</groupId>
-      <artifactId>spring-context</artifactId>
-      <version>${springframework.version}</version>
-      <scope>provided</scope>
-    </dependency>
-    <dependency>
-      <groupId>javax.servlet</groupId>
-      <artifactId>servlet-api</artifactId>
-      <version>${servlet-api.version}</version>
-      <scope>provided</scope>
-    </dependency>
-  </dependencies>
-
-</project>
diff --git a/test/src/main/java/de/halbekunst/juplo/test/HttpTestCase.java b/test/src/main/java/de/halbekunst/juplo/test/HttpTestCase.java
deleted file mode 100644 (file)
index 76c4646..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-package de.halbekunst.juplo.test;
-
-import com.meterware.httpunit.WebResponse;
-import com.meterware.servletunit.InvocationContext;
-import com.meterware.servletunit.ServletRunner;
-import com.meterware.servletunit.ServletUnitClient;
-import java.io.File;
-import java.util.Enumeration;
-import javax.servlet.http.HttpServletRequest;
-import org.junit.Before;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- *
- * @author kai
- */
-public abstract class HttpTestCase {
-
-  private final static Logger log = LoggerFactory.getLogger(HttpTestCase.class);
-
-  private ServletRunner sr;
-  private File config;
-  public ServletUnitClient client;
-
-
-  public HttpTestCase(String config) {
-    this(new File(config));
-  }
-
-  public HttpTestCase(File config) {
-    if (!config.exists())
-      throw new RuntimeException("web.xml is missing: " + config + " does not exist.");
-    this.config = config;
-  }
-
-
-  @Before
-  public void init() throws Exception {
-    sr = new ServletRunner(config, ""); // Dies ist der einzige Konstruktor, der die Context-Root der Webapp im Testfall korrekt initialisiert :/
-    client = sr.newClient();
-  }
-
-  public WebResponse executeRequest(String uri) throws Exception {
-    log.debug("---------- GET: {}", uri);
-    InvocationContext invocation = client.newInvocation(uri);
-    HttpServletRequest request = invocation.getRequest();
-    log.debug("Request - {}: {}", request.getMethod(), request.getProtocol());
-    Enumeration<String> headers = request.getHeaderNames();
-    while (headers.hasMoreElements()) {
-      String header = headers.nextElement();
-      Enumeration<String> values = request.getHeaders(header);
-      while (values.hasMoreElements())
-        log.debug("Request - {}: {}", header, values.nextElement());
-    }
-
-    log.debug("Invocing service method.");
-
-    /**
-     * We cannot call invocation.service(), because we have to wrap the
-     * response. Therefore this was coppied from InvocationContextImpl.
-     */
-    LoggingHttpServletResponseWrapper wrappedResponse =
-        new LoggingHttpServletResponseWrapper(uri, invocation.getResponse());
-    if (invocation.isFilterActive()) {
-      invocation.getFilter().doFilter(invocation.getRequest(), wrappedResponse, invocation.getFilterChain());
-    }
-    else {
-      invocation.getServlet().service(invocation.getRequest(), wrappedResponse);
-    }
-    long count = wrappedResponse.close();
-
-    WebResponse response = invocation.getServletResponse();
-    log.debug("Response - {}: {}", response.getResponseCode(), response.getResponseMessage());
-    log.debug("Response - {}, {} bytes", response.getContentType(), count);
-    for (String header : response.getHeaderFieldNames()) {
-      for (String value : response.getHeaderFields(header)) {
-        log.debug("Response - {}: {}", header, value);
-      }
-    }
-    return response;
-  }
-}
-
diff --git a/test/src/main/java/de/halbekunst/juplo/test/LoggingHttpServletResponseFilter.java b/test/src/main/java/de/halbekunst/juplo/test/LoggingHttpServletResponseFilter.java
deleted file mode 100644 (file)
index b9dad51..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-package de.halbekunst.juplo.test;
-
-import java.io.IOException;
-import javax.servlet.Filter;
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- *
- * @author kai
- */
-public class LoggingHttpServletResponseFilter implements Filter {
-
-  private final static Logger log = LoggerFactory.getLogger(LoggingHttpServletResponseWrapper.class);
-
-
-  @Override
-  public void init(FilterConfig filterConfig) throws ServletException {
-  }
-
-  @Override
-  public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
-    HttpServletRequest request = (HttpServletRequest)req;
-    HttpServletResponse response = (HttpServletResponse)res;
-    log.info("counting request {}", request.getRequestURI());
-    LoggingHttpServletResponseWrapper wrappedResponse =
-        new LoggingHttpServletResponseWrapper(request.getRequestURI(), response);
-    chain.doFilter(request, wrappedResponse);
-    log.info("response-size: {}", wrappedResponse.close());
-  }
-
-  @Override
-  public void destroy() {
-  }
-}
diff --git a/test/src/main/java/de/halbekunst/juplo/test/LoggingHttpServletResponseWrapper.java b/test/src/main/java/de/halbekunst/juplo/test/LoggingHttpServletResponseWrapper.java
deleted file mode 100644 (file)
index 075e6a7..0000000
+++ /dev/null
@@ -1,359 +0,0 @@
-package de.halbekunst.juplo.test;
-
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.util.Collection;
-import java.util.Date;
-import java.util.Locale;
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.Cookie;
-import javax.servlet.http.HttpServletResponse;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * @author kai
- */
-public class LoggingHttpServletResponseWrapper implements HttpServletResponse {
-
-  private final static Logger log = LoggerFactory.getLogger(LoggingHttpServletResponseWrapper.class);
-
-  public final static int DEFAULT_BUFFER_SIZE = 1024;
-
-  private static long count = 0;
-
-  private final Long no;
-  private final HttpServletResponse response;
-  private CountingServletOutputStream out;
-  private ServletOutputStream stream;
-  private PrintWriter writer;
-  private int buffer = DEFAULT_BUFFER_SIZE;
-  private boolean committed = false;
-
-
-  LoggingHttpServletResponseWrapper(String name, HttpServletResponse response) {
-    no = ++count;
-    log.debug("New request {}: {}", no, name);
-    this.response = response;
-  }
-
-
-  public long close() throws IOException {
-    if (out == null) {
-      return -1l;
-    }
-    else {
-      if (writer != null)
-        writer.close();
-      else
-        stream.close();
-      long result = out.count;
-      out = null;
-      buffer = DEFAULT_BUFFER_SIZE;
-      return result;
-    }
-  }
-
-  @Override
-  public void flushBuffer() throws IOException {
-    log.debug("{} -- flushing buffer", no);
-    committed = true;
-    response.flushBuffer();
-  }
-
-  @Override
-  public int getBufferSize() {
-    log.trace("{} -- getting buffer size: {}", no, buffer);
-    return buffer;
-  }
-
-  @Override
-  public boolean isCommitted() {
-    Boolean result = committed || response.isCommitted();
-    log.trace("{} -- commited? {}", no, result);
-    return result;
-  }
-
-  @Override
-  public void reset() {
-    log.debug("{} -- reset!", no);
-    if (committed)
-      throw new IllegalStateException("call to reset() after response has been commited!");
-    if (out != null)
-      out.count = 0;
-    response.reset();
-  }
-
-  @Override
-  public void resetBuffer() {
-    log.debug("{} -- resetting buffer", no);
-    if (committed)
-      throw new IllegalStateException("call to resetBuffer() after response has been commited!");
-    if (out != null)
-      out.count = 0;
-    response.resetBuffer();
-  }
-
-  @Override
-  public void setBufferSize(int size) {
-    log.debug("{} -- setting buffer size to {}", no, size);
-    if (out != null && out.count > 0)
-      throw new IllegalStateException("call to setBuffer() after content has been written!");
-    response.setBufferSize(size);
-    buffer = size;
-  }
-
-  @Override
-  public ServletOutputStream getOutputStream() throws IOException {
-    log.debug("{} -- getting output stream", no);
-
-    if (writer != null)
-      throw new IllegalStateException("ServletOutputStream and PrintWriter cannot be requested both!");
-
-    if (stream == null) {
-      log.debug("{} -- creating new servlet output stream", no);
-      out = new CountingServletOutputStream(response.getOutputStream());
-      stream = out;
-    }
-
-    return stream;
-  }
-
-  @Override
-  public PrintWriter getWriter() throws IOException {
-    log.debug("{} -- getting print writer", no);
-
-    if (stream != null)
-      throw new IllegalStateException("ServletOutputStream and PrintWriter cannot be requested both!");
-
-    if (writer == null) {
-      log.debug("{} -- creating new print writer", no);
-      out = new CountingServletOutputStream(response.getOutputStream());
-      OutputStreamWriter streamWriter = new OutputStreamWriter(out, response.getCharacterEncoding());
-      writer = new PrintWriter(streamWriter);
-    }
-
-    return writer;
-  }
-
-  @Override
-  public void addCookie(Cookie cookie) {
-    log.debug("{} -- adding cookie: {}", no, cookie);
-    response.addCookie(cookie);
-  }
-
-  @Override
-  public boolean containsHeader(String name) {
-    Boolean result = response.containsHeader(name);
-    log.trace("{} -- contains header {}? {}", new Object[] { no, name, result });
-    return result;
-  }
-
-  @Override
-  public String encodeURL(String url) {
-    log.trace("{} -- encoding url {}", no, url);
-    return response.encodeURL(url);
-  }
-
-  @Override
-  public String encodeRedirectURL(String url) {
-    log.trace("{} -- encoding redirect url {}", no, url);
-    return response.encodeRedirectURL(url);
-  }
-
-  @Override
-  public String encodeUrl(String url) {
-    log.trace("{} -- encoding url {}", no, url);
-    return response.encodeUrl(url);
-  }
-
-  @Override
-  public String encodeRedirectUrl(String url) {
-    log.trace("{} -- encoding redirect url {}", no, url);
-    return response.encodeRedirectUrl(url);
-  }
-
-  @Override
-  public void sendError(int sc, String msg) throws IOException {
-    log.debug("{} -- sending error: {}. {}", new Object[] { no, sc, msg });
-    response.sendError(sc, msg);
-  }
-
-  @Override
-  public void sendError(int sc) throws IOException {
-    log.debug("{} -- sending error: {}", no, sc);
-  }
-
-  @Override
-  public void sendRedirect(String location) throws IOException {
-    log.debug("{} -- sending redirect: {}", no, location);
-    response.sendRedirect(location);
-  }
-
-  @Override
-  public void setDateHeader(String name, long date) {
-    log.debug("{} -- setting date header {} to {}", new Object[] { no, name, new Date(date) });
-    response.setDateHeader(name, date);
-  }
-
-  @Override
-  public void addDateHeader(String name, long date) {
-    log.debug("{} -- adding date header {}: {}", new Object[] { no, name, new Date(date) });
-    response.addDateHeader(name, date);
-  }
-
-  @Override
-  public void setHeader(String name, String value) {
-    log.debug("{} -- setting header {} to {}", new Object[] { no, name, value });
-    response.setHeader(name, value);
-  }
-
-  @Override
-  public void addHeader(String name, String value) {
-    log.debug("{} -- adding header {}: {}", new Object[] { no, name, value });
-    response.addHeader(name, value);
-  }
-
-  @Override
-  public void setIntHeader(String name, int value) {
-    log.debug("{} -- seting int header {} to {}", new Object[] { no, name, value });
-    response.setIntHeader(name, value);
-  }
-
-  @Override
-  public void addIntHeader(String name, int value) {
-    log.debug("{} -- adding int header {}: {}", new Object[] { no, name, value });
-    response.addIntHeader(name, value);
-  }
-
-  @Override
-  public void setStatus(int sc) {
-    log.debug("{} -- setting status to {}", no, sc);
-    response.setStatus(sc);
-  }
-
-  @Override
-  public void setStatus(int sc, String sm) {
-    log.debug("{} -- setting status to {} (message: {})", new Object[] { no, sc, sm });
-    response.setStatus(sc, sm);
-  }
-
-  @Override
-  public String getCharacterEncoding() {
-    String result = response.getCharacterEncoding();
-    log.trace("{} -- character encoding: {}", no, result);
-    return result;
-  }
-
-  @Override
-  public String getContentType() {
-    String result = response.getContentType();
-    log.trace("{} -- content type: {}", no, result);
-    return result;
-  }
-
-  @Override
-  public void setCharacterEncoding(String charset) {
-    log.debug("{} -- setting character encoding to {}", no, charset);
-    response.setCharacterEncoding(charset);
-  }
-
-  @Override
-  public void setContentLength(int len) {
-    log.debug("{} -- setting content length to {}", no, len);
-    response.setContentLength(len);
-  }
-
-  @Override
-  public void setContentType(String type) {
-    log.debug("{} -- setting content type to {}", no, type);
-    response.setContentType(type);
-  }
-
-  @Override
-  public void setLocale(Locale loc) {
-    log.debug("{} -- setting locale to {}", no, loc);
-    response.setLocale(loc);
-  }
-
-  @Override
-  public Locale getLocale() {
-    Locale locale = response.getLocale();
-    log.trace("{} -- locale: {}", no, locale);
-    return locale;
-  }
-
-  @Override
-  public int getStatus() {
-    Integer status = response.getStatus();
-    log.trace("{} -- status: {}", no, status);
-    return status;
-  }
-
-  @Override
-  public String getHeader(String name) {
-    String value = response.getHeader(name);
-    log.trace("{} -- header \"{}\": {}", new Object[] { no, name, value });
-    return value;
-  }
-
-  @Override
-  public Collection<String> getHeaders(String name) {
-    Collection<String> values = response.getHeaders(name);
-    if (log.isTraceEnabled()) {
-      StringBuilder builder = new StringBuilder();
-      builder.append(no);
-      builder.append(" -- headers \"");
-      builder.append(name);
-      builder.append("\":");
-      for (String value : values) {
-        builder.append(' ');
-        builder.append(value);
-      }
-      log.trace(builder.toString());
-    }
-    return values;
-  }
-
-  @Override
-  public Collection<String> getHeaderNames() {
-    Collection<String> values = response.getHeaderNames();
-    if (log.isTraceEnabled()) {
-      StringBuilder builder = new StringBuilder();
-      builder.append(no);
-      builder.append(" -- header-names:");
-      for (String value : values) {
-        builder.append(' ');
-        builder.append(value);
-      }
-      log.trace(builder.toString());
-    }
-    return values;
-  }
-
-
-  class CountingServletOutputStream extends ServletOutputStream {
-
-    private ServletOutputStream out;
-    long count = 0l;
-
-
-    CountingServletOutputStream(ServletOutputStream out) {
-      this.out = out;
-    }
-
-
-    @Override
-    public void write(int i) throws IOException {
-      count++;
-      /** Simulate commit, when count is getting bigger then buffer */
-      if (count == buffer + 1) {
-        log.info("{} -- simulating commit because buffer overflow! buffer: {}, count: {}", new Object[] { no, buffer, count });
-        committed = true;
-      }
-      log.trace("{} -- writing byte {}: {}", new Object[] { no, count, (char)i });
-      out.write(i);
-    }
-  }
-}
diff --git a/test/src/main/java/de/halbekunst/juplo/test/TestServlet.java b/test/src/main/java/de/halbekunst/juplo/test/TestServlet.java
deleted file mode 100644 (file)
index b2b29f4..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-package de.halbekunst.juplo.test;
-
-import java.io.IOException;
-import java.util.Map;
-import javax.servlet.RequestDispatcher;
-import javax.servlet.ServletException;
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- *
- * @author kai
- */
-public class TestServlet extends HttpServlet {
-  private final static Logger log = LoggerFactory.getLogger(TestServlet.class);
-
-  private static final String FORWARDED = TestServlet.class.getName() + ".FORWARDED";
-  private static final String INCLUDED = TestServlet.class.getName() + ".INCLUDED";
-
-  @Override
-  protected long getLastModified(HttpServletRequest req) {
-    try {
-      /** Der Reqeust-Parameter "lm" wird als Wert für Last-Modified zurückgegeben */
-      return Long.parseLong(req.getParameter("l"));
-    }
-    catch (Exception e) {
-      return -1l;
-    }
-  }
-
-  @Override
-  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
-    /** Angeforderte Header setzen */
-    Map<String, String[]> map = request.getParameterMap();
-    for (String param : map.keySet()) {
-      if (param.equals("n") || param.equals("l") || param.equals("f") || param.equals("i"))
-        continue;
-      /**
-       * Alle Request-Parameter außer die Sonder-Parameter "n" und "lm"
-       * werden als Schlüssel/Wert-Paare für die Antwort-Header interpretiert!
-       */
-      for (String value : map.get(param))
-        response.setHeader(param, value);
-    }
-
-    int n = 0;
-    try {
-      /**
-       * Wenn der Parameter n gesetzt ist, wird ein Antwort-Body erzeugt, der
-       * exakt die Anzahl der geforderten Bytes enthält.
-       */
-      n = Integer.parseInt(request.getParameter("n"));
-    }
-    catch(Exception e) {}
-    log.debug("GET {} bytes: {}", n, request.getRequestURI());
-    ServletOutputStream out = response.getOutputStream();
-    for (int i=0; i<n; i++)
-      out.write(i%2 + 48); /** ASCII-Codes für "0" und "1" */
-
-    if (request.getParameter("i") != null && request.getAttribute(INCLUDED) == null) {
-      for (String include : request.getParameterValues("i")) {
-        RequestDispatcher dispatcher = request.getRequestDispatcher(include);
-        request.setAttribute(INCLUDED, include);
-        dispatcher.include(request, response);
-      }
-    }
-
-    if (request.getParameter("f") != null && request.getAttribute(FORWARDED) == null) {
-      String forward = request.getParameter("f");
-      request.setAttribute(FORWARDED, forward);
-      RequestDispatcher dispatcher = request.getRequestDispatcher(forward);
-      dispatcher.forward(request, response);
-    }
-  }
-}
diff --git a/test/src/main/java/de/halbekunst/juplo/test/WebContextTestExecutionListener.java b/test/src/main/java/de/halbekunst/juplo/test/WebContextTestExecutionListener.java
deleted file mode 100644 (file)
index 68985c2..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-package de.halbekunst.juplo.test;
-
-import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
-import org.springframework.beans.factory.config.Scope;
-import org.springframework.context.support.GenericApplicationContext;
-import org.springframework.context.support.SimpleThreadScope;
-import org.springframework.test.context.TestContext;
-import org.springframework.test.context.support.AbstractTestExecutionListener;
-
-/**
- * Diese Klasse ermöglicht es, Beans mit dem Scope REQUEST in JUnit-Testfällen
- * zu verwenden.
- * <p>
- * Source: http://stackoverflow.com/questions/2411343/request-scoped-beans-in-spring-testing
- * <p>
- * Anwendung:
- * <code>
- * @RunWith(SpringJUnit4ClassRunner.class)
- * @ContextConfiguration(locations = "classpath:spring/TestScopedBeans-context.xml")
- * @TestExecutionListeners({
- *      WebContextTestExecutionListener.class,
- *      DependencyInjectionTestExecutionListener.class,
- *      DirtiesContextTestExecutionListener.class })
- * public class TestScopedBeans {
- * ...
- * </code>
- */
-public class WebContextTestExecutionListener extends AbstractTestExecutionListener {
-    @Override
-    public void prepareTestInstance(TestContext testContext) throws Exception {
-
-        if (testContext.getApplicationContext() instanceof GenericApplicationContext) {
-            GenericApplicationContext context = (GenericApplicationContext) testContext.getApplicationContext();
-            ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
-            Scope requestScope = new SimpleThreadScope();
-            beanFactory.registerScope("request", requestScope);
-            Scope sessionScope = new SimpleThreadScope();
-            beanFactory.registerScope("session", sessionScope);
-        }
-    }
-}
diff --git a/testingtools/pom.xml b/testingtools/pom.xml
new file mode 100644 (file)
index 0000000..93e2393
--- /dev/null
@@ -0,0 +1,184 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <groupId>de.juplo</groupId>
+  <artifactId>juplo-testingtools</artifactId>
+  <name>Juplo - Testing-Tools</name>
+  <version>1.0</version>
+  <url>http://www.juplo.de/testingtools</url>
+
+  <prerequisites>
+    <maven>2.0.6</maven>
+  </prerequisites>
+
+  <developers>
+    <developer>
+      <id>kai</id>
+      <name>Kai Moritz</name>
+      <email>kai@juplo.de</email>
+    </developer>
+  </developers>
+
+
+  <properties>
+    <!-- Zeichensatz -->
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <!-- Verwendete Versionen -->
+    <httpunit.version>1.7.1</httpunit.version>
+    <jetty.version>8.1.4.v20120524</jetty.version>
+    <junit.version>4.8.1</junit.version>
+    <servlet-api.version>2.5</servlet-api.version>
+    <slf4j.version>1.6.1</slf4j.version>
+    <springframework.version>3.0.6.RELEASE</springframework.version>
+  </properties>
+
+  <dependencies>
+    <!-- public dependencies -->
+    <dependency>
+      <groupId>httpunit</groupId>
+      <artifactId>httpunit</artifactId>
+      <version>${httpunit.version}</version>
+      <exclusions>
+        <exclusion>
+          <groupId>junit</groupId>
+          <artifactId>junit</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>javax.servlet</groupId>
+          <artifactId>servlet-api</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>${junit.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-test</artifactId>
+      <version>${springframework.version}</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-servlet</artifactId>
+      <version>${jetty.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+      <version>${slf4j.version}</version>
+    </dependency>
+    <!-- hidden dependencies -->
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-beans</artifactId>
+      <version>${springframework.version}</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-context</artifactId>
+      <version>${springframework.version}</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>javax.servlet</groupId>
+      <artifactId>servlet-api</artifactId>
+      <version>${servlet-api.version}</version>
+      <scope>provided</scope>
+    </dependency>
+  </dependencies>
+
+  <distributionManagement>
+    <repository>
+      <id>juplo.internal</id>
+      <name>Internal Release Repository</name>
+      <url>http://juplo.de/archiva/repository/internal/</url>
+    </repository>
+    <snapshotRepository>
+      <id>juplo.snapshots</id>
+      <name>Internal Snapshot Repository</name>
+      <url>http://juplo.de/archiva/repository/snapshots/</url>
+    </snapshotRepository>
+  </distributionManagement>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <configuration>
+          <source>1.6</source>
+          <target>1.6</target>
+          <encoding>utf8</encoding>
+          <showWarnings>true</showWarnings>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-dependency-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>install</id>
+            <phase>install</phase>
+            <goals>
+              <goal>sources</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-source-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>attach-sources</id>
+            <phase>verify</phase>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+  <reporting>
+    <plugins>
+      <plugin>
+        <artifactId>maven-changes-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>cobertura-maven-plugin</artifactId>
+        <version>2.0</version>
+      </plugin>
+      <plugin>
+        <artifactId>maven-javadoc-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <artifactId>maven-jxr-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <artifactId>maven-pmd-plugin</artifactId>
+        <configuration>
+          <linkXref>true</linkXref>
+          <targetJdk>1.5</targetJdk>
+        </configuration>
+      </plugin>
+      <plugin>
+        <artifactId>maven-surefire-report-plugin</artifactId>
+        <version>2.7.2</version>
+      </plugin>
+    </plugins>
+  </reporting>
+
+</project>
diff --git a/testingtools/src/main/java/de/juplo/testingtools/HttpTestCase.java b/testingtools/src/main/java/de/juplo/testingtools/HttpTestCase.java
new file mode 100644 (file)
index 0000000..a243bc3
--- /dev/null
@@ -0,0 +1,84 @@
+package de.juplo.testingtools;
+
+import com.meterware.httpunit.WebResponse;
+import com.meterware.servletunit.InvocationContext;
+import com.meterware.servletunit.ServletRunner;
+import com.meterware.servletunit.ServletUnitClient;
+import java.io.File;
+import java.util.Enumeration;
+import javax.servlet.http.HttpServletRequest;
+import org.junit.Before;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ *
+ * @author kai
+ */
+public abstract class HttpTestCase {
+
+  private final static Logger log = LoggerFactory.getLogger(HttpTestCase.class);
+
+  private ServletRunner sr;
+  private File config;
+  public ServletUnitClient client;
+
+
+  public HttpTestCase(String config) {
+    this(new File(config));
+  }
+
+  public HttpTestCase(File config) {
+    if (!config.exists())
+      throw new RuntimeException("web.xml is missing: " + config + " does not exist.");
+    this.config = config;
+  }
+
+
+  @Before
+  public void init() throws Exception {
+    sr = new ServletRunner(config, ""); // Dies ist der einzige Konstruktor, der die Context-Root der Webapp im Testfall korrekt initialisiert :/
+    client = sr.newClient();
+  }
+
+  public WebResponse executeRequest(String uri) throws Exception {
+    log.debug("---------- GET: {}", uri);
+    InvocationContext invocation = client.newInvocation(uri);
+    HttpServletRequest request = invocation.getRequest();
+    log.debug("Request - {}: {}", request.getMethod(), request.getProtocol());
+    Enumeration<String> headers = request.getHeaderNames();
+    while (headers.hasMoreElements()) {
+      String header = headers.nextElement();
+      Enumeration<String> values = request.getHeaders(header);
+      while (values.hasMoreElements())
+        log.debug("Request - {}: {}", header, values.nextElement());
+    }
+
+    log.debug("Invocing service method.");
+
+    /**
+     * We cannot call invocation.service(), because we have to wrap the
+     * response. Therefore this was coppied from InvocationContextImpl.
+     */
+    LoggingHttpServletResponseWrapper wrappedResponse =
+        new LoggingHttpServletResponseWrapper(uri, invocation.getResponse());
+    if (invocation.isFilterActive()) {
+      invocation.getFilter().doFilter(invocation.getRequest(), wrappedResponse, invocation.getFilterChain());
+    }
+    else {
+      invocation.getServlet().service(invocation.getRequest(), wrappedResponse);
+    }
+    long count = wrappedResponse.close();
+
+    WebResponse response = invocation.getServletResponse();
+    log.debug("Response - {}: {}", response.getResponseCode(), response.getResponseMessage());
+    log.debug("Response - {}, {} bytes", response.getContentType(), count);
+    for (String header : response.getHeaderFieldNames()) {
+      for (String value : response.getHeaderFields(header)) {
+        log.debug("Response - {}: {}", header, value);
+      }
+    }
+    return response;
+  }
+}
+
diff --git a/testingtools/src/main/java/de/juplo/testingtools/LoggingHttpServletResponseFilter.java b/testingtools/src/main/java/de/juplo/testingtools/LoggingHttpServletResponseFilter.java
new file mode 100644 (file)
index 0000000..4181f20
--- /dev/null
@@ -0,0 +1,42 @@
+package de.juplo.testingtools;
+
+import java.io.IOException;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ *
+ * @author kai
+ */
+public class LoggingHttpServletResponseFilter implements Filter {
+
+  private final static Logger log = LoggerFactory.getLogger(LoggingHttpServletResponseWrapper.class);
+
+
+  @Override
+  public void init(FilterConfig filterConfig) throws ServletException {
+  }
+
+  @Override
+  public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
+    HttpServletRequest request = (HttpServletRequest)req;
+    HttpServletResponse response = (HttpServletResponse)res;
+    log.info("counting request {}", request.getRequestURI());
+    LoggingHttpServletResponseWrapper wrappedResponse =
+        new LoggingHttpServletResponseWrapper(request.getRequestURI(), response);
+    chain.doFilter(request, wrappedResponse);
+    log.info("response-size: {}", wrappedResponse.close());
+  }
+
+  @Override
+  public void destroy() {
+  }
+}
diff --git a/testingtools/src/main/java/de/juplo/testingtools/LoggingHttpServletResponseWrapper.java b/testingtools/src/main/java/de/juplo/testingtools/LoggingHttpServletResponseWrapper.java
new file mode 100644 (file)
index 0000000..eb6a067
--- /dev/null
@@ -0,0 +1,359 @@
+package de.juplo.testingtools;
+
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Locale;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author kai
+ */
+public class LoggingHttpServletResponseWrapper implements HttpServletResponse {
+
+  private final static Logger log = LoggerFactory.getLogger(LoggingHttpServletResponseWrapper.class);
+
+  public final static int DEFAULT_BUFFER_SIZE = 1024;
+
+  private static long count = 0;
+
+  private final Long no;
+  private final HttpServletResponse response;
+  private CountingServletOutputStream out;
+  private ServletOutputStream stream;
+  private PrintWriter writer;
+  private int buffer = DEFAULT_BUFFER_SIZE;
+  private boolean committed = false;
+
+
+  LoggingHttpServletResponseWrapper(String name, HttpServletResponse response) {
+    no = ++count;
+    log.debug("New request {}: {}", no, name);
+    this.response = response;
+  }
+
+
+  public long close() throws IOException {
+    if (out == null) {
+      return -1l;
+    }
+    else {
+      if (writer != null)
+        writer.close();
+      else
+        stream.close();
+      long result = out.count;
+      out = null;
+      buffer = DEFAULT_BUFFER_SIZE;
+      return result;
+    }
+  }
+
+  @Override
+  public void flushBuffer() throws IOException {
+    log.debug("{} -- flushing buffer", no);
+    committed = true;
+    response.flushBuffer();
+  }
+
+  @Override
+  public int getBufferSize() {
+    log.trace("{} -- getting buffer size: {}", no, buffer);
+    return buffer;
+  }
+
+  @Override
+  public boolean isCommitted() {
+    Boolean result = committed || response.isCommitted();
+    log.trace("{} -- commited? {}", no, result);
+    return result;
+  }
+
+  @Override
+  public void reset() {
+    log.debug("{} -- reset!", no);
+    if (committed)
+      throw new IllegalStateException("call to reset() after response has been commited!");
+    if (out != null)
+      out.count = 0;
+    response.reset();
+  }
+
+  @Override
+  public void resetBuffer() {
+    log.debug("{} -- resetting buffer", no);
+    if (committed)
+      throw new IllegalStateException("call to resetBuffer() after response has been commited!");
+    if (out != null)
+      out.count = 0;
+    response.resetBuffer();
+  }
+
+  @Override
+  public void setBufferSize(int size) {
+    log.debug("{} -- setting buffer size to {}", no, size);
+    if (out != null && out.count > 0)
+      throw new IllegalStateException("call to setBuffer() after content has been written!");
+    response.setBufferSize(size);
+    buffer = size;
+  }
+
+  @Override
+  public ServletOutputStream getOutputStream() throws IOException {
+    log.debug("{} -- getting output stream", no);
+
+    if (writer != null)
+      throw new IllegalStateException("ServletOutputStream and PrintWriter cannot be requested both!");
+
+    if (stream == null) {
+      log.debug("{} -- creating new servlet output stream", no);
+      out = new CountingServletOutputStream(response.getOutputStream());
+      stream = out;
+    }
+
+    return stream;
+  }
+
+  @Override
+  public PrintWriter getWriter() throws IOException {
+    log.debug("{} -- getting print writer", no);
+
+    if (stream != null)
+      throw new IllegalStateException("ServletOutputStream and PrintWriter cannot be requested both!");
+
+    if (writer == null) {
+      log.debug("{} -- creating new print writer", no);
+      out = new CountingServletOutputStream(response.getOutputStream());
+      OutputStreamWriter streamWriter = new OutputStreamWriter(out, response.getCharacterEncoding());
+      writer = new PrintWriter(streamWriter);
+    }
+
+    return writer;
+  }
+
+  @Override
+  public void addCookie(Cookie cookie) {
+    log.debug("{} -- adding cookie: {}", no, cookie);
+    response.addCookie(cookie);
+  }
+
+  @Override
+  public boolean containsHeader(String name) {
+    Boolean result = response.containsHeader(name);
+    log.trace("{} -- contains header {}? {}", new Object[] { no, name, result });
+    return result;
+  }
+
+  @Override
+  public String encodeURL(String url) {
+    log.trace("{} -- encoding url {}", no, url);
+    return response.encodeURL(url);
+  }
+
+  @Override
+  public String encodeRedirectURL(String url) {
+    log.trace("{} -- encoding redirect url {}", no, url);
+    return response.encodeRedirectURL(url);
+  }
+
+  @Override
+  public String encodeUrl(String url) {
+    log.trace("{} -- encoding url {}", no, url);
+    return response.encodeUrl(url);
+  }
+
+  @Override
+  public String encodeRedirectUrl(String url) {
+    log.trace("{} -- encoding redirect url {}", no, url);
+    return response.encodeRedirectUrl(url);
+  }
+
+  @Override
+  public void sendError(int sc, String msg) throws IOException {
+    log.debug("{} -- sending error: {}. {}", new Object[] { no, sc, msg });
+    response.sendError(sc, msg);
+  }
+
+  @Override
+  public void sendError(int sc) throws IOException {
+    log.debug("{} -- sending error: {}", no, sc);
+  }
+
+  @Override
+  public void sendRedirect(String location) throws IOException {
+    log.debug("{} -- sending redirect: {}", no, location);
+    response.sendRedirect(location);
+  }
+
+  @Override
+  public void setDateHeader(String name, long date) {
+    log.debug("{} -- setting date header {} to {}", new Object[] { no, name, new Date(date) });
+    response.setDateHeader(name, date);
+  }
+
+  @Override
+  public void addDateHeader(String name, long date) {
+    log.debug("{} -- adding date header {}: {}", new Object[] { no, name, new Date(date) });
+    response.addDateHeader(name, date);
+  }
+
+  @Override
+  public void setHeader(String name, String value) {
+    log.debug("{} -- setting header {} to {}", new Object[] { no, name, value });
+    response.setHeader(name, value);
+  }
+
+  @Override
+  public void addHeader(String name, String value) {
+    log.debug("{} -- adding header {}: {}", new Object[] { no, name, value });
+    response.addHeader(name, value);
+  }
+
+  @Override
+  public void setIntHeader(String name, int value) {
+    log.debug("{} -- seting int header {} to {}", new Object[] { no, name, value });
+    response.setIntHeader(name, value);
+  }
+
+  @Override
+  public void addIntHeader(String name, int value) {
+    log.debug("{} -- adding int header {}: {}", new Object[] { no, name, value });
+    response.addIntHeader(name, value);
+  }
+
+  @Override
+  public void setStatus(int sc) {
+    log.debug("{} -- setting status to {}", no, sc);
+    response.setStatus(sc);
+  }
+
+  @Override
+  public void setStatus(int sc, String sm) {
+    log.debug("{} -- setting status to {} (message: {})", new Object[] { no, sc, sm });
+    response.setStatus(sc, sm);
+  }
+
+  @Override
+  public String getCharacterEncoding() {
+    String result = response.getCharacterEncoding();
+    log.trace("{} -- character encoding: {}", no, result);
+    return result;
+  }
+
+  @Override
+  public String getContentType() {
+    String result = response.getContentType();
+    log.trace("{} -- content type: {}", no, result);
+    return result;
+  }
+
+  @Override
+  public void setCharacterEncoding(String charset) {
+    log.debug("{} -- setting character encoding to {}", no, charset);
+    response.setCharacterEncoding(charset);
+  }
+
+  @Override
+  public void setContentLength(int len) {
+    log.debug("{} -- setting content length to {}", no, len);
+    response.setContentLength(len);
+  }
+
+  @Override
+  public void setContentType(String type) {
+    log.debug("{} -- setting content type to {}", no, type);
+    response.setContentType(type);
+  }
+
+  @Override
+  public void setLocale(Locale loc) {
+    log.debug("{} -- setting locale to {}", no, loc);
+    response.setLocale(loc);
+  }
+
+  @Override
+  public Locale getLocale() {
+    Locale locale = response.getLocale();
+    log.trace("{} -- locale: {}", no, locale);
+    return locale;
+  }
+
+  @Override
+  public int getStatus() {
+    Integer status = response.getStatus();
+    log.trace("{} -- status: {}", no, status);
+    return status;
+  }
+
+  @Override
+  public String getHeader(String name) {
+    String value = response.getHeader(name);
+    log.trace("{} -- header \"{}\": {}", new Object[] { no, name, value });
+    return value;
+  }
+
+  @Override
+  public Collection<String> getHeaders(String name) {
+    Collection<String> values = response.getHeaders(name);
+    if (log.isTraceEnabled()) {
+      StringBuilder builder = new StringBuilder();
+      builder.append(no);
+      builder.append(" -- headers \"");
+      builder.append(name);
+      builder.append("\":");
+      for (String value : values) {
+        builder.append(' ');
+        builder.append(value);
+      }
+      log.trace(builder.toString());
+    }
+    return values;
+  }
+
+  @Override
+  public Collection<String> getHeaderNames() {
+    Collection<String> values = response.getHeaderNames();
+    if (log.isTraceEnabled()) {
+      StringBuilder builder = new StringBuilder();
+      builder.append(no);
+      builder.append(" -- header-names:");
+      for (String value : values) {
+        builder.append(' ');
+        builder.append(value);
+      }
+      log.trace(builder.toString());
+    }
+    return values;
+  }
+
+
+  class CountingServletOutputStream extends ServletOutputStream {
+
+    private ServletOutputStream out;
+    long count = 0l;
+
+
+    CountingServletOutputStream(ServletOutputStream out) {
+      this.out = out;
+    }
+
+
+    @Override
+    public void write(int i) throws IOException {
+      count++;
+      /** Simulate commit, when count is getting bigger then buffer */
+      if (count == buffer + 1) {
+        log.info("{} -- simulating commit because buffer overflow! buffer: {}, count: {}", new Object[] { no, buffer, count });
+        committed = true;
+      }
+      log.trace("{} -- writing byte {}: {}", new Object[] { no, count, (char)i });
+      out.write(i);
+    }
+  }
+}
diff --git a/testingtools/src/main/java/de/juplo/testingtools/WebContextTestExecutionListener.java b/testingtools/src/main/java/de/juplo/testingtools/WebContextTestExecutionListener.java
new file mode 100644 (file)
index 0000000..9309584
--- /dev/null
@@ -0,0 +1,41 @@
+package de.juplo.testingtools;
+
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.beans.factory.config.Scope;
+import org.springframework.context.support.GenericApplicationContext;
+import org.springframework.context.support.SimpleThreadScope;
+import org.springframework.test.context.TestContext;
+import org.springframework.test.context.support.AbstractTestExecutionListener;
+
+/**
+ * Diese Klasse ermöglicht es, Beans mit dem Scope REQUEST in JUnit-Testfällen
+ * zu verwenden.
+ * <p>
+ * Source: http://stackoverflow.com/questions/2411343/request-scoped-beans-in-spring-testing
+ * <p>
+ * Anwendung:
+ * <code>
+ * @RunWith(SpringJUnit4ClassRunner.class)
+ * @ContextConfiguration(locations = "classpath:spring/TestScopedBeans-context.xml")
+ * @TestExecutionListeners({
+ *      WebContextTestExecutionListener.class,
+ *      DependencyInjectionTestExecutionListener.class,
+ *      DirtiesContextTestExecutionListener.class })
+ * public class TestScopedBeans {
+ * ...
+ * </code>
+ */
+public class WebContextTestExecutionListener extends AbstractTestExecutionListener {
+    @Override
+    public void prepareTestInstance(TestContext testContext) throws Exception {
+
+        if (testContext.getApplicationContext() instanceof GenericApplicationContext) {
+            GenericApplicationContext context = (GenericApplicationContext) testContext.getApplicationContext();
+            ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
+            Scope requestScope = new SimpleThreadScope();
+            beanFactory.registerScope("request", requestScope);
+            Scope sessionScope = new SimpleThreadScope();
+            beanFactory.registerScope("session", sessionScope);
+        }
+    }
+}
diff --git a/utils/pom.xml b/utils/pom.xml
deleted file mode 100644 (file)
index bb58b1f..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-
-  <modelVersion>4.0.0</modelVersion>
-
-  <parent>
-    <groupId>de.halbekunst</groupId>
-    <artifactId>juplo</artifactId>
-    <version>1.0.1</version>
-  </parent>
-
-  <artifactId>${pom.parent.artifactId}-utils</artifactId>
-  <name>Juplo - Utils</name>
-
-  <dependencies>
-    <dependency>
-      <groupId>junit</groupId>
-      <artifactId>junit</artifactId>
-      <version>${junit.version}</version>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.slf4j</groupId>
-      <artifactId>slf4j-log4j12</artifactId>
-      <version>${slf4j.version}</version>
-      <exclusions>
-        <exclusion>
-          <groupId>log4j</groupId>
-          <artifactId>log4j</artifactId>
-        </exclusion>
-      </exclusions>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>log4j</groupId>
-      <artifactId>log4j</artifactId>
-      <version>${log4j.version}</version>
-      <exclusions>
-        <exclusion>
-          <groupId>javax.mail</groupId>
-          <artifactId>mail</artifactId>
-        </exclusion>
-        <exclusion>
-          <groupId>javax.jms</groupId>
-          <artifactId>jms</artifactId>
-        </exclusion>
-        <exclusion>
-          <groupId>com.sun.jdmk</groupId>
-          <artifactId>jmxtools</artifactId>
-        </exclusion>
-        <exclusion>
-          <groupId>com.sun.jmx</groupId>
-          <artifactId>jmxri</artifactId>
-        </exclusion>
-      </exclusions>
-      <scope>test</scope>
-    </dependency>
-  </dependencies>
-
-</project>
diff --git a/utils/src/main/java/de/halbekunst/juplo/utils/PercentCodec.java b/utils/src/main/java/de/halbekunst/juplo/utils/PercentCodec.java
deleted file mode 100644 (file)
index d4c53ad..0000000
+++ /dev/null
@@ -1,198 +0,0 @@
-package de.halbekunst.juplo.utils;
-
-import java.nio.charset.Charset;
-
-/**
- * This class performes percent-encoding/-decoding like described in RFC 3986.
- * <p>
- * Complete URI's are not handled by this implementation.
- * That is done best with the original {@linkplain java.net.URI}-class from core Java.
- * The purpose of this class is to have a simple tool to encode/decode the
- * inner parts of an URI, like a segment of the URI-path (the part between two
- * forward slashes) or a name or value segment of the query, where all reserved
- * characters must be encoded/decoded.
- *
- * @author kai
- */
-public class PercentCodec {
-  private final Charset charset;
-
-
-  public PercentCodec(String encoding) {
-    charset = Charset.forName(encoding);
-  }
-
-
-  public String encode(CharSequence in) {
-    StringBuilder out = new StringBuilder();
-    int i = 0;
-    int length = in.length();
-    while (i < length) {
-      int codePoint = Character.codePointAt(in, i);
-      i += Character.charCount(codePoint);
-      switch (codePoint) {
-        case 'a':
-        case 'A':
-        case 'b':
-        case 'B':
-        case 'c':
-        case 'C':
-        case 'd':
-        case 'D':
-        case 'e':
-        case 'E':
-        case 'f':
-        case 'F':
-        case 'g':
-        case 'G':
-        case 'h':
-        case 'H':
-        case 'i':
-        case 'I':
-        case 'j':
-        case 'J':
-        case 'k':
-        case 'K':
-        case 'l':
-        case 'L':
-        case 'm':
-        case 'M':
-        case 'n':
-        case 'N':
-        case 'o':
-        case 'O':
-        case 'p':
-        case 'P':
-        case 'q':
-        case 'Q':
-        case 'r':
-        case 'R':
-        case 's':
-        case 'S':
-        case 't':
-        case 'T':
-        case 'u':
-        case 'U':
-        case 'v':
-        case 'V':
-        case 'w':
-        case 'W':
-        case 'x':
-        case 'X':
-        case 'y':
-        case 'Y':
-        case 'z':
-        case 'Z':
-        case '0':
-        case '1':
-        case '2':
-        case '3':
-        case '4':
-        case '5':
-        case '6':
-        case '7':
-        case '8':
-        case '9':
-        case '-':
-        case '_':
-        case '.':
-        case '~':
-          /**
-           * Unreserved characters can (and should!) stay unchanged!
-           * (See {@link http://en.wikipedia.org/wiki/Percent-encoding#Types_of_URI_characters})
-           */
-          out.append(Character.toChars(codePoint));
-          break;
-        default:
-          /**
-           * All other characters are reserved or special characters and,
-           * hence, must be encoded!
-           */
-          String encoded = new String(Character.toChars(codePoint));
-          byte[] bytes = encoded.getBytes(charset);
-          for (int j = 0; j < bytes.length; j++) {
-            out.append('%');
-            out.append(Character.forDigit((bytes[j] >> 4) & 0xF, 16));
-            out.append(Character.forDigit((bytes[j]) & 0xF, 16));
-          }
-      }
-    }
-    return out.toString();
-  }
-
-  public String decode(CharSequence in) {
-    StringBuilder out = new StringBuilder();
-    int i = 0;
-    int length = in.length();
-    while (i < length) {
-      char c = in.charAt(i);
-      if (c != '%') {
-        out.append(c);
-        i++;
-      }
-      else {
-        byte[] bytes = new byte[length-i/3];
-        int pos = 0;
-        while (i+2 < length && in.charAt(i) == '%' ) {
-          int b = 0;
-          switch (in.charAt(i+1)) {
-            case '0': break;
-            case '1': b = 16*1; break;
-            case '2': b = 16*2; break;
-            case '3': b = 16*3; break;
-            case '4': b = 16*4; break;
-            case '5': b = 16*5; break;
-            case '6': b = 16*6; break;
-            case '7': b = 16*7; break;
-            case '8': b = 16*8; break;
-            case '9': b = 16*9; break;
-            case 'a':
-            case 'A': b = 16*10; break;
-            case 'b':
-            case 'B': b = 16*11; break;
-            case 'c':
-            case 'C': b = 16*12; break;
-            case 'd':
-            case 'D': b = 16*13; break;
-            case 'e':
-            case 'E': b = 16*14; break;
-            case 'f':
-            case 'F': b = 16*15; break;
-            default: throw new IllegalArgumentException("Illegal escape-sequence: %" + in.subSequence(i, i+3));
-          }
-          switch (in.charAt(i+2)) {
-            case '0': break;
-            case '1': b += 1; break;
-            case '2': b += 2; break;
-            case '3': b += 3; break;
-            case '4': b += 4; break;
-            case '5': b += 5; break;
-            case '6': b += 6; break;
-            case '7': b += 7; break;
-            case '8': b += 8; break;
-            case '9': b += 9; break;
-            case 'a':
-            case 'A': b += 10; break;
-            case 'b':
-            case 'B': b += 11; break;
-            case 'c':
-            case 'C': b += 12; break;
-            case 'd':
-            case 'D': b += 13; break;
-            case 'e':
-            case 'E': b += 14; break;
-            case 'f':
-            case 'F': b += 15; break;
-            default: throw new IllegalArgumentException("Illegal escape-sequence: %" + in.subSequence(i, i+3));
-          }
-          bytes[pos++] = (byte)b;
-          i += 3;
-        }
-        out.append(new String(bytes, 0, pos, charset));
-        if (i < length && in.charAt(i) == '%')
-          throw  new IllegalArgumentException("Incomplete escape-sequence: %" + in.subSequence(i, length));
-      }
-    }
-    return out.toString();
-  }
-}
diff --git a/utils/src/test/java/de/halbekunst/juplo/utils/PercentCodecTest.java b/utils/src/test/java/de/halbekunst/juplo/utils/PercentCodecTest.java
deleted file mode 100644 (file)
index a06784e..0000000
+++ /dev/null
@@ -1,114 +0,0 @@
-package de.halbekunst.juplo.utils;
-
-import junit.framework.Assert;
-import org.junit.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-
-public class PercentCodecTest {
-  private final static Logger log = LoggerFactory.getLogger(PercentCodecTest.class);
-
-  public final static char[] decoded = { ' ', '+', 'q', 's', '8', '0', 'x', 'ä', 'ß', 'à', '€', '¢', '@', '/', '?', '#', ';','.', '&', '%' };
-  public final static String[] encoded_latin1 = { "%20", "%2b", "q", "s", "8", "0", "x", "%e4", "%df", "%e0", "%3f", "%a2", "%40", "%2f", "%3f", "%23", "%3b",".", "%26", "%25" };
-  public final static String[] encoded_utf8 = { "%20", "%2b", "q", "s", "8", "0", "x", "%c3%a4", "%c3%9f", "%c3%a0", "%e2%82%ac", "%c2%a2", "%40", "%2f", "%3f", "%23", "%3b",".", "%26", "%25" };
-
-
-  @Test
-  public void testEncodeLatin1() throws Exception {
-    PercentCodec codec = new PercentCodec("latin1");
-
-    for (int a = 0; a < decoded.length; a++) {
-      for (int b = 0; b < decoded.length; b++) {
-        for (int c = 0; c < decoded.length; c++) {
-          /** Das Zeichen '€' existiert in Latin1 nicht! */
-          if (a == 10 || b == 10 || c == 10)
-            continue;
-          StringBuilder input = new StringBuilder();
-          input.append(decoded[a]);
-          input.append(decoded[b]);
-          input.append(decoded[c]);
-          StringBuilder expected = new StringBuilder();
-          expected.append(encoded_latin1[a]);
-          expected.append(encoded_latin1[b]);
-          expected.append(encoded_latin1[c]);
-          String output = codec.encode(input);
-          log.debug("{}\t-> {}", input, output);
-          Assert.assertEquals("\"" + input + "\" was encoded falsely", expected.toString(), output);
-        }
-      }
-    }
-  }
-
-  @Test
-  public void testDecodeLatin1() throws Exception {
-    PercentCodec codec = new PercentCodec("latin1");
-
-    for (int a = 0; a < decoded.length; a++) {
-      for (int b = 0; b < decoded.length; b++) {
-        for (int c = 0; c < decoded.length; c++) {
-          /** Das Zeichen '€' existiert in Latin1 nicht! */
-          if (a == 10 || b == 10 || c == 10)
-            continue;
-          StringBuilder input = new StringBuilder();
-          input.append(encoded_latin1[a]);
-          input.append(encoded_latin1[b]);
-          input.append(encoded_latin1[c]);
-          StringBuilder expected = new StringBuilder();
-          expected.append(decoded[a]);
-          expected.append(decoded[b]);
-          expected.append(decoded[c]);
-          String output = codec.decode(input);
-          log.debug("{}\t-> {}", input, output);
-          Assert.assertEquals("\"" + input + "\" was decoded falsely", expected.toString(), output);
-        }
-      }
-    }
-  }
-
-  @Test
-  public void testEncodeUtf8() throws Exception {
-    PercentCodec codec = new PercentCodec("UTF-8");
-
-    for (int a = 0; a < decoded.length; a++) {
-      for (int b = 0; b < decoded.length; b++) {
-        for (int c = 0; c < decoded.length; c++) {
-          StringBuilder input = new StringBuilder();
-          input.append(decoded[a]);
-          input.append(decoded[b]);
-          input.append(decoded[c]);
-          StringBuilder expected = new StringBuilder();
-          expected.append(encoded_utf8[a]);
-          expected.append(encoded_utf8[b]);
-          expected.append(encoded_utf8[c]);
-          String output = codec.encode(input);
-          log.debug("{}\t-> {}", input, output);
-          Assert.assertEquals("\"" + input + "\" was encoded falsely", expected.toString(), output);
-        }
-      }
-    }
-  }
-
-  @Test
-  public void testDecodeUtf8() throws Exception {
-    PercentCodec codec = new PercentCodec("UTF-8");
-
-    for (int a = 0; a < decoded.length; a++) {
-      for (int b = 0; b < decoded.length; b++) {
-        for (int c = 0; c < decoded.length; c++) {
-          StringBuilder input = new StringBuilder();
-          input.append(encoded_utf8[a]);
-          input.append(encoded_utf8[b]);
-          input.append(encoded_utf8[c]);
-          StringBuilder expected = new StringBuilder();
-          expected.append(decoded[a]);
-          expected.append(decoded[b]);
-          expected.append(decoded[c]);
-          String output = codec.decode(input);
-          log.debug("{}\t-> {}", input, output);
-          Assert.assertEquals("\"" + input + "\" was decoded falsely", expected.toString(), output);
-        }
-      }
-    }
-  }
-}
diff --git a/utils/src/test/resources/log4j.xml b/utils/src/test/resources/log4j.xml
deleted file mode 100644 (file)
index 18822a5..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE log4j:configuration PUBLIC "-//LOGGER" "log4j.dtd">
-
-<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
-
-       <!-- Appenders -->
-       <appender name="console" class="org.apache.log4j.ConsoleAppender">
-               <param name="Target" value="System.out" />
-               <layout class="org.apache.log4j.PatternLayout">
-                       <param name="ConversionPattern" value="%-5p: %c - %m%n" />
-               </layout>
-       </appender>
-
-       <root>
-               <priority value="debug" />
-               <appender-ref ref="console" />
-       </root>
-
-</log4j:configuration>