--- /dev/null
+#!/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
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?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
+ <when> and <otherwise>
+ </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 <%= ... >, 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 <,>,&,'," 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 <choose> that follows <when> 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 <choose> 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>
--- /dev/null
+<%@page contentType="text/html" pageEncoding="UTF-8" session="false"%>
+<p>Hello World, again...</p>
--- /dev/null
+<?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>
--- /dev/null
+<%@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>
--- /dev/null
+<!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>
--- /dev/null
+<%@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>
--- /dev/null
+<%@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>
--- /dev/null
+<%@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>
--- /dev/null
+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());
+ }
+}
--- /dev/null
+#!/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
--- /dev/null
+<?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>
--- /dev/null
+#!/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
--- /dev/null
+<?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>
--- /dev/null
+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!");
+ }
+}
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<!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>
--- /dev/null
+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());
+ }
+}
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+<?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>
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<%@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>
--- /dev/null
+<%@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>
--- /dev/null
+<!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>
--- /dev/null
+<%@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>
--- /dev/null
+<?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>
--- /dev/null
+<%@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...
+
+--%>
--- /dev/null
+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());
+ }
+}
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<!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>
--- /dev/null
+<!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>
--- /dev/null
+body {
+ background-color: #ccc;
+ color: #444;
+}
+h1,h2,h3,h4 {
+ color: #000;
+}
\ No newline at end of file
--- /dev/null
+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());
+ }
+}
--- /dev/null
+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());
+ }
+}
--- /dev/null
+<?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>
--- /dev/null
+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
--- /dev/null
+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);
+ }
+ }
+ }
+ }
+}
--- /dev/null
+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;
+ }
+}
--- /dev/null
+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);
+}
--- /dev/null
+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";
+
+}
--- /dev/null
+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 {}
--- /dev/null
+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 {};
+}
--- /dev/null
+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 {
+}
--- /dev/null
+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 */
+}
--- /dev/null
+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;
+}
--- /dev/null
+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 {};
+}
--- /dev/null
+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;
+}
--- /dev/null
+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
+ * (<body></body>).
+ **/
+ 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
+ * (<body></body>).
+ **/
+ 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;
+}
--- /dev/null
+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());
+ }
+}
--- /dev/null
+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);
+ }
+ }
+}
--- /dev/null
+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);
+ }
+ }
+}
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?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>
+++ /dev/null
-<?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>
+++ /dev/null
-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
+++ /dev/null
-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);
- }
- }
- }
- }
-}
+++ /dev/null
-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;
- }
-}
+++ /dev/null
-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);
-}
+++ /dev/null
-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";
-
-}
+++ /dev/null
-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 {}
+++ /dev/null
-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 {};
-}
+++ /dev/null
-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 {
-}
+++ /dev/null
-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 */
-}
+++ /dev/null
-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;
-}
+++ /dev/null
-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 {};
-}
+++ /dev/null
-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;
-}
+++ /dev/null
-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
- * (<body></body>).
- **/
- 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
- * (<body></body>).
- **/
- 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;
-}
+++ /dev/null
-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());
- }
-}
+++ /dev/null
-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);
- }
- }
-}
+++ /dev/null
-<?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>
+++ /dev/null
-<?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>
+++ /dev/null
-<?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>
+++ /dev/null
-<?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
+++ /dev/null
-#!/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
+++ /dev/null
-<?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>
+++ /dev/null
-<?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>
+++ /dev/null
-<?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>
+++ /dev/null
-<?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
- <when> and <otherwise>
- </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 <%= ... >, 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 <,>,&,'," 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 <choose> that follows <when> 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 <choose> 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>
+++ /dev/null
-<%@page contentType="text/html" pageEncoding="UTF-8" session="false"%>
-<p>Hello World, again...</p>
+++ /dev/null
-<?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>
+++ /dev/null
-<%@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>
+++ /dev/null
-<!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>
+++ /dev/null
-<%@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>
+++ /dev/null
-<%@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>
+++ /dev/null
-<%@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>
+++ /dev/null
-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());
- }
-}
+++ /dev/null
-#!/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
+++ /dev/null
-<?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>
+++ /dev/null
-#!/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
+++ /dev/null
-<?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>
+++ /dev/null
-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!");
- }
-}
+++ /dev/null
-<?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>
+++ /dev/null
-<?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>
+++ /dev/null
-<?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>
+++ /dev/null
-<!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>
+++ /dev/null
-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());
- }
-}
+++ /dev/null
-#!/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
+++ /dev/null
-#!/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
+++ /dev/null
-<?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>
+++ /dev/null
-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
+++ /dev/null
-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
+++ /dev/null
-<?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>
+++ /dev/null
-<?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>
+++ /dev/null
-<%@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>
+++ /dev/null
-<%@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>
+++ /dev/null
-<!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>
+++ /dev/null
-<%@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>
+++ /dev/null
-<?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>
+++ /dev/null
-<%@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...
-
---%>
+++ /dev/null
-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());
- }
-}
+++ /dev/null
-#!/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
+++ /dev/null
-#!/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
+++ /dev/null
-<?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>
+++ /dev/null
-<?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>
+++ /dev/null
-<?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>
+++ /dev/null
-<?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>
+++ /dev/null
-<!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>
+++ /dev/null
-<!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>
+++ /dev/null
-body {
- background-color: #ccc;
- color: #444;
-}
-h1,h2,h3,h4 {
- color: #000;
-}
\ No newline at end of file
+++ /dev/null
-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());
- }
-}
+++ /dev/null
-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());
- }
-}
--- /dev/null
+<?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>
--- /dev/null
+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);
+ }
+}
+}
--- /dev/null
+<?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>
+++ /dev/null
-<?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>
+++ /dev/null
-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);
- }
-}
-}
+++ /dev/null
-<?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
--- /dev/null
+<?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>
--- /dev/null
+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();
+ }
+}
--- /dev/null
+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);
+ }
+ }
+ }
+ }
+}
--- /dev/null
+<?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>
<modelVersion>4.0.0</modelVersion>
- <groupId>de.halbekunst</groupId>
+ <groupId>de.juplo</groupId>
<artifactId>juplo</artifactId>
- <version>2.0-SNAPSHOT</version>
+ <version>1.0-SNAPSHOT</version>
<name>Juplo</name>
<packaging>pom</packaging>
- <url>http://www.halbekunst.de</url>
+ <url>http://www.juplo.de</url>
<prerequisites>
<maven>2.0.6</maven>
<developer>
<id>kai</id>
<name>Kai Moritz</name>
- <email>kai@ich-geh-kaputt.de</email>
+ <email>kai@juplo.de</email>
</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>
-
<!-- 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>
- <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>
- <id>halbekunst.internal</id>
+ <id>juplo.internal</id>
<name>Internal Release Repository</name>
- <url>http://halbekunst.de/archiva/repository/internal/</url>
+ <url>http://juplo.de/archiva/repository/internal/</url>
</repository>
<snapshotRepository>
- <id>halbekunst.snapshots</id>
+ <id>juplo.snapshots</id>
<name>Internal Snapshot Repository</name>
- <url>http://halbekunst.de/archiva/repository/snapshots/</url>
+ <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>
<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>
+++ /dev/null
-<?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>
+++ /dev/null
-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;
- }
-}
-
+++ /dev/null
-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() {
- }
-}
+++ /dev/null
-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);
- }
- }
-}
+++ /dev/null
-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);
- }
- }
-}
+++ /dev/null
-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);
- }
- }
-}
--- /dev/null
+<?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>
--- /dev/null
+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;
+ }
+}
+
--- /dev/null
+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() {
+ }
+}
--- /dev/null
+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);
+ }
+ }
+}
--- /dev/null
+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);
+ }
+ }
+}
+++ /dev/null
-<?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>
+++ /dev/null
-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();
- }
-}
+++ /dev/null
-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);
- }
- }
- }
- }
-}
+++ /dev/null
-<?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>