diff --git a/.github/ISSUE_TEMPLATE/issue-template.md b/.github/ISSUE_TEMPLATE/issue-template.md index 98e009121fc..0cc86aeeaac 100644 --- a/.github/ISSUE_TEMPLATE/issue-template.md +++ b/.github/ISSUE_TEMPLATE/issue-template.md @@ -11,7 +11,7 @@ labels: Bug **Jetty Environment** - + **Java version/vendor** `(use: java -version)` diff --git a/.github/ISSUE_TEMPLATE/question-template.md b/.github/ISSUE_TEMPLATE/question-template.md index a36ca9e1bbe..7c5da7f28a7 100644 --- a/.github/ISSUE_TEMPLATE/question-template.md +++ b/.github/ISSUE_TEMPLATE/question-template.md @@ -10,7 +10,7 @@ assignees: '' **Jetty Version** **Jetty Environment** - + **Java Version** diff --git a/README.md b/README.md index 75b76b2ad71..40c924ea75d 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Jetty is a modern fully asynchronous web server that has a long history as a com ```shell $ mkdir jetty-base && cd jetty-base -$ java -jar $JETTY_HOME/start.jar --add-modules=http,ee10-deploy +$ java -jar $JETTY_HOME/start.jar --add-modules=http,ee11-deploy $ cp ~/src/myproj/target/mywebapp.war webapps $ java -jar $JETTY_HOME/start.jar ``` @@ -20,7 +20,7 @@ $ java -jar $JETTY_HOME/start.jar ```shell $ mkdir jetty-base && cd jetty-base -$ java -jar $JETTY_HOME/start.jar --add-modules=http,ee10-deploy,ee8-deploy +$ java -jar $JETTY_HOME/start.jar --add-modules=http,ee11-deploy,ee8-deploy $ cp ~/src/myproj/target/mywebapp10.war webapps $ cp ~/src/myproj/target/mywebapp8.war webapps $ echo "environment: ee8" > webapps/mywebapp8.properties diff --git a/build/pom.xml b/build/pom.xml index e8185f55923..bc1bed6a619 100644 --- a/build/pom.xml +++ b/build/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty jetty-project - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.build build diff --git a/build/scripts/dependency-update-reports.sh b/build/scripts/dependency-update-reports.sh index e29a76d073b..98bf5799bf8 100755 --- a/build/scripts/dependency-update-reports.sh +++ b/build/scripts/dependency-update-reports.sh @@ -24,6 +24,11 @@ mvn -B -Pdependency-updates-reports validate -Dmaven.build.cache.enabled=false cp target/site/dependency-updates-aggregate-report.html $REPORT_OUTPUT_DIR/dependency-updates-report-core.html popd +pushd jetty-ee11 +mvn -B -Pdependency-updates-reports validate -Dmaven.build.cache.enabled=false +cp target/site/dependency-updates-aggregate-report.html $REPORT_OUTPUT_DIR/dependency-updates-report-ee11.html +popd + pushd jetty-ee10 mvn -B -Pdependency-updates-reports validate -Dmaven.build.cache.enabled=false cp target/site/dependency-updates-aggregate-report.html $REPORT_OUTPUT_DIR/dependency-updates-report-ee10.html diff --git a/build/scripts/dependency-update.sh b/build/scripts/dependency-update.sh index 1882126316f..18f02db3016 100755 --- a/build/scripts/dependency-update.sh +++ b/build/scripts/dependency-update.sh @@ -11,6 +11,10 @@ pushd jetty-core mvn -N -B -Pupdate-dependencies-core validate -Dmaven.build.cache.enabled=false popd +pushd jetty-ee11 +mvn -N -B -Pupdate-dependencies-ee11 validate -Dmaven.build.cache.enabled=false +popd + pushd jetty-ee10 mvn -N -B -Pupdate-dependencies-ee10 validate -Dmaven.build.cache.enabled=false popd diff --git a/documentation/jetty-asciidoctor-extensions/pom.xml b/documentation/jetty-asciidoctor-extensions/pom.xml index 72dcacfd7dd..9eec08e2a8c 100644 --- a/documentation/jetty-asciidoctor-extensions/pom.xml +++ b/documentation/jetty-asciidoctor-extensions/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.documentation documentation - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-asciidoctor-extensions jar diff --git a/documentation/jetty-documentation/pom.xml b/documentation/jetty-documentation/pom.xml index 1cfce63173e..8f9d4262c4a 100644 --- a/documentation/jetty-documentation/pom.xml +++ b/documentation/jetty-documentation/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.documentation documentation - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-documentation pom diff --git a/documentation/pom.xml b/documentation/pom.xml index abacbbce27d..e4ef199f180 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty jetty-project - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.documentation documentation diff --git a/javadoc/pom.xml b/javadoc/pom.xml index 6886ca12fcb..0a58683a311 100644 --- a/javadoc/pom.xml +++ b/javadoc/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty jetty-project - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT javadoc jar @@ -24,8 +24,8 @@ - org.eclipse.jetty.ee10 - jetty-ee10-bom + org.eclipse.jetty.ee11 + jetty-ee11-bom ${project.version} pom import @@ -163,110 +163,110 @@ provided - + - org.eclipse.jetty.ee10 - jetty-ee10-annotations + org.eclipse.jetty.ee11 + jetty-ee11-annotations provided - org.eclipse.jetty.ee10 - jetty-ee10-apache-jsp + org.eclipse.jetty.ee11 + jetty-ee11-apache-jsp provided - org.eclipse.jetty.ee10 - jetty-ee10-cdi + org.eclipse.jetty.ee11 + jetty-ee11-cdi provided - org.eclipse.jetty.ee10 - jetty-ee10-fcgi-proxy + org.eclipse.jetty.ee11 + jetty-ee11-fcgi-proxy provided - org.eclipse.jetty.ee10 - jetty-ee10-glassfish-jstl + org.eclipse.jetty.ee11 + jetty-ee11-glassfish-jstl provided - org.eclipse.jetty.ee10 - jetty-ee10-jaspi + org.eclipse.jetty.ee11 + jetty-ee11-jaspi provided - org.eclipse.jetty.ee10 - jetty-ee10-jndi + org.eclipse.jetty.ee11 + jetty-ee11-jndi provided - org.eclipse.jetty.ee10 - jetty-ee10-plus + org.eclipse.jetty.ee11 + jetty-ee11-plus provided - org.eclipse.jetty.ee10 - jetty-ee10-proxy + org.eclipse.jetty.ee11 + jetty-ee11-proxy provided - org.eclipse.jetty.ee10 - jetty-ee10-quickstart + org.eclipse.jetty.ee11 + jetty-ee11-quickstart provided - org.eclipse.jetty.ee10 - jetty-ee10-runner + org.eclipse.jetty.ee11 + jetty-ee11-runner provided - org.eclipse.jetty.ee10 - jetty-ee10-servlet + org.eclipse.jetty.ee11 + jetty-ee11-servlet provided - org.eclipse.jetty.ee10 - jetty-ee10-servlets + org.eclipse.jetty.ee11 + jetty-ee11-servlets provided - org.eclipse.jetty.ee10 - jetty-ee10-webapp + org.eclipse.jetty.ee11 + jetty-ee11-webapp provided - org.eclipse.jetty.ee10.osgi - jetty-ee10-osgi-alpn + org.eclipse.jetty.ee11.osgi + jetty-ee11-osgi-alpn provided - org.eclipse.jetty.ee10.osgi - jetty-ee10-osgi-boot + org.eclipse.jetty.ee11.osgi + jetty-ee11-osgi-boot provided - org.eclipse.jetty.ee10.osgi - jetty-ee10-osgi-boot-jsp + org.eclipse.jetty.ee11.osgi + jetty-ee11-osgi-boot-jsp provided - org.eclipse.jetty.ee10.websocket - jetty-ee10-websocket-jakarta-client + org.eclipse.jetty.ee11.websocket + jetty-ee11-websocket-jakarta-client provided - org.eclipse.jetty.ee10.websocket - jetty-ee10-websocket-jakarta-server + org.eclipse.jetty.ee11.websocket + jetty-ee11-websocket-jakarta-server provided - org.eclipse.jetty.ee10.websocket - jetty-ee10-websocket-jetty-server + org.eclipse.jetty.ee11.websocket + jetty-ee11-websocket-jetty-server provided - org.eclipse.jetty.ee10.websocket - jetty-ee10-websocket-servlet + org.eclipse.jetty.ee11.websocket + jetty-ee11-websocket-servlet provided diff --git a/jetty-core/jetty-alpn/jetty-alpn-client/pom.xml b/jetty-core/jetty-alpn/jetty-alpn-client/pom.xml index 9c960063959..8b23ef409aa 100644 --- a/jetty-core/jetty-alpn/jetty-alpn-client/pom.xml +++ b/jetty-core/jetty-alpn/jetty-alpn-client/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty jetty-alpn - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-alpn-client Core :: ALPN :: Client diff --git a/jetty-core/jetty-alpn/jetty-alpn-conscrypt-client/pom.xml b/jetty-core/jetty-alpn/jetty-alpn-conscrypt-client/pom.xml index 8452ed9186f..6fbf1fc0701 100644 --- a/jetty-core/jetty-alpn/jetty-alpn-conscrypt-client/pom.xml +++ b/jetty-core/jetty-alpn/jetty-alpn-conscrypt-client/pom.xml @@ -6,7 +6,7 @@ org.eclipse.jetty jetty-alpn - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-alpn-conscrypt-client Core :: ALPN :: Conscrypt Client diff --git a/jetty-core/jetty-alpn/jetty-alpn-conscrypt-server/pom.xml b/jetty-core/jetty-alpn/jetty-alpn-conscrypt-server/pom.xml index ee4b1921b70..6e77e1025c1 100644 --- a/jetty-core/jetty-alpn/jetty-alpn-conscrypt-server/pom.xml +++ b/jetty-core/jetty-alpn/jetty-alpn-conscrypt-server/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty jetty-alpn - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-alpn-conscrypt-server Core :: ALPN :: Conscrypt Server diff --git a/jetty-core/jetty-alpn/jetty-alpn-java-client/pom.xml b/jetty-core/jetty-alpn/jetty-alpn-java-client/pom.xml index e66df7987ba..f6b17a876f9 100644 --- a/jetty-core/jetty-alpn/jetty-alpn-java-client/pom.xml +++ b/jetty-core/jetty-alpn/jetty-alpn-java-client/pom.xml @@ -6,7 +6,7 @@ org.eclipse.jetty jetty-alpn - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-alpn-java-client Core :: ALPN :: Java Client diff --git a/jetty-core/jetty-alpn/jetty-alpn-java-server/pom.xml b/jetty-core/jetty-alpn/jetty-alpn-java-server/pom.xml index ada99a7125e..40a30c5f13d 100644 --- a/jetty-core/jetty-alpn/jetty-alpn-java-server/pom.xml +++ b/jetty-core/jetty-alpn/jetty-alpn-java-server/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty jetty-alpn - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-alpn-java-server Core :: ALPN :: Java Server diff --git a/jetty-core/jetty-alpn/jetty-alpn-server/pom.xml b/jetty-core/jetty-alpn/jetty-alpn-server/pom.xml index 442940800ca..9a45d1ab549 100644 --- a/jetty-core/jetty-alpn/jetty-alpn-server/pom.xml +++ b/jetty-core/jetty-alpn/jetty-alpn-server/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty jetty-alpn - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-alpn-server Core :: ALPN :: Server diff --git a/jetty-core/jetty-alpn/pom.xml b/jetty-core/jetty-alpn/pom.xml index 241d103dc2b..fe31b8f4be3 100644 --- a/jetty-core/jetty-alpn/pom.xml +++ b/jetty-core/jetty-alpn/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty jetty-core - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-alpn pom diff --git a/jetty-core/jetty-bom/pom.xml b/jetty-core/jetty-bom/pom.xml index 312134cfc47..7ca797d651f 100644 --- a/jetty-core/jetty-bom/pom.xml +++ b/jetty-core/jetty-bom/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty jetty-core - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-bom @@ -18,282 +18,282 @@ org.eclipse.jetty jetty-alpn-client - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty jetty-alpn-conscrypt-client - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty jetty-alpn-conscrypt-server - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty jetty-alpn-java-client - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty jetty-alpn-java-server - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty jetty-alpn-server - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty jetty-client - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty jetty-deploy - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty jetty-http - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty jetty-http-spi - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty jetty-http-tools - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty jetty-io - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty jetty-jmx - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty jetty-jndi - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty jetty-keystore - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty jetty-openid - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty jetty-osgi - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty jetty-plus - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty jetty-proxy - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty jetty-rewrite - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty jetty-security - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty jetty-server - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty jetty-session - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty jetty-slf4j-impl - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty jetty-start - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty jetty-unixdomain-server - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty jetty-util - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty jetty-util-ajax - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty jetty-xml - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.demos jetty-demo-handler - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.fcgi jetty-fcgi-client - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.fcgi jetty-fcgi-proxy - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.fcgi jetty-fcgi-server - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.http2 jetty-http2-client - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.http2 jetty-http2-client-transport - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.http2 jetty-http2-common - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.http2 jetty-http2-hpack - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.http2 jetty-http2-server - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.http3 jetty-http3-client - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.http3 jetty-http3-client-transport - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.http3 jetty-http3-common - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.http3 jetty-http3-qpack - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.http3 jetty-http3-server - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.quic jetty-quic-client - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.quic jetty-quic-common - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.quic jetty-quic-quiche-common - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.quic jetty-quic-quiche-foreign - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.quic jetty-quic-quiche-jna - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.quic jetty-quic-server - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.websocket jetty-websocket-core-client - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.websocket jetty-websocket-core-common - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.websocket jetty-websocket-core-server - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.websocket jetty-websocket-jetty-api - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.websocket jetty-websocket-jetty-client - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.websocket jetty-websocket-jetty-common - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.websocket jetty-websocket-jetty-server - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT diff --git a/jetty-core/jetty-client/pom.xml b/jetty-core/jetty-client/pom.xml index 78ec3882e2c..a04f1b5cf9b 100644 --- a/jetty-core/jetty-client/pom.xml +++ b/jetty-core/jetty-client/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty jetty-core - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-client Core :: HTTP Client diff --git a/jetty-core/jetty-demos/jetty-demo-handler/pom.xml b/jetty-core/jetty-demos/jetty-demo-handler/pom.xml index a16c755d556..c1592e4d5f9 100644 --- a/jetty-core/jetty-demos/jetty-demo-handler/pom.xml +++ b/jetty-core/jetty-demos/jetty-demo-handler/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty.demos jetty-demos - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-demo-handler Core :: Demo Handler diff --git a/jetty-core/jetty-demos/pom.xml b/jetty-core/jetty-demos/pom.xml index 4469eb3f566..9ff8dcbdba4 100644 --- a/jetty-core/jetty-demos/pom.xml +++ b/jetty-core/jetty-demos/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty jetty-core - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.demos jetty-demos diff --git a/jetty-core/jetty-deploy/pom.xml b/jetty-core/jetty-deploy/pom.xml index c51e49ee6fd..cb7c1a5fd96 100644 --- a/jetty-core/jetty-deploy/pom.xml +++ b/jetty-core/jetty-deploy/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty jetty-core - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-deploy Core :: Deployers diff --git a/jetty-core/jetty-deploy/src/main/config/etc/jetty-decorate.xml b/jetty-core/jetty-deploy/src/main/config/etc/jetty-decorate.xml index 9b5002c8eff..270fe956466 100644 --- a/jetty-core/jetty-deploy/src/main/config/etc/jetty-decorate.xml +++ b/jetty-core/jetty-deploy/src/main/config/etc/jetty-decorate.xml @@ -5,7 +5,7 @@ - + /etc/jetty-web-decorate.xml diff --git a/jetty-core/jetty-deploy/src/main/config/modules/global-webapp-common.d/global-webapp-common.xml b/jetty-core/jetty-deploy/src/main/config/modules/global-webapp-common.d/global-webapp-common.xml index b3bfe3e17f2..665c385c80d 100644 --- a/jetty-core/jetty-deploy/src/main/config/modules/global-webapp-common.d/global-webapp-common.xml +++ b/jetty-core/jetty-deploy/src/main/config/modules/global-webapp-common.d/global-webapp-common.xml @@ -5,7 +5,7 @@ - + diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java index d1b53f3a32c..2bd5d43f364 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java @@ -113,13 +113,13 @@ public class DeploymentManagerTest }); assertThat(depman.getDefaultEnvironmentName(), is("ee12")); - Environment.ensure("ee10"); + Environment.ensure("ee11"); depman.addAppProvider(new MockAppProvider() { @Override public String getEnvironmentName() { - return "ee10"; + return "ee11"; } }); assertThat(depman.getDefaultEnvironmentName(), is("ee12")); @@ -150,7 +150,7 @@ public class DeploymentManagerTest "other", "somethingElse", "ee7", - "ee10", + "ee11", "ee12" )); } diff --git a/jetty-core/jetty-ee/pom.xml b/jetty-core/jetty-ee/pom.xml index 8ff96f24bd7..5a5c411f43a 100644 --- a/jetty-core/jetty-ee/pom.xml +++ b/jetty-core/jetty-ee/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty jetty-core - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee Core :: EE Common diff --git a/jetty-core/jetty-fcgi/jetty-fcgi-client/pom.xml b/jetty-core/jetty-fcgi/jetty-fcgi-client/pom.xml index 8e32fccf7ac..5e75731c135 100644 --- a/jetty-core/jetty-fcgi/jetty-fcgi-client/pom.xml +++ b/jetty-core/jetty-fcgi/jetty-fcgi-client/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.fcgi jetty-fcgi - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-fcgi-client Core :: FastCGI :: Client diff --git a/jetty-core/jetty-fcgi/jetty-fcgi-proxy/pom.xml b/jetty-core/jetty-fcgi/jetty-fcgi-proxy/pom.xml index e606cfcafc4..1aa2f587644 100644 --- a/jetty-core/jetty-fcgi/jetty-fcgi-proxy/pom.xml +++ b/jetty-core/jetty-fcgi/jetty-fcgi-proxy/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.fcgi jetty-fcgi - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-fcgi-proxy Core :: FastCGI :: Proxy diff --git a/jetty-core/jetty-fcgi/jetty-fcgi-server/pom.xml b/jetty-core/jetty-fcgi/jetty-fcgi-server/pom.xml index e8aeeff3bc3..e5e80bf42e4 100644 --- a/jetty-core/jetty-fcgi/jetty-fcgi-server/pom.xml +++ b/jetty-core/jetty-fcgi/jetty-fcgi-server/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.fcgi jetty-fcgi - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-fcgi-server Core :: FastCGI :: Server diff --git a/jetty-core/jetty-fcgi/pom.xml b/jetty-core/jetty-fcgi/pom.xml index 159735da7ad..4179f0c45da 100644 --- a/jetty-core/jetty-fcgi/pom.xml +++ b/jetty-core/jetty-fcgi/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty jetty-core - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.fcgi jetty-fcgi diff --git a/jetty-core/jetty-http-spi/pom.xml b/jetty-core/jetty-http-spi/pom.xml index a99842e8352..7cec3782403 100644 --- a/jetty-core/jetty-http-spi/pom.xml +++ b/jetty-core/jetty-http-spi/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty jetty-core - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-http-spi Core :: HTTP SPI diff --git a/jetty-core/jetty-http-tools/pom.xml b/jetty-core/jetty-http-tools/pom.xml index 95a10969874..d30ac00c06c 100644 --- a/jetty-core/jetty-http-tools/pom.xml +++ b/jetty-core/jetty-http-tools/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty jetty-core - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-http-tools Core :: HTTP Tools diff --git a/jetty-core/jetty-http/pom.xml b/jetty-core/jetty-http/pom.xml index e175b81c99a..0ffc230195d 100644 --- a/jetty-core/jetty-http/pom.xml +++ b/jetty-core/jetty-http/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty jetty-core - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-http Core :: HTTP diff --git a/jetty-core/jetty-http2/jetty-http2-client-transport/pom.xml b/jetty-core/jetty-http2/jetty-http2-client-transport/pom.xml index 036b8bdb720..64d6d83c3c8 100644 --- a/jetty-core/jetty-http2/jetty-http2-client-transport/pom.xml +++ b/jetty-core/jetty-http2/jetty-http2-client-transport/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.http2 jetty-http2 - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-http2-client-transport Core :: HTTP2 :: Client Transport diff --git a/jetty-core/jetty-http2/jetty-http2-client/pom.xml b/jetty-core/jetty-http2/jetty-http2-client/pom.xml index f248198c32f..e4e2237e7d6 100644 --- a/jetty-core/jetty-http2/jetty-http2-client/pom.xml +++ b/jetty-core/jetty-http2/jetty-http2-client/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.http2 jetty-http2 - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-http2-client Core :: HTTP2 :: Client diff --git a/jetty-core/jetty-http2/jetty-http2-common/pom.xml b/jetty-core/jetty-http2/jetty-http2-common/pom.xml index f082c5aa24a..ac4ebe6ee97 100644 --- a/jetty-core/jetty-http2/jetty-http2-common/pom.xml +++ b/jetty-core/jetty-http2/jetty-http2-common/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.http2 jetty-http2 - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-http2-common Core :: HTTP2 :: Common diff --git a/jetty-core/jetty-http2/jetty-http2-hpack/pom.xml b/jetty-core/jetty-http2/jetty-http2-hpack/pom.xml index c23cd86c8b1..a52ac921416 100644 --- a/jetty-core/jetty-http2/jetty-http2-hpack/pom.xml +++ b/jetty-core/jetty-http2/jetty-http2-hpack/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.http2 jetty-http2 - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-http2-hpack Core :: HTTP2 :: HPACK diff --git a/jetty-core/jetty-http2/jetty-http2-server/pom.xml b/jetty-core/jetty-http2/jetty-http2-server/pom.xml index 561ed273ade..4eb37c1d2bd 100644 --- a/jetty-core/jetty-http2/jetty-http2-server/pom.xml +++ b/jetty-core/jetty-http2/jetty-http2-server/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.http2 jetty-http2 - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-http2-server Core :: HTTP2 :: Server diff --git a/jetty-core/jetty-http2/jetty-http2-tests/pom.xml b/jetty-core/jetty-http2/jetty-http2-tests/pom.xml index ebeb5f59bc0..7daadf09918 100644 --- a/jetty-core/jetty-http2/jetty-http2-tests/pom.xml +++ b/jetty-core/jetty-http2/jetty-http2-tests/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.http2 jetty-http2 - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-http2-tests Core :: HTTP2 :: Tests diff --git a/jetty-core/jetty-http2/pom.xml b/jetty-core/jetty-http2/pom.xml index 88c77bdb534..5b0c8258ea0 100644 --- a/jetty-core/jetty-http2/pom.xml +++ b/jetty-core/jetty-http2/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty jetty-core - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.http2 jetty-http2 diff --git a/jetty-core/jetty-http3/jetty-http3-client-transport/pom.xml b/jetty-core/jetty-http3/jetty-http3-client-transport/pom.xml index 1d85ac666ea..4056f05881b 100644 --- a/jetty-core/jetty-http3/jetty-http3-client-transport/pom.xml +++ b/jetty-core/jetty-http3/jetty-http3-client-transport/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.http3 jetty-http3 - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-http3-client-transport Core :: HTTP3 :: HTTP Client Transport diff --git a/jetty-core/jetty-http3/jetty-http3-client/pom.xml b/jetty-core/jetty-http3/jetty-http3-client/pom.xml index c1ad6cfea9d..ec39bab17e0 100644 --- a/jetty-core/jetty-http3/jetty-http3-client/pom.xml +++ b/jetty-core/jetty-http3/jetty-http3-client/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.http3 jetty-http3 - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-http3-client Core :: HTTP3 :: Client diff --git a/jetty-core/jetty-http3/jetty-http3-common/pom.xml b/jetty-core/jetty-http3/jetty-http3-common/pom.xml index 8c670b9ba2b..d73fc81d928 100644 --- a/jetty-core/jetty-http3/jetty-http3-common/pom.xml +++ b/jetty-core/jetty-http3/jetty-http3-common/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.http3 jetty-http3 - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-http3-common Core :: HTTP3 :: Common diff --git a/jetty-core/jetty-http3/jetty-http3-qpack/pom.xml b/jetty-core/jetty-http3/jetty-http3-qpack/pom.xml index 87f153d9218..b878c7f5813 100644 --- a/jetty-core/jetty-http3/jetty-http3-qpack/pom.xml +++ b/jetty-core/jetty-http3/jetty-http3-qpack/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.http3 jetty-http3 - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-http3-qpack Core :: HTTP3 :: QPACK diff --git a/jetty-core/jetty-http3/jetty-http3-server/pom.xml b/jetty-core/jetty-http3/jetty-http3-server/pom.xml index f9cca6df2ec..52213f6d58d 100644 --- a/jetty-core/jetty-http3/jetty-http3-server/pom.xml +++ b/jetty-core/jetty-http3/jetty-http3-server/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.http3 jetty-http3 - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-http3-server Core :: HTTP3 :: Server diff --git a/jetty-core/jetty-http3/jetty-http3-tests/pom.xml b/jetty-core/jetty-http3/jetty-http3-tests/pom.xml index 0b88ac5679a..942f3e43e96 100644 --- a/jetty-core/jetty-http3/jetty-http3-tests/pom.xml +++ b/jetty-core/jetty-http3/jetty-http3-tests/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.http3 jetty-http3 - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-http3-tests Core :: HTTP3 :: Tests diff --git a/jetty-core/jetty-http3/pom.xml b/jetty-core/jetty-http3/pom.xml index 6773cb67bad..239218567a8 100644 --- a/jetty-core/jetty-http3/pom.xml +++ b/jetty-core/jetty-http3/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty jetty-core - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.http3 jetty-http3 diff --git a/jetty-core/jetty-io/pom.xml b/jetty-core/jetty-io/pom.xml index 8e2e99fbf0a..061e40510c9 100644 --- a/jetty-core/jetty-io/pom.xml +++ b/jetty-core/jetty-io/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty jetty-core - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-io Core :: IO diff --git a/jetty-core/jetty-jmx/pom.xml b/jetty-core/jetty-jmx/pom.xml index b8631618ed9..403e147edbc 100644 --- a/jetty-core/jetty-jmx/pom.xml +++ b/jetty-core/jetty-jmx/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty jetty-core - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-jmx Core :: JMX diff --git a/jetty-core/jetty-jndi/pom.xml b/jetty-core/jetty-jndi/pom.xml index 7d670115c3d..2fcfd7245ea 100644 --- a/jetty-core/jetty-jndi/pom.xml +++ b/jetty-core/jetty-jndi/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty jetty-core - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-jndi Core :: JNDI diff --git a/jetty-core/jetty-keystore/pom.xml b/jetty-core/jetty-keystore/pom.xml index de62d3f3360..5ce81278e30 100644 --- a/jetty-core/jetty-keystore/pom.xml +++ b/jetty-core/jetty-keystore/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty jetty-core - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-keystore jar diff --git a/jetty-core/jetty-maven/pom.xml b/jetty-core/jetty-maven/pom.xml index b2aea6afb32..b70f4872404 100644 --- a/jetty-core/jetty-maven/pom.xml +++ b/jetty-core/jetty-maven/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty jetty-core - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-maven Core :: Maven diff --git a/jetty-core/jetty-openid/pom.xml b/jetty-core/jetty-openid/pom.xml index b3c59806e04..05a9f0fa2db 100644 --- a/jetty-core/jetty-openid/pom.xml +++ b/jetty-core/jetty-openid/pom.xml @@ -5,10 +5,10 @@ org.eclipse.jetty jetty-core - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-openid - EE10 :: OpenID + EE11 :: OpenID Jetty OpenID Connect Infrastructure diff --git a/jetty-core/jetty-osgi/pom.xml b/jetty-core/jetty-osgi/pom.xml index b91463a3004..759369a07be 100644 --- a/jetty-core/jetty-osgi/pom.xml +++ b/jetty-core/jetty-osgi/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty jetty-core - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-osgi Core :: OSGi diff --git a/jetty-core/jetty-plus/pom.xml b/jetty-core/jetty-plus/pom.xml index 413310fa900..f828a54fbd4 100644 --- a/jetty-core/jetty-plus/pom.xml +++ b/jetty-core/jetty-plus/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty jetty-core - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-plus Core :: Plus diff --git a/jetty-core/jetty-proxy/pom.xml b/jetty-core/jetty-proxy/pom.xml index f9a23bab55d..c953ee131a6 100644 --- a/jetty-core/jetty-proxy/pom.xml +++ b/jetty-core/jetty-proxy/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty jetty-core - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-proxy jar diff --git a/jetty-core/jetty-quic/jetty-quic-client/pom.xml b/jetty-core/jetty-quic/jetty-quic-client/pom.xml index a98afbc7633..c574470d89e 100644 --- a/jetty-core/jetty-quic/jetty-quic-client/pom.xml +++ b/jetty-core/jetty-quic/jetty-quic-client/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.quic jetty-quic - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-quic-client Core :: QUIC :: Client diff --git a/jetty-core/jetty-quic/jetty-quic-common/pom.xml b/jetty-core/jetty-quic/jetty-quic-common/pom.xml index 24ecc476318..5759e888906 100644 --- a/jetty-core/jetty-quic/jetty-quic-common/pom.xml +++ b/jetty-core/jetty-quic/jetty-quic-common/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.quic jetty-quic - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-quic-common Core :: QUIC :: Common diff --git a/jetty-core/jetty-quic/jetty-quic-quiche/jetty-quic-quiche-common/pom.xml b/jetty-core/jetty-quic/jetty-quic-quiche/jetty-quic-quiche-common/pom.xml index 61321ca5e78..3d81e9f1b21 100644 --- a/jetty-core/jetty-quic/jetty-quic-quiche/jetty-quic-quiche-common/pom.xml +++ b/jetty-core/jetty-quic/jetty-quic-quiche/jetty-quic-quiche-common/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.quic jetty-quic-quiche - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-quic-quiche-common Core :: QUIC :: Quiche :: Common diff --git a/jetty-core/jetty-quic/jetty-quic-quiche/jetty-quic-quiche-foreign/pom.xml b/jetty-core/jetty-quic/jetty-quic-quiche/jetty-quic-quiche-foreign/pom.xml index 45682973a73..4bdc57894f5 100644 --- a/jetty-core/jetty-quic/jetty-quic-quiche/jetty-quic-quiche-foreign/pom.xml +++ b/jetty-core/jetty-quic/jetty-quic-quiche/jetty-quic-quiche-foreign/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.quic jetty-quic-quiche - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-quic-quiche-foreign Core :: QUIC :: Quiche :: Foreign diff --git a/jetty-core/jetty-quic/jetty-quic-quiche/jetty-quic-quiche-jna/pom.xml b/jetty-core/jetty-quic/jetty-quic-quiche/jetty-quic-quiche-jna/pom.xml index baece1cccd7..84f750ba744 100644 --- a/jetty-core/jetty-quic/jetty-quic-quiche/jetty-quic-quiche-jna/pom.xml +++ b/jetty-core/jetty-quic/jetty-quic-quiche/jetty-quic-quiche-jna/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.quic jetty-quic-quiche - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-quic-quiche-jna Core :: QUIC :: Quiche :: JNA Binding diff --git a/jetty-core/jetty-quic/jetty-quic-quiche/pom.xml b/jetty-core/jetty-quic/jetty-quic-quiche/pom.xml index 320e36f2ca6..033aa6745d2 100644 --- a/jetty-core/jetty-quic/jetty-quic-quiche/pom.xml +++ b/jetty-core/jetty-quic/jetty-quic-quiche/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.quic jetty-quic - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-quic-quiche pom diff --git a/jetty-core/jetty-quic/jetty-quic-server/pom.xml b/jetty-core/jetty-quic/jetty-quic-server/pom.xml index a0d6feb2aa7..364445f192e 100644 --- a/jetty-core/jetty-quic/jetty-quic-server/pom.xml +++ b/jetty-core/jetty-quic/jetty-quic-server/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.quic jetty-quic - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-quic-server Core :: QUIC :: Server diff --git a/jetty-core/jetty-quic/pom.xml b/jetty-core/jetty-quic/pom.xml index 113f84c467a..1ef396276fe 100644 --- a/jetty-core/jetty-quic/pom.xml +++ b/jetty-core/jetty-quic/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty jetty-core - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.quic jetty-quic diff --git a/jetty-core/jetty-rewrite/pom.xml b/jetty-core/jetty-rewrite/pom.xml index 3f3addf339e..eb921d221b2 100644 --- a/jetty-core/jetty-rewrite/pom.xml +++ b/jetty-core/jetty-rewrite/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty jetty-core - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-rewrite Core :: Rewrite diff --git a/jetty-core/jetty-security/pom.xml b/jetty-core/jetty-security/pom.xml index 24a533548d0..6a0e188a1cf 100644 --- a/jetty-core/jetty-security/pom.xml +++ b/jetty-core/jetty-security/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty jetty-core - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-security Core :: Security diff --git a/jetty-core/jetty-server/pom.xml b/jetty-core/jetty-server/pom.xml index 2bb373dabaa..cdf77b5e693 100644 --- a/jetty-core/jetty-server/pom.xml +++ b/jetty-core/jetty-server/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty jetty-core - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-server Core :: Server diff --git a/jetty-core/jetty-session/pom.xml b/jetty-core/jetty-session/pom.xml index 3312c053806..7eacf43744c 100644 --- a/jetty-core/jetty-session/pom.xml +++ b/jetty-core/jetty-session/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty jetty-core - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-session Core :: Sessions diff --git a/jetty-core/jetty-slf4j-impl/pom.xml b/jetty-core/jetty-slf4j-impl/pom.xml index a906461c16e..f9c021b3043 100644 --- a/jetty-core/jetty-slf4j-impl/pom.xml +++ b/jetty-core/jetty-slf4j-impl/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty jetty-core - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-slf4j-impl Core :: SLF4J Implementation diff --git a/jetty-core/jetty-start/pom.xml b/jetty-core/jetty-start/pom.xml index fe0d999905f..3bb5c3763d8 100644 --- a/jetty-core/jetty-start/pom.xml +++ b/jetty-core/jetty-start/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty jetty-core - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-start Core :: Start diff --git a/jetty-core/jetty-tests/jetty-test-client-transports/pom.xml b/jetty-core/jetty-tests/jetty-test-client-transports/pom.xml index 7c34aa4131b..453f061ffd5 100644 --- a/jetty-core/jetty-tests/jetty-test-client-transports/pom.xml +++ b/jetty-core/jetty-tests/jetty-test-client-transports/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty jetty-tests - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-test-client-transports Core :: Tests :: Client Transports diff --git a/jetty-core/jetty-tests/jetty-test-jmx/pom.xml b/jetty-core/jetty-tests/jetty-test-jmx/pom.xml index 1b475033db7..80339312b78 100644 --- a/jetty-core/jetty-tests/jetty-test-jmx/pom.xml +++ b/jetty-core/jetty-tests/jetty-test-jmx/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty jetty-tests - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-test-jmx Core :: Tests :: JMX diff --git a/jetty-core/jetty-tests/pom.xml b/jetty-core/jetty-tests/pom.xml index b341f055bb9..896ecc9f62f 100644 --- a/jetty-core/jetty-tests/pom.xml +++ b/jetty-core/jetty-tests/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty jetty-core - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-tests pom diff --git a/jetty-core/jetty-unixdomain-server/pom.xml b/jetty-core/jetty-unixdomain-server/pom.xml index 2dc6a1e4edc..f9cc0157c9a 100644 --- a/jetty-core/jetty-unixdomain-server/pom.xml +++ b/jetty-core/jetty-unixdomain-server/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty jetty-core - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-unixdomain-server Core :: Unix-Domain Sockets :: Server diff --git a/jetty-core/jetty-util-ajax/pom.xml b/jetty-core/jetty-util-ajax/pom.xml index b288acc68b8..b6466274833 100644 --- a/jetty-core/jetty-util-ajax/pom.xml +++ b/jetty-core/jetty-util-ajax/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty jetty-core - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-util-ajax Core :: Utilities :: JSON diff --git a/jetty-core/jetty-util/pom.xml b/jetty-core/jetty-util/pom.xml index 19735c936b6..800f2b7bc78 100644 --- a/jetty-core/jetty-util/pom.xml +++ b/jetty-core/jetty-util/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty jetty-core - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-util Core :: Utilities diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-client/pom.xml b/jetty-core/jetty-websocket/jetty-websocket-core-client/pom.xml index 7aaa5f26ecb..f883c19e014 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-client/pom.xml +++ b/jetty-core/jetty-websocket/jetty-websocket-core-client/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.websocket jetty-websocket - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-websocket-core-client Core :: Websocket :: Client diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-common/pom.xml b/jetty-core/jetty-websocket/jetty-websocket-core-common/pom.xml index f588c288d57..5135c16dd92 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-common/pom.xml +++ b/jetty-core/jetty-websocket/jetty-websocket-core-common/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.websocket jetty-websocket - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-websocket-core-common Core :: Websocket :: Common diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-server/pom.xml b/jetty-core/jetty-websocket/jetty-websocket-core-server/pom.xml index 3729e845947..ff2cc7c5e5f 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-server/pom.xml +++ b/jetty-core/jetty-websocket/jetty-websocket-core-server/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.websocket jetty-websocket - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-websocket-core-server Core :: Websocket :: Server diff --git a/jetty-core/jetty-websocket/jetty-websocket-core-tests/pom.xml b/jetty-core/jetty-websocket/jetty-websocket-core-tests/pom.xml index 6b1feedbcde..f0f14cf3f9e 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-core-tests/pom.xml +++ b/jetty-core/jetty-websocket/jetty-websocket-core-tests/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.websocket jetty-websocket - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-websocket-core-tests Core :: Websocket :: Tests diff --git a/jetty-core/jetty-websocket/jetty-websocket-jetty-api/pom.xml b/jetty-core/jetty-websocket/jetty-websocket-jetty-api/pom.xml index 29cf02acb9b..89f37a33103 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-jetty-api/pom.xml +++ b/jetty-core/jetty-websocket/jetty-websocket-jetty-api/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.websocket jetty-websocket - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-websocket-jetty-api Core :: Websocket :: Jetty API diff --git a/jetty-core/jetty-websocket/jetty-websocket-jetty-client/pom.xml b/jetty-core/jetty-websocket/jetty-websocket-jetty-client/pom.xml index d4f04d0e76f..a9335ce0538 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-jetty-client/pom.xml +++ b/jetty-core/jetty-websocket/jetty-websocket-jetty-client/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.websocket jetty-websocket - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-websocket-jetty-client Core :: Websocket :: Jetty Client diff --git a/jetty-core/jetty-websocket/jetty-websocket-jetty-common/pom.xml b/jetty-core/jetty-websocket/jetty-websocket-jetty-common/pom.xml index 30136a4071f..3a90f8d0d4a 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-jetty-common/pom.xml +++ b/jetty-core/jetty-websocket/jetty-websocket-jetty-common/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.websocket jetty-websocket - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-websocket-jetty-common Core :: Websocket :: Jetty Common diff --git a/jetty-core/jetty-websocket/jetty-websocket-jetty-server/pom.xml b/jetty-core/jetty-websocket/jetty-websocket-jetty-server/pom.xml index 9f27cd14c2c..72969752ac9 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-jetty-server/pom.xml +++ b/jetty-core/jetty-websocket/jetty-websocket-jetty-server/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.websocket jetty-websocket - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-websocket-jetty-server Core :: Websocket :: Jetty Server diff --git a/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/pom.xml b/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/pom.xml index c3839fe6753..f8f8573ac3b 100644 --- a/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/pom.xml +++ b/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.websocket jetty-websocket - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-websocket-jetty-tests Core :: Websocket :: Jetty Tests diff --git a/jetty-core/jetty-websocket/pom.xml b/jetty-core/jetty-websocket/pom.xml index e436e425153..2d7e28027a1 100644 --- a/jetty-core/jetty-websocket/pom.xml +++ b/jetty-core/jetty-websocket/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty jetty-core - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.websocket jetty-websocket diff --git a/jetty-core/jetty-xml/pom.xml b/jetty-core/jetty-xml/pom.xml index 62f655fdea1..fa87c47d277 100644 --- a/jetty-core/jetty-xml/pom.xml +++ b/jetty-core/jetty-xml/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty jetty-core - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-xml Core :: XML diff --git a/jetty-core/pom.xml b/jetty-core/pom.xml index c5f629994c1..c117bf3208b 100644 --- a/jetty-core/pom.xml +++ b/jetty-core/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty jetty-project - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-core pom diff --git a/jetty-ee10/jetty-ee10-annotations/pom.xml b/jetty-ee10/jetty-ee10-annotations/pom.xml index 4de90716037..ce5a94efd14 100644 --- a/jetty-ee10/jetty-ee10-annotations/pom.xml +++ b/jetty-ee10/jetty-ee10-annotations/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10 jetty-ee10 - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-annotations EE10 :: Servlet Annotations diff --git a/jetty-ee10/jetty-ee10-apache-jsp/pom.xml b/jetty-ee10/jetty-ee10-apache-jsp/pom.xml index 51bfe361b61..792655f7a46 100644 --- a/jetty-ee10/jetty-ee10-apache-jsp/pom.xml +++ b/jetty-ee10/jetty-ee10-apache-jsp/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10 jetty-ee10 - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-apache-jsp EE10 :: Apache JSP diff --git a/jetty-ee10/jetty-ee10-bom/pom.xml b/jetty-ee10/jetty-ee10-bom/pom.xml index f68b66eb2c4..a4e29f21304 100644 --- a/jetty-ee10/jetty-ee10-bom/pom.xml +++ b/jetty-ee10/jetty-ee10-bom/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10 jetty-ee10 - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-bom @@ -19,132 +19,132 @@ org.eclipse.jetty.ee10 jetty-ee10-annotations - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.ee10 jetty-ee10-apache-jsp - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.ee10 jetty-ee10-cdi - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.ee10 jetty-ee10-fcgi-proxy - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.ee10 jetty-ee10-glassfish-jstl - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.ee10 jetty-ee10-jaspi - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.ee10 jetty-ee10-jndi - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.ee10 jetty-ee10-jspc-maven-plugin - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.ee10 jetty-ee10-maven-plugin - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.ee10 jetty-ee10-plus - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.ee10 jetty-ee10-proxy - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.ee10 jetty-ee10-quickstart - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.ee10 jetty-ee10-runner - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.ee10 jetty-ee10-servlet - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.ee10 jetty-ee10-servlets - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.ee10 jetty-ee10-webapp - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.ee10.osgi jetty-ee10-osgi-alpn - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.ee10.osgi jetty-ee10-osgi-boot - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.ee10.osgi jetty-ee10-osgi-boot-jsp - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.ee10.websocket jetty-ee10-websocket-jakarta-client - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.ee10.websocket jetty-ee10-websocket-jakarta-client-webapp - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.ee10.websocket jetty-ee10-websocket-jakarta-common - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.ee10.websocket jetty-ee10-websocket-jakarta-server - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.ee10.websocket jetty-ee10-websocket-jetty-client-webapp - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.ee10.websocket jetty-ee10-websocket-jetty-server - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.ee10.websocket jetty-ee10-websocket-servlet - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT diff --git a/jetty-ee10/jetty-ee10-cdi/pom.xml b/jetty-ee10/jetty-ee10-cdi/pom.xml index 820a3669d4f..312b09bb8fd 100644 --- a/jetty-ee10/jetty-ee10-cdi/pom.xml +++ b/jetty-ee10/jetty-ee10-cdi/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty.ee10 jetty-ee10 - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-cdi jar diff --git a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-async-rest/jetty-ee10-demo-async-rest-jar/pom.xml b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-async-rest/jetty-ee10-demo-async-rest-jar/pom.xml index efa2d94ebff..006a0789f9c 100644 --- a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-async-rest/jetty-ee10-demo-async-rest-jar/pom.xml +++ b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-async-rest/jetty-ee10-demo-async-rest-jar/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10.demos jetty-ee10-demo-async-rest - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-demo-async-rest-jar jar diff --git a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-async-rest/jetty-ee10-demo-async-rest-server/pom.xml b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-async-rest/jetty-ee10-demo-async-rest-server/pom.xml index 139dbbfc2a1..2f8e2e6bd1b 100644 --- a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-async-rest/jetty-ee10-demo-async-rest-server/pom.xml +++ b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-async-rest/jetty-ee10-demo-async-rest-server/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10.demos jetty-ee10-demo-async-rest - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-demo-async-rest-server jar diff --git a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-async-rest/jetty-ee10-demo-async-rest-webapp/pom.xml b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-async-rest/jetty-ee10-demo-async-rest-webapp/pom.xml index 1dd967f7505..8658c2110d6 100644 --- a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-async-rest/jetty-ee10-demo-async-rest-webapp/pom.xml +++ b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-async-rest/jetty-ee10-demo-async-rest-webapp/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10.demos jetty-ee10-demo-async-rest - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-demo-async-rest-webapp war diff --git a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-async-rest/pom.xml b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-async-rest/pom.xml index 90ae49edfc5..c3291c19aac 100644 --- a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-async-rest/pom.xml +++ b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-async-rest/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10.demos jetty-ee10-demos - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-demo-async-rest pom diff --git a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-embedded/pom.xml b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-embedded/pom.xml index 1e369fab151..4de8383bcd4 100644 --- a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-embedded/pom.xml +++ b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-embedded/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty.ee10.demos jetty-ee10-demos - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-demo-embedded EE10 :: Demo :: Embedded Jetty diff --git a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-jaas-webapp/pom.xml b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-jaas-webapp/pom.xml index 41fcab72068..f27dd6ef6ff 100644 --- a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-jaas-webapp/pom.xml +++ b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-jaas-webapp/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty.ee10.demos jetty-ee10-demos - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-demo-jaas-webapp war diff --git a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-jetty-webapp/pom.xml b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-jetty-webapp/pom.xml index 9ef5bd19e65..32016794916 100644 --- a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-jetty-webapp/pom.xml +++ b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-jetty-webapp/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty.ee10.demos jetty-ee10-demos - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-demo-jetty-webapp war diff --git a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-jndi-webapp/pom.xml b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-jndi-webapp/pom.xml index 0fb2b16028f..82e93d2f9a3 100644 --- a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-jndi-webapp/pom.xml +++ b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-jndi-webapp/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty.ee10.demos jetty-ee10-demos - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-demo-jndi-webapp war diff --git a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-jsp-webapp/pom.xml b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-jsp-webapp/pom.xml index a40ba02d31f..e4cd2fa4bbb 100644 --- a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-jsp-webapp/pom.xml +++ b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-jsp-webapp/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10.demos jetty-ee10-demos - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-demo-jsp-webapp war diff --git a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-mock-resources/pom.xml b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-mock-resources/pom.xml index 3305e2455c3..4c6771fed11 100644 --- a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-mock-resources/pom.xml +++ b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-mock-resources/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty.ee10.demos jetty-ee10-demos - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-demo-mock-resources jar diff --git a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-proxy-webapp/pom.xml b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-proxy-webapp/pom.xml index e98b8d34f65..0576e846a05 100644 --- a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-proxy-webapp/pom.xml +++ b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-proxy-webapp/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty.ee10.demos jetty-ee10-demos - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-demo-proxy-webapp war diff --git a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-simple-webapp/pom.xml b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-simple-webapp/pom.xml index 715c65f73c9..7df24e20d52 100644 --- a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-simple-webapp/pom.xml +++ b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-simple-webapp/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10.demos jetty-ee10-demos - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-demo-simple-webapp war diff --git a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-spec/jetty-ee10-demo-container-initializer/pom.xml b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-spec/jetty-ee10-demo-container-initializer/pom.xml index 7fe8e65a23b..24070118630 100644 --- a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-spec/jetty-ee10-demo-container-initializer/pom.xml +++ b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-spec/jetty-ee10-demo-container-initializer/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty.ee10.demos jetty-ee10-demos - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT ../../pom.xml jetty-ee10-demo-container-initializer diff --git a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-spec/jetty-ee10-demo-spec-webapp/pom.xml b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-spec/jetty-ee10-demo-spec-webapp/pom.xml index 5261171222f..3ee3bf3c3fa 100644 --- a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-spec/jetty-ee10-demo-spec-webapp/pom.xml +++ b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-spec/jetty-ee10-demo-spec-webapp/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty.ee10.demos jetty-ee10-demos - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT ../../pom.xml jetty-ee10-demo-spec-webapp diff --git a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-spec/jetty-ee10-demo-web-fragment/pom.xml b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-spec/jetty-ee10-demo-web-fragment/pom.xml index 5e7db7e1669..974a82a6f58 100644 --- a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-spec/jetty-ee10-demo-web-fragment/pom.xml +++ b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-spec/jetty-ee10-demo-web-fragment/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty.ee10.demos jetty-ee10-demos - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT ../../pom.xml jetty-ee10-demo-web-fragment diff --git a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-spec/pom.xml b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-spec/pom.xml index 86e03025465..fd10d42733e 100644 --- a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-spec/pom.xml +++ b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-spec/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty.ee10.demos jetty-ee10-demos - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-demo-spec pom diff --git a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-template/pom.xml b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-template/pom.xml index 2cd68950f19..f7070f22bd3 100644 --- a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-template/pom.xml +++ b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-template/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10.demos jetty-ee10-demos - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-demo-template jar diff --git a/jetty-ee10/jetty-ee10-demos/pom.xml b/jetty-ee10/jetty-ee10-demos/pom.xml index 6ef600a0ccd..11f8e475fdf 100644 --- a/jetty-ee10/jetty-ee10-demos/pom.xml +++ b/jetty-ee10/jetty-ee10-demos/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10 jetty-ee10 - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.ee10.demos jetty-ee10-demos diff --git a/jetty-ee10/jetty-ee10-examples/pom.xml b/jetty-ee10/jetty-ee10-examples/pom.xml index 4c46a665859..b0421c36997 100644 --- a/jetty-ee10/jetty-ee10-examples/pom.xml +++ b/jetty-ee10/jetty-ee10-examples/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10 jetty-ee10 - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-examples EE10 :: Examples diff --git a/jetty-ee10/jetty-ee10-fcgi-proxy/pom.xml b/jetty-ee10/jetty-ee10-fcgi-proxy/pom.xml index 843edfc7752..c74514541a3 100644 --- a/jetty-ee10/jetty-ee10-fcgi-proxy/pom.xml +++ b/jetty-ee10/jetty-ee10-fcgi-proxy/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10 jetty-ee10 - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-fcgi-proxy EE10 :: FCGI Proxy diff --git a/jetty-ee10/jetty-ee10-glassfish-jstl/pom.xml b/jetty-ee10/jetty-ee10-glassfish-jstl/pom.xml index 296fa65e66d..49ec16a5513 100644 --- a/jetty-ee10/jetty-ee10-glassfish-jstl/pom.xml +++ b/jetty-ee10/jetty-ee10-glassfish-jstl/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty.ee10 jetty-ee10 - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-glassfish-jstl jar diff --git a/jetty-ee10/jetty-ee10-home/pom.xml b/jetty-ee10/jetty-ee10-home/pom.xml index ee814e92e9b..09235168205 100644 --- a/jetty-ee10/jetty-ee10-home/pom.xml +++ b/jetty-ee10/jetty-ee10-home/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty.ee10 jetty-ee10 - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT ../pom.xml jetty-ee10-home diff --git a/jetty-ee10/jetty-ee10-jaspi/pom.xml b/jetty-ee10/jetty-ee10-jaspi/pom.xml index 60ad3236bf8..78f1661c786 100644 --- a/jetty-ee10/jetty-ee10-jaspi/pom.xml +++ b/jetty-ee10/jetty-ee10-jaspi/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10 jetty-ee10 - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-jaspi EE10 :: JASPI diff --git a/jetty-ee10/jetty-ee10-jndi/pom.xml b/jetty-ee10/jetty-ee10-jndi/pom.xml index 979fb0e1e83..3f36c5a60d2 100644 --- a/jetty-ee10/jetty-ee10-jndi/pom.xml +++ b/jetty-ee10/jetty-ee10-jndi/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10 jetty-ee10 - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-jndi EE10 :: JNDI diff --git a/jetty-ee10/jetty-ee10-jspc-maven-plugin/pom.xml b/jetty-ee10/jetty-ee10-jspc-maven-plugin/pom.xml index 52a47093b4f..c1025c7e16d 100644 --- a/jetty-ee10/jetty-ee10-jspc-maven-plugin/pom.xml +++ b/jetty-ee10/jetty-ee10-jspc-maven-plugin/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty.ee10 jetty-ee10 - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-jspc-maven-plugin maven-plugin diff --git a/jetty-ee10/jetty-ee10-maven-plugin/pom.xml b/jetty-ee10/jetty-ee10-maven-plugin/pom.xml index 2d4ed9fe4cb..8d4af94884f 100644 --- a/jetty-ee10/jetty-ee10-maven-plugin/pom.xml +++ b/jetty-ee10/jetty-ee10-maven-plugin/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty.ee10 jetty-ee10 - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT ../pom.xml jetty-ee10-maven-plugin diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-alpn/pom.xml b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-alpn/pom.xml index 89d134dff22..ae8d3290a5a 100644 --- a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-alpn/pom.xml +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-alpn/pom.xml @@ -7,7 +7,7 @@ org.eclipse.jetty.ee10.osgi jetty-ee10-osgi - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-osgi-alpn jar diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot-jsp/pom.xml b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot-jsp/pom.xml index e6b44366c80..63b35025780 100644 --- a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot-jsp/pom.xml +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot-jsp/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty.ee10.osgi jetty-ee10-osgi - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-osgi-boot-jsp EE10 :: OSGi :: Boot JSP diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/pom.xml b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/pom.xml index dd13a1cc59d..c0d9392fde0 100644 --- a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/pom.xml +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty.ee10.osgi jetty-ee10-osgi - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-osgi-boot EE10 :: OSGi :: Boot diff --git a/jetty-ee10/jetty-ee10-osgi/pom.xml b/jetty-ee10/jetty-ee10-osgi/pom.xml index 1b83144a9c5..955e3a5119a 100644 --- a/jetty-ee10/jetty-ee10-osgi/pom.xml +++ b/jetty-ee10/jetty-ee10-osgi/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10 jetty-ee10 - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.ee10.osgi jetty-ee10-osgi diff --git a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-fragment/pom.xml b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-fragment/pom.xml index 33062350406..a5b917ae4e0 100644 --- a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-fragment/pom.xml +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-fragment/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty.ee10.osgi jetty-ee10-osgi - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT test-jetty-ee10-osgi-fragment EE10 :: OSGi :: WebApp Fragment diff --git a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-server/pom.xml b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-server/pom.xml index 3ffa7090b99..807ad2f9e6c 100644 --- a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-server/pom.xml +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-server/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty.ee10.osgi jetty-ee10-osgi - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT test-jetty-ee10-osgi-server EE10 :: OSGi :: Server diff --git a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-webapp-resources/pom.xml b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-webapp-resources/pom.xml index 623500414d6..0ac52b27acc 100644 --- a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-webapp-resources/pom.xml +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-webapp-resources/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty.ee10.osgi jetty-ee10-osgi - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT test-jetty-ee10-osgi-webapp-resources war diff --git a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/pom.xml b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/pom.xml index 668e56d8f01..bc8528e0d2e 100644 --- a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/pom.xml +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty.ee10.osgi jetty-ee10-osgi - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT test-jetty-ee10-osgi EE10 :: OSGi :: Test diff --git a/jetty-ee10/jetty-ee10-plus/pom.xml b/jetty-ee10/jetty-ee10-plus/pom.xml index c255cabfaaf..0ded74a9e93 100644 --- a/jetty-ee10/jetty-ee10-plus/pom.xml +++ b/jetty-ee10/jetty-ee10-plus/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10 jetty-ee10 - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-plus EE10 :: Plus diff --git a/jetty-ee10/jetty-ee10-proxy/pom.xml b/jetty-ee10/jetty-ee10-proxy/pom.xml index 72c54ef8f1f..418f178bc8a 100644 --- a/jetty-ee10/jetty-ee10-proxy/pom.xml +++ b/jetty-ee10/jetty-ee10-proxy/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10 jetty-ee10 - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-proxy EE10 :: Proxy diff --git a/jetty-ee10/jetty-ee10-quickstart/pom.xml b/jetty-ee10/jetty-ee10-quickstart/pom.xml index b8ba39b5082..8b6a42d27a2 100644 --- a/jetty-ee10/jetty-ee10-quickstart/pom.xml +++ b/jetty-ee10/jetty-ee10-quickstart/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10 jetty-ee10 - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-quickstart EE10 :: Quick Start diff --git a/jetty-ee10/jetty-ee10-runner/pom.xml b/jetty-ee10/jetty-ee10-runner/pom.xml index a05afb51b4f..a6e6a2683e5 100644 --- a/jetty-ee10/jetty-ee10-runner/pom.xml +++ b/jetty-ee10/jetty-ee10-runner/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty.ee10 jetty-ee10 - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-runner EE10 :: Runner diff --git a/jetty-ee10/jetty-ee10-servlet/pom.xml b/jetty-ee10/jetty-ee10-servlet/pom.xml index c1c4574961a..7e28cda668a 100644 --- a/jetty-ee10/jetty-ee10-servlet/pom.xml +++ b/jetty-ee10/jetty-ee10-servlet/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10 jetty-ee10 - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-servlet EE10 :: Servlet diff --git a/jetty-ee10/jetty-ee10-servlets/pom.xml b/jetty-ee10/jetty-ee10-servlets/pom.xml index d33decf3582..af289dceccb 100644 --- a/jetty-ee10/jetty-ee10-servlets/pom.xml +++ b/jetty-ee10/jetty-ee10-servlets/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10 jetty-ee10 - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-servlets EE10 :: Utility Servlets and Filters diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-bad-websocket-webapp/pom.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-bad-websocket-webapp/pom.xml index ddfb2e4d3d9..51cc7b0016a 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-bad-websocket-webapp/pom.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-bad-websocket-webapp/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10 jetty-ee10-tests - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-test-bad-websocket-webapp war diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-badinit-webapp/pom.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-badinit-webapp/pom.xml index 8ad11fb1002..75412f7d4cb 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-badinit-webapp/pom.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-badinit-webapp/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty.ee10 jetty-ee10-tests - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-test-badinit-webapp diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-cdi-common-webapp/pom.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-cdi-common-webapp/pom.xml index 94732d5cada..6583820ad92 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-cdi-common-webapp/pom.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-cdi-common-webapp/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10 jetty-ee10-tests - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-test-cdi-common-webapp war diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-cdi/pom.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-cdi/pom.xml index b661f8359bd..74db379c64b 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-cdi/pom.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-cdi/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10 jetty-ee10-tests - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-test-cdi jar diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/pom.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/pom.xml index 8a9a0c46aef..4742c3aa473 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/pom.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-client-transports/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10 jetty-ee10-tests - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-test-client-transports jar diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-felix-webapp/pom.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-felix-webapp/pom.xml index 66f4c2aa105..66fec8a2f06 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-felix-webapp/pom.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-felix-webapp/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10 jetty-ee10-tests - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-test-felix-webapp war diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-http2-webapp/pom.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-http2-webapp/pom.xml index 757280fde2b..c05ffbdbb7b 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-http2-webapp/pom.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-http2-webapp/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10 jetty-ee10-tests - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-test-http2-webapp war diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/pom.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/pom.xml index 0365fd0751f..a37681d2684 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/pom.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty.ee10 jetty-ee10-tests - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-test-integration jar diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-jersey/pom.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-jersey/pom.xml index 9029fb1c85b..d9dadfa5b55 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-jersey/pom.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-jersey/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10 jetty-ee10-tests - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-test-jersey jar diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-jmx/jetty-ee10-jmx-webapp-it/pom.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-jmx/jetty-ee10-jmx-webapp-it/pom.xml index bf97cbe7d75..7cf57cf29fa 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-jmx/jetty-ee10-jmx-webapp-it/pom.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-jmx/jetty-ee10-jmx-webapp-it/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty.ee10 jetty-ee10-test-jmx - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-jmx-webapp-it jar diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-jmx/jetty-ee10-jmx-webapp/pom.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-jmx/jetty-ee10-jmx-webapp/pom.xml index 1bcd7308344..b9335094eb9 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-jmx/jetty-ee10-jmx-webapp/pom.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-jmx/jetty-ee10-jmx-webapp/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty.ee10 jetty-ee10-test-jmx - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-jmx-webapp war diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-jmx/pom.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-jmx/pom.xml index b1c202290f4..02ebb2ce4dd 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-jmx/pom.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-jmx/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty.ee10 jetty-ee10-tests - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-test-jmx pom diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-jndi/pom.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-jndi/pom.xml index caa446a156a..60434f4daec 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-jndi/pom.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-jndi/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10 jetty-ee10-tests - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT ../pom.xml jetty-ee10-test-jndi diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-log4j2-webapp/pom.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-log4j2-webapp/pom.xml index a91aabb20b4..bd7a9d5b0fb 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-log4j2-webapp/pom.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-log4j2-webapp/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10 jetty-ee10-tests - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-test-log4j2-webapp war diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-loginservice/pom.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-loginservice/pom.xml index e2391e25068..5776eff1e65 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-loginservice/pom.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-loginservice/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty.ee10 jetty-ee10-tests - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-test-loginservice EE10 :: Tests :: Login Service diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-openid-webapp/pom.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-openid-webapp/pom.xml index 7c5c00f3284..937f47e20aa 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-openid-webapp/pom.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-openid-webapp/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10 jetty-ee10-tests - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-test-openid-webapp war diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-owb-cdi-webapp/pom.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-owb-cdi-webapp/pom.xml index 493272f63e5..91946499d47 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-owb-cdi-webapp/pom.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-owb-cdi-webapp/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10 jetty-ee10-tests - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-test-owb-cdi-webapp war diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-quickstart/pom.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-quickstart/pom.xml index 96e0e5337de..180e0a335d8 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-quickstart/pom.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-quickstart/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty.ee10 jetty-ee10-tests - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-test-quickstart EE10 :: Tests :: Quick Start diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-sessions/jetty-ee10-test-sessions-common/pom.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-sessions/jetty-ee10-test-sessions-common/pom.xml index de75ae50f33..c0a28afefb1 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-sessions/jetty-ee10-test-sessions-common/pom.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-sessions/jetty-ee10-test-sessions-common/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty.ee10 jetty-ee10-test-sessions - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-test-sessions-common EE10 :: Tests :: Sessions :: Common diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-sessions/jetty-ee10-test-sessions-file/pom.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-sessions/jetty-ee10-test-sessions-file/pom.xml index 7ea38bfeeac..8ff8fc51211 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-sessions/jetty-ee10-test-sessions-file/pom.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-sessions/jetty-ee10-test-sessions-file/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty.ee10 jetty-ee10-test-sessions - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-test-sessions-file EE10 :: Tests :: Sessions :: File diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-sessions/jetty-ee10-test-sessions-gcloud/pom.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-sessions/jetty-ee10-test-sessions-gcloud/pom.xml index 13cd9a11645..e97a019593f 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-sessions/jetty-ee10-test-sessions-gcloud/pom.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-sessions/jetty-ee10-test-sessions-gcloud/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty.ee10 jetty-ee10-test-sessions - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-test-sessions-gcloud EE10 :: Tests :: Sessions :: GCloud diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-sessions/jetty-ee10-test-sessions-hazelcast/pom.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-sessions/jetty-ee10-test-sessions-hazelcast/pom.xml index 33b8dae91dc..0dde7cba90f 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-sessions/jetty-ee10-test-sessions-hazelcast/pom.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-sessions/jetty-ee10-test-sessions-hazelcast/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty.ee10 jetty-ee10-test-sessions - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-test-sessions-hazelcast EE10 :: Tests :: Sessions :: Hazelcast diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-sessions/jetty-ee10-test-sessions-infinispan/pom.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-sessions/jetty-ee10-test-sessions-infinispan/pom.xml index 3bf276b00ed..1940169640c 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-sessions/jetty-ee10-test-sessions-infinispan/pom.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-sessions/jetty-ee10-test-sessions-infinispan/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty.ee10 jetty-ee10-test-sessions - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-test-sessions-infinispan EE10 :: Tests :: Sessions :: Infinispan diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-sessions/jetty-ee10-test-sessions-jdbc/pom.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-sessions/jetty-ee10-test-sessions-jdbc/pom.xml index 28f99c0f074..d36eb42b0d5 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-sessions/jetty-ee10-test-sessions-jdbc/pom.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-sessions/jetty-ee10-test-sessions-jdbc/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty.ee10 jetty-ee10-test-sessions - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-test-sessions-jdbc EE10 :: Tests :: Sessions :: JDBC diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-sessions/jetty-ee10-test-sessions-memcached/pom.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-sessions/jetty-ee10-test-sessions-memcached/pom.xml index 88b071bcf5f..e1b1ab92274 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-sessions/jetty-ee10-test-sessions-memcached/pom.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-sessions/jetty-ee10-test-sessions-memcached/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty.ee10 jetty-ee10-test-sessions - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-test-sessions-memcached EE10 :: Tests :: Sessions :: Memcached diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-sessions/jetty-ee10-test-sessions-mongodb/pom.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-sessions/jetty-ee10-test-sessions-mongodb/pom.xml index b841b667e69..8c112a1d9e6 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-sessions/jetty-ee10-test-sessions-mongodb/pom.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-sessions/jetty-ee10-test-sessions-mongodb/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty.ee10 jetty-ee10-test-sessions - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-test-sessions-mongodb EE10 :: Tests :: Sessions :: Mongo diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-sessions/pom.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-sessions/pom.xml index 6abd603c694..fb0e7606942 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-sessions/pom.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-sessions/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty.ee10 jetty-ee10-tests - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-test-sessions pom diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-simple-session-webapp/pom.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-simple-session-webapp/pom.xml index 295e6557e79..7930beeba48 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-simple-session-webapp/pom.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-simple-session-webapp/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty.ee10 jetty-ee10-tests - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-test-simple-session-webapp diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-webapp-rfc2616/pom.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-webapp-rfc2616/pom.xml index 218394f1aba..38730dfa85c 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-webapp-rfc2616/pom.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-webapp-rfc2616/pom.xml @@ -4,7 +4,7 @@ org.eclipse.jetty.ee10 jetty-ee10-tests - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-test-webapp-rfc2616 war diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-websocket-client-provided-webapp/pom.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-websocket-client-provided-webapp/pom.xml index 5cc1529f117..e015fbcf300 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-websocket-client-provided-webapp/pom.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-websocket-client-provided-webapp/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10 jetty-ee10-tests - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-test-websocket-client-provided-webapp war diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-websocket-client-webapp/pom.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-websocket-client-webapp/pom.xml index 077239c1d8b..23843bfafd5 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-websocket-client-webapp/pom.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-websocket-client-webapp/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10 jetty-ee10-tests - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-test-websocket-client-webapp war diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-websocket-webapp/pom.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-websocket-webapp/pom.xml index af21900e4c0..61711212969 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-websocket-webapp/pom.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-websocket-webapp/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10 jetty-ee10-tests - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-test-websocket-webapp war diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-weld-cdi-webapp/pom.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-weld-cdi-webapp/pom.xml index 2e79ee42733..2d3b19204fc 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-weld-cdi-webapp/pom.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-weld-cdi-webapp/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10 jetty-ee10-tests - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-test-weld-cdi-webapp war diff --git a/jetty-ee10/jetty-ee10-tests/pom.xml b/jetty-ee10/jetty-ee10-tests/pom.xml index 9c0dab6c357..cb0530eda11 100644 --- a/jetty-ee10/jetty-ee10-tests/pom.xml +++ b/jetty-ee10/jetty-ee10-tests/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10 jetty-ee10 - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT ../pom.xml jetty-ee10-tests diff --git a/jetty-ee10/jetty-ee10-webapp/pom.xml b/jetty-ee10/jetty-ee10-webapp/pom.xml index 1c1671c896b..f7b5e6558b3 100644 --- a/jetty-ee10/jetty-ee10-webapp/pom.xml +++ b/jetty-ee10/jetty-ee10-webapp/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10 jetty-ee10 - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-webapp EE10 :: WebApp diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-client-webapp/pom.xml b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-client-webapp/pom.xml index 81c22ea4924..6330201be87 100644 --- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-client-webapp/pom.xml +++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-client-webapp/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10.websocket jetty-ee10-websocket - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-websocket-jakarta-client-webapp EE10 :: Websocket :: Jakarta Client WebApp diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-client/pom.xml b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-client/pom.xml index 4743d8964ca..96356365030 100644 --- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-client/pom.xml +++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-client/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10.websocket jetty-ee10-websocket - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-websocket-jakarta-client EE10 :: Websocket :: Jakarta Client diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/pom.xml b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/pom.xml index d8ac2dff125..836cec58e0b 100644 --- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/pom.xml +++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10.websocket jetty-ee10-websocket - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-websocket-jakarta-common EE10 :: Websocket :: Jakarta Common diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-server/pom.xml b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-server/pom.xml index e35a4e44d97..c2e3b6ce4ac 100644 --- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-server/pom.xml +++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-server/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10.websocket jetty-ee10-websocket - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-websocket-jakarta-server EE10 :: Websocket :: Jakarta Server diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/pom.xml b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/pom.xml index 7d82fb13ce5..bcd3c57854b 100644 --- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/pom.xml +++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10.websocket jetty-ee10-websocket - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-websocket-jakarta-tests EE10 :: Websocket :: Jakarta Tests diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-client-webapp/pom.xml b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-client-webapp/pom.xml index 34954bab744..6daa2966e8a 100644 --- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-client-webapp/pom.xml +++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-client-webapp/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10.websocket jetty-ee10-websocket - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-websocket-jetty-client-webapp EE10 :: Websocket :: Jetty Client WebApp diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-server/pom.xml b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-server/pom.xml index 6ebd4b4abb2..07d6cd92d6d 100644 --- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-server/pom.xml +++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-server/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10.websocket jetty-ee10-websocket - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-websocket-jetty-server EE10 :: Websocket :: Jetty Server diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/pom.xml b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/pom.xml index 85a9f664e91..09083126b2b 100644 --- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/pom.xml +++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10.websocket jetty-ee10-websocket - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-websocket-jetty-tests EE10 :: Websocket :: Jetty Tests diff --git a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-servlet/pom.xml b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-servlet/pom.xml index afc356a3bd0..edf69130632 100644 --- a/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-servlet/pom.xml +++ b/jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-servlet/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10.websocket jetty-ee10-websocket - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT jetty-ee10-websocket-servlet EE10 :: Websocket :: Servlet diff --git a/jetty-ee10/jetty-ee10-websocket/pom.xml b/jetty-ee10/jetty-ee10-websocket/pom.xml index 998e0abf26e..27c4e9fab6f 100644 --- a/jetty-ee10/jetty-ee10-websocket/pom.xml +++ b/jetty-ee10/jetty-ee10-websocket/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty.ee10 jetty-ee10 - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.ee10.websocket jetty-ee10-websocket diff --git a/jetty-ee10/pom.xml b/jetty-ee10/pom.xml index 2ccb16eefb4..7918fb29caa 100644 --- a/jetty-ee10/pom.xml +++ b/jetty-ee10/pom.xml @@ -5,7 +5,7 @@ org.eclipse.jetty jetty-project - 12.0.9-SNAPSHOT + 12.1.0-SNAPSHOT org.eclipse.jetty.ee10 jetty-ee10 diff --git a/jetty-ee11/jetty-ee11-annotations/pom.xml b/jetty-ee11/jetty-ee11-annotations/pom.xml new file mode 100644 index 00000000000..0794a418568 --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/pom.xml @@ -0,0 +1,98 @@ + + + + 4.0.0 + + org.eclipse.jetty.ee11 + jetty-ee11 + 12.1.0-SNAPSHOT + + jetty-ee11-annotations + EE11 :: Servlet Annotations + Annotation support for deploying servlets in jetty. + + ${project.groupId}.annotations + org.eclipse.jetty.ee11.annotations.* + + + + + jakarta.annotation + jakarta.annotation-api + + + jakarta.servlet + jakarta.servlet-api + + + org.eclipse.jetty.ee11 + jetty-ee11-plus + + + org.eclipse.jetty.ee11 + jetty-ee11-webapp + + + org.ow2.asm + asm + + + org.ow2.asm + asm-commons + + + org.slf4j + slf4j-api + + + jakarta.enterprise + jakarta.enterprise.cdi-api + test + + + jakarta.interceptor + jakarta.interceptor-api + test + + + jakarta.transaction + jakarta.transaction-api + test + + + org.eclipse.jetty + jetty-slf4j-impl + test + + + org.eclipse.jetty.toolchain + jetty-test-helper + test + + + + + + + org.apache.felix + maven-bundle-plugin + true + + + ${osgi.slf4j.import.packages},org.objectweb.asm;version="[$(version;==;${asm.version}),$(version;+;${asm.version}))",* + osgi.extender; filter:="(osgi.extender=osgi.serviceloader.registrar)", osgi.serviceloader; filter:="(osgi.serviceloader=jakarta.servlet.ServletContainerInitializer)";resolution:=optional;cardinality:=multiple, osgi.extender; filter:="(osgi.extender=osgi.serviceloader.processor)";resolution:=optional + osgi.serviceloader; osgi.serviceloader=org.eclipse.jetty.ee11.webapp.Configuration + + + + + maven-surefire-plugin + + @{argLine} ${jetty.surefire.argLine} + --add-reads org.eclipse.jetty.ee11.annotations=org.eclipse.jetty.logging + --add-opens org.eclipse.jetty.ee11.annotations/org.eclipse.jetty.ee11.annotations.resources=org.eclipse.jetty.plus + + + + + diff --git a/jetty-ee11/jetty-ee11-annotations/src/main/config/modules/ee11-annotations.mod b/jetty-ee11/jetty-ee11-annotations/src/main/config/modules/ee11-annotations.mod new file mode 100644 index 00000000000..4e16b175698 --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/main/config/modules/ee11-annotations.mod @@ -0,0 +1,25 @@ +# DO NOT EDIT THIS FILE - See: https://eclipse.dev/jetty/documentation/ + +[description] +Enables Annotation scanning for deployed web applications. + +[environment] +ee11 + +[depend] +ee11-plus + +[ini] +ee11.asm.version?=@asm.version@ +ee11.jakarta.annotation.api.version?=@jakarta.annotation.api.version@ + +[lib] +lib/jetty-ee11-annotations-${jetty.version}.jar +lib/ee11-annotations/asm-${ee11.asm.version}.jar +lib/ee11-annotations/asm-analysis-${ee11.asm.version}.jar +lib/ee11-annotations/asm-commons-${ee11.asm.version}.jar +lib/ee11-annotations/asm-tree-${ee11.asm.version}.jar +lib/ee11-annotations/jakarta.annotation-api-${ee11.jakarta.annotation.api.version}.jar + +[jpms] +add-modules:org.objectweb.asm diff --git a/jetty-ee11/jetty-ee11-annotations/src/main/java/module-info.java b/jetty-ee11/jetty-ee11-annotations/src/main/java/module-info.java new file mode 100644 index 00000000000..1f574c15f7b --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/main/java/module-info.java @@ -0,0 +1,33 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +import org.eclipse.jetty.ee11.annotations.AnnotationConfiguration; +import org.eclipse.jetty.ee11.webapp.Configuration; + +module org.eclipse.jetty.ee11.annotations +{ + requires jakarta.annotation; + requires java.naming; + requires org.slf4j; + + requires transitive org.eclipse.jetty.plus; + requires transitive org.eclipse.jetty.ee11.plus; + requires transitive org.objectweb.asm; + + exports org.eclipse.jetty.ee11.annotations; + + uses jakarta.servlet.ServletContainerInitializer; + + provides Configuration with + AnnotationConfiguration; +} diff --git a/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/AbstractDiscoverableAnnotationHandler.java b/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/AbstractDiscoverableAnnotationHandler.java new file mode 100644 index 00000000000..bc763e1ced3 --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/AbstractDiscoverableAnnotationHandler.java @@ -0,0 +1,37 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.annotations; + +import org.eclipse.jetty.ee11.webapp.DiscoveredAnnotation; +import org.eclipse.jetty.ee11.webapp.WebAppContext; + +/** + * DiscoverableAnnotationHandler + * + * Base class for handling the discovery of an annotation. + */ +public abstract class AbstractDiscoverableAnnotationHandler extends AnnotationParser.AbstractHandler +{ + protected WebAppContext _context; + + public AbstractDiscoverableAnnotationHandler(WebAppContext context) + { + _context = context; + } + + public void addAnnotation(DiscoveredAnnotation a) + { + _context.getMetaData().addDiscoveredAnnotation(a); + } +} diff --git a/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/AnnotationConfiguration.java b/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/AnnotationConfiguration.java new file mode 100644 index 00000000000..2d18fa87518 --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/AnnotationConfiguration.java @@ -0,0 +1,1168 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.annotations; + +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.ServiceLoader; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import java.util.regex.Pattern; +import java.util.stream.Stream; + +import jakarta.servlet.ServletContainerInitializer; +import jakarta.servlet.annotation.HandlesTypes; +import org.eclipse.jetty.ee11.plus.webapp.PlusConfiguration; +import org.eclipse.jetty.ee11.servlet.ServletContainerInitializerHolder; +import org.eclipse.jetty.ee11.servlet.Source; +import org.eclipse.jetty.ee11.servlet.Source.Origin; +import org.eclipse.jetty.ee11.webapp.AbstractConfiguration; +import org.eclipse.jetty.ee11.webapp.FragmentConfiguration; +import org.eclipse.jetty.ee11.webapp.FragmentDescriptor; +import org.eclipse.jetty.ee11.webapp.JettyWebXmlConfiguration; +import org.eclipse.jetty.ee11.webapp.MetaInfConfiguration; +import org.eclipse.jetty.ee11.webapp.WebAppClassLoader; +import org.eclipse.jetty.ee11.webapp.WebAppContext; +import org.eclipse.jetty.ee11.webapp.WebDescriptor; +import org.eclipse.jetty.ee11.webapp.WebXmlConfiguration; +import org.eclipse.jetty.util.ExceptionUtil; +import org.eclipse.jetty.util.JavaVersion; +import org.eclipse.jetty.util.Loader; +import org.eclipse.jetty.util.NanoTime; +import org.eclipse.jetty.util.ProcessorUtils; +import org.eclipse.jetty.util.StringUtil; +import org.eclipse.jetty.util.TypeUtil; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.util.resource.ResourceFactory; +import org.eclipse.jetty.util.statistic.CounterStatistic; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Configuration for Annotations + */ +public class AnnotationConfiguration extends AbstractConfiguration +{ + private static final Logger LOG = LoggerFactory.getLogger(AnnotationConfiguration.class); + + public static final String SERVLET_CONTAINER_INITIALIZER_EXCLUSION_PATTERN = "org.eclipse.jetty.containerInitializerExclusionPattern"; + public static final String SERVLET_CONTAINER_INITIALIZER_ORDER = "org.eclipse.jetty.containerInitializerOrder"; + public static final String CLASS_INHERITANCE_MAP = "org.eclipse.jetty.classInheritanceMap"; + public static final String CONTAINER_INITIALIZERS = "org.eclipse.jetty.containerInitializers"; + public static final String CONTAINER_INITIALIZER_STARTER = "org.eclipse.jetty.containerInitializerStarter"; + public static final String MULTI_THREADED = "org.eclipse.jetty.annotations.multiThreaded"; + public static final String MAX_SCAN_WAIT = "org.eclipse.jetty.annotations.maxWait"; + protected static final String STATE = "org.eclipse.jetty.annotations.state"; + + public static final int DEFAULT_MAX_SCAN_WAIT = 60; /* time in sec */ + public static final boolean DEFAULT_MULTI_THREADED = true; + + protected static class State + { + State(WebAppContext context) + { + _context = context; + } + + public final WebAppContext _context; + public final List _discoverableAnnotationHandlers = new ArrayList<>(); + public final List _containerInitializerAnnotationHandlers = new ArrayList<>(); + public final List _sciHolders = new ArrayList<>(); + public ClassInheritanceHandler _classInheritanceHandler; + public List _parserTasks; + public CounterStatistic _containerPathStats; + public CounterStatistic _webInfLibStats; + public CounterStatistic _webInfClassesStats; + public Pattern _sciExcludePattern; + } + + public AnnotationConfiguration() + { + super(new Builder() + .addDependencies(WebXmlConfiguration.class, MetaInfConfiguration.class, FragmentConfiguration.class, PlusConfiguration.class) + .addDependents(JettyWebXmlConfiguration.class) + .hide("org.objectweb.asm.")); + } + + /** + * Simple class to capture elapsed time of an operation. + */ + public static class TimeStatistic + { + public long _start = 0; + public long _end = 0; + + public void start() + { + _start = NanoTime.now(); + } + + public void end() + { + _end = NanoTime.now(); + } + + public long getElapsedNanos() + { + return NanoTime.elapsed(_start, _end); + } + } + + /** + * ParserTask + *

+ * Task to executing scanning of a resource for annotations. + */ + public static class ParserTask implements Callable + { + protected final AnnotationParser _parser; + protected final Set _handlers; + protected final Resource _resource; + protected TimeStatistic _stat; + + public ParserTask(AnnotationParser parser, Set handlers, Resource resource) + { + _parser = parser; + _handlers = handlers; + _resource = resource; + } + + public void setStatistic(TimeStatistic stat) + { + _stat = stat; + } + + @Override + public Void call() throws Exception + { + if (_stat != null) + _stat.start(); + if (_parser != null) + _parser.parse(_handlers, _resource); + if (_stat != null) + _stat.end(); + return null; + } + + public TimeStatistic getStatistic() + { + return _stat; + } + + public Resource getResource() + { + return _resource; + } + } + + /** + * ServletContainerInitializerOrdering + *

Applies an ordering to the {@link ServletContainerInitializer}s for the context, using + * the value of the "org.eclipse.jetty.containerInitializerOrder" context attribute. + * The attribute value is a list of classnames of ServletContainerInitializers in the order in which + * they are to be called. One name only in the list can be "*", which is a + * wildcard which matches any other ServletContainerInitializer name not already + * matched.

+ */ + public static class ServletContainerInitializerOrdering + { + private final Map _indexMap = new HashMap<>(); + private Integer _star = null; + private String _ordering = null; + + public ServletContainerInitializerOrdering(String ordering) + { + if (ordering != null) + { + _ordering = ordering; + + String[] tmp = StringUtil.csvSplit(ordering); + + for (int i = 0; i < tmp.length; i++) + { + String s = tmp[i].trim(); + _indexMap.put(s, i); + if ("*".equals(s)) + { + if (_star != null) + throw new IllegalArgumentException("Duplicate wildcards in ServletContainerInitializer ordering " + ordering); + _star = i; + } + } + } + } + + /** + * @return true if "*" is one of the values. + */ + public boolean hasWildcard() + { + return _star != null; + } + + /** + * @return the index of the "*" element, if it is specified. -1 otherwise. + */ + public int getWildcardIndex() + { + if (!hasWildcard()) + return -1; + return _star; + } + + /** + * @return true if the ordering contains a single value of "*" + */ + public boolean isDefaultOrder() + { + return (getSize() == 1 && hasWildcard()); + } + + /** + * Get the order index of the given classname + * + * @param name the classname to look up + * @return the index of the class name (or -1 if not found) + */ + public int getIndexOf(String name) + { + Integer i = _indexMap.get(name); + if (i == null) + return -1; + return i; + } + + /** + * Get the number of elements of the ordering + * + * @return the size of the index + */ + public int getSize() + { + return _indexMap.size(); + } + + @Override + public String toString() + { + if (_ordering == null) + return ""; + return _ordering; + } + } + + /** + * ServletContainerInitializerComparator + *

+ * Comparator impl that orders a set of ServletContainerInitializers according to the + * list of classnames (optionally containing a "*" wildcard character) established in a + * ServletContainerInitializerOrdering. + * + * @see ServletContainerInitializerOrdering + */ + public static class ServletContainerInitializerComparator implements Comparator + { + private final ServletContainerInitializerOrdering _ordering; + + public ServletContainerInitializerComparator(ServletContainerInitializerOrdering ordering) + { + _ordering = ordering; + } + + @Override + public int compare(ServletContainerInitializer sci1, ServletContainerInitializer sci2) + { + String c1 = (sci1 != null ? sci1.getClass().getName() : null); + String c2 = (sci2 != null ? sci2.getClass().getName() : null); + + if (c1 == null && c2 == null) + return 0; + + int i1 = _ordering.getIndexOf(c1); + if (i1 < 0 && _ordering.hasWildcard()) + i1 = _ordering.getWildcardIndex(); + int i2 = _ordering.getIndexOf(c2); + if (i2 < 0 && _ordering.hasWildcard()) + i2 = _ordering.getWildcardIndex(); + + return Integer.compare(i1, i2); + } + } + + public static class DiscoveredServletContainerInitializerHolder extends ServletContainerInitializerHolder + { + private final Set> _handlesTypes = new HashSet<>(); + private final Set _discoveredClassNames = new HashSet<>(); + + public DiscoveredServletContainerInitializerHolder(Source source, ServletContainerInitializer sci, Class... startupClasses) + { + super(source, sci); + //take the classes and set them aside until we can calculate all of their + //subclasses as necessary + _handlesTypes.addAll(_startupClasses); + } + + /** + * Classes that have annotations that are listed in @HandlesTypes + * are discovered by the ContainerInitializerAnnotationHandler + * and added here. + * @param names of classnames that have an annotation that is listed as a class in HandlesTypes + */ + @Override + public void addStartupClasses(String... names) + { + _discoveredClassNames.addAll(Arrays.asList(names)); + } + + /** + * Classes that are listed in @HandlesTypes and found by + * the createServletContainerInitializerAnnotationHandlers method. + * @param clazzes classes listed in HandlesTypes + */ + @Override + public void addStartupClasses(Class... clazzes) + { + _handlesTypes.addAll(Arrays.asList(clazzes)); + } + + @Override + protected Set> resolveStartupClasses() throws Exception + { + final Set> classes = new HashSet<>(); + WebAppClassLoader.runWithServerClassAccess(() -> + { + for (String name:_startupClassNames) + { + classes.add(Loader.loadClass(name)); + } + return null; + }); + return classes; + } + + /** + * Process each of the classes that are not annotations from @HandlesTypes and + * find all of the subclasses/implementations. + * Also process all of the classes that were discovered to have an annotation + * that was listed in @HandlesTypes, and find all of their subclasses/implementations + * in order to generate a complete set of classnames that can be passed into the + * onStartup method. + * + * @param classMap complete inheritance tree of all classes in the webapp, can be + * null if @HandlesTypes did not specify any classes. + */ + void resolveClasses(Map> classMap) + { + Set finalClassnames = new HashSet<>(); + + if (classMap != null) + { + for (Class c : _handlesTypes) + { + //find all subclasses/implementations of the classes (not annotations) named in @HandlesTypes + if (!c.isAnnotation()) + addInheritedTypes(finalClassnames, classMap, classMap.get(c.getName())); + } + + for (String classname:_discoveredClassNames) + { + //add each of the classes that were discovered to have an annotation listed in @HandlesTypes + finalClassnames.add(classname); + //walk its hierarchy and find all types that extend or implement the class + addInheritedTypes(finalClassnames, classMap, classMap.get(classname)); + } + } + + //finally, add the complete set of startup classnames + super.addStartupClasses(finalClassnames.toArray(new String[0])); + } + + /** + * Recursively walk the class hierarchy for the given set of classnames. + * + * @param results all classes related to the set of classnames in names + * @param classMap full inheritance tree for all classes in the webapp + * @param names the names of classes for which to walk the hierarchy + */ + private void addInheritedTypes(Set results, Map> classMap, Set names) + { + if (names == null || names.isEmpty()) + return; + + for (String s : names) + { + results.add(s); + + //walk the hierarchy and find all types that extend or implement the class + addInheritedTypes(results, classMap, classMap.get(s)); + } + } + } + + @Override + public void preConfigure(final WebAppContext context) + { + String tmp = (String)context.getAttribute(SERVLET_CONTAINER_INITIALIZER_EXCLUSION_PATTERN); + State state = new State(context); + context.setAttribute(STATE, state); + state._sciExcludePattern = (tmp == null ? null : Pattern.compile(tmp)); + } + + private State getState(WebAppContext context) + { + if (context.getAttribute(STATE) instanceof State state) + return state; + throw new IllegalStateException("No state"); + } + + @Override + public void configure(WebAppContext context) throws Exception + { + State state = getState(context); + + //handle introspectable annotations (postconstruct,predestroy, multipart etc etc) + context.getObjectFactory().addDecorator(new AnnotationDecorator(context)); + + if (!context.getMetaData().isMetaDataComplete()) + { + //If web.xml not metadata-complete, if this is a servlet 3 webapp or above + //or configDiscovered is true, we need to search for annotations + if (context.getServletContext().getEffectiveMajorVersion() >= 3 || context.isConfigurationDiscovered()) + { + state._discoverableAnnotationHandlers.add(new WebServletAnnotationHandler(context)); + state._discoverableAnnotationHandlers.add(new WebFilterAnnotationHandler(context)); + state._discoverableAnnotationHandlers.add(new WebListenerAnnotationHandler(context)); + } + } + + //Regardless of metadata, if there are any ServletContainerInitializers with @HandlesTypes, then we need to scan all the + //classes so we can call their onStartup() methods correctly + createServletContainerInitializerAnnotationHandlers(context, getNonExcludedInitializers(state)); + + if (!state._discoverableAnnotationHandlers.isEmpty() || state._classInheritanceHandler != null || !state._containerInitializerAnnotationHandlers.isEmpty()) + scanForAnnotations(context, state); + + Map> map = (Map>)context.getAttribute(AnnotationConfiguration.CLASS_INHERITANCE_MAP); + for (DiscoveredServletContainerInitializerHolder holder: state._sciHolders) + { + holder.resolveClasses(map); + context.addServletContainerInitializer(holder); //only add the holder now all classes are fully available + } + } + + @Override + public void postConfigure(WebAppContext context) throws Exception + { + Map> classMap = (ClassInheritanceMap)context.getAttribute(CLASS_INHERITANCE_MAP); + if (classMap != null) + classMap.clear(); + context.removeAttribute(CLASS_INHERITANCE_MAP); + + if (!(context.removeAttribute(STATE) instanceof State state)) + throw new IllegalStateException("No state"); + state._discoverableAnnotationHandlers.clear(); + state._classInheritanceHandler = null; + state._containerInitializerAnnotationHandlers.clear(); + state._sciHolders.clear(); + + if (state._parserTasks != null) + { + state._parserTasks.clear(); + state._parserTasks = null; + } + + super.postConfigure(context); + } + + /** + * Perform scanning of classes for discoverable + * annotations such as WebServlet/WebFilter/WebListener + * + * @param context the context for the scan + * @throws Exception if unable to scan + */ + protected void scanForAnnotations(WebAppContext context, State state) + throws Exception + { + int javaPlatform = 0; + Object target = context.getAttribute(JavaVersion.JAVA_TARGET_PLATFORM); + if (target != null) + javaPlatform = Integer.parseInt(target.toString()); + AnnotationParser parser = createAnnotationParser(javaPlatform); + state._parserTasks = new ArrayList<>(); + + if (LOG.isDebugEnabled()) + LOG.debug("Annotation scanning commencing: webxml={}, metadatacomplete={}, configurationDiscovered={}, multiThreaded={}, maxScanWait={}", + context.getServletContext().getEffectiveMajorVersion(), + context.getMetaData().isMetaDataComplete(), + context.isConfigurationDiscovered(), + isUseMultiThreading(context), + getMaxScanWait(context)); + + //scan selected jars on the container classpath first + parseContainerPath(context, parser); + //email from Rajiv Mordani jsrs 315 7 April 2010 + // If there is a then the ordering should be + // WEB-INF/classes the order of the declared elements + others. + // In case there is no others then it is + // WEB-INF/classes + order of the elements. + parseWebInfClasses(state, parser); + //scan non-excluded, non medatadata-complete jars in web-inf lib + parseWebInfLib(state, parser); + + long start = NanoTime.now(); + + //execute scan, either effectively synchronously (1 thread only), or asynchronously (limited by number of processors available) + final Semaphore task_limit = (isUseMultiThreading(context) ? new Semaphore(ProcessorUtils.availableProcessors()) : new Semaphore(1)); + final CountDownLatch latch = new CountDownLatch(state._parserTasks.size()); + final ExceptionUtil.MultiException multiException = new ExceptionUtil.MultiException(); + + for (final ParserTask p : state._parserTasks) + { + task_limit.acquire(); + context.getServer().getThreadPool().execute(() -> + { + multiException.callAndCatch(p::call); + task_limit.release(); + latch.countDown(); + }); + } + + boolean timeout = !latch.await(getMaxScanWait(context), TimeUnit.SECONDS); + long elapsedMs = NanoTime.millisSince(start); + + if (LOG.isDebugEnabled()) + { + LOG.debug("Annotation scanning elapsed time={}ms", elapsedMs); + for (ParserTask p : state._parserTasks) + { + LOG.debug("Scanned {} in {}ms", p.getResource(), TimeUnit.NANOSECONDS.toMillis(p.getStatistic().getElapsedNanos())); + } + + LOG.debug("Scanned {} container path jars, {} WEB-INF/lib jars, {} WEB-INF/classes dirs in {}ms for context {}", + (state._containerPathStats == null ? -1 : state._containerPathStats.getTotal()), + (state._webInfLibStats == null ? -1 : state._webInfLibStats.getTotal()), + (state._webInfClassesStats == null ? -1 : state._webInfClassesStats.getTotal()), + elapsedMs, + context); + } + + if (timeout) + multiException.add(new Exception("Timeout scanning annotations")); + multiException.ifExceptionThrow(); + } + + /** + * @param javaPlatform The java platform to scan for. + * @return a new AnnotationParser. This method can be overridden to use a different implementation of + * the AnnotationParser. Note that this is considered internal API. + */ + protected AnnotationParser createAnnotationParser(int javaPlatform) + { + return new AnnotationParser(javaPlatform); + } + + /** + * Check if we should use multiple threads to scan for annotations or not + * + * @param context the context of the multi threaded setting + * @return true if multi threading is enabled on the context, server, or via a System property. + * @see #MULTI_THREADED + */ + protected boolean isUseMultiThreading(WebAppContext context) + { + //try context attribute to see if we should use multithreading + Object o = context.getAttribute(MULTI_THREADED); + if (o instanceof Boolean) + { + return (Boolean)o; + } + //try server attribute to see if we should use multithreading + o = context.getServer().getAttribute(MULTI_THREADED); + if (o instanceof Boolean) + { + return (Boolean)o; + } + //try system property to see if we should use multithreading + return Boolean.parseBoolean(System.getProperty(MULTI_THREADED, Boolean.toString(DEFAULT_MULTI_THREADED))); + } + + /** + * Work out how long we should wait for the async scanning to occur. + * + * @param context the context of the max scan wait setting + * @return the max scan wait setting on the context, or server, or via a System property. + * @see #MAX_SCAN_WAIT + */ + protected int getMaxScanWait(WebAppContext context) + { + //try context attribute to get max time in sec to wait for scan completion + Object o = context.getAttribute(MAX_SCAN_WAIT); + if (o instanceof Number) + { + return ((Number)o).intValue(); + } + //try server attribute to get max time in sec to wait for scan completion + o = context.getServer().getAttribute(MAX_SCAN_WAIT); + if (o instanceof Number) + { + return ((Number)o).intValue(); + } + //try system property to get max time in sec to wait for scan completion + return Integer.getInteger(MAX_SCAN_WAIT, DEFAULT_MAX_SCAN_WAIT); + } + + public void createServletContainerInitializerAnnotationHandlers(WebAppContext context, List scis) + throws Exception + { + if (scis == null || scis.isEmpty()) + return; // nothing to do + + State state = getState(context); + + for (ServletContainerInitializer sci : scis) + { + Class[] classes = new Class[0]; + HandlesTypes annotation = sci.getClass().getAnnotation(HandlesTypes.class); + if (annotation != null) + classes = annotation.value(); + + DiscoveredServletContainerInitializerHolder holder = new DiscoveredServletContainerInitializerHolder(new Source(Origin.ANNOTATION, sci.getClass()), sci); + state._sciHolders.add(holder); + + if (classes.length > 0) + { + if (LOG.isDebugEnabled()) + LOG.debug("HandlesTypes {} on initializer {}", Arrays.asList(classes), sci.getClass()); + + //If we haven't already done so, we need to register a handler that will + //process the whole class hierarchy to satisfy the ServletContainerInitializer + if (context.getAttribute(CLASS_INHERITANCE_MAP) == null) + { + Map> map = new ClassInheritanceMap(); + context.setAttribute(CLASS_INHERITANCE_MAP, map); + state._classInheritanceHandler = new ClassInheritanceHandler(map); + } + + for (Class c : classes) + { + //The value of one of the HandlesTypes classes is actually an Annotation itself so + //register a handler for it to discover all classes that contain this annotation + if (c.isAnnotation()) + { + if (LOG.isDebugEnabled()) + LOG.debug("Registering annotation handler for {}", c.getName()); + state._containerInitializerAnnotationHandlers.add(new ContainerInitializerAnnotationHandler(holder, c)); + } + + holder.addStartupClasses(c); + } + } + } + } + + protected Resource getJarFor(WebAppContext context, ServletContainerInitializer service) + { + URI uri = TypeUtil.getLocationOfClass(service.getClass()); + if (uri == null) + return null; + return ResourceFactory.of(context).newResource(uri); + } + + /** + * Check to see if the ServletContainerIntializer loaded via the ServiceLoader came + * from a jar that is excluded by the fragment ordering. See ServletSpec 3.0 p.85. + * + * @param context the context for the jars + * @param sci the servlet container initializer + * @param sciResource the resource for the servlet container initializer + * @return true if excluded + */ + public boolean isFromExcludedJar(WebAppContext context, ServletContainerInitializer sci, Resource sciResource) + { + if (sci == null) + throw new IllegalArgumentException("ServletContainerInitializer null"); + if (context == null) + throw new IllegalArgumentException("WebAppContext null"); + + //if we don't know where its from it can't be excluded + if (sciResource == null) + { + if (LOG.isDebugEnabled()) + LOG.debug("!Excluded {} null resource", sci); + return false; + } + + //A ServletContainerInitialier that came from WEB-INF/classes or equivalent cannot be excluded by an ordering + if (isFromWebInfClasses(context, sciResource)) + { + if (LOG.isDebugEnabled()) + LOG.debug("!Excluded {} from web-inf/classes", sci); + return false; + } + + //A ServletContainerInitializer that came from the container's classpath cannot be excluded by an ordering + //of WEB-INF/lib jars + if (isFromContainerClassPath(context, sci)) + { + if (LOG.isDebugEnabled()) + LOG.debug("!Excluded {} from container classpath", sci); + return false; + } + + //If no ordering, nothing is excluded + if (!context.getMetaData().isOrdered()) + { + if (LOG.isDebugEnabled()) + LOG.debug("!Excluded {} no ordering", sci); + return false; + } + + List orderedJars = context.getMetaData().getWebInfResources(true); + + //there is an ordering, but there are no jars resulting from the ordering, everything excluded + if (orderedJars.isEmpty()) + { + if (LOG.isDebugEnabled()) + LOG.debug("Excluded {} empty ordering", sci); + return true; + } + + //Check if it is excluded by an ordering + boolean included = false; + for (Resource r : orderedJars) + { + included = r.equals(sciResource); + if (included) + break; + } + + if (LOG.isDebugEnabled()) + LOG.debug("{}Excluded {} found={}", included ? "!" : "", sci, included); + return !included; + } + + /** + * Test if the ServletContainerIntializer is excluded by the + * o.e.j.containerInitializerExclusionPattern + * + * @param state the state of the Annotation parsing + * @param sci the ServletContainerIntializer + * @return true if the ServletContainerIntializer is excluded + */ + protected boolean matchesExclusionPattern(State state, ServletContainerInitializer sci) + { + //no exclusion pattern, no SCI is excluded by it + if (state._sciExcludePattern == null) + return false; + + //test if name of class matches the regex + if (LOG.isDebugEnabled()) + LOG.debug("Checking {} against containerInitializerExclusionPattern", sci.getClass().getName()); + return state._sciExcludePattern.matcher(sci.getClass().getName()).matches(); + } + + /** + * Test if the ServletContainerInitializer is from the container classpath + * + * @param context the context for the webapp classpath + * @param sci the ServletContainerIntializer + * @return true if ServletContainerIntializer is from container classpath + */ + public boolean isFromContainerClassPath(WebAppContext context, ServletContainerInitializer sci) + { + if (sci == null) + return false; + + ClassLoader sciLoader = sci.getClass().getClassLoader(); + + //if loaded by bootstrap loader, then its the container classpath + if (sciLoader == null) + return true; + + //if there is no context classloader, then its the container classpath + if (context.getClassLoader() == null) + return true; + + ClassLoader loader = sciLoader; + while (loader != null) + { + if (loader == context.getClassLoader()) + return false; //the webapp classloader is in the ancestry of the classloader for the sci + else + loader = loader.getParent(); + } + + return true; + } + + /** + * Test if the ServletContainerInitializer is from WEB-INF/classes + * + * @param context the webapp to test + * @param sci a Resource representing the SCI + * @return true if the sci Resource is inside a WEB-INF/classes directory, false otherwise + */ + public boolean isFromWebInfClasses(WebAppContext context, Resource sci) + { + for (Resource dir : context.getMetaData().getWebInfClassesResources()) + { + if (dir.equals(sci)) + { + return true; + } + } + return false; + } + + /** + * Get SCIs that are not excluded from consideration + * + * @param state the web app annotation parse state + * @return the list of non-excluded servlet container initializers + */ + protected List getNonExcludedInitializers(State state) + { + WebAppContext context = state._context; + ArrayList nonExcludedInitializers = new ArrayList<>(); + + //We use the ServiceLoader mechanism to find the ServletContainerInitializer classes to inspect + long start = NanoTime.now(); + List scis = TypeUtil.serviceProviderStream(ServiceLoader.load(ServletContainerInitializer.class)).flatMap(provider -> + { + try + { + return Stream.of(provider.get()); + } + catch (Error e) + { + // Probably a SCI discovered on the system classpath that is hidden by the context classloader + if (LOG.isDebugEnabled()) + LOG.debug("Error: {} for {}", e.getMessage(), state._context, e); + else + LOG.info("Error: {} for {}", e.getMessage(), state._context); + return Stream.of(); + } + }).toList(); + + if (LOG.isDebugEnabled()) + LOG.debug("Service loaders found in {}ms", NanoTime.millisSince(start)); + + Map sciResourceMap = new HashMap<>(); + ServletContainerInitializerOrdering initializerOrdering = getInitializerOrdering(context); + + //Get initial set of SCIs that aren't from excluded jars or excluded by the containerExclusionPattern, or excluded + //because containerInitializerOrdering omits it + for (ServletContainerInitializer sci : scis) + { + if (matchesExclusionPattern(state, sci)) + { + if (LOG.isDebugEnabled()) + LOG.debug("{} excluded by pattern", sci); + continue; + } + + Resource sciResource = getJarFor(context, sci); + if (isFromExcludedJar(context, sci, sciResource)) + { + if (LOG.isDebugEnabled()) + LOG.debug("{} is from excluded jar", sci); + continue; + } + + //check containerInitializerOrdering doesn't exclude it + String name = sci.getClass().getName(); + if (initializerOrdering != null && (!initializerOrdering.hasWildcard() && initializerOrdering.getIndexOf(name) < 0)) + { + if (LOG.isDebugEnabled()) + LOG.debug("{} is excluded by ordering", sci); + continue; + } + + sciResourceMap.put(sci, sciResource); + } + + //Order the SCIs that are included + if (initializerOrdering != null && !initializerOrdering.isDefaultOrder()) + { + if (LOG.isDebugEnabled()) + LOG.debug("Ordering ServletContainerInitializers with {}", initializerOrdering); + + //There is an ordering that is not just "*". + //Arrange ServletContainerInitializers according to the ordering of classnames given, irrespective of coming from container or webapp classpaths + nonExcludedInitializers.addAll(sciResourceMap.keySet()); + nonExcludedInitializers.sort(new ServletContainerInitializerComparator(initializerOrdering)); + } + else + { + //No jetty-specific ordering specified, or just the wildcard value "*" specified. + //Fallback to ordering the ServletContainerInitializers according to: + //container classpath first, WEB-INF/classes then WEB-INF/lib (obeying any web.xml jar ordering) + + //First add in all SCIs that can't be excluded + int lastContainerSCI = -1; + for (Map.Entry entry : sciResourceMap.entrySet()) + { + if (entry.getKey().getClass().getClassLoader() == context.getClassLoader().getParent()) + { + nonExcludedInitializers.add(++lastContainerSCI, entry.getKey()); //add all container SCIs before any webapp SCIs + } + else if (entry.getValue() == null) //can't work out provenance of SCI, so can't be ordered/excluded + { + nonExcludedInitializers.add(entry.getKey()); //add at end of list + } + else + { + for (Resource dir : context.getMetaData().getWebInfClassesResources()) + { + if (dir.equals(entry.getValue()))//from WEB-INF/classes so can't be ordered/excluded + { + nonExcludedInitializers.add(entry.getKey()); + } + } + } + } + + //throw out the ones we've already accounted for + for (ServletContainerInitializer s : nonExcludedInitializers) + { + sciResourceMap.remove(s); + } + + if (context.getMetaData().getOrdering() == null) + { + if (LOG.isDebugEnabled()) + LOG.debug("No web.xml ordering, ServletContainerInitializers in random order"); + //add the rest of the scis + nonExcludedInitializers.addAll(sciResourceMap.keySet()); + } + else + { + if (LOG.isDebugEnabled()) + LOG.debug("Ordering ServletContainerInitializers with ordering {}", context.getMetaData().getOrdering()); + + //add SCIs according to the ordering of its containing jar + for (Resource webInfJar : context.getMetaData().getWebInfResources(true)) + { + for (Map.Entry entry : sciResourceMap.entrySet()) + { + if (webInfJar.equals(entry.getValue())) + nonExcludedInitializers.add(entry.getKey()); + } + } + } + } + + //final pass over the non-excluded SCIs if the webapp version is < 3, in which case + //we will only call SCIs that are on the server's classpath + if (context.getServletContext().getEffectiveMajorVersion() < 3 && !context.isConfigurationDiscovered()) + { + ListIterator it = nonExcludedInitializers.listIterator(); + while (it.hasNext()) + { + ServletContainerInitializer sci = it.next(); + if (!isFromContainerClassPath(context, sci)) + { + if (LOG.isDebugEnabled()) + LOG.debug("Ignoring SCI {}: old web.xml version {}.{}", sci.getClass().getName(), + context.getServletContext().getEffectiveMajorVersion(), + context.getServletContext().getEffectiveMinorVersion()); + it.remove(); + } + } + } + + if (LOG.isDebugEnabled()) + { + int i = 0; + for (ServletContainerInitializer sci : nonExcludedInitializers) + { + LOG.debug("ServletContainerInitializer: {} {} from {}", (++i), sci.getClass().getName(), sciResourceMap.get(sci)); + } + } + + return nonExcludedInitializers; + } + + /** + * Jetty-specific extension that allows an ordering to be applied across ALL ServletContainerInitializers. + * + * @param context the context for the initializer ordering configuration + * @return the ordering of the ServletContainerIntializer's + */ + public ServletContainerInitializerOrdering getInitializerOrdering(WebAppContext context) + { + if (context == null) + return null; + + String tmp = (String)context.getAttribute(SERVLET_CONTAINER_INITIALIZER_ORDER); + if (StringUtil.isBlank(tmp)) + return null; + + return new ServletContainerInitializerOrdering(tmp); + } + + /** + * Scan jars on container path. + * + * @param context the context for the scan + * @param parser the parser to scan with + */ + public void parseContainerPath(final WebAppContext context, final AnnotationParser parser) + { + State state = getState(context); + //always parse for discoverable annotations as well as class hierarchy and servletcontainerinitializer related annotations + final Set handlers = new HashSet<>(); + handlers.addAll(state._discoverableAnnotationHandlers); + handlers.addAll(state._containerInitializerAnnotationHandlers); + if (state._classInheritanceHandler != null) + handlers.add(state._classInheritanceHandler); + + if (LOG.isDebugEnabled()) + state._containerPathStats = new CounterStatistic(); + + //scan the container classpath jars that were selected by + //filtering in MetaInfConfiguration + for (Resource r : context.getMetaData().getContainerResources()) + { + if (state._parserTasks != null) + { + ParserTask task = new ParserTask(parser, handlers, r); + state._parserTasks.add(task); + if (LOG.isDebugEnabled()) + { + state._containerPathStats.increment(); + task.setStatistic(new TimeStatistic()); + } + } + } + } + + /** + * Scan jars in WEB-INF/lib. + *

+ * Only jars selected by MetaInfConfiguration, and that are not excluded + * by an ordering will be considered. + * + * @param state the state for the scan + * @param parser the annotation parser to use + * @throws Exception if unable to scan and/or parse + */ + protected void parseWebInfLib(State state, final AnnotationParser parser) throws Exception + { + //email from Rajiv Mordani jsrs 315 7 April 2010 + //jars that do not have a web-fragment.xml are still considered fragments + //they have to participate in the ordering + + //if there is an ordering, the ordered jars should be used. + //If there is no ordering, jars will be unordered. + WebAppContext context = state._context; + List jars = context.getMetaData().getWebInfResources(context.getMetaData().isOrdered()); + + if (LOG.isDebugEnabled()) + { + if (state._webInfLibStats == null) + state._webInfLibStats = new CounterStatistic(); + } + + for (Resource r : jars) + { + //for each jar, we decide which set of annotations we need to parse for + final Set handlers = new HashSet<>(); + + FragmentDescriptor f = context.getMetaData().getFragmentDescriptorForJar(r); + + //if its from a fragment jar that is metadata complete, we should skip scanning for @webservlet etc + // but yet we still need to do the scanning for the classes on behalf of the servletcontainerinitializers + //if a jar has no web-fragment.xml we scan it (because it is not excluded by the ordering) + //or if it has a fragment we scan it if it is not metadata complete + if (!WebDescriptor.isMetaDataComplete(f) || state._classInheritanceHandler != null || !state._containerInitializerAnnotationHandlers.isEmpty()) + { + //register the classinheritance handler if there is one + if (state._classInheritanceHandler != null) + handlers.add(state._classInheritanceHandler); + + //register the handlers for the @HandlesTypes values that are themselves annotations if there are any + handlers.addAll(state._containerInitializerAnnotationHandlers); + + //only register the discoverable annotation handlers if this fragment is not metadata complete, or has no fragment descriptor + if (!WebDescriptor.isMetaDataComplete(f)) + handlers.addAll(state._discoverableAnnotationHandlers); + + if (state._parserTasks != null) + { + ParserTask task = new ParserTask(parser, handlers, r); + state._parserTasks.add(task); + if (LOG.isDebugEnabled()) + { + state._webInfLibStats.increment(); + task.setStatistic(new TimeStatistic()); + } + } + } + } + } + + /** + * Scan classes in WEB-INF/classes. + * + * @param state the state for the scan + * @param parser the annotation parser to use + */ + protected void parseWebInfClasses(State state, AnnotationParser parser) + { + WebAppContext context = state._context; + Set handlers = new HashSet<>(state._discoverableAnnotationHandlers); + if (state._classInheritanceHandler != null) + handlers.add(state._classInheritanceHandler); + handlers.addAll(state._containerInitializerAnnotationHandlers); + + if (LOG.isDebugEnabled()) + state._webInfClassesStats = new CounterStatistic(); + + for (Resource dir : context.getMetaData().getWebInfClassesResources()) + { + if (state._parserTasks != null) + { + ParserTask task = new ParserTask(parser, handlers, dir); + state._parserTasks.add(task); + if (LOG.isDebugEnabled()) + { + state._webInfClassesStats.increment(); + task.setStatistic(new TimeStatistic()); + } + } + } + } + + public static class ClassInheritanceMap extends ConcurrentHashMap> + { + @Override + public String toString() + { + return String.format("ClassInheritanceMap@%x{size=%d}", hashCode(), size()); + } + } +} diff --git a/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/AnnotationDecorator.java b/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/AnnotationDecorator.java new file mode 100644 index 00000000000..e40f2fc228a --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/AnnotationDecorator.java @@ -0,0 +1,84 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.annotations; + +import java.util.Objects; + +import org.eclipse.jetty.ee11.webapp.WebAppContext; +import org.eclipse.jetty.util.DecoratedObjectFactory; +import org.eclipse.jetty.util.Decorator; + +/** + * AnnotationDecorator + */ +public class AnnotationDecorator implements Decorator +{ + protected AnnotationIntrospector _introspector; + protected WebAppContext _context; + + public AnnotationDecorator(WebAppContext context) + { + _context = Objects.requireNonNull(context); + _introspector = new AnnotationIntrospector(_context); + registerHandlers(); + } + + private void registerHandlers() + { + _introspector.registerHandler(new ResourceAnnotationHandler(_context)); + _introspector.registerHandler(new ResourcesAnnotationHandler(_context)); + _introspector.registerHandler(new RunAsAnnotationHandler(_context)); + _introspector.registerHandler(new PostConstructAnnotationHandler(_context)); + _introspector.registerHandler(new PreDestroyAnnotationHandler(_context)); + _introspector.registerHandler(new DeclareRolesAnnotationHandler(_context)); + _introspector.registerHandler(new MultiPartConfigAnnotationHandler(_context)); + _introspector.registerHandler(new ServletSecurityAnnotationHandler(_context)); + } + + /** + * Look for annotations that can be discovered with introspection: + *

    + *
  • Resource
  • + *
  • Resources
  • + *
  • RunAs
  • + *
  • PostConstruct
  • + *
  • PreDestroy
  • + *
  • DeclareRoles
  • + *
  • MultiPart
  • + *
  • ServletSecurity
  • + *
+ * + * @param o the object to introspect + * @param metaInfo information about the object to introspect + */ + protected void introspect(Object o, Object metaInfo) + { + if (o == null) + return; + _introspector.introspect(o, metaInfo); + } + + @Override + public T decorate(T o) + { + introspect(o, DecoratedObjectFactory.getAssociatedInfo()); + return o; + } + + @Override + public void destroy(Object o) + { + + } +} diff --git a/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/AnnotationIntrospector.java b/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/AnnotationIntrospector.java new file mode 100644 index 00000000000..7d38a70d685 --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/AnnotationIntrospector.java @@ -0,0 +1,229 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.annotations; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +import org.eclipse.jetty.ee11.servlet.BaseHolder; +import org.eclipse.jetty.ee11.servlet.Source.Origin; +import org.eclipse.jetty.ee11.webapp.WebAppContext; +import org.eclipse.jetty.ee11.webapp.WebDescriptor; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.util.thread.AutoLock; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Introspects a class to find various types of + * annotations as defined by the servlet specification. + */ +public class AnnotationIntrospector +{ + private static final Logger LOG = LoggerFactory.getLogger(AnnotationIntrospector.class); + + private final AutoLock _lock = new AutoLock(); + private final Set> _introspectedClasses = new HashSet<>(); + private final List _handlers = new ArrayList<>(); + private final WebAppContext _context; + + /** + * Interface for all handlers that wish to introspect a class to find a particular annotation + */ + public interface IntrospectableAnnotationHandler + { + void handle(Class clazz); + } + + /** + * Base class for handlers that introspect a class to find a particular annotation. + * A handler can optionally introspect the parent hierarchy of a class. + */ + public abstract static class AbstractIntrospectableAnnotationHandler implements IntrospectableAnnotationHandler + { + protected boolean _introspectAncestors; + protected WebAppContext _context; + + public abstract void doHandle(Class clazz); + + public AbstractIntrospectableAnnotationHandler(boolean introspectAncestors, WebAppContext context) + { + _context = Objects.requireNonNull(context); + _introspectAncestors = introspectAncestors; + } + + /** + * Check if the given class is permitted to have Servlet annotation. + * + * @param c the class + * @return true if the spec permits the class to have Servlet annotations, false otherwise + */ + protected static boolean isAnnotatableServletClass(Class c) + { + return jakarta.servlet.Servlet.class.isAssignableFrom(c) || + jakarta.servlet.Filter.class.isAssignableFrom(c) || + jakarta.servlet.ServletContextListener.class.isAssignableFrom(c) || + jakarta.servlet.ServletContextAttributeListener.class.isAssignableFrom(c) || + jakarta.servlet.ServletRequestListener.class.isAssignableFrom(c) || + jakarta.servlet.ServletRequestAttributeListener.class.isAssignableFrom(c) || + jakarta.servlet.http.HttpSessionListener.class.isAssignableFrom(c) || + jakarta.servlet.http.HttpSessionAttributeListener.class.isAssignableFrom(c) || + jakarta.servlet.http.HttpSessionIdListener.class.isAssignableFrom(c) || + jakarta.servlet.AsyncListener.class.isAssignableFrom(c) || + jakarta.servlet.http.HttpUpgradeHandler.class.isAssignableFrom(c); + } + + @Override + public void handle(Class clazz) + { + Class c = clazz; + + //process the whole inheritance hierarchy for the class + while (c != null && (!c.equals(Object.class))) + { + doHandle(c); + if (!_introspectAncestors) + break; + + c = c.getSuperclass(); + } + } + + public WebAppContext getContext() + { + return _context; + } + } + + public AnnotationIntrospector(WebAppContext context) + { + _context = Objects.requireNonNull(context); + } + + public void registerHandler(IntrospectableAnnotationHandler handler) + { + _handlers.add(handler); + } + + /** + * Test if an object should be introspected for some specific types of annotations + * like PostConstruct/PreDestroy/MultiPart etc etc. + *

+ * According to servlet 4.0, these types of annotations should only be evaluated iff any + * of the following are true: + *

    + *
  1. the object was created by the jakarta.servlet.ServletContext.createServlet/Filter/Listener method
  2. + *
  3. the object comes either from a discovered annotation (WebServlet/Filter/Listener) or a declaration + * in a descriptor AND web.xml is NOT metadata-complete AND any web-fragment.xml associated with the location of + * the class is NOT metadata-complete
  4. + *
+ * + * We also support evaluations of these types of annotations for objects that were created directly + * by the jetty api. + * + * @param o the object to check for its ability to be introspected for annotations + * @param metaInfo meta information about the object to be introspected + * @return true if it can be introspected according to servlet 4.0 rules + */ + public boolean isIntrospectable(Object o, Object metaInfo) + { + if (o == null) + return false; //nothing to introspect + + if (metaInfo == null) + return true; //no information about the object to introspect, assume introspectable + + BaseHolder holder; + + try + { + holder = (BaseHolder)metaInfo; + } + catch (ClassCastException e) + { + LOG.warn("Not introspectable {}", metaInfo.getClass().getName(), e); + return true; //not the type of information we were expecting, assume introspectable + } + + Origin origin = (holder.getSource() == null ? null : holder.getSource().getOrigin()); + if (origin == null) + return true; //assume introspectable + + switch (origin) + { + case EMBEDDED: + case JAKARTA_API: + { + return true; //objects created from the jetty or servlet api are always introspectable + } + case ANNOTATION: + { + return true; //we will have discovered annotations only if metadata-complete==false + } + default: + { + //must be from a descriptor. Only introspect if the descriptor with which it was associated + //is not metadata-complete + if (_context.getMetaData().isMetaDataComplete()) + return false; + + Resource descriptorLocation = holder.getSource().getResource(); + if (descriptorLocation == null) + return true; //no descriptor, can't be metadata-complete + return !WebDescriptor.isMetaDataComplete(_context.getMetaData().getFragmentDescriptor(descriptorLocation)); + } + } + } + + /** + * + */ + public void introspect(Object o, Object metaInfo) + { + if (!isIntrospectable(o, metaInfo)) + return; + + Class clazz = o.getClass(); + + try (AutoLock ignored = _lock.lock()) + { + // Lock to ensure that only 1 thread can be introspecting, and that + // thread must have fully finished generating the products of + // the introspection before another thread is allowed in. + // We remember the classes that we have introspected to avoid + // reprocessing the same class. + if (_introspectedClasses.add(clazz)) + { + for (IntrospectableAnnotationHandler handler : _handlers) + { + try + { + handler.handle(clazz); + } + catch (RuntimeException e) + { + throw e; + } + catch (Exception e) + { + throw new RuntimeException(e); + } + } + } + } + } +} diff --git a/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/AnnotationParser.java b/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/AnnotationParser.java new file mode 100644 index 00000000000..eca238850d0 --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/AnnotationParser.java @@ -0,0 +1,684 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.annotations; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Collections; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import org.eclipse.jetty.util.ExceptionUtil; +import org.eclipse.jetty.util.FileID; +import org.eclipse.jetty.util.StringUtil; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.util.resource.ResourceFactory; +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * AnnotationParser + *

+ * Use asm to scan classes for annotations. A SAX-style parsing is done. + * Handlers are registered which will be called back when various types of + * entity are encountered, eg a class, a method, a field. + *

+ * Handlers are not called back in any particular order and are assumed + * to be order-independent. + *

+ * As a registered Handler will be called back for each annotation discovered + * on a class, a method, a field, the Handler should test to see if the annotation + * is one that it is interested in. + *

+ * For the servlet spec, we are only interested in annotations on classes, methods and fields, + * so the callbacks for handling finding a class, a method a field are themselves + * not fully implemented. + */ +public class AnnotationParser +{ + private static final Logger LOG = LoggerFactory.getLogger(AnnotationParser.class); + private static final int ASM_VERSION = asmVersion(); + + /** + * Map of classnames scanned and the first location from which scan occurred + */ + protected Map _parsedClassNames = new ConcurrentHashMap<>(); + private final int _asmVersion; + + /** + * Determine the runtime version of asm. + * + * @return the {@link org.objectweb.asm.Opcodes} ASM value matching the runtime version of asm. + * TODO: can this be a jetty-util utility method to allow reuse across ee#? + * TODO: we should probably keep ASM centralized, as it's not EE specific, but Java Runtime specific behavior to keep up to date + */ + private static int asmVersion() + { + // Find the highest available ASM version on the runtime/classpath, because + // if we run with a lower than available ASM version, against a class with + // new language features we'll get an UnsupportedOperationException, even if + // the ASM version supports the new language features. + // Also, if we run with a higher than available ASM version, we'll get + // an IllegalArgumentException from org.objectweb.asm.ClassVisitor. + // So must find exactly the maximum ASM api version available. + + Optional asmVersion = Arrays.stream(Opcodes.class.getFields()).sequential() + .filter((f) -> f.getName().matches("ASM[0-9]+")) + .map((f) -> f.getName().substring(3)) + .map(Integer::parseInt) + .max(Integer::compareTo); + + if (asmVersion.isEmpty()) + throw new IllegalStateException("Invalid " + Opcodes.class.getName()); + + int asmFieldId = asmVersion.get(); + try + { + String fieldName = "ASM" + asmFieldId; + if (LOG.isDebugEnabled()) + LOG.debug("Using ASM API from {}.{}", Opcodes.class.getName(), fieldName); + return (int)Opcodes.class.getField(fieldName).get(null); + } + catch (Throwable e) + { + throw new IllegalStateException(e); + } + } + + /** + * Convert internal name to simple name + * + * @param name the internal name + * @return the simple name + */ + public static String normalize(String name) + { + if (name == null) + return null; + + if (name.startsWith("L") && name.endsWith(";")) + name = name.substring(1, name.length() - 1); + + if (name.endsWith(".class")) + name = name.substring(0, name.length() - ".class".length()); + + return StringUtil.replace(name, '/', '.'); + } + + /** + * Convert internal names to simple names. + * + * @param list the list of internal names + * @return the array of simple names + */ + public static String[] normalize(String[] list) + { + if (list == null) + return null; + String[] normalList = new String[list.length]; + int i = 0; + for (String s : list) + { + normalList[i++] = normalize(s); + } + return normalList; + } + + /** + * Immutable information gathered by parsing class header. + */ + public static class ClassInfo + { + final Resource _containingResource; + final String _className; + final int _version; + final int _access; + final String _signature; + final String _superName; + final String[] _interfaces; + + public ClassInfo(Resource resource, String className, int version, int access, String signature, String superName, String[] interfaces) + { + super(); + _containingResource = resource; + _className = className; + _version = version; + _access = access; + _signature = signature; + _superName = superName; + _interfaces = interfaces; + } + + public String getClassName() + { + return _className; + } + + public int getVersion() + { + return _version; + } + + public int getAccess() + { + return _access; + } + + public String getSignature() + { + return _signature; + } + + public String getSuperName() + { + return _superName; + } + + public String[] getInterfaces() + { + return _interfaces; + } + + public Resource getContainingResource() + { + return _containingResource; + } + } + + /** + * Immutable information gathered by parsing a method on a class. + */ + public static class MethodInfo + { + final ClassInfo _classInfo; + final String _methodName; + final int _access; + final String _desc; + final String _signature; + final String[] _exceptions; + + public MethodInfo(ClassInfo classInfo, String methodName, int access, String desc, String signature, String[] exceptions) + { + super(); + _classInfo = classInfo; + _methodName = methodName; + _access = access; + _desc = desc; + _signature = signature; + _exceptions = exceptions; + } + + public ClassInfo getClassInfo() + { + return _classInfo; + } + + public String getMethodName() + { + return _methodName; + } + + public int getAccess() + { + return _access; + } + + public String getDesc() + { + return _desc; + } + + public String getSignature() + { + return _signature; + } + + public String[] getExceptions() + { + return _exceptions; + } + } + + /** + * Immutable information gathered by parsing a field on a class. + */ + public static class FieldInfo + { + final ClassInfo _classInfo; + final String _fieldName; + final int _access; + final String _fieldType; + final String _signature; + final Object _value; + + public FieldInfo(ClassInfo classInfo, String fieldName, int access, String fieldType, String signature, Object value) + { + super(); + _classInfo = classInfo; + _fieldName = fieldName; + _access = access; + _fieldType = fieldType; + _signature = signature; + _value = value; + } + + public ClassInfo getClassInfo() + { + return _classInfo; + } + + public String getFieldName() + { + return _fieldName; + } + + public int getAccess() + { + return _access; + } + + public String getFieldType() + { + return _fieldType; + } + + public String getSignature() + { + return _signature; + } + + public Object getValue() + { + return _value; + } + } + + /** + * Signature for all handlers that respond to parsing class files. + */ + public interface Handler + { + void handle(ClassInfo classInfo); + + void handle(MethodInfo methodInfo); + + void handle(FieldInfo fieldInfo); + + void handle(ClassInfo info, String annotationName); + + void handle(MethodInfo info, String annotationName); + + void handle(FieldInfo info, String annotationName); + } + + /** + * Convenience base class to provide no-ops for all Handler methods. + */ + public abstract static class AbstractHandler implements Handler + { + @Override + public void handle(ClassInfo classInfo) + { + } + + @Override + public void handle(MethodInfo methodInfo) + { + } + + @Override + public void handle(FieldInfo fieldInfo) + { + } + + @Override + public void handle(ClassInfo info, String annotationName) + { + } + + @Override + public void handle(MethodInfo info, String annotationName) + { + } + + @Override + public void handle(FieldInfo info, String annotationName) + { + } + } + + /** + * ASM Visitor for parsing a method. We are only interested in the annotations on methods. + */ + public static class MyMethodVisitor extends MethodVisitor + { + final MethodInfo _mi; + final Set _handlers; + + public MyMethodVisitor(final Set handlers, + final ClassInfo classInfo, + final int access, + final String name, + final String methodDesc, + final String signature, + final String[] exceptions, + final int asmVersion) + { + super(asmVersion); + _handlers = handlers; + _mi = new MethodInfo(classInfo, name, access, methodDesc, signature, exceptions); + } + + /** + * We are only interested in finding the annotations on methods. + */ + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) + { + String annotationName = normalize(desc); + for (Handler h : _handlers) + { + h.handle(_mi, annotationName); + } + return null; + } + } + + /** + * An ASM visitor for parsing Fields. + * We are only interested in visiting annotations on Fields. + */ + public static class MyFieldVisitor extends FieldVisitor + { + final FieldInfo _fieldInfo; + final Set _handlers; + + public MyFieldVisitor(final Set handlers, + final ClassInfo classInfo, + final int access, + final String fieldName, + final String fieldType, + final String signature, + final Object value, + final int asmVersion) + { + super(asmVersion); + _handlers = handlers; + _fieldInfo = new FieldInfo(classInfo, fieldName, access, fieldType, signature, value); + } + + /** + * Parse an annotation found on a Field. + */ + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) + { + String annotationName = normalize(desc); + for (Handler h : _handlers) + { + h.handle(_fieldInfo, annotationName); + } + + return null; + } + } + + /** + * ASM visitor for a class. + */ + public static class MyClassVisitor extends ClassVisitor + { + final int _asmVersion; + final Resource _containingResource; + final Set _handlers; + ClassInfo _ci; + + public MyClassVisitor(Set handlers, Resource containingResource, int asmVersion) + { + super(asmVersion); + _asmVersion = asmVersion; + _handlers = handlers; + _containingResource = containingResource; + } + + @Override + public void visit(final int version, + final int access, + final String name, + final String signature, + final String superName, + final String[] interfaces) + { + _ci = new ClassInfo(_containingResource, normalize(name), version, access, signature, normalize(superName), normalize(interfaces)); + for (Handler h : _handlers) + { + h.handle(_ci); + } + } + + /** + * Visit an annotation on a Class + */ + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) + { + String annotationName = normalize(desc); + for (Handler h : _handlers) + { + h.handle(_ci, annotationName); + } + return null; + } + + /** + * Visit a method to extract its annotations + */ + @Override + public MethodVisitor visitMethod(final int access, + final String name, + final String methodDesc, + final String signature, + final String[] exceptions) + { + return new MyMethodVisitor(_handlers, _ci, access, name, methodDesc, signature, exceptions, _asmVersion); + } + + /** + * Visit a field to extract its annotations + */ + @Override + public FieldVisitor visitField(final int access, + final String fieldName, + final String fieldType, + final String signature, + final Object value) + { + return new MyFieldVisitor(_handlers, _ci, access, fieldName, fieldType, signature, value, _asmVersion); + } + } + + public AnnotationParser() + { + this(ASM_VERSION); + } + + /** + * @param asmVersion The target asm version or 0 for the internal version. + */ + public AnnotationParser(int asmVersion) + { + if (asmVersion == 0) + asmVersion = ASM_VERSION; + _asmVersion = asmVersion; + } + + /** + * Parse a resource + * + * @param handlers the handlers to look for classes in + * @param r the resource to parse + * @throws Exception if unable to parse + */ + public void parse(final Set handlers, Resource r) throws Exception + { + if (r == null) + return; + + if (!r.exists()) + return; + + if (FileID.isJavaArchive(r.getPath())) + { + parseJar(handlers, r); + return; + } + + if (r.isDirectory()) + { + parseDir(handlers, r); + return; + } + + if (FileID.isClassFile(r.getPath())) + { + parseClass(handlers, null, r.getPath()); + } + + if (LOG.isDebugEnabled()) + LOG.warn("Resource not able to be scanned for classes: {}", r); + } + + /** + * Parse all classes in a directory + * + * @param handlers the set of handlers to look for classes in + * @param dirResource the resource representing the baseResource being scanned (jar, dir, etc) + * @throws Exception if unable to parse + */ + protected void parseDir(Set handlers, Resource dirResource) throws Exception + { + if (LOG.isDebugEnabled()) + LOG.debug("Scanning dir {}", dirResource); + + assert dirResource.isDirectory(); + + ExceptionUtil.MultiException multiException = new ExceptionUtil.MultiException(); + + for (Resource candidate : dirResource.getAllResources()) + { + // Skip directories + if (candidate.isDirectory()) + continue; + + // Get the path relative to the base resource + Path relative = dirResource.getPathTo(candidate); + + // select only relative non-hidden class files that are not modules nor versions + if (relative == null || + FileID.isHidden(relative) || + FileID.isMetaInfVersions(relative) || + FileID.isModuleInfoClass(relative) || + !FileID.isClassFile(relative)) + continue; + + try + { + parseClass(handlers, dirResource, candidate.getPath()); + } + catch (Exception ex) + { + multiException.add(new RuntimeException("Error scanning entry " + ex, ex)); + } + } + + multiException.ifExceptionThrow(); + } + + /** + * Parse a resource that is a jar file. + * + * @param handlers the handlers to look for classes in + * @param jarResource the jar resource to parse + * @throws Exception if unable to parse + */ + protected void parseJar(Set handlers, Resource jarResource) throws Exception + { + if (jarResource == null) + return; + + /* if (!FileID.isJavaArchive(jarResource.getPath())) + return;*/ + + if (LOG.isDebugEnabled()) + LOG.debug("Scanning jar {}", jarResource); + + try (ResourceFactory.Closeable resourceFactory = ResourceFactory.closeable()) + { + Resource insideJarResource = resourceFactory.newJarFileResource(jarResource.getURI()); + parseDir(handlers, insideJarResource); + } + } + + /** + * Use ASM on a class + * + * @param handlers the handlers to look for classes in + * @param containingResource the dir or jar that the class is contained within, can be null if not known + * @param classFile the class file to parse + * @throws IOException if unable to parse + */ + protected void parseClass(Set handlers, Resource containingResource, Path classFile) throws IOException + { + if (LOG.isDebugEnabled()) + LOG.debug("Parse class from {}", classFile.toUri()); + + URI location = classFile.toUri(); + + try (InputStream in = Files.newInputStream(classFile)) + { + ClassReader reader = new ClassReader(in); + reader.accept(new MyClassVisitor(handlers, containingResource, _asmVersion), ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); + + String classname = normalize(reader.getClassName()); + URI existing = _parsedClassNames.putIfAbsent(classname, location); + if (existing != null) + LOG.warn("{} scanned from multiple locations: {}, {}", classname, existing, location); + } + catch (IllegalArgumentException | IOException e) + { + throw new IOException("Unable to parse class: " + classFile.toUri(), e); + } + } + + /** + * Useful mostly for testing to expose the list of parsed classes. + * @return the map of classnames to their URIs + */ + Map getParsedClassNames() + { + return Collections.unmodifiableMap(_parsedClassNames); + } +} diff --git a/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/ClassInheritanceHandler.java b/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/ClassInheritanceHandler.java new file mode 100644 index 00000000000..bdff5b3ca9f --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/ClassInheritanceHandler.java @@ -0,0 +1,79 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.annotations; + +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * As asm scans for classes, remember the type hierarchy. + */ +public class ClassInheritanceHandler extends AnnotationParser.AbstractHandler +{ + private static final Logger LOG = LoggerFactory.getLogger(ClassInheritanceHandler.class); + + Map> _inheritanceMap; + + public ClassInheritanceHandler(Map> map) + { + _inheritanceMap = map; + } + + @Override + public void handle(AnnotationParser.ClassInfo classInfo) + { + try + { + //Don't scan Object + if ("java.lang.Object".equals(classInfo.getClassName())) + return; + + for (int i = 0; classInfo.getInterfaces() != null && i < classInfo.getInterfaces().length; i++) + { + addToInheritanceMap(classInfo.getInterfaces()[i], classInfo.getClassName()); + } + //To save memory, we don't record classes that only extend Object, as that can be assumed + if (!"java.lang.Object".equals(classInfo.getSuperName())) + { + addToInheritanceMap(classInfo.getSuperName(), classInfo.getClassName()); + } + } + catch (Exception e) + { + LOG.warn("Failed to handle {}", classInfo, e); + } + } + + private void addToInheritanceMap(String interfaceOrSuperClassName, String implementingOrExtendingClassName) + { + + //As it is likely that the interfaceOrSuperClassName is already in the map, try getting it first + Set implementingClasses = _inheritanceMap.get(interfaceOrSuperClassName); + //If it isn't in the map, then add it in, but test to make sure that someone else didn't get in + //first and add it + if (implementingClasses == null) + { + implementingClasses = ConcurrentHashMap.newKeySet(); + Set tmp = _inheritanceMap.putIfAbsent(interfaceOrSuperClassName, implementingClasses); + if (tmp != null) + implementingClasses = tmp; + } + + implementingClasses.add(implementingOrExtendingClassName); + } +} diff --git a/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/ContainerInitializerAnnotationHandler.java b/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/ContainerInitializerAnnotationHandler.java new file mode 100644 index 00000000000..7c8ba9e2c1a --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/ContainerInitializerAnnotationHandler.java @@ -0,0 +1,76 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.annotations; + +import java.util.Objects; + +import org.eclipse.jetty.ee11.servlet.ServletContainerInitializerHolder; + +/** + * Discovers classes that contain the specified annotation, either at class or + * method level. The specified annotation is derived from an @HandlesTypes on + * a ServletContainerInitializer class. + */ +public class ContainerInitializerAnnotationHandler extends AnnotationParser.AbstractHandler +{ + final ServletContainerInitializerHolder _holder; + final Class _annotation; + + public ContainerInitializerAnnotationHandler(ServletContainerInitializerHolder holder, Class annotation) + { + _holder = Objects.requireNonNull(holder); + _annotation = Objects.requireNonNull(annotation); + } + + /** + * Handle finding a class that is annotated with the annotation we were constructed with. + * + * @see AnnotationParser.Handler#handle(AnnotationParser.ClassInfo, String) + */ + @Override + public void handle(AnnotationParser.ClassInfo info, String annotationName) + { + if (!_annotation.getName().equals(annotationName)) + return; + + _holder.addStartupClasses(info.getClassName()); + } + + /** + * Handle finding a field that is annotated with the annotation we were constructed with. + * + * @see AnnotationParser.Handler#handle(AnnotationParser.FieldInfo, String) + */ + @Override + public void handle(AnnotationParser.FieldInfo info, String annotationName) + { + if (!_annotation.getName().equals(annotationName)) + return; + + _holder.addStartupClasses(info.getClassInfo().getClassName()); + } + + /** + * Handle finding a method that is annotated with the annotation we were constructed with. + * + * @see AnnotationParser.Handler#handle(AnnotationParser.MethodInfo, String) + */ + @Override + public void handle(AnnotationParser.MethodInfo info, String annotationName) + { + if (!_annotation.getName().equals(annotationName)) + return; + _holder.addStartupClasses(info.getClassInfo().getClassName()); + } +} diff --git a/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/DeclareRolesAnnotationHandler.java b/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/DeclareRolesAnnotationHandler.java new file mode 100644 index 00000000000..bed1bc14dd0 --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/DeclareRolesAnnotationHandler.java @@ -0,0 +1,63 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.annotations; + +import jakarta.annotation.security.DeclareRoles; +import jakarta.servlet.Servlet; +import org.eclipse.jetty.ee11.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler; +import org.eclipse.jetty.ee11.servlet.security.ConstraintAware; +import org.eclipse.jetty.ee11.servlet.security.ConstraintSecurityHandler; +import org.eclipse.jetty.ee11.webapp.WebAppContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * DeclaresRolesAnnotationHandler + */ +public class DeclareRolesAnnotationHandler extends AbstractIntrospectableAnnotationHandler +{ + private static final Logger LOG = LoggerFactory.getLogger(DeclareRolesAnnotationHandler.class); + + public DeclareRolesAnnotationHandler(WebAppContext context) + { + super(false, context); + } + + @Override + public void doHandle(Class clazz) + { + if (!Servlet.class.isAssignableFrom(clazz)) + return; //only applicable on jakarta.servlet.Servlet derivatives + + if (!(_context.getSecurityHandler() instanceof ConstraintAware)) + { + LOG.warn("SecurityHandler not ConstraintAware, skipping security annotation processing"); + return; + } + + DeclareRoles declareRoles = clazz.getAnnotation(DeclareRoles.class); + if (declareRoles == null) + return; + + String[] roles = declareRoles.value(); + if (roles != null) + { + for (String r : roles) + { + ((ConstraintSecurityHandler)_context.getSecurityHandler()).addKnownRole(r); + _context.getMetaData().setOrigin("security-role." + r, declareRoles, clazz); + } + } + } +} diff --git a/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/MultiPartConfigAnnotationHandler.java b/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/MultiPartConfigAnnotationHandler.java new file mode 100644 index 00000000000..45007b8d763 --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/MultiPartConfigAnnotationHandler.java @@ -0,0 +1,64 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.annotations; + +import jakarta.servlet.MultipartConfigElement; +import jakarta.servlet.Servlet; +import jakarta.servlet.annotation.MultipartConfig; +import org.eclipse.jetty.ee11.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler; +import org.eclipse.jetty.ee11.servlet.ServletHolder; +import org.eclipse.jetty.ee11.webapp.Descriptor; +import org.eclipse.jetty.ee11.webapp.MetaData; +import org.eclipse.jetty.ee11.webapp.WebAppContext; + +/** + * MultiPartConfigAnnotationHandler + */ +public class MultiPartConfigAnnotationHandler extends AbstractIntrospectableAnnotationHandler +{ + public MultiPartConfigAnnotationHandler(WebAppContext context) + { + //TODO verify that MultipartConfig is not inheritable + super(false, context); + } + + @Override + public void doHandle(Class clazz) + { + if (!Servlet.class.isAssignableFrom(clazz)) + return; + + MultipartConfig multi = clazz.getAnnotation(MultipartConfig.class); + if (multi == null) + return; + + MetaData metaData = _context.getMetaData(); + + //TODO: The MultipartConfigElement needs to be set on the ServletHolder's Registration. + //How to identify the correct Servlet? If the Servlet has no WebServlet annotation on it, does it mean that this MultipartConfig + //annotation applies to all declared instances in web.xml/programmatically? + //Assuming TRUE for now. + for (ServletHolder holder : _context.getServletHandler().getServlets(clazz)) + { + Descriptor d = metaData.getOriginDescriptor(holder.getName() + ".servlet.multipart-config"); + //if a descriptor has already set the value for multipart config, do not + //let the annotation override it + if (d == null) + { + metaData.setOrigin(holder.getName() + ".servlet.multipart-config", multi, clazz); + holder.getRegistration().setMultipartConfig(new MultipartConfigElement(multi)); + } + } + } +} diff --git a/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/PostConstructAnnotationHandler.java b/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/PostConstructAnnotationHandler.java new file mode 100644 index 00000000000..fdd6b5f07f5 --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/PostConstructAnnotationHandler.java @@ -0,0 +1,84 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.annotations; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +import jakarta.annotation.PostConstruct; +import org.eclipse.jetty.ee11.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler; +import org.eclipse.jetty.ee11.webapp.Origin; +import org.eclipse.jetty.ee11.webapp.WebAppContext; +import org.eclipse.jetty.plus.annotation.LifeCycleCallbackCollection; +import org.eclipse.jetty.plus.annotation.PostConstructCallback; + +public class PostConstructAnnotationHandler extends AbstractIntrospectableAnnotationHandler +{ + public PostConstructAnnotationHandler(WebAppContext wac) + { + super(true, wac); + } + + @Override + public void doHandle(Class clazz) + { + //Check that the PostConstruct is on a class that we're interested in + if (supportsPostConstruct(clazz)) + { + Method[] methods = clazz.getDeclaredMethods(); + for (Method method : methods) + { + if (method.isAnnotationPresent(PostConstruct.class)) + { + if (method.getParameterCount() != 0) + throw new IllegalStateException(method + " has parameters"); + if (method.getReturnType() != Void.TYPE) + throw new IllegalStateException(method + " is not void"); + if (method.getExceptionTypes().length != 0) + throw new IllegalStateException(method + " throws checked exceptions"); + if (Modifier.isStatic(method.getModifiers())) + throw new IllegalStateException(method + " is static"); + + //ServletSpec 3.0 p80 If web.xml declares even one post-construct then all post-constructs + //in fragments must be ignored. Otherwise, they are additive. + Origin origin = _context.getMetaData().getOrigin("post-construct"); + if ((origin == Origin.WebXml || + origin == Origin.WebDefaults || + origin == Origin.WebOverride)) + return; + + PostConstructCallback callback = new PostConstructCallback(clazz, method.getName()); + LifeCycleCallbackCollection lifecycles = (LifeCycleCallbackCollection)_context.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION); + if (lifecycles == null) + { + lifecycles = new LifeCycleCallbackCollection(); + _context.setAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION, lifecycles); + } + lifecycles.add(callback); + } + } + } + } + + /** + * Check if the given class is permitted to have PostConstruct annotation. + * + * @param c the class + * @return true if the spec permits the class to have PostConstruct, false otherwise + */ + public boolean supportsPostConstruct(Class c) + { + return isAnnotatableServletClass(c); + } +} diff --git a/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/PreDestroyAnnotationHandler.java b/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/PreDestroyAnnotationHandler.java new file mode 100644 index 00000000000..6fa60e20228 --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/PreDestroyAnnotationHandler.java @@ -0,0 +1,86 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.annotations; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +import jakarta.annotation.PreDestroy; +import org.eclipse.jetty.ee11.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler; +import org.eclipse.jetty.ee11.webapp.Origin; +import org.eclipse.jetty.ee11.webapp.WebAppContext; +import org.eclipse.jetty.plus.annotation.LifeCycleCallbackCollection; +import org.eclipse.jetty.plus.annotation.PreDestroyCallback; + +public class PreDestroyAnnotationHandler extends AbstractIntrospectableAnnotationHandler +{ + public PreDestroyAnnotationHandler(WebAppContext wac) + { + super(true, wac); + } + + @Override + public void doHandle(Class clazz) + { + //Check that the PreDestroy is on a class that we're interested in + if (supportsPreDestroy(clazz)) + { + Method[] methods = clazz.getDeclaredMethods(); + for (Method method : methods) + { + if (method.isAnnotationPresent(PreDestroy.class)) + { + if (method.getParameterCount() != 0) + throw new IllegalStateException(method + " has parameters"); + if (method.getReturnType() != Void.TYPE) + throw new IllegalStateException(method + " is not void"); + if (method.getExceptionTypes().length != 0) + throw new IllegalStateException(method + " throws checked exceptions"); + if (Modifier.isStatic(method.getModifiers())) + throw new IllegalStateException(method + " is static"); + + //ServletSpec 3.0 p80 If web.xml declares even one predestroy then all predestroys + //in fragments must be ignored. Otherwise, they are additive. + Origin origin = _context.getMetaData().getOrigin("pre-destroy"); + if ((origin == Origin.WebXml || + origin == Origin.WebDefaults || + origin == Origin.WebOverride)) + return; + + PreDestroyCallback callback = new PreDestroyCallback(clazz, method.getName()); + + LifeCycleCallbackCollection lifecycles = (LifeCycleCallbackCollection)_context.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION); + if (lifecycles == null) + { + lifecycles = new LifeCycleCallbackCollection(); + _context.setAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION, lifecycles); + } + + lifecycles.add(callback); + } + } + } + } + + /** + * Check if the spec permits the given class to use the PreDestroy annotation. + * + * @param c the class + * @return true if permitted, false otherwise + */ + public boolean supportsPreDestroy(Class c) + { + return isAnnotatableServletClass(c); + } +} diff --git a/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/ResourceAnnotationHandler.java b/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/ResourceAnnotationHandler.java new file mode 100644 index 00000000000..bbecf31461f --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/ResourceAnnotationHandler.java @@ -0,0 +1,401 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.annotations; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.List; +import java.util.Locale; +import javax.naming.InitialContext; +import javax.naming.NameNotFoundException; +import javax.naming.NamingException; + +import jakarta.annotation.Resource; +import org.eclipse.jetty.ee11.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler; +import org.eclipse.jetty.ee11.servlet.ServletContextHandler; +import org.eclipse.jetty.ee11.webapp.MetaData; +import org.eclipse.jetty.ee11.webapp.WebAppContext; +import org.eclipse.jetty.plus.annotation.Injection; +import org.eclipse.jetty.plus.annotation.InjectionCollection; +import org.eclipse.jetty.plus.jndi.NamingEntryUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ResourceAnnotationHandler extends AbstractIntrospectableAnnotationHandler +{ + private static final Logger LOG = LoggerFactory.getLogger(ResourceAnnotationHandler.class); + + protected static final List> ENV_ENTRY_TYPES = List.of( + String.class, + Character.class, + Integer.class, + Boolean.class, + Double.class, + Byte.class, + Short.class, + Long.class, + Float.class + ); + + public ResourceAnnotationHandler(WebAppContext wac) + { + super(true, wac); + } + + /** + * Class level Resource annotations declare a name in the + * environment that will be looked up at runtime. They do + * not specify an injection. + */ + @Override + public void doHandle(Class clazz) + { + if (supportsResourceInjection(clazz)) + { + handleClass(clazz); + + Method[] methods = clazz.getDeclaredMethods(); + for (Method method : methods) + { + handleMethod(clazz, method); + } + Field[] fields = clazz.getDeclaredFields(); + //For each field, get all of it's annotations + for (Field field : fields) + { + handleField(clazz, field); + } + } + } + + public void handleClass(Class clazz) + { + Resource resource = clazz.getAnnotation(Resource.class); + if (resource != null) + { + String name = resource.name(); + String mappedName = resource.mappedName(); + + if (name == null || name.trim().isEmpty()) + throw new IllegalStateException("Class level Resource annotations must contain a name (Common Annotations Spec Section 2.3)"); + + try + { + if (!NamingEntryUtil.bindToENC(_context, name, mappedName)) + if (!NamingEntryUtil.bindToENC(_context.getServer(), name, mappedName)) + throw new IllegalStateException("No resource at " + (mappedName == null ? name : mappedName)); + } + catch (NamingException e) + { + LOG.warn("Unable to bind name {} to {} from class {}", name, mappedName, clazz, e); + } + } + } + + public void handleField(Class clazz, Field field) + { + Resource resource = field.getAnnotation(Resource.class); + if (resource != null) + { + //JavaEE Spec 5.2.3: Field cannot be static + if (Modifier.isStatic(field.getModifiers())) + { + LOG.warn("Skipping Resource annotation on {}.{}: cannot be static", clazz.getName(), field.getName()); + return; + } + + //JavaEE Spec 5.2.3: Field cannot be final + if (Modifier.isFinal(field.getModifiers())) + { + LOG.warn("Skipping Resource annotation on {}.{}: cannot be final", clazz.getName(), field.getName()); + return; + } + + //work out default name + String name = clazz.getName() + "/" + field.getName(); + + //allow @Resource name= to override the field name + name = (resource.name() != null && !resource.name().trim().isEmpty() ? resource.name() : name); + String mappedName = (resource.mappedName() != null && !resource.mappedName().trim().isEmpty() ? resource.mappedName() : null); + //get the type of the Field + Class type = field.getType(); + + //Servlet Spec 3.0 p. 76 + //If a descriptor has specified at least 1 injection target for this + //resource, then it overrides this annotation + MetaData metaData = _context.getMetaData(); + if (metaData.getOriginDescriptor("resource-ref." + name + ".injection") != null) + { + //at least 1 injection was specified for this resource by a descriptor, so + //it overrides this annotation + return; + } + + //No injections for this resource in any descriptors, so we can add it + //Does the injection already exist? + InjectionCollection injections = (InjectionCollection)_context.getAttribute(InjectionCollection.INJECTION_COLLECTION); + if (injections == null) + { + injections = new InjectionCollection(); + _context.setAttribute(InjectionCollection.INJECTION_COLLECTION, injections); + } + Injection injection = injections.getInjection(name, clazz, field); + if (injection == null) + { + //No injection has been specified, add it + try + { + //try webapp scope first + boolean bound = NamingEntryUtil.bindToENC(_context, name, mappedName); + + //try environment scope next + if (!bound) + bound = NamingEntryUtil.bindToENC(ServletContextHandler.ENVIRONMENT.getName(), name, mappedName); + + //try Server scope next + if (!bound) + bound = NamingEntryUtil.bindToENC(_context.getServer(), name, mappedName); + + //try jvm scope next + if (!bound) + bound = NamingEntryUtil.bindToENC(null, name, mappedName); + if (!bound) + { + //see if there is an env-entry value been bound + try + { + InitialContext ic = new InitialContext(); + String nameInEnvironment = (mappedName != null ? mappedName : name); + ic.lookup("java:comp/env/" + nameInEnvironment); + bound = true; + } + catch (NameNotFoundException e) + { + if (LOG.isTraceEnabled()) + LOG.trace("ignored", e); + } + } + //Check there is a JNDI entry for this annotation + if (bound) + { + LOG.debug("Bound {} as {}", (mappedName == null ? name : mappedName), name); + // Make the Injection for it if the binding succeeded + injection = new Injection(clazz, field, type, name, mappedName); + injections.add(injection); + + //TODO - an @Resource is equivalent to a resource-ref, resource-env-ref, message-destination + metaData.setOrigin("resource-ref." + name + ".injection", resource, clazz); + } + else if (!isEnvEntryType(type)) + { + //if this is an env-entry type resource and there is no value bound for it, it isn't + //an error, it just means that perhaps the code will use a default value instead + // JavaEE Spec. sec 5.4.1.3 + + throw new IllegalStateException("No resource at " + (mappedName == null ? name : mappedName)); + } + } + catch (NamingException e) + { + //if this is an env-entry type resource and there is no value bound for it, it isn't + //an error, it just means that perhaps the code will use a default value instead + // JavaEE Spec. sec 5.4.1.3 + if (!isEnvEntryType(type)) + throw new IllegalStateException(e); + } + } + } + } + + /** + * Process a Resource annotation on a Method. + *

+ * This will generate a JNDI entry, and an Injection to be + * processed when an instance of the class is created. + * + * @param clazz the class to process + * @param method the method to process + */ + public void handleMethod(Class clazz, Method method) + { + + Resource resource = method.getAnnotation(Resource.class); + if (resource != null) + { + /* + * Commons Annotations Spec 2.3 + * " The Resource annotation is used to declare a reference to a resource. + * It can be specified on a class, methods or on fields. When the + * annotation is applied on a field or method, the container will + * inject an instance of the requested resource into the application + * when the application is initialized... Even though this annotation + * is not marked Inherited, if used all superclasses MUST be examined + * to discover all uses of this annotation. All such annotation instances + * specify resources that are needed by the application. Note that this + * annotation may appear on private fields and methods of the superclasses. + * Injection of the declared resources needs to happen in these cases as + * well, even if a method with such an annotation is overridden by a subclass." + * + * Which IMHO, put more succinctly means "If you find a @Resource on any method + * or field, inject it!". + */ + //JavaEE Spec 5.2.3: Method cannot be static + if (Modifier.isStatic(method.getModifiers())) + { + LOG.warn("Skipping Resource annotation on {}.{}: cannot be static", clazz.getName(), method.getName()); + return; + } + + // Check it is a valid javabean: must be void return type, the name must start with "set" and it must have + // only 1 parameter + if (!method.getName().startsWith("set")) + { + LOG.warn("Skipping Resource annotation on {}.{}: invalid java bean, does not start with 'set'", clazz.getName(), method.getName()); + return; + } + + if (method.getParameterCount() != 1) + { + LOG.warn("Skipping Resource annotation on {}.{}: invalid java bean, not single argument to method", clazz.getName(), method.getName()); + return; + } + + if (Void.TYPE != method.getReturnType()) + { + LOG.warn("Skipping Resource annotation on {}.{}: invalid java bean, not void", clazz.getName(), method.getName()); + return; + } + + //default name is the javabean property name + String name = method.getName().substring(3); + name = name.substring(0, 1).toLowerCase(Locale.ENGLISH) + name.substring(1); + name = clazz.getName() + "/" + name; + + name = (resource.name() != null && !resource.name().trim().isEmpty() ? resource.name() : name); + String mappedName = (resource.mappedName() != null && !resource.mappedName().trim().isEmpty() ? resource.mappedName() : null); + Class paramType = method.getParameterTypes()[0]; + + Class resourceType = resource.type(); + + //Servlet Spec 3.0 p. 76 + //If a descriptor has specified at least 1 injection target for this + //resource, then it overrides this annotation + MetaData metaData = _context.getMetaData(); + if (metaData.getOriginDescriptor("resource-ref." + name + ".injection") != null) + { + //at least 1 injection was specified for this resource by a descriptor, so + //it overrides this annotation + return; + } + + //check if an injection has already been setup for this target by web.xml + InjectionCollection injections = (InjectionCollection)_context.getAttribute(InjectionCollection.INJECTION_COLLECTION); + if (injections == null) + { + injections = new InjectionCollection(); + _context.setAttribute(InjectionCollection.INJECTION_COLLECTION, injections); + } + Injection injection = injections.getInjection(name, clazz, method, paramType); + if (injection == null) + { + try + { + //try binding name to environment + //try the webapp's scope first + boolean bound = NamingEntryUtil.bindToENC(_context, name, mappedName); + + //try the environment's scope + if (!bound) + bound = NamingEntryUtil.bindToENC(ServletContextHandler.ENVIRONMENT.getName(), name, mappedName); + + //try the server's scope + if (!bound) + bound = NamingEntryUtil.bindToENC(_context.getServer(), name, mappedName); + + //try the jvm's scope + if (!bound) + bound = NamingEntryUtil.bindToENC(null, name, mappedName); + + //TODO if it is an env-entry from web.xml it can be injected, in which case there will be no + //NamingEntry, just a value bound in java:comp/env + if (!bound) + { + try + { + InitialContext ic = new InitialContext(); + String nameInEnvironment = (mappedName != null ? mappedName : name); + ic.lookup("java:comp/env/" + nameInEnvironment); + bound = true; + } + catch (NameNotFoundException e) + { + if (LOG.isTraceEnabled()) + LOG.trace("ignored", e); + } + } + + if (bound) + { + LOG.debug("Bound {} as {}", (mappedName == null ? name : mappedName), name); + // Make the Injection for it + injection = new Injection(clazz, method, paramType, resourceType, name, mappedName); + injections.add(injection); + //TODO - an @Resource is equivalent to a resource-ref, resource-env-ref, message-destination + metaData.setOrigin("resource-ref." + name + ".injection", resource, clazz); + } + else if (!isEnvEntryType(paramType)) + { + + //if this is an env-entry type resource and there is no value bound for it, it isn't + //an error, it just means that perhaps the code will use a default value instead + // JavaEE Spec. sec 5.4.1.3 + throw new IllegalStateException("No resource at " + (mappedName == null ? name : mappedName)); + } + } + catch (NamingException e) + { + //if this is an env-entry type resource and there is no value bound for it, it isn't + //an error, it just means that perhaps the code will use a default value instead + // JavaEE Spec. sec 5.4.1.3 + if (!isEnvEntryType(paramType)) + throw new IllegalStateException(e); + } + } + } + } + + /** + * Check if the given Class is one that the specification allows to have a Resource annotation. + * + * @param c the class + * @return true if Resource annotation permitted, false otherwise + */ + public boolean supportsResourceInjection(Class c) + { + return isAnnotatableServletClass(c); + } + + /** + * Check if the class is one of the basic java types permitted as + * env-entries. + * + * @param clazz the class to check + * @return true if class is permitted by the spec to be an env-entry value + */ + public boolean isEnvEntryType(Class clazz) + { + return ENV_ENTRY_TYPES.contains(clazz); + } +} diff --git a/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/ResourcesAnnotationHandler.java b/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/ResourcesAnnotationHandler.java new file mode 100644 index 00000000000..d00ab838046 --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/ResourcesAnnotationHandler.java @@ -0,0 +1,72 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.annotations; + +import javax.naming.NamingException; + +import jakarta.annotation.Resource; +import jakarta.annotation.Resources; +import org.eclipse.jetty.ee11.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler; +import org.eclipse.jetty.ee11.webapp.WebAppContext; +import org.eclipse.jetty.plus.jndi.NamingEntryUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ResourcesAnnotationHandler extends AbstractIntrospectableAnnotationHandler +{ + private static final Logger LOG = LoggerFactory.getLogger(ResourcesAnnotationHandler.class); + + public ResourcesAnnotationHandler(WebAppContext wac) + { + super(true, wac); + } + + @Override + public void doHandle(Class clazz) + { + Resources resources = clazz.getAnnotation(Resources.class); + if (resources != null) + { + Resource[] resArray = resources.value(); + if (resArray == null || resArray.length == 0) + { + LOG.warn("Skipping empty or incorrect Resources annotation on {}", clazz.getName()); + return; + } + + for (Resource resource : resArray) + { + String name = resource.name(); + String mappedName = resource.mappedName(); + + if (name == null || name.trim().isEmpty()) + throw new IllegalStateException("Class level Resource annotations must contain a name (Common Annotations Spec Section 2.3)"); + + try + { + //TODO don't ignore the shareable, auth etc etc + + if (!NamingEntryUtil.bindToENC(_context, name, mappedName)) + if (!NamingEntryUtil.bindToENC(_context.getServer(), name, mappedName)) + LOG.warn("Skipping Resources(Resource) annotation on {} for name {}: no resource bound at {}", + clazz.getName(), name, (mappedName == null ? name : mappedName)); + } + catch (NamingException e) + { + LOG.warn("Unable to bind {} to {}", name, mappedName, e); + } + } + } + } +} diff --git a/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/RunAsAnnotationHandler.java b/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/RunAsAnnotationHandler.java new file mode 100644 index 00000000000..adbe3bf1769 --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/RunAsAnnotationHandler.java @@ -0,0 +1,75 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.annotations; + +import jakarta.servlet.Servlet; +import org.eclipse.jetty.ee11.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler; +import org.eclipse.jetty.ee11.servlet.ServletHolder; +import org.eclipse.jetty.ee11.webapp.Descriptor; +import org.eclipse.jetty.ee11.webapp.MetaData; +import org.eclipse.jetty.ee11.webapp.WebAppContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class RunAsAnnotationHandler extends AbstractIntrospectableAnnotationHandler +{ + private static final Logger LOG = LoggerFactory.getLogger(RunAsAnnotationHandler.class); + + public RunAsAnnotationHandler(WebAppContext wac) + { + //Introspect only the given class for a RunAs annotation, as it is a class level annotation, + //and according to Common Annotation Spec p2-6 a class-level annotation is not inheritable. + super(false, wac); + } + + @Override + public void doHandle(Class clazz) + { + if (!Servlet.class.isAssignableFrom(clazz)) + return; + + jakarta.annotation.security.RunAs runAs = clazz.getAnnotation(jakarta.annotation.security.RunAs.class); + if (runAs != null) + { + String role = runAs.value(); + if (role != null) + { + for (ServletHolder holder : _context.getServletHandler().getServlets(clazz)) + { + MetaData metaData = _context.getMetaData(); + Descriptor d = metaData.getOriginDescriptor(holder.getName() + ".servlet.run-as"); + //if a descriptor has already set the value for run-as, do not + //let the annotation override it + if (d == null) + { + metaData.setOrigin(holder.getName() + ".servlet.run-as", runAs, clazz); + holder.setRunAsRole(role); + } + } + } + else + LOG.warn("Bad value for @RunAs annotation on class {}", clazz.getName()); + } + } + + public void handleField(String className, String fieldName, int access, String fieldType, String signature, Object value, String annotation) + { + LOG.warn("@RunAs annotation not applicable for fields: {}.{}", className, fieldName); + } + + public void handleMethod(String className, String methodName, int access, String params, String signature, String[] exceptions, String annotation) + { + LOG.warn("@RunAs annotation ignored on method: {}.{} {}", className, methodName, signature); + } +} diff --git a/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/ServletSecurityAnnotationHandler.java b/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/ServletSecurityAnnotationHandler.java new file mode 100644 index 00000000000..cae390bed95 --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/ServletSecurityAnnotationHandler.java @@ -0,0 +1,159 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.annotations; + +import java.util.ArrayList; +import java.util.List; + +import jakarta.servlet.ServletSecurityElement; +import jakarta.servlet.annotation.ServletSecurity; +import org.eclipse.jetty.ee11.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler; +import org.eclipse.jetty.ee11.servlet.ServletHolder; +import org.eclipse.jetty.ee11.servlet.ServletMapping; +import org.eclipse.jetty.ee11.servlet.security.ConstraintAware; +import org.eclipse.jetty.ee11.servlet.security.ConstraintMapping; +import org.eclipse.jetty.ee11.servlet.security.ConstraintSecurityHandler; +import org.eclipse.jetty.ee11.webapp.WebAppContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + *

Inspect a class to see if it has an @ServletSecurity annotation on it, + * setting up the <security-constraint>s. + *

+ *

A servlet can be defined in:

+ *
    + *
  • web.xml
  • + *
  • web-fragment.xml
  • + *
  • @WebServlet annotation discovered
  • + *
  • ServletContext.createServlet
  • + *
+ *

+ * The ServletSecurity annotation for a servlet should only be processed + * iff metadata-complete == false.

+ */ +public class ServletSecurityAnnotationHandler extends AbstractIntrospectableAnnotationHandler +{ + private static final Logger LOG = LoggerFactory.getLogger(ServletSecurityAnnotationHandler.class); + + public ServletSecurityAnnotationHandler(WebAppContext wac) + { + super(false, wac); + } + + @Override + public void doHandle(Class clazz) + { + if (!(_context.getSecurityHandler() instanceof ConstraintAware securityHandler)) + { + LOG.warn("SecurityHandler not ConstraintAware, skipping security annotation processing"); + return; + } + + ServletSecurity servletSecurity = clazz.getAnnotation(ServletSecurity.class); + if (servletSecurity == null) + return; + + //If there are already constraints defined (ie from web.xml) that match any + //of the url patterns defined for this servlet, then skip the security annotation. + + List servletMappings = getServletMappings(clazz.getCanonicalName()); + List constraintMappings = ((ConstraintAware)_context.getSecurityHandler()).getConstraintMappings(); + + if (constraintsExist(servletMappings, constraintMappings)) + { + LOG.warn("Constraints already defined for {}, skipping ServletSecurity annotation", clazz.getName()); + return; + } + + //Make a fresh list + constraintMappings = new ArrayList<>(); + + ServletSecurityElement securityElement = new ServletSecurityElement(servletSecurity); + for (ServletMapping sm : servletMappings) + { + for (String url : sm.getPathSpecs()) + { + _context.getMetaData().setOrigin("constraint.url." + url, servletSecurity, clazz); + constraintMappings.addAll(ConstraintSecurityHandler.createConstraintsWithMappingsForPath(clazz.getName(), url, securityElement)); + } + } + + //set up the security constraints produced by the annotation + constraintMappings.forEach(securityHandler::addConstraintMapping); + + //Servlet Spec 3.1 requires paths with uncovered http methods to be reported + securityHandler.checkPathsWithUncoveredHttpMethods(); + } + + /** + * Get the ServletMappings for the servlet's class. + * + * @param className the class name + * @return the servlet mappings for the class + */ + protected List getServletMappings(String className) + { + List results = new ArrayList<>(); + ServletMapping[] mappings = _context.getServletHandler().getServletMappings(); + for (ServletMapping mapping : mappings) + { + //Check the name of the servlet that this mapping applies to, and then find the ServletHolder for it to find it's class + ServletHolder holder = _context.getServletHandler().getServlet(mapping.getServletName()); + if (holder.getClassName() != null && holder.getClassName().equals(className)) + results.add(mapping); + } + return results; + } + + /** + * Check if there are already <security-constraint> elements defined that match the url-patterns for + * the servlet. + * + * @param servletMappings the servlet mappings + * @param constraintMappings the constraint mappings + * @return true if constraint exists + */ + protected boolean constraintsExist(List servletMappings, List constraintMappings) + { + boolean exists = false; + + //Check to see if the path spec on each constraint mapping matches a pathSpec in the servlet mappings. + //If it does, then we should ignore the security annotations. + for (ServletMapping mapping : servletMappings) + { + //Get its url mappings + String[] pathSpecs = mapping.getPathSpecs(); + if (pathSpecs == null) + continue; + + //Check through the constraints to see if there are any whose pathSpecs (url mappings) + //match the servlet. If so, then we already have constraints defined for this servlet, + //and we will not be processing the annotation (ie web.xml or programmatic override). + for (int i = 0; constraintMappings != null && i < constraintMappings.size() && !exists; i++) + { + for (String pathSpec : pathSpecs) + { + //TODO decide if we need to check the origin + if (pathSpec.equals(constraintMappings.get(i).getPathSpec())) + { + exists = true; + break; + } + } + } + } + return exists; + } +} diff --git a/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/WebFilterAnnotation.java b/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/WebFilterAnnotation.java new file mode 100644 index 00000000000..426d60392a1 --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/WebFilterAnnotation.java @@ -0,0 +1,198 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.annotations; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.EnumSet; + +import jakarta.servlet.DispatcherType; +import jakarta.servlet.Filter; +import jakarta.servlet.annotation.WebFilter; +import jakarta.servlet.annotation.WebInitParam; +import org.eclipse.jetty.ee11.servlet.FilterHolder; +import org.eclipse.jetty.ee11.servlet.FilterMapping; +import org.eclipse.jetty.ee11.servlet.Source; +import org.eclipse.jetty.ee11.webapp.DiscoveredAnnotation; +import org.eclipse.jetty.ee11.webapp.MetaData; +import org.eclipse.jetty.ee11.webapp.Origin; +import org.eclipse.jetty.ee11.webapp.WebAppContext; +import org.eclipse.jetty.http.pathmap.ServletPathSpec; +import org.eclipse.jetty.util.resource.Resource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * WebFilterAnnotation + */ +public class WebFilterAnnotation extends DiscoveredAnnotation +{ + private static final Logger LOG = LoggerFactory.getLogger(WebFilterAnnotation.class); + + public WebFilterAnnotation(WebAppContext context, String className) + { + super(context, className); + } + + public WebFilterAnnotation(WebAppContext context, String className, Resource resource) + { + super(context, className, resource); + } + + @Override + public void apply() + { + // TODO verify against rules for annotation v descriptor + + Class clazz = getTargetClass(); + if (clazz == null) + { + LOG.warn("{} cannot be loaded", _className); + return; + } + + //Servlet Spec 8.1.2 + if (!Filter.class.isAssignableFrom(clazz)) + { + LOG.warn("{} is not assignable from jakarta.servlet.Filter", clazz.getName()); + return; + } + MetaData metaData = _context.getMetaData(); + + WebFilter filterAnnotation = (WebFilter)clazz.getAnnotation(WebFilter.class); + + if (filterAnnotation.value().length > 0 && filterAnnotation.urlPatterns().length > 0) + { + LOG.warn("{} defines both @WebFilter.value and @WebFilter.urlPatterns", clazz.getName()); + return; + } + + String name = (filterAnnotation.filterName().isEmpty() ? clazz.getName() : filterAnnotation.filterName()); + String[] urlPatterns = filterAnnotation.value(); + if (urlPatterns.length == 0) + urlPatterns = filterAnnotation.urlPatterns(); + + FilterHolder holder = _context.getServletHandler().getFilter(name); + if (holder == null) + { + //Filter with this name does not already exist, so add it + holder = _context.getServletHandler().newFilterHolder(new Source(Source.Origin.ANNOTATION, clazz)); + holder.setName(name); + + holder.setHeldClass(clazz); + metaData.setOrigin(name + ".filter.filter-class", filterAnnotation, clazz); + + holder.setDisplayName(filterAnnotation.displayName()); + metaData.setOrigin(name + ".filter.display-name", filterAnnotation, clazz); + + for (WebInitParam ip : filterAnnotation.initParams()) + { + holder.setInitParameter(ip.name(), ip.value()); + metaData.setOrigin(name + ".filter.init-param." + ip.name(), ip, clazz); + } + + FilterMapping mapping = new FilterMapping(); + mapping.setFilterName(holder.getName()); + metaData.setOrigin(name + ".filter.mapping." + Long.toHexString(mapping.hashCode()), filterAnnotation, clazz); + if (urlPatterns.length > 0) + { + ArrayList paths = new ArrayList<>(); + for (String s : urlPatterns) + { + paths.add(ServletPathSpec.normalize(s)); + } + mapping.setPathSpecs(paths.toArray(new String[0])); + } + + if (filterAnnotation.servletNames().length > 0) + { + ArrayList names = new ArrayList<>(); + Collections.addAll(names, filterAnnotation.servletNames()); + mapping.setServletNames(names.toArray(new String[0])); + } + + EnumSet dispatcherSet = EnumSet.noneOf(DispatcherType.class); + dispatcherSet.addAll(Arrays.asList(filterAnnotation.dispatcherTypes())); + mapping.setDispatcherTypes(dispatcherSet); + metaData.setOrigin(name + ".filter.mappings", filterAnnotation, clazz); + + holder.setAsyncSupported(filterAnnotation.asyncSupported()); + metaData.setOrigin(name + ".filter.async-supported", filterAnnotation, clazz); + + _context.getServletHandler().addFilter(holder); + _context.getServletHandler().addFilterMapping(mapping); + } + else + { + //A Filter definition for the same name already exists from web.xml + //ServletSpec 3.0 p81 if the Filter is already defined and has mappings, + //they override the annotation. If it already has DispatcherType set, that + //also overrides the annotation. Init-params are additive, but web.xml overrides + //init-params of the same name. + for (WebInitParam ip : filterAnnotation.initParams()) + { + //if (holder.getInitParameter(ip.name()) == null) + if (metaData.getOrigin(name + ".filter.init-param." + ip.name()) == Origin.NotSet) + { + holder.setInitParameter(ip.name(), ip.value()); + metaData.setOrigin(name + ".filter.init-param." + ip.name(), ip, clazz); + } + } + + FilterMapping[] mappings = _context.getServletHandler().getFilterMappings(); + boolean mappingExists = false; + if (mappings != null) + { + for (FilterMapping m : mappings) + { + if (m.getFilterName().equals(name)) + { + mappingExists = true; + break; + } + } + } + //if a descriptor didn't specify at least one mapping, use the mappings from the annotation and the DispatcherTypes + //from the annotation + if (!mappingExists) + { + FilterMapping mapping = new FilterMapping(); + mapping.setFilterName(holder.getName()); + metaData.setOrigin(holder.getName() + ".filter.mapping." + Long.toHexString(mapping.hashCode()), filterAnnotation, clazz); + if (urlPatterns.length > 0) + { + ArrayList paths = new ArrayList<>(); + for (String s : urlPatterns) + { + paths.add(ServletPathSpec.normalize(s)); + } + mapping.setPathSpecs(paths.toArray(new String[0])); + } + if (filterAnnotation.servletNames().length > 0) + { + ArrayList names = new ArrayList<>(); + Collections.addAll(names, filterAnnotation.servletNames()); + mapping.setServletNames(names.toArray(new String[0])); + } + + EnumSet dispatcherSet = EnumSet.noneOf(DispatcherType.class); + Collections.addAll(dispatcherSet, filterAnnotation.dispatcherTypes()); + mapping.setDispatcherTypes(dispatcherSet); + _context.getServletHandler().addFilterMapping(mapping); + metaData.setOrigin(name + ".filter.mappings", filterAnnotation, clazz); + } + } + } +} diff --git a/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/WebFilterAnnotationHandler.java b/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/WebFilterAnnotationHandler.java new file mode 100644 index 00000000000..78a3ad7d7ea --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/WebFilterAnnotationHandler.java @@ -0,0 +1,57 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.annotations; + +import org.eclipse.jetty.ee11.webapp.WebAppContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * WebFilterAnnotationHandler + */ +public class WebFilterAnnotationHandler extends AbstractDiscoverableAnnotationHandler +{ + private static final Logger LOG = LoggerFactory.getLogger(WebFilterAnnotationHandler.class); + + public WebFilterAnnotationHandler(WebAppContext context) + { + super(context); + } + + @Override + public void handle(AnnotationParser.ClassInfo info, String annotationName) + { + if (!"jakarta.servlet.annotation.WebFilter".equals(annotationName)) + return; + + WebFilterAnnotation wfAnnotation = new WebFilterAnnotation(_context, info.getClassName(), info.getContainingResource()); + addAnnotation(wfAnnotation); + } + + @Override + public void handle(AnnotationParser.FieldInfo info, String annotationName) + { + if (!"jakarta.servlet.annotation.WebFilter".equals(annotationName)) + return; + LOG.warn("@WebFilter not applicable for fields: {}.{}", info.getClassInfo().getClassName(), info.getFieldName()); + } + + @Override + public void handle(AnnotationParser.MethodInfo info, String annotationName) + { + if (!"jakarta.servlet.annotation.WebFilter".equals(annotationName)) + return; + LOG.warn("@WebFilter not applicable for methods: {}.{} {}", info.getClassInfo().getClassName(), info.getMethodName(), info.getSignature()); + } +} diff --git a/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/WebListenerAnnotation.java b/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/WebListenerAnnotation.java new file mode 100644 index 00000000000..c070dbab130 --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/WebListenerAnnotation.java @@ -0,0 +1,91 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.annotations; + +import java.util.EventListener; + +import jakarta.servlet.ServletContextAttributeListener; +import jakarta.servlet.ServletContextListener; +import jakarta.servlet.ServletRequestAttributeListener; +import jakarta.servlet.ServletRequestListener; +import jakarta.servlet.annotation.WebListener; +import jakarta.servlet.http.HttpSessionAttributeListener; +import jakarta.servlet.http.HttpSessionIdListener; +import jakarta.servlet.http.HttpSessionListener; +import org.eclipse.jetty.ee11.servlet.ListenerHolder; +import org.eclipse.jetty.ee11.servlet.Source; +import org.eclipse.jetty.ee11.webapp.DiscoveredAnnotation; +import org.eclipse.jetty.ee11.webapp.MetaData; +import org.eclipse.jetty.ee11.webapp.Origin; +import org.eclipse.jetty.ee11.webapp.WebAppContext; +import org.eclipse.jetty.util.resource.Resource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * WebListenerAnnotation + */ +public class WebListenerAnnotation extends DiscoveredAnnotation +{ + private static final Logger LOG = LoggerFactory.getLogger(WebListenerAnnotation.class); + + public WebListenerAnnotation(WebAppContext context, String className) + { + super(context, className); + } + + public WebListenerAnnotation(WebAppContext context, String className, Resource resource) + { + super(context, className, resource); + } + + @Override + public void apply() + { + Class clazz = (Class)getTargetClass(); + + if (clazz == null) + { + LOG.warn("{} cannot be loaded", _className); + return; + } + + try + { + if (ServletContextListener.class.isAssignableFrom(clazz) || + ServletContextAttributeListener.class.isAssignableFrom(clazz) || + ServletRequestListener.class.isAssignableFrom(clazz) || + ServletRequestAttributeListener.class.isAssignableFrom(clazz) || + HttpSessionListener.class.isAssignableFrom(clazz) || + HttpSessionAttributeListener.class.isAssignableFrom(clazz) || + HttpSessionIdListener.class.isAssignableFrom(clazz)) + { + MetaData metaData = _context.getMetaData(); + if (metaData.getOrigin(clazz.getName() + ".listener") == Origin.NotSet) + { + ListenerHolder h = _context.getServletHandler().newListenerHolder(new Source(Source.Origin.ANNOTATION, clazz)); + h.setHeldClass(clazz); + _context.getServletHandler().addListener(h); + metaData.setOrigin(clazz.getName() + ".listener", clazz.getAnnotation(WebListener.class), clazz); + } + } + else + LOG.warn("{} does not implement one of the servlet listener interfaces", clazz.getName()); + } + catch (Exception e) + { + LOG.warn("Unable to add listener {}", clazz, e); + } + } +} diff --git a/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/WebListenerAnnotationHandler.java b/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/WebListenerAnnotationHandler.java new file mode 100644 index 00000000000..3a43444a62f --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/WebListenerAnnotationHandler.java @@ -0,0 +1,54 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.annotations; + +import org.eclipse.jetty.ee11.webapp.WebAppContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class WebListenerAnnotationHandler extends AbstractDiscoverableAnnotationHandler +{ + private static final Logger LOG = LoggerFactory.getLogger(WebListenerAnnotationHandler.class); + + public WebListenerAnnotationHandler(WebAppContext context) + { + super(context); + } + + @Override + public void handle(AnnotationParser.ClassInfo info, String annotationName) + { + if (!"jakarta.servlet.annotation.WebListener".equals(annotationName)) + return; + + WebListenerAnnotation wlAnnotation = new WebListenerAnnotation(_context, info.getClassName(), info.getContainingResource()); + addAnnotation(wlAnnotation); + } + + @Override + public void handle(AnnotationParser.FieldInfo info, String annotationName) + { + if (!"jakarta.servlet.annotation.WebListener".equals(annotationName)) + return; + LOG.warn("@WebListener is not applicable to fields: {}.{}", info.getClassInfo().getClassName(), info.getFieldName()); + } + + @Override + public void handle(AnnotationParser.MethodInfo info, String annotationName) + { + if (!"jakarta.servlet.annotation.WebListener".equals(annotationName)) + return; + LOG.warn("@WebListener is not applicable to methods: {}.{} {}", info.getClassInfo().getClassName(), info.getMethodName(), info.getSignature()); + } +} diff --git a/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/WebServletAnnotation.java b/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/WebServletAnnotation.java new file mode 100644 index 00000000000..9a9dfbae1b3 --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/WebServletAnnotation.java @@ -0,0 +1,269 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.annotations; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import jakarta.servlet.Servlet; +import jakarta.servlet.annotation.WebInitParam; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import org.eclipse.jetty.ee11.servlet.ServletHolder; +import org.eclipse.jetty.ee11.servlet.ServletMapping; +import org.eclipse.jetty.ee11.servlet.Source; +import org.eclipse.jetty.ee11.webapp.DiscoveredAnnotation; +import org.eclipse.jetty.ee11.webapp.MetaData; +import org.eclipse.jetty.ee11.webapp.Origin; +import org.eclipse.jetty.ee11.webapp.WebAppContext; +import org.eclipse.jetty.http.pathmap.ServletPathSpec; +import org.eclipse.jetty.util.ArrayUtil; +import org.eclipse.jetty.util.LazyList; +import org.eclipse.jetty.util.resource.Resource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * WebServletAnnotation + */ +public class WebServletAnnotation extends DiscoveredAnnotation +{ + private static final Logger LOG = LoggerFactory.getLogger(WebServletAnnotation.class); + + public WebServletAnnotation(WebAppContext context, String className) + { + super(context, className); + } + + public WebServletAnnotation(WebAppContext context, String className, Resource resource) + { + super(context, className, resource); + } + + @Override + public void apply() + { + //TODO check this algorithm with new rules for applying descriptors and annotations in order + Class clazz = (Class)getTargetClass(); + + if (clazz == null) + { + LOG.warn("{} cannot be loaded", _className); + return; + } + + //Servlet Spec 8.1.1 + if (!HttpServlet.class.isAssignableFrom(clazz)) + { + LOG.warn("{} is not assignable from jakarta.servlet.http.HttpServlet", clazz.getName()); + return; + } + + WebServlet annotation = clazz.getAnnotation(WebServlet.class); + + if (annotation.urlPatterns().length > 0 && annotation.value().length > 0) + { + LOG.warn("{} defines both @WebServlet.value and @WebServlet.urlPatterns", clazz.getName()); + return; + } + + String[] urlPatterns = annotation.value(); + if (urlPatterns.length == 0) + urlPatterns = annotation.urlPatterns(); + + if (urlPatterns.length == 0) + { + LOG.warn("{} defines neither @WebServlet.value nor @WebServlet.urlPatterns", clazz.getName()); + return; + } + + //canonicalize the patterns + ArrayList urlPatternList = new ArrayList<>(); + for (String p : urlPatterns) + { + urlPatternList.add(ServletPathSpec.normalize(p)); + } + + String servletName = (annotation.name().isEmpty() ? clazz.getName() : annotation.name()); + + MetaData metaData = _context.getMetaData(); + ServletMapping mapping = null; //the new mapping + + //Find out if a already exists with this name + ServletHolder[] holders = _context.getServletHandler().getServlets(); + + ServletHolder holder = null; + if (holders != null) + { + for (ServletHolder h : holders) + { + if (h.getName() != null && servletName.equals(h.getName())) + { + holder = h; + break; + } + } + } + + //handle creation/completion of a servlet + if (holder == null) + { + //No servlet of this name has already been defined, either by a descriptor + //or another annotation (which would be impossible). + Source source = new Source(Source.Origin.ANNOTATION, clazz); + + holder = _context.getServletHandler().newServletHolder(source); + holder.setHeldClass(clazz); + metaData.setOrigin(servletName + ".servlet.servlet-class", annotation, clazz); + + holder.setName(servletName); + holder.setDisplayName(annotation.displayName()); + metaData.setOrigin(servletName + ".servlet.display-name", annotation, clazz); + + holder.setInitOrder(annotation.loadOnStartup()); + metaData.setOrigin(servletName + ".servlet.load-on-startup", annotation, clazz); + + holder.setAsyncSupported(annotation.asyncSupported()); + metaData.setOrigin(servletName + ".servlet.async-supported", annotation, clazz); + + for (WebInitParam ip : annotation.initParams()) + { + holder.setInitParameter(ip.name(), ip.value()); + metaData.setOrigin(servletName + ".servlet.init-param." + ip.name(), ip, clazz); + } + + _context.getServletHandler().addServlet(holder); + + mapping = new ServletMapping(source); + mapping.setServletName(holder.getName()); + mapping.setPathSpecs(LazyList.toStringArray(urlPatternList)); + _context.getMetaData().setOrigin(servletName + ".servlet.mapping." + Long.toHexString(mapping.hashCode()), annotation, clazz); + } + else + { + //set the class according to the servlet that is annotated, if it wasn't already + //NOTE: this may be considered as "completing" an incomplete servlet registration, and it is + //not clear from servlet 3.0 spec whether this is intended, or if only a ServletContext.addServlet() call + //can complete it, see http://java.net/jira/browse/SERVLET_SPEC-42 + if (holder.getClassName() == null) + holder.setClassName(clazz.getName()); + if (holder.getHeldClass() == null) + holder.setHeldClass(clazz); + + //check if the existing servlet has each init-param from the annotation + //if not, add it + for (WebInitParam ip : annotation.initParams()) + { + if (metaData.getOrigin(servletName + ".servlet.init-param." + ip.name()) == Origin.NotSet) + { + holder.setInitParameter(ip.name(), ip.value()); + metaData.setOrigin(servletName + ".servlet.init-param." + ip.name(), ip, clazz); + } + } + + //check the url-patterns + //ServletSpec 3.0 p81 If a servlet already has url mappings from a + //webxml or fragment descriptor the annotation is ignored. + //However, we want to be able to replace mappings that were given in webdefault-ee11.xml + List existingMappings = getServletMappingsForServlet(servletName); + + //if any mappings for this servlet already set by a descriptor that is not webdefault-ee11.xml forget + //about processing these url mappings + if (existingMappings.isEmpty() || !containsNonDefaultMappings(existingMappings)) + { + mapping = new ServletMapping(new Source(Source.Origin.ANNOTATION, clazz)); + mapping.setServletName(servletName); + mapping.setPathSpecs(LazyList.toStringArray(urlPatternList)); + _context.getMetaData().setOrigin(servletName + ".servlet.mapping." + Long.toHexString(mapping.hashCode()), annotation, clazz); + } + } + + //We also want to be able to replace mappings that were defined in webdefault-ee11.xml + //that were for a different servlet eg a mapping in webdefault-ee11.xml for / to the jetty + //default servlet should be able to be replaced by an annotation for / to a different + //servlet + if (mapping != null) + { + //url mapping was permitted by annotation processing rules + + //take a copy of the existing servlet mappings that we can iterate over and remove from. This is + //because the ServletHandler interface does not support removal of individual mappings. + List allMappings = ArrayUtil.asMutableList(_context.getServletHandler().getServletMappings()); + + //for each of the urls in the annotation, check if a mapping to same/different servlet exists + // if mapping exists and is from a default descriptor, it can be replaced. NOTE: we do not + // guard against duplicate path mapping here: that is the job of the ServletHandler + for (String p : urlPatternList) + { + ServletMapping existingMapping = _context.getServletHandler().getServletMapping(p); + if (existingMapping != null && existingMapping.isFromDefaultDescriptor()) + { + String[] updatedPaths = ArrayUtil.removeFromArray(existingMapping.getPathSpecs(), p); + //if we removed the last path from a servletmapping, delete the servletmapping + if (updatedPaths == null || updatedPaths.length == 0) + { + boolean success = allMappings.remove(existingMapping); + if (LOG.isDebugEnabled()) + LOG.debug("Removed empty mapping {} from defaults descriptor success:{}", existingMapping, success); + } + else + { + existingMapping.setPathSpecs(updatedPaths); + if (LOG.isDebugEnabled()) + LOG.debug("Removed path {} from mapping {} from defaults descriptor ", p, existingMapping); + } + } + _context.getMetaData().setOrigin(servletName + ".servlet.mapping.url" + p, annotation, clazz); + } + allMappings.add(mapping); + _context.getServletHandler().setServletMappings(allMappings.toArray(new ServletMapping[0])); + } + } + + /** + * + */ + private List getServletMappingsForServlet(String name) + { + ServletMapping[] allMappings = _context.getServletHandler().getServletMappings(); + if (allMappings == null) + return Collections.emptyList(); + + List mappings = new ArrayList<>(); + for (ServletMapping m : allMappings) + { + if (m.getServletName() != null && name.equals(m.getServletName())) + { + mappings.add(m); + } + } + return mappings; + } + + /** + * + */ + private boolean containsNonDefaultMappings(List mappings) + { + if (mappings == null) + return false; + for (ServletMapping m : mappings) + { + if (!m.isFromDefaultDescriptor()) + return true; + } + return false; + } +} diff --git a/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/WebServletAnnotationHandler.java b/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/WebServletAnnotationHandler.java new file mode 100644 index 00000000000..01bca11fd27 --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/WebServletAnnotationHandler.java @@ -0,0 +1,62 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.annotations; + +import org.eclipse.jetty.ee11.webapp.WebAppContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Process a WebServlet annotation on a class. + */ +public class WebServletAnnotationHandler extends AbstractDiscoverableAnnotationHandler +{ + private static final Logger LOG = LoggerFactory.getLogger(WebServletAnnotationHandler.class); + + public WebServletAnnotationHandler(WebAppContext context) + { + super(context); + } + + /** + * Handle discovering a WebServlet annotation. + */ + @Override + public void handle(AnnotationParser.ClassInfo info, String annotationName) + { + if (!"jakarta.servlet.annotation.WebServlet".equals(annotationName)) + return; + + WebServletAnnotation annotation = new WebServletAnnotation(_context, info.getClassName(), info.getContainingResource()); + addAnnotation(annotation); + } + + @Override + public void handle(AnnotationParser.FieldInfo info, String annotationName) + { + if (!"jakarta.servlet.annotation.WebServlet".equals(annotationName)) + return; + + LOG.warn("@WebServlet annotation not supported for fields"); + } + + @Override + public void handle(AnnotationParser.MethodInfo info, String annotationName) + { + if (!"jakarta.servlet.annotation.WebServlet".equals(annotationName)) + return; + + LOG.warn("@WebServlet annotation not supported for methods"); + } +} diff --git a/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/package-info.java b/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/package-info.java new file mode 100644 index 00000000000..36f3b2fcd3d --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/main/java/org/eclipse/jetty/ee11/annotations/package-info.java @@ -0,0 +1,17 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +/** + * Jetty Annotations : Support for Servlet Annotations + */ +package org.eclipse.jetty.ee11.annotations; diff --git a/jetty-ee11/jetty-ee11-annotations/src/main/resources/META-INF/services/org.eclipse.jetty.ee11.webapp.Configuration b/jetty-ee11/jetty-ee11-annotations/src/main/resources/META-INF/services/org.eclipse.jetty.ee11.webapp.Configuration new file mode 100644 index 00000000000..92e58b441ae --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/main/resources/META-INF/services/org.eclipse.jetty.ee11.webapp.Configuration @@ -0,0 +1 @@ +org.eclipse.jetty.ee11.annotations.AnnotationConfiguration diff --git a/jetty-ee11/jetty-ee11-annotations/src/test/jar/test-sci-for-container-path.jar b/jetty-ee11/jetty-ee11-annotations/src/test/jar/test-sci-for-container-path.jar new file mode 100644 index 00000000000..c66593efae1 Binary files /dev/null and b/jetty-ee11/jetty-ee11-annotations/src/test/jar/test-sci-for-container-path.jar differ diff --git a/jetty-ee11/jetty-ee11-annotations/src/test/jar/test-sci-for-webinf.jar b/jetty-ee11/jetty-ee11-annotations/src/test/jar/test-sci-for-webinf.jar new file mode 100644 index 00000000000..bffda88ae1a Binary files /dev/null and b/jetty-ee11/jetty-ee11-annotations/src/test/jar/test-sci-for-webinf.jar differ diff --git a/jetty-ee11/jetty-ee11-annotations/src/test/jar/test-sci-with-ordering.jar b/jetty-ee11/jetty-ee11-annotations/src/test/jar/test-sci-with-ordering.jar new file mode 100644 index 00000000000..37c5a2b4886 Binary files /dev/null and b/jetty-ee11/jetty-ee11-annotations/src/test/jar/test-sci-with-ordering.jar differ diff --git a/jetty-ee11/jetty-ee11-annotations/src/test/jar/test-sci.jar b/jetty-ee11/jetty-ee11-annotations/src/test/jar/test-sci.jar new file mode 100644 index 00000000000..8f35be4b125 Binary files /dev/null and b/jetty-ee11/jetty-ee11-annotations/src/test/jar/test-sci.jar differ diff --git a/jetty-ee11/jetty-ee11-annotations/src/test/java/org/acme/ClassOne.java b/jetty-ee11/jetty-ee11-annotations/src/test/java/org/acme/ClassOne.java new file mode 100644 index 00000000000..6debfc14440 --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/test/java/org/acme/ClassOne.java @@ -0,0 +1,25 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.acme; + +/** + * ClassOne + */ +public class ClassOne +{ + + public void one() + { + } +} diff --git a/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/ClassA.java b/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/ClassA.java new file mode 100644 index 00000000000..9189326126b --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/ClassA.java @@ -0,0 +1,91 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.annotations; + +/** + * ClassA + */ +@Sample(1) +public class ClassA +{ + private Integer e; + private Integer f; + private Integer g; + private Integer h; + private Integer j; + private Integer k; + + public static class Foo + { + + } + + @Sample(7) + private Integer m; + + @Sample(2) + public void a(Integer[] x) + { + System.err.println("ClassA.public"); + } + + @Sample(3) + protected void b(Foo[] f) + { + System.err.println("ClassA.protected"); + } + + @Sample(4) + void c(int[] x) + { + System.err.println("ClassA.package"); + } + + @Sample(5) + private void d(int x, String y) + { + System.err.println("ClassA.private"); + } + + @Sample(6) + protected void l() + { + System.err.println("ClassA.protected method l"); + } + + public Integer getE() + { + return this.e; + } + + public Integer getF() + { + return this.f; + } + + public Integer getG() + { + return this.g; + } + + public Integer getJ() + { + return this.j; + } + + public void x() + { + System.err.println("ClassA.x"); + } +} diff --git a/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/ClassB.java b/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/ClassB.java new file mode 100644 index 00000000000..6605624f6e1 --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/ClassB.java @@ -0,0 +1,50 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.annotations; + +/** + * ClassB + */ +@Sample(value = 50) +@Multi({"do", "re", "mi"}) +public class ClassB extends ClassA implements InterfaceD +{ + + //test override of public scope method + @Sample(value = 51) + @Multi({"fa", "so", "la"}) + public void a() + { + System.err.println("ClassB.public"); + } + + //test override of package scope method + @Sample(value = 52) + void c() + { + System.err.println("ClassB.package"); + } + + @Override + public void l() + { + System.err.println("Overridden method l has no annotation"); + } + + //test no annotation + public void z() + { + System.err.println("ClassB.z"); + } +} diff --git a/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/FilterC.java b/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/FilterC.java new file mode 100644 index 00000000000..00abe559c0f --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/FilterC.java @@ -0,0 +1,78 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.annotations; + +import java.io.IOException; + +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; +import jakarta.annotation.Resource; +import jakarta.annotation.security.RunAs; +import jakarta.servlet.DispatcherType; +import jakarta.servlet.Filter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.FilterConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.annotation.WebFilter; +import jakarta.servlet.annotation.WebInitParam; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; + +@WebFilter(filterName = "CFilter", dispatcherTypes = {DispatcherType.REQUEST}, urlPatterns = {"/*"}, initParams = { + @WebInitParam(name = "a", value = "99") + }, asyncSupported = false) +@RunAs("admin") +public class FilterC implements Filter +{ + @Resource(mappedName = "foo") + private Double foo; + + @PreDestroy + public void pre() + { + + } + + @PostConstruct + public void post() + { + + } + + @Override + public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2) + throws IOException, ServletException + { + HttpServletRequest request = (HttpServletRequest)arg0; + HttpServletResponse response = (HttpServletResponse)arg1; + HttpSession session = request.getSession(true); + String val = request.getParameter("action"); + if (val != null) + session.setAttribute("action", val); + arg2.doFilter(request, response); + } + + @Override + public void destroy() + { + } + + @Override + public void init(FilterConfig arg0) throws ServletException + { + } +} diff --git a/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/InterfaceD.java b/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/InterfaceD.java new file mode 100644 index 00000000000..5fa08213ae7 --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/InterfaceD.java @@ -0,0 +1,22 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.annotations; + +/** + * InterfaceD + */ +public interface InterfaceD +{ + +} diff --git a/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/ListenerC.java b/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/ListenerC.java new file mode 100644 index 00000000000..1ea6af0775d --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/ListenerC.java @@ -0,0 +1,35 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.annotations; + +import jakarta.servlet.ServletContextEvent; +import jakarta.servlet.ServletContextListener; +import jakarta.servlet.annotation.WebListener; + +@WebListener +public class ListenerC implements ServletContextListener +{ + + @Override + public void contextDestroyed(ServletContextEvent arg0) + { + + } + + @Override + public void contextInitialized(ServletContextEvent arg0) + { + + } +} diff --git a/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/Multi.java b/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/Multi.java new file mode 100644 index 00000000000..5cad85666bc --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/Multi.java @@ -0,0 +1,26 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD}) +public @interface Multi +{ + String[] value(); +} diff --git a/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/Sample.java b/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/Sample.java new file mode 100644 index 00000000000..f0944b4a981 --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/Sample.java @@ -0,0 +1,26 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD}) +public @interface Sample +{ + int value(); +} diff --git a/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/ServletC.java b/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/ServletC.java new file mode 100644 index 00000000000..af710c58ff0 --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/ServletC.java @@ -0,0 +1,68 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.annotations; + +import java.io.IOException; + +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; +import jakarta.annotation.Resource; +import jakarta.annotation.security.DeclareRoles; +import jakarta.annotation.security.RunAs; +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.HttpConstraint; +import jakarta.servlet.annotation.HttpMethodConstraint; +import jakarta.servlet.annotation.MultipartConfig; +import jakarta.servlet.annotation.ServletSecurity; +import jakarta.servlet.annotation.WebInitParam; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +@DeclareRoles({"alice"}) +@WebServlet(urlPatterns = {"/foo/*", "/bah/*"}, name = "CServlet", initParams = { + @WebInitParam(name = "x", value = "y") + }, loadOnStartup = 2, asyncSupported = false) +@MultipartConfig(fileSizeThreshold = 1000, maxFileSize = 2000, maxRequestSize = 3000) +@RunAs("admin") +@ServletSecurity(value = @HttpConstraint(rolesAllowed = {"fred", "bill", "dorothy"}), httpMethodConstraints = { + @HttpMethodConstraint(value = "GET", rolesAllowed = + {"bob", "carol", "ted"}) +}) +public class ServletC extends HttpServlet +{ + @Resource(mappedName = "foo", type = Double.class) + private Double foo; + + @PreDestroy + public void pre() + { + + } + + @PostConstruct + public void post() + { + + } + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + response.setContentType("text/html"); + response.getWriter().println("

Annotated Servlet

"); + response.getWriter().println("An annotated Servlet."); + } +} diff --git a/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/ServletD.java b/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/ServletD.java new file mode 100644 index 00000000000..09babac7426 --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/ServletD.java @@ -0,0 +1,26 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.annotations; + +import jakarta.servlet.annotation.WebInitParam; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; + +@WebServlet(urlPatterns = {"/", "/bah/*"}, name = "DServlet", initParams = { + @WebInitParam(name = "x", value = "y") + }, loadOnStartup = 1, asyncSupported = false) +public class ServletD extends HttpServlet +{ + // no op +} diff --git a/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/ServletE.java b/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/ServletE.java new file mode 100644 index 00000000000..3942987927c --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/ServletE.java @@ -0,0 +1,25 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.annotations; + +import jakarta.annotation.PreDestroy; +import jakarta.servlet.http.HttpServlet; + +public class ServletE extends HttpServlet +{ + @PreDestroy + public void preDestroy() + { + } +} diff --git a/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/TestAnnotationConfiguration.java b/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/TestAnnotationConfiguration.java new file mode 100644 index 00000000000..96bbb31443a --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/TestAnnotationConfiguration.java @@ -0,0 +1,418 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.annotations; + +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +import jakarta.servlet.ServletContainerInitializer; +import org.eclipse.jetty.ee11.annotations.AnnotationConfiguration.State; +import org.eclipse.jetty.ee11.webapp.RelativeOrdering; +import org.eclipse.jetty.ee11.webapp.WebAppContext; +import org.eclipse.jetty.ee11.webapp.WebDescriptor; +import org.eclipse.jetty.toolchain.test.FS; +import org.eclipse.jetty.toolchain.test.JAR; +import org.eclipse.jetty.toolchain.test.MavenPaths; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.util.resource.ResourceFactory; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@ExtendWith(WorkDirExtension.class) +public class TestAnnotationConfiguration +{ + public static class TestableAnnotationConfiguration extends AnnotationConfiguration + { + public void assertAnnotationDiscovery(WebAppContext context, boolean b) + { + State state = (State)context.getAttribute(STATE); + if (!b) + assertTrue(state._discoverableAnnotationHandlers.isEmpty()); + else + assertFalse(state._discoverableAnnotationHandlers.isEmpty()); + } + } + + public Path web25; + public Path web31false; + public Path web31true; + public Path jarDir; + public Path testSciJar; + public Path testContainerSciJar; + public Path testWebInfClassesJar; + public WorkDir workDir; + public URLClassLoader containerLoader; + public URLClassLoader webAppLoader; + public List classes; + public Resource targetClasses; + public Resource webInfClasses; + + @BeforeEach + public void setup() throws Exception + { + web25 = MavenTestingUtils.getTestResourcePathFile("web25.xml"); + web31false = MavenTestingUtils.getTestResourcePathFile("web31false.xml"); + web31true = MavenTestingUtils.getTestResourcePathFile("web31true.xml"); + + // prepare an sci that will be on the webapp's classpath + jarDir = MavenTestingUtils.getProjectDirPath("src/test/jar"); + testSciJar = jarDir.resolve("test-sci.jar"); + assertTrue(Files.exists(testSciJar)); + + testContainerSciJar = jarDir.resolve("test-sci-for-container-path.jar"); + testWebInfClassesJar = jarDir.resolve("test-sci-for-webinf.jar"); + Path unpacked = workDir.getEmptyPathDir(); + // unpack some classes to pretend that are in WEB-INF/classes + FS.cleanDirectory(unpacked); + JAR.unpack(testWebInfClassesJar.toFile(), unpacked.toFile()); + webInfClasses = ResourceFactory.root().newResource(unpacked); + + containerLoader = new URLClassLoader(new URL[]{ + testContainerSciJar.toUri().toURL() + }, Thread.currentThread().getContextClassLoader()); + + targetClasses = ResourceFactory.root().newResource(MavenPaths.targetDir().resolve("test-classes")); + + classes = List.of(webInfClasses, targetClasses); + + webAppLoader = new URLClassLoader(new URL[]{ + testSciJar.toUri().toURL(), targetClasses.getURI().toURL(), webInfClasses.getURI().toURL() + }, + containerLoader); + } + + @Test + public void testAnnotationScanControl() throws Exception + { + //check that a 2.5 webapp with configurationDiscovered will discover annotations + TestableAnnotationConfiguration config25 = new TestableAnnotationConfiguration(); + WebAppContext context25 = new WebAppContext(); + config25.preConfigure(context25); + context25.setClassLoader(Thread.currentThread().getContextClassLoader()); + context25.setAttribute(AnnotationConfiguration.MULTI_THREADED, Boolean.FALSE); + context25.setAttribute(AnnotationConfiguration.MAX_SCAN_WAIT, 0); + context25.setConfigurationDiscovered(false); + context25.getMetaData().setWebDescriptor(new WebDescriptor(context25.getResourceFactory().newResource(web25))); + context25.getContext().getServletContext().setEffectiveMajorVersion(2); + context25.getContext().getServletContext().setEffectiveMinorVersion(5); + config25.configure(context25); + config25.assertAnnotationDiscovery(context25, false); + + //check that a 2.5 webapp discover annotations + TestableAnnotationConfiguration config25b = new TestableAnnotationConfiguration(); + WebAppContext context25b = new WebAppContext(); + config25b.preConfigure(context25b); + context25b.setClassLoader(Thread.currentThread().getContextClassLoader()); + context25b.setAttribute(AnnotationConfiguration.MULTI_THREADED, Boolean.FALSE); + context25b.setAttribute(AnnotationConfiguration.MAX_SCAN_WAIT, 0); + context25b.getMetaData().setWebDescriptor(new WebDescriptor(context25b.getResourceFactory().newResource(web25))); + context25b.getContext().getServletContext().setEffectiveMajorVersion(2); + context25b.getContext().getServletContext().setEffectiveMinorVersion(5); + config25b.configure(context25b); + config25b.assertAnnotationDiscovery(context25b, true); + + //check that a 3.x webapp with metadata true won't discover annotations + TestableAnnotationConfiguration config31 = new TestableAnnotationConfiguration(); + WebAppContext context31 = new WebAppContext(); + config31.preConfigure(context31); + context31.setClassLoader(Thread.currentThread().getContextClassLoader()); + context31.setAttribute(AnnotationConfiguration.MULTI_THREADED, Boolean.FALSE); + context31.setAttribute(AnnotationConfiguration.MAX_SCAN_WAIT, 0); + context31.getMetaData().setWebDescriptor(new WebDescriptor(context31.getResourceFactory().newResource(web31true))); + context31.getContext().getServletContext().setEffectiveMajorVersion(3); + context31.getContext().getServletContext().setEffectiveMinorVersion(1); + config31.configure(context31); + config31.assertAnnotationDiscovery(context31, false); + + //check that a 3.x webapp with metadata false will discover annotations + TestableAnnotationConfiguration config31b = new TestableAnnotationConfiguration(); + WebAppContext context31b = new WebAppContext(); + config31b.preConfigure(context31b); + context31b.setClassLoader(Thread.currentThread().getContextClassLoader()); + context31b.setAttribute(AnnotationConfiguration.MULTI_THREADED, Boolean.FALSE); + context31b.setAttribute(AnnotationConfiguration.MAX_SCAN_WAIT, 0); + context31b.getMetaData().setWebDescriptor(new WebDescriptor(context31b.getResourceFactory().newResource(web31false))); + context31b.getContext().getServletContext().setEffectiveMajorVersion(3); + context31b.getContext().getServletContext().setEffectiveMinorVersion(1); + config31b.configure(context31b); + config31b.assertAnnotationDiscovery(context31b, true); + } + + @Test + public void testServerAndWebappSCIs() throws Exception + { + ClassLoader old = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader(webAppLoader); + + try + { + AnnotationConfiguration config = new AnnotationConfiguration(); + WebAppContext context = new WebAppContext(); + config.preConfigure(context); + List scis; + + //test 3.1 webapp loads both server and app scis + context.setClassLoader(webAppLoader); + context.getMetaData().addWebInfResource(ResourceFactory.root().newResource(testSciJar)); + context.getMetaData().setWebDescriptor(new WebDescriptor(context.getResourceFactory().newResource(web31true))); + context.getMetaData().setWebInfClassesResources(classes); + context.getContext().getServletContext().setEffectiveMajorVersion(3); + context.getContext().getServletContext().setEffectiveMinorVersion(1); + config.preConfigure(context); + State state = (State)context.getAttribute(AnnotationConfiguration.STATE); + scis = config.getNonExcludedInitializers(state); + assertNotNull(scis); + assertEquals(3, scis.size()); + assertEquals("com.acme.ServerServletContainerInitializer", scis.get(0).getClass().getName()); //container path + assertEquals("com.acme.webinf.WebInfClassServletContainerInitializer", scis.get(1).getClass().getName()); // web-inf + assertEquals("com.acme.initializer.FooInitializer", scis.get(2).getClass().getName()); //web-inf jar no web-fragment + } + finally + { + Thread.currentThread().setContextClassLoader(old); + } + } + + @Test + public void testClassScanHandlersForSCIs() throws Exception + { + //test that SCIs with a @HandlesTypes that is an annotation registers + //handlers for the scanning phase that will capture the class hierarchy, + //and also capture all classes that contain the annotation + ClassLoader old = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader(webAppLoader); + + try + { + class MyAnnotationConfiguration extends AnnotationConfiguration + { + + @Override + public void createServletContainerInitializerAnnotationHandlers(WebAppContext context, List scis) throws Exception + { + State state = (State)context.getAttribute(STATE); + super.createServletContainerInitializerAnnotationHandlers(context, scis); + //check class hierarchy scanner handler is registered + assertNotNull(state._classInheritanceHandler); + //check + assertEquals(1, state._containerInitializerAnnotationHandlers.size()); + ContainerInitializerAnnotationHandler handler = state._containerInitializerAnnotationHandlers.get(0); + assertThat(handler._holder.toString(), containsString("com.acme.initializer.FooInitializer")); + assertEquals("com.acme.initializer.Foo", handler._annotation.getName()); + } + } + + MyAnnotationConfiguration config = new MyAnnotationConfiguration(); + + WebAppContext context = new WebAppContext(); + config.preConfigure(context); + List scis; + + context.setClassLoader(webAppLoader); + context.getMetaData().addWebInfResource(ResourceFactory.root().newResource(testSciJar)); + context.getMetaData().setWebDescriptor(new WebDescriptor(context.getResourceFactory().newResource(web31true))); + context.getMetaData().setWebInfClassesResources(classes); + context.getContext().getServletContext().setEffectiveMajorVersion(3); + context.getContext().getServletContext().setEffectiveMinorVersion(1); + config.preConfigure(context); + State state = (State)context.getAttribute(AnnotationConfiguration.STATE); + scis = config.getNonExcludedInitializers(state); + assertNotNull(scis); + assertEquals(3, scis.size()); + + config.createServletContainerInitializerAnnotationHandlers(context, scis); + } + finally + { + Thread.currentThread().setContextClassLoader(old); + } + } + + @Test + public void testMetaDataCompleteSCIs() throws Exception + { + ClassLoader old = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader(webAppLoader); + + try + { + AnnotationConfiguration config = new AnnotationConfiguration(); + WebAppContext context = new WebAppContext(); + config.preConfigure(context); + List scis; + // test a 3.1 webapp with metadata-complete=false loads both server + // and webapp scis + context.setClassLoader(webAppLoader); + context.getMetaData().setWebDescriptor(new WebDescriptor(context.getResourceFactory().newResource(web31false))); + context.getMetaData().setWebInfClassesResources(classes); + context.getMetaData().addWebInfResource(ResourceFactory.root().newResource(testSciJar)); + context.getContext().getServletContext().setEffectiveMajorVersion(3); + context.getContext().getServletContext().setEffectiveMinorVersion(1); + config.preConfigure(context); + State state = (State)context.getAttribute(AnnotationConfiguration.STATE); + scis = config.getNonExcludedInitializers(state); + assertNotNull(scis); + assertEquals(3, scis.size()); + assertEquals("com.acme.ServerServletContainerInitializer", scis.get(0).getClass().getName()); // container + // path + assertEquals("com.acme.webinf.WebInfClassServletContainerInitializer", scis.get(1).getClass().getName()); // web-inf + assertEquals("com.acme.initializer.FooInitializer", scis.get(2).getClass().getName()); // web-inf + // jar + // no + // web-fragment + } + finally + { + Thread.currentThread().setContextClassLoader(old); + } + } + + @Test + public void testRelativeOrderingWithSCIs() throws Exception + { + // test a 3.1 webapp with RELATIVE ORDERING loads sci from + // equivalent of WEB-INF/classes first as well as container path + + ClassLoader old = Thread.currentThread().getContextClassLoader(); + + Path orderedFragmentJar = jarDir.resolve("test-sci-with-ordering.jar"); + assertTrue(Files.exists(orderedFragmentJar)); + URLClassLoader orderedLoader = new URLClassLoader(new URL[]{ + orderedFragmentJar.toUri().toURL(), testSciJar.toUri().toURL(), + targetClasses.getURI().toURL(), webInfClasses.getURI().toURL() + }, + containerLoader); + Thread.currentThread().setContextClassLoader(orderedLoader); + + try + { + AnnotationConfiguration config = new AnnotationConfiguration(); + WebAppContext context = new WebAppContext(); + config.preConfigure(context); + List scis; + context.setClassLoader(orderedLoader); + context.getMetaData().setWebDescriptor(new WebDescriptor(context.getResourceFactory().newResource(web31true))); + RelativeOrdering ordering = new RelativeOrdering(context.getMetaData()); + context.getMetaData().setOrdering(ordering); + context.getMetaData().addWebInfResource(ResourceFactory.root().newResource(orderedFragmentJar)); + context.getMetaData().addWebInfResource(ResourceFactory.root().newResource(testSciJar)); + context.getMetaData().setWebInfClassesResources(classes); + context.getMetaData().orderFragments(); + context.getContext().getServletContext().setEffectiveMajorVersion(3); + context.getContext().getServletContext().setEffectiveMinorVersion(1); + config.preConfigure(context); + State state = (State)context.getAttribute(AnnotationConfiguration.STATE); + scis = config.getNonExcludedInitializers(state); + assertNotNull(scis); + assertEquals(4, scis.size()); + assertEquals("com.acme.ServerServletContainerInitializer", scis.get(0).getClass().getName()); //container path + assertEquals("com.acme.webinf.WebInfClassServletContainerInitializer", scis.get(1).getClass().getName()); // web-inf + assertEquals("com.acme.ordering.AcmeServletContainerInitializer", scis.get(2).getClass().getName()); // first + assertEquals("com.acme.initializer.FooInitializer", scis.get(3).getClass().getName()); //other in ordering + } + finally + { + Thread.currentThread().setContextClassLoader(old); + } + } + + @Test + public void testDiscoveredFalseWithSCIs() throws Exception + { + ClassLoader old = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader(webAppLoader); + try + { + //test 2.5 webapp with configurationDiscovered=false loads only server scis + AnnotationConfiguration config = new AnnotationConfiguration(); + WebAppContext context = new WebAppContext(); + config.preConfigure(context); + List scis; + context.setConfigurationDiscovered(false); + context.setClassLoader(webAppLoader); + context.getMetaData().setWebDescriptor(new WebDescriptor(context.getResourceFactory().newResource(web25))); + context.getMetaData().setWebInfClassesResources(classes); + context.getMetaData().addWebInfResource(ResourceFactory.root().newResource(testSciJar)); + context.getContext().getServletContext().setEffectiveMajorVersion(2); + context.getContext().getServletContext().setEffectiveMinorVersion(5); + config.preConfigure(context); + State state = (State)context.getAttribute(AnnotationConfiguration.STATE); + scis = config.getNonExcludedInitializers(state); + assertNotNull(scis); + for (ServletContainerInitializer s : scis) + { + //should not have any of the web-inf lib scis in here + assertNotEquals("com.acme.ordering.AcmeServletContainerInitializer", s.getClass().getName()); + assertNotEquals("com.acme.initializer.FooInitializer", s.getClass().getName()); + //NOTE: should also not have the web-inf classes scis in here either, but due to the + //way the test is set up, the sci we're pretending is in web-inf classes will actually + //NOT be loaded by the webapp's classloader, but rather by the junit classloader, so + //it looks as if it is a container class. + } + } + finally + { + Thread.currentThread().setContextClassLoader(old); + } + } + + @Test + public void testDiscoveredTrueWithSCIs() throws Exception + { + ClassLoader old = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader(webAppLoader); + try + { + //test 2.5 webapp with configurationDiscovered=true loads both server and webapp scis + AnnotationConfiguration config = new AnnotationConfiguration(); + WebAppContext context = new WebAppContext(); + config.preConfigure(context); + List scis; + context.setConfigurationDiscovered(true); + context.setClassLoader(webAppLoader); + context.getMetaData().setWebDescriptor(new WebDescriptor(context.getResourceFactory().newResource(web25))); + context.getMetaData().setWebInfClassesResources(classes); + context.getMetaData().addWebInfResource(ResourceFactory.root().newResource(testSciJar)); + context.getContext().getServletContext().setEffectiveMajorVersion(2); + context.getContext().getServletContext().setEffectiveMinorVersion(5); + config.preConfigure(context); + State state = (State)context.getAttribute(AnnotationConfiguration.STATE); + scis = config.getNonExcludedInitializers(state); + assertNotNull(scis); + assertEquals(3, scis.size(), scis::toString); + assertEquals("com.acme.ServerServletContainerInitializer", scis.get(0).getClass().getName()); //container path + assertEquals("com.acme.webinf.WebInfClassServletContainerInitializer", scis.get(1).getClass().getName()); // web-inf + assertEquals("com.acme.initializer.FooInitializer", scis.get(2).getClass().getName()); //web-inf jar no web-fragment + } + finally + { + Thread.currentThread().setContextClassLoader(old); + } + } +} diff --git a/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/TestAnnotationDecorator.java b/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/TestAnnotationDecorator.java new file mode 100644 index 00000000000..a66643b628f --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/TestAnnotationDecorator.java @@ -0,0 +1,131 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.annotations; + +import java.nio.file.Files; +import java.nio.file.Path; + +import org.eclipse.jetty.ee11.servlet.ServletHolder; +import org.eclipse.jetty.ee11.servlet.Source; +import org.eclipse.jetty.ee11.webapp.WebAppContext; +import org.eclipse.jetty.ee11.webapp.WebDescriptor; +import org.eclipse.jetty.plus.annotation.LifeCycleCallbackCollection; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; +import org.eclipse.jetty.util.DecoratedObjectFactory; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.util.resource.ResourceFactory; +import org.eclipse.jetty.xml.XmlParser; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@ExtendWith(WorkDirExtension.class) +public class TestAnnotationDecorator +{ + public static class TestWebDescriptor extends WebDescriptor + { + public TestWebDescriptor(Resource xml, Boolean metadataComplete) + { + super(xml); + _metaDataComplete = metadataComplete; + } + + @Override + public void parse(XmlParser parser) throws Exception + { + } + + @Override + public void processVersion() + { + } + + @Override + public void processOrdering() + { + } + + @Override + public void processDistributable() + { + } + + @Override + public int getMajorVersion() + { + return 4; + } + + @Override + public int getMinorVersion() + { + return 0; + } + } + + @Test + public void testAnnotationDecorator(WorkDir workDir) throws Exception + { + Path docroot = workDir.getEmptyPathDir(); + Path dummyDescriptor = docroot.resolve("dummy.xml"); + Files.createFile(dummyDescriptor); + Resource dummyResource = ResourceFactory.root().newResource(dummyDescriptor); + + assertThrows(NullPointerException.class, () -> new AnnotationDecorator(null)); + + WebAppContext context = new WebAppContext(); + AnnotationDecorator decorator = new AnnotationDecorator(context); + ServletE servlet = new ServletE(); + //test without BaseHolder metadata + decorator.decorate(servlet); + LifeCycleCallbackCollection callbacks = (LifeCycleCallbackCollection)context.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION); + assertNotNull(callbacks); + assertFalse(callbacks.getPreDestroyCallbacks().isEmpty()); + + //reset + context.removeAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION); + + //test with BaseHolder metadata, should not introspect with metadata-complete==true + context.getMetaData().setWebDescriptor(new TestWebDescriptor(dummyResource, true)); + assertTrue(context.getMetaData().isMetaDataComplete()); + ServletHolder holder = new ServletHolder(new Source(Source.Origin.DESCRIPTOR)); + holder.setHeldClass(ServletE.class); + context.getServletHandler().addServlet(holder); + DecoratedObjectFactory.associateInfo(holder); + decorator = new AnnotationDecorator(context); + decorator.decorate(servlet); + DecoratedObjectFactory.disassociateInfo(); + callbacks = (LifeCycleCallbackCollection)context.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION); + assertNull(callbacks); + + //reset + context.removeAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION); + + //test with BaseHolder metadata, should introspect with metadata-complete==false + context.getMetaData().setWebDescriptor(new TestWebDescriptor(dummyResource, false)); + DecoratedObjectFactory.associateInfo(holder); + decorator = new AnnotationDecorator(context); + decorator.decorate(servlet); + DecoratedObjectFactory.disassociateInfo(); + callbacks = (LifeCycleCallbackCollection)context.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION); + assertNotNull(callbacks); + assertFalse(callbacks.getPreDestroyCallbacks().isEmpty()); + } +} diff --git a/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/TestAnnotationIntrospector.java b/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/TestAnnotationIntrospector.java new file mode 100644 index 00000000000..a368bf002f5 --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/TestAnnotationIntrospector.java @@ -0,0 +1,95 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.annotations; + +import java.nio.file.Path; + +import org.eclipse.jetty.ee11.servlet.ServletHolder; +import org.eclipse.jetty.ee11.servlet.Source; +import org.eclipse.jetty.ee11.webapp.FragmentDescriptor; +import org.eclipse.jetty.ee11.webapp.WebAppContext; +import org.eclipse.jetty.ee11.webapp.WebDescriptor; +import org.eclipse.jetty.logging.StacklessLogging; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.util.resource.Resource; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class TestAnnotationIntrospector +{ + @Test + public void testIsIntrospectable() throws Exception + { + try (StacklessLogging ignore = new StacklessLogging(AnnotationIntrospector.class)) + { + WebAppContext wac = new WebAppContext(); + AnnotationIntrospector introspector = new AnnotationIntrospector(wac); + //can't introspect nothing + assertFalse(introspector.isIntrospectable(null, null)); + + //can introspect if no metadata to say otherwise + assertTrue(introspector.isIntrospectable(new Object(), null)); + + //can introspect if metdata isn't a BaseHolder + assertTrue(introspector.isIntrospectable(new Object(), new Object())); + + //an EMBEDDED sourced servlet can be introspected + ServletHolder holder = new ServletHolder(); + holder.setHeldClass(ServletE.class); + assertTrue(introspector.isIntrospectable(new ServletE(), holder)); + + //a JAKARTA API sourced servlet can be introspected + holder = new ServletHolder(Source.JAKARTA_API); + holder.setHeldClass(ServletE.class); + assertTrue(introspector.isIntrospectable(new ServletE(), holder)); + + //an ANNOTATION sourced servlet can be introspected + holder = new ServletHolder(new Source(Source.Origin.ANNOTATION, ServletE.class)); + holder.setHeldClass(ServletE.class); + assertTrue(introspector.isIntrospectable(new ServletE(), holder)); + + //a DESCRIPTOR sourced servlet can be introspected if web.xml metdata-complete==false + Path xml = MavenTestingUtils.getTestResourcePathFile("web31false.xml"); + Resource xmlResource = wac.getResourceFactory().newResource(xml); + wac.getMetaData().setWebDescriptor(new WebDescriptor(xmlResource)); + holder = new ServletHolder(new Source(Source.Origin.DESCRIPTOR, xmlResource)); + assertTrue(introspector.isIntrospectable(new ServletE(), holder)); + + //a DESCRIPTOR sourced servlet can be introspected if web-fragment.xml medata-complete==false && web.xml metadata-complete==false + xml = MavenTestingUtils.getTestResourcePathFile("web-fragment4false.xml"); + xmlResource = wac.getResourceFactory().newResource(xml); + Resource parent = wac.getResourceFactory().newResource(xml.getParent()); + wac.getMetaData().addFragmentDescriptor(parent, new FragmentDescriptor(xmlResource)); + holder = new ServletHolder(new Source(Source.Origin.DESCRIPTOR, xmlResource)); + assertTrue(introspector.isIntrospectable(new ServletE(), holder)); + + //a DESCRIPTOR sourced servlet cannot be introspected if web-fragment.xml medata-complete==true (&& web.xml metadata-complete==false) + xml = MavenTestingUtils.getTestResourcePathFile("web-fragment4true.xml"); + xmlResource = wac.getResourceFactory().newResource(xml); + parent = wac.getResourceFactory().newResource(xml.getParent()); + wac.getMetaData().addFragmentDescriptor(parent, new FragmentDescriptor(xmlResource)); + holder = new ServletHolder(new Source(Source.Origin.DESCRIPTOR, xmlResource)); + assertFalse(introspector.isIntrospectable(new ServletE(), holder)); + + //a DESCRIPTOR sourced servlet cannot be introspected if web.xml medata-complete==true + xml = MavenTestingUtils.getTestResourcePathFile("web31true.xml"); + xmlResource = wac.getResourceFactory().newResource(xml); + wac.getMetaData().setWebDescriptor(new WebDescriptor(xmlResource)); + holder = new ServletHolder(new Source(Source.Origin.DESCRIPTOR, xmlResource)); + assertFalse(introspector.isIntrospectable(new ServletE(), holder)); + } + } +} diff --git a/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/TestAnnotationParser.java b/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/TestAnnotationParser.java new file mode 100644 index 00000000000..3df623a98f5 --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/TestAnnotationParser.java @@ -0,0 +1,333 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.annotations; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; + +import org.eclipse.jetty.toolchain.test.FS; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; +import org.eclipse.jetty.util.TypeUtil; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.util.resource.ResourceFactory; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.in; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@ExtendWith(WorkDirExtension.class) +public class TestAnnotationParser +{ + public static class TrackingAnnotationHandler extends AnnotationParser.AbstractHandler + { + private final String annotationName; + public final Set foundClasses; + + public TrackingAnnotationHandler(String annotationName) + { + this.annotationName = annotationName; + this.foundClasses = new HashSet<>(); + } + + @Override + public void handle(AnnotationParser.ClassInfo info, String annotation) + { + if (!annotationName.equals(annotation)) + return; + foundClasses.add(info.getClassName()); + } + } + + public static class DuplicateClassScanHandler extends AnnotationParser.AbstractHandler + { + private Map> _classMap = new ConcurrentHashMap<>(); + + @Override + public void handle(AnnotationParser.ClassInfo info) + { + List list = new CopyOnWriteArrayList<>(); + Resource r = info.getContainingResource(); + list.add((r == null ? "" : r.toString())); + + List existing = _classMap.putIfAbsent(info.getClassName(), list); + if (existing != null) + { + existing.addAll(list); + } + } + + public List getParsedList(String classname) + { + return _classMap.get(classname); + } + } + + @Test + public void testSampleAnnotation(WorkDir workDir) throws Exception + { + Path root = workDir.getEmptyPathDir(); + copyClass(ClassA.class, root); + + AnnotationParser parser = new AnnotationParser(); + + class SampleAnnotationHandler extends AnnotationParser.AbstractHandler + { + private List methods = Arrays.asList("a", "b", "c", "d", "l"); + + @Override + public void handle(AnnotationParser.ClassInfo info, String annotation) + { + if (!Sample.class.getName().equals(annotation)) + return; + + assertEquals(ClassA.class.getName(), info.getClassName()); + } + + @Override + public void handle(AnnotationParser.FieldInfo info, String annotation) + { + if (!Sample.class.getName().equals(annotation)) + return; + assertEquals("m", info.getFieldName()); + assertEquals(org.objectweb.asm.Type.OBJECT, org.objectweb.asm.Type.getType(info.getFieldType()).getSort()); + } + + @Override + public void handle(AnnotationParser.MethodInfo info, String annotation) + { + if (!Sample.class.getName().equals(annotation)) + return; + assertEquals(ClassA.class.getName(), info.getClassInfo().getClassName()); + assertThat(info.getMethodName(), is(in(methods))); + assertEquals(Sample.class.getName(), annotation); + } + } + + try (ResourceFactory.Closeable resourceFactory = ResourceFactory.closeable()) + { + parser.parse(Collections.singleton(new SampleAnnotationHandler()), resourceFactory.newResource(root)); + } + } + + @Test + public void testMultiAnnotation(WorkDir workDir) throws Exception + { + Path root = workDir.getEmptyPathDir(); + copyClass(ClassB.class, root); + AnnotationParser parser = new AnnotationParser(); + + class MultiAnnotationHandler extends AnnotationParser.AbstractHandler + { + @Override + public void handle(AnnotationParser.ClassInfo info, String annotation) + { + if (!Multi.class.getName().equals(annotation)) + return; + assertEquals(ClassB.class.getName(), info.getClassName()); + } + + @Override + public void handle(AnnotationParser.FieldInfo info, String annotation) + { + assertNotEquals(Multi.class.getName(), annotation, "There should not be any"); + } + + @Override + public void handle(AnnotationParser.MethodInfo info, String annotation) + { + if (!Multi.class.getName().equals(annotation)) + return; + assertEquals(ClassB.class.getName(), info.getClassInfo().getClassName()); + assertEquals("a", info.getMethodName()); + } + } + + try (ResourceFactory.Closeable resourceFactory = ResourceFactory.closeable()) + { + parser.parse(Collections.singleton(new MultiAnnotationHandler()), resourceFactory.newResource(root)); + } + } + + @Test + public void testHiddenFilesInJar() throws Exception + { + Path badClassesJar = MavenTestingUtils.getTestResourcePathFile("bad-classes.jar"); + AnnotationParser parser = new AnnotationParser(); + Set emptySet = Collections.emptySet(); + + try (ResourceFactory.Closeable resourceFactory = ResourceFactory.closeable()) + { + parser.parse(emptySet, resourceFactory.newResource(badClassesJar)); + // only the valid classes inside bad-classes.jar should be parsed. If any invalid classes are parsed and exception would be thrown here + } + } + + @Test + public void testModuleInfoClassInJar() throws Exception + { + Path badClassesJar = MavenTestingUtils.getTestResourcePathFile("jdk9/slf4j-api-1.8.0-alpha2.jar"); + AnnotationParser parser = new AnnotationParser(); + Set emptySet = Collections.emptySet(); + + try (ResourceFactory.Closeable resourceFactory = ResourceFactory.closeable()) + { + parser.parse(emptySet, resourceFactory.newResource(badClassesJar)); + // Should throw no exceptions, and happily skip the module-info.class files + } + } + + @Test + public void testJep238MultiReleaseInJar() throws Exception + { + Path badClassesJar = MavenTestingUtils.getTestResourcePathFile("jdk9/log4j-api-2.9.0.jar"); + AnnotationParser parser = new AnnotationParser(); + try (ResourceFactory.Closeable resourceFactory = ResourceFactory.closeable()) + { + // Should throw no exceptions and work with the META-INF/versions without incident + parser.parse(Collections.emptySet(), resourceFactory.newResource(badClassesJar)); + + //check for a class that is only in versions 9 + Map parsed = parser.getParsedClassNames(); + URI processIdUtilURI = parsed.get("org.apache.logging.log4j.util.ProcessIdUtil"); + assertNotNull(processIdUtilURI); + if (Runtime.version().feature() > 17) + assertThat(processIdUtilURI.toString(), containsString("META-INF/versions/9")); + } + } + + @Test + public void testJep238MultiReleaseInJarJDK10() throws Exception + { + Path jdk10Jar = MavenTestingUtils.getTestResourcePathFile("jdk10/multirelease-10.jar"); + AnnotationParser parser = new AnnotationParser(); + + try (ResourceFactory.Closeable resourceFactory = ResourceFactory.closeable()) + { + // Should throw no exceptions + parser.parse(Collections.emptySet(), resourceFactory.newResource(jdk10Jar)); + + Map parsed = parser.getParsedClassNames(); + assertEquals(3, parsed.size()); + assertThat(parsed.keySet(), containsInAnyOrder("hello.DetailedVer", "hello.Greetings", "hello.Hello")); + if (Runtime.version().feature() > 17) + assertThat(parsed.get("hello.Greetings").toString(), containsString("META-INF/versions/10")); + } + } + + @Test + public void testBasedirExclusion(WorkDir workDir) throws Exception + { + Path testdir = workDir.getEmptyPathDir(); + // Build up basedir, which itself has a path segment that violates java package and classnaming. + // The basedir should have no effect on annotation scanning. + // Intentionally using a base directory name that starts with a "." + // This mimics what you see in jenkins, hudson, hadoop, solr, camel, and selenium for their + // installed and/or managed webapps + Path basedir = testdir.resolve(".base/workspace/classes"); + FS.ensureEmpty(basedir); + + // Copy in class that is known to have annotations. + copyClass(ClassA.class, basedir); + + // Setup Tracker + TrackingAnnotationHandler tracker = new TrackingAnnotationHandler(Sample.class.getName()); + + // Setup annotation scanning + AnnotationParser parser = new AnnotationParser(); + + // Parse + try (ResourceFactory.Closeable resourceFactory = ResourceFactory.closeable()) + { + parser.parse(Collections.singleton(tracker), resourceFactory.newResource(basedir)); + } + + // Validate + assertThat("Found Class", tracker.foundClasses, contains(ClassA.class.getName())); + } + + @Test + public void testScanDuplicateClassesInJars() throws Exception + { + try (ResourceFactory.Closeable resourceFactory = ResourceFactory.closeable()) + { + Resource testJar = resourceFactory.newResource(MavenTestingUtils.getTargetPath("test-classes/tinytest.jar")); + Resource testJar2 = resourceFactory.newResource(MavenTestingUtils.getTargetPath("test-classes/tinytest_copy.jar")); + AnnotationParser parser = new AnnotationParser(); + DuplicateClassScanHandler handler = new DuplicateClassScanHandler(); + Set handlers = Collections.singleton(handler); + parser.parse(handlers, testJar); + parser.parse(handlers, testJar2); + List locations = handler.getParsedList("org.acme.ClassOne"); + assertNotNull(locations); + assertEquals(2, locations.size()); + assertNotEquals(locations.get(0), locations.get(1)); + } + } + + @Test + public void testScanDuplicateClasses() throws Exception + { + try (ResourceFactory.Closeable resourceFactory = ResourceFactory.closeable()) + { + Resource testJar = resourceFactory.newResource(MavenTestingUtils.getTargetFile("test-classes/tinytest.jar").toPath()); + File testClasses = new File(MavenTestingUtils.getTargetDir(), "test-classes"); + AnnotationParser parser = new AnnotationParser(); + DuplicateClassScanHandler handler = new DuplicateClassScanHandler(); + Set handlers = Collections.singleton(handler); + parser.parse(handlers, testJar); + parser.parse(handlers, resourceFactory.newResource(testClasses.toPath())); + List locations = handler.getParsedList("org.acme.ClassOne"); + assertNotNull(locations); + assertEquals(2, locations.size()); + assertNotEquals(locations.get(0), locations.get(1)); + } + } + + private void copyClass(Class clazz, Path outputDir) throws IOException, URISyntaxException + { + String classRef = TypeUtil.toClassReference(clazz); + URL url = this.getClass().getResource('/' + classRef); + assertThat("URL for: " + classRef, url, notNullValue()); + + Path srcClass = Paths.get(url.toURI()); + Path dest = outputDir.resolve(classRef); + FS.ensureDirExists(dest.getParent()); + Files.copy(srcClass, dest); + } +} diff --git a/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/TestDiscoveredServletContainerInitializerHolder.java b/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/TestDiscoveredServletContainerInitializerHolder.java new file mode 100644 index 00000000000..34960d01bf2 --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/TestDiscoveredServletContainerInitializerHolder.java @@ -0,0 +1,98 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.annotations; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import jakarta.servlet.ServletContainerInitializer; +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.HandlesTypes; +import org.eclipse.jetty.ee11.servlet.Source; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; + +public class TestDiscoveredServletContainerInitializerHolder +{ + /** + * A marker type that is passed as an arg to @HandlesTypes + */ + interface Ordinary + { + + } + + /** + * An class with an annotation (that is listed in @HandlesTypes) + */ + @Sample(value = 1) + public static class ASample + { + } + + /** + * A class that extends a class with an annotation + */ + public static class BSample extends ASample + { + } + + @HandlesTypes({Sample.class}) + public static class SampleServletContainerInitializer implements ServletContainerInitializer + { + @Override + public void onStartup(Set> c, ServletContext ctx) throws ServletException + { + } + } + + @Test + public void test() throws Exception + { + //SCI with @HandlesTypes[Ordinary, Sample] + SampleServletContainerInitializer sci = new SampleServletContainerInitializer(); + + AnnotationConfiguration.DiscoveredServletContainerInitializerHolder holder = + new AnnotationConfiguration.DiscoveredServletContainerInitializerHolder(new Source(Source.Origin.ANNOTATION, sci.getClass()), + sci); + + //add the @HandlesTypes to the holder + holder.addStartupClasses(Ordinary.class, Sample.class); + + //pretend scanned and discovered that ASample has the Sample annotation + holder.addStartupClasses(ASample.class.getName()); + + //pretend we scanned the entire class hierarchy and found: + // com.acme.tom and com.acme.dick both extend Ordinary + // ASample has subclass BSample + Map> classMap = new HashMap<>(); + classMap.put(Ordinary.class.getName(), new HashSet(Arrays.asList("com.acme.tom", "com.acme.dick"))); + classMap.put(ASample.class.getName(), new HashSet(Arrays.asList(BSample.class.getName()))); + holder.resolveClasses(classMap); + + //we should now have the following classes that will be passed to the SampleServletContainerInitializer.onStartup + String toString = holder.toString(); + assertThat(toString, containsString("com.acme.tom")); + assertThat(toString, containsString("com.acme.dick")); + assertThat(toString, containsString(ASample.class.getName())); + assertThat(toString, containsString(BSample.class.getName())); + assertThat(toString, containsString("applicable=[],annotated=[]")); + } +} diff --git a/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/TestRunAsAnnotation.java b/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/TestRunAsAnnotation.java new file mode 100644 index 00000000000..5b13374f57a --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/TestRunAsAnnotation.java @@ -0,0 +1,65 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.annotations; + +import java.nio.file.Files; +import java.nio.file.Path; + +import org.eclipse.jetty.ee11.servlet.ServletHolder; +import org.eclipse.jetty.ee11.webapp.WebAppContext; +import org.eclipse.jetty.ee11.webapp.WebDescriptor; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +@ExtendWith(WorkDirExtension.class) +public class TestRunAsAnnotation +{ + + @Test + public void testRunAsAnnotation(WorkDir workDir) throws Exception + { + Path tmpPath = workDir.getEmptyPathDir(); + WebAppContext wac = new WebAppContext(); + + //pre-add a servlet but not by descriptor + ServletHolder holder = new ServletHolder(); + holder.setName("foo1"); + holder.setHeldClass(ServletC.class); + holder.setInitOrder(1); //load on startup + wac.getServletHandler().addServletWithMapping(holder, "/foo/*"); + + //add another servlet of the same class, but as if by descriptor + ServletHolder holder2 = new ServletHolder(); + holder2.setName("foo2"); + holder2.setHeldClass(ServletC.class); + holder2.setInitOrder(1); + wac.getServletHandler().addServletWithMapping(holder2, "/foo2/*"); + Path fakeXml = tmpPath.resolve("fake.xml"); + Files.createFile(fakeXml); + wac.getMetaData().setOrigin(holder2.getName() + ".servlet.run-as", new WebDescriptor(wac.getResourceFactory().newResource(fakeXml))); + + AnnotationIntrospector parser = new AnnotationIntrospector(wac); + RunAsAnnotationHandler handler = new RunAsAnnotationHandler(wac); + parser.registerHandler(handler); + parser.introspect(new ServletC(), null); + + assertEquals("admin", holder.getRunAsRole()); + assertNull(holder2.getRunAsRole()); + } +} diff --git a/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/TestSecurityAnnotationConversions.java b/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/TestSecurityAnnotationConversions.java new file mode 100644 index 00000000000..a027bf11c70 --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/TestSecurityAnnotationConversions.java @@ -0,0 +1,340 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.annotations; + +import java.util.List; + +import jakarta.servlet.annotation.HttpConstraint; +import jakarta.servlet.annotation.HttpMethodConstraint; +import jakarta.servlet.annotation.ServletSecurity; +import jakarta.servlet.annotation.ServletSecurity.EmptyRoleSemantic; +import jakarta.servlet.annotation.ServletSecurity.TransportGuarantee; +import jakarta.servlet.http.HttpServlet; +import org.eclipse.jetty.ee11.servlet.ServletHolder; +import org.eclipse.jetty.ee11.servlet.ServletMapping; +import org.eclipse.jetty.ee11.servlet.security.ConstraintAware; +import org.eclipse.jetty.ee11.servlet.security.ConstraintMapping; +import org.eclipse.jetty.ee11.servlet.security.ConstraintSecurityHandler; +import org.eclipse.jetty.ee11.webapp.WebAppContext; +import org.eclipse.jetty.security.Constraint; +import org.eclipse.jetty.security.Constraint.Transport; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.fail; + +public class TestSecurityAnnotationConversions +{ + @ServletSecurity(value = @HttpConstraint(value = EmptyRoleSemantic.DENY)) + public static class DenyServlet extends HttpServlet + { + } + + @ServletSecurity + public static class PermitServlet extends HttpServlet + { + } + + @ServletSecurity(value = @HttpConstraint(value = EmptyRoleSemantic.PERMIT, transportGuarantee = TransportGuarantee.CONFIDENTIAL, rolesAllowed = + { + "tom", "dick", "harry" + })) + public static class RolesServlet extends HttpServlet + { + } + + @ServletSecurity(value = @HttpConstraint(value = EmptyRoleSemantic.PERMIT, transportGuarantee = TransportGuarantee.CONFIDENTIAL, rolesAllowed = + { + "tom", "dick", "harry" + }), httpMethodConstraints = {@HttpMethodConstraint(value = "GET")}) + public static class Method1Servlet extends HttpServlet + { + } + + @ServletSecurity( + value = @HttpConstraint( + value = EmptyRoleSemantic.PERMIT, + transportGuarantee = TransportGuarantee.CONFIDENTIAL, + rolesAllowed = { + "tom", "dick", "harry" + }), + httpMethodConstraints = { + @HttpMethodConstraint(value = "GET", transportGuarantee = TransportGuarantee.CONFIDENTIAL) + }) + public static class Method2Servlet extends HttpServlet + { + } + + public void setUp() + { + } + + @Test + public void testDenyAllOnClass() throws Exception + { + WebAppContext wac = makeWebAppContext(DenyServlet.class.getCanonicalName(), "denyServlet", new String[]{ + "/foo/*", "*.foo" + }); + + //Assume we found 1 servlet with a @HttpConstraint with value=EmptyRoleSemantic.DENY security annotation + ServletSecurityAnnotationHandler annotationHandler = new ServletSecurityAnnotationHandler(wac); + AnnotationIntrospector introspector = new AnnotationIntrospector(wac); + introspector.registerHandler(annotationHandler); + + //set up the expected outcomes: + //1 ConstraintMapping per ServletMapping pathSpec + Constraint expectedConstraint = new Constraint.Builder() + .authorization(Constraint.Authorization.FORBIDDEN) + .transport(Transport.ANY) + .build(); + + ConstraintMapping[] expectedMappings = new ConstraintMapping[2]; + + expectedMappings[0] = new ConstraintMapping(); + expectedMappings[0].setConstraint(expectedConstraint); + expectedMappings[0].setPathSpec("/foo/*"); + + expectedMappings[1] = new ConstraintMapping(); + expectedMappings[1].setConstraint(expectedConstraint); + expectedMappings[1].setPathSpec("*.foo"); + + introspector.introspect(new DenyServlet(), null); + + compareResults(expectedMappings, ((ConstraintAware)wac.getSecurityHandler()).getConstraintMappings()); + } + + @Test + public void testPermitAll() throws Exception + { + //Assume we found 1 servlet with a @ServletSecurity security annotation + WebAppContext wac = makeWebAppContext(PermitServlet.class.getCanonicalName(), "permitServlet", new String[]{ + "/foo/*", "*.foo" + }); + + ServletSecurityAnnotationHandler annotationHandler = new ServletSecurityAnnotationHandler(wac); + AnnotationIntrospector introspector = new AnnotationIntrospector(wac); + introspector.registerHandler(annotationHandler); + + //set up the expected outcomes - no constraints at all as per Servlet Spec 3.1 pg 129 + //1 ConstraintMapping per ServletMapping pathSpec + + ConstraintMapping[] expectedMappings = new ConstraintMapping[]{}; + PermitServlet permit = new PermitServlet(); + introspector.introspect(permit, null); + + compareResults(expectedMappings, ((ConstraintAware)wac.getSecurityHandler()).getConstraintMappings()); + } + + @Test + public void testRolesAllowedWithTransportGuarantee() throws Exception + { + //Assume we found 1 servlet with annotation with roles defined and + //and a TransportGuarantee + + WebAppContext wac = makeWebAppContext(RolesServlet.class.getCanonicalName(), "rolesServlet", new String[]{ + "/foo/*", "*.foo" + }); + + ServletSecurityAnnotationHandler annotationHandler = new ServletSecurityAnnotationHandler(wac); + AnnotationIntrospector introspector = new AnnotationIntrospector(wac); + introspector.registerHandler(annotationHandler); + + //set up the expected outcomes:compareResults + //1 ConstraintMapping per ServletMapping + Constraint expectedConstraint = new Constraint.Builder() + .roles("tom", "dick", "harry") + .transport(Transport.SECURE) + .build(); + + ConstraintMapping[] expectedMappings = new ConstraintMapping[2]; + expectedMappings[0] = new ConstraintMapping(); + expectedMappings[0].setConstraint(expectedConstraint); + expectedMappings[0].setPathSpec("/foo/*"); + + expectedMappings[1] = new ConstraintMapping(); + expectedMappings[1].setConstraint(expectedConstraint); + expectedMappings[1].setPathSpec("*.foo"); + introspector.introspect(new RolesServlet(), null); + compareResults(expectedMappings, ((ConstraintAware)wac.getSecurityHandler()).getConstraintMappings()); + } + + @Test + public void testMethodAnnotation() throws Exception + { + //ServletSecurity annotation with HttpConstraint of TransportGuarantee.CONFIDENTIAL, and a list of rolesAllowed, and + //an HttpMethodConstraint for GET method that permits all and has TransportGuarantee.NONE (ie is default) + + WebAppContext wac = makeWebAppContext(Method1Servlet.class.getCanonicalName(), "method1Servlet", new String[]{ + "/foo/*", "*.foo" + }); + + //set up the expected outcomes: - a Constraint for the RolesAllowed on the class + //with userdata constraint of DC_CONFIDENTIAL + //and mappings for each of the pathSpecs + Constraint expectedConstraint1 = new Constraint.Builder() + .roles("tom", "dick", "harry") + .transport(Transport.SECURE) + .build(); + + //a Constraint for the PermitAll on the doGet method + Constraint expectedConstraint2 = Constraint.ALLOWED_ANY_TRANSPORT; + + ConstraintMapping[] expectedMappings = new ConstraintMapping[4]; + expectedMappings[0] = new ConstraintMapping(); + expectedMappings[0].setConstraint(expectedConstraint1); + expectedMappings[0].setPathSpec("/foo/*"); + expectedMappings[0].setMethodOmissions(new String[]{"GET"}); + expectedMappings[1] = new ConstraintMapping(); + expectedMappings[1].setConstraint(expectedConstraint1); + expectedMappings[1].setPathSpec("*.foo"); + expectedMappings[1].setMethodOmissions(new String[]{"GET"}); + + expectedMappings[2] = new ConstraintMapping(); + expectedMappings[2].setConstraint(expectedConstraint2); + expectedMappings[2].setPathSpec("/foo/*"); + expectedMappings[2].setMethod("GET"); + expectedMappings[3] = new ConstraintMapping(); + expectedMappings[3].setConstraint(expectedConstraint2); + expectedMappings[3].setPathSpec("*.foo"); + expectedMappings[3].setMethod("GET"); + + AnnotationIntrospector introspector = new AnnotationIntrospector(wac); + ServletSecurityAnnotationHandler annotationHandler = new ServletSecurityAnnotationHandler(wac); + introspector.registerHandler(annotationHandler); + introspector.introspect(new Method1Servlet(), null); + compareResults(expectedMappings, ((ConstraintAware)wac.getSecurityHandler()).getConstraintMappings()); + } + + @Test + public void testMethodAnnotation2() throws Exception + { + //A ServletSecurity annotation that has HttpConstraint of CONFIDENTIAL with defined roles, but a + //HttpMethodConstraint for GET that permits all, but also requires CONFIDENTIAL + WebAppContext wac = makeWebAppContext(Method2Servlet.class.getCanonicalName(), "method2Servlet", new String[]{ + "/foo/*", "*.foo" + }); + + AnnotationIntrospector introspector = new AnnotationIntrospector(wac); + ServletSecurityAnnotationHandler annotationHandler = new ServletSecurityAnnotationHandler(wac); + introspector.registerHandler(annotationHandler); + + //set up the expected outcomes: - a Constraint for the RolesAllowed on the class + //with userdata constraint of DC_CONFIDENTIAL + //and mappings for each of the pathSpecs + Constraint expectedConstraint1 = new Constraint.Builder() + .roles("tom", "dick", "harry") + .transport(Transport.SECURE).build(); + + //a Constraint for the Permit on the GET method with a userdata + //constraint of DC_CONFIDENTIAL + Constraint expectedConstraint2 = new Constraint.Builder() + .authorization(Constraint.Authorization.ALLOWED) + .transport(Transport.SECURE) + .build(); + + ConstraintMapping[] expectedMappings = new ConstraintMapping[4]; + expectedMappings[0] = new ConstraintMapping(); + expectedMappings[0].setConstraint(expectedConstraint1); + expectedMappings[0].setPathSpec("/foo/*"); + expectedMappings[0].setMethodOmissions(new String[]{"GET"}); + expectedMappings[1] = new ConstraintMapping(); + expectedMappings[1].setConstraint(expectedConstraint1); + expectedMappings[1].setPathSpec("*.foo"); + expectedMappings[1].setMethodOmissions(new String[]{"GET"}); + + expectedMappings[2] = new ConstraintMapping(); + expectedMappings[2].setConstraint(expectedConstraint2); + expectedMappings[2].setPathSpec("/foo/*"); + expectedMappings[2].setMethod("GET"); + expectedMappings[3] = new ConstraintMapping(); + expectedMappings[3].setConstraint(expectedConstraint2); + expectedMappings[3].setPathSpec("*.foo"); + expectedMappings[3].setMethod("GET"); + + introspector.introspect(new Method2Servlet(), null); + compareResults(expectedMappings, ((ConstraintAware)wac.getSecurityHandler()).getConstraintMappings()); + } + + private void compareResults(ConstraintMapping[] expectedMappings, List actualMappings) + { + assertNotNull(actualMappings); + assertEquals(expectedMappings.length, actualMappings.size()); + + for (ConstraintMapping am : actualMappings) + { + boolean matched = false; + + for (int i = 0; i < expectedMappings.length && !matched; i++) + { + ConstraintMapping em = expectedMappings[i]; + if (em.getPathSpec().equals(am.getPathSpec())) + { + if ((em.getMethod() == null && am.getMethod() == null) || em.getMethod() != null && em.getMethod().equals(am.getMethod())) + { + matched = true; + + assertEquals(em.getConstraint().getAuthorization(), am.getConstraint().getAuthorization()); + assertEquals(em.getConstraint().getTransport(), am.getConstraint().getTransport()); + if (em.getMethodOmissions() == null) + { + assertNull(am.getMethodOmissions()); + } + else + { + assertArrayEquals(am.getMethodOmissions(), em.getMethodOmissions()); + } + + if (em.getConstraint().getRoles() == null) + { + assertNull(am.getConstraint().getRoles()); + } + else + { + assertThat(am.getConstraint().getRoles(), Matchers.equalTo(em.getConstraint().getRoles())); + } + } + } + } + + if (!matched) + fail("No expected ConstraintMapping matching method:" + am.getMethod() + " pathSpec: " + am.getPathSpec()); + } + } + + private WebAppContext makeWebAppContext(String className, String servletName, String[] paths) + { + WebAppContext wac = new WebAppContext(); + + ServletHolder[] holders = new ServletHolder[1]; + holders[0] = new ServletHolder(); + holders[0].setClassName(className); + holders[0].setName(servletName); + holders[0].setServletHandler(wac.getServletHandler()); + wac.getServletHandler().setServlets(holders); + wac.setSecurityHandler(new ConstraintSecurityHandler()); + + ServletMapping[] servletMappings = new ServletMapping[1]; + servletMappings[0] = new ServletMapping(); + + servletMappings[0].setPathSpecs(paths); + servletMappings[0].setServletName(servletName); + wac.getServletHandler().setServletMappings(servletMappings); + return wac; + } +} diff --git a/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/TestServletAnnotations.java b/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/TestServletAnnotations.java new file mode 100644 index 00000000000..63678d12f8a --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/TestServletAnnotations.java @@ -0,0 +1,318 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.annotations; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import org.eclipse.jetty.ee11.servlet.ServletHolder; +import org.eclipse.jetty.ee11.servlet.ServletMapping; +import org.eclipse.jetty.ee11.servlet.security.ConstraintSecurityHandler; +import org.eclipse.jetty.ee11.webapp.DiscoveredAnnotation; +import org.eclipse.jetty.ee11.webapp.WebAppContext; +import org.eclipse.jetty.toolchain.test.FS; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; +import org.eclipse.jetty.util.TypeUtil; +import org.eclipse.jetty.util.resource.ResourceFactory; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.anyOf; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +/** + * TestServletAnnotations + */ +@ExtendWith(WorkDirExtension.class) +public class TestServletAnnotations +{ + public class TestWebServletAnnotationHandler extends WebServletAnnotationHandler + { + List _list = null; + + public TestWebServletAnnotationHandler(WebAppContext context, List list) + { + super(context); + _list = list; + } + + @Override + public void addAnnotation(DiscoveredAnnotation a) + { + super.addAnnotation(a); + _list.add(a); + } + } + + @Test + public void testServletAnnotation(WorkDir workDir) throws Exception + { + Path root = workDir.getEmptyPathDir(); + copyClass(org.eclipse.jetty.ee11.annotations.ServletC.class, root); + + AnnotationParser parser = new AnnotationParser(); + + WebAppContext wac = new WebAppContext(); + List results = new ArrayList(); + + TestWebServletAnnotationHandler handler = new TestWebServletAnnotationHandler(wac, results); + + try (ResourceFactory.Closeable resourceFactory = ResourceFactory.closeable()) + { + parser.parse(Collections.singleton(handler), resourceFactory.newResource(root)); + } + + assertEquals(1, results.size()); + assertTrue(results.get(0) instanceof WebServletAnnotation); + + results.get(0).apply(); + + ServletHolder[] holders = wac.getServletHandler().getServlets(); + assertNotNull(holders); + assertEquals(1, holders.length); + + // Verify servlet annotations + ServletHolder cholder = holders[0]; + assertThat("Servlet Name", cholder.getName(), is("CServlet")); + assertThat("InitParameter[x]", cholder.getInitParameter("x"), is("y")); + assertThat("Init Order", cholder.getInitOrder(), is(2)); + assertThat("Async Supported", cholder.isAsyncSupported(), is(false)); + + // Verify mappings + ServletMapping[] mappings = wac.getServletHandler().getServletMappings(); + assertNotNull(mappings); + assertEquals(1, mappings.length); + String[] paths = mappings[0].getPathSpecs(); + assertNotNull(paths); + assertEquals(2, paths.length); + } + + @Test + public void testWebServletAnnotationOverrideDefault() + { + //if the existing servlet mapping TO A DIFFERENT SERVLET IS from a default descriptor we + //DO allow the annotation to replace the mapping. + + WebAppContext wac = new WebAppContext(); + ServletHolder defaultServlet = new ServletHolder(); + defaultServlet.setClassName("org.eclipse.jetty.ee11.servlet.DefaultServlet"); + defaultServlet.setName("default"); + wac.getServletHandler().addServlet(defaultServlet); + + ServletMapping m = new ServletMapping(); + m.setPathSpec("/"); + m.setServletName("default"); + m.setFromDefaultDescriptor(true); //this mapping will be from a default descriptor + wac.getServletHandler().addServletMapping(m); + + WebServletAnnotation annotation = new WebServletAnnotation(wac, "org.eclipse.jetty.ee11.annotations.ServletD", null); + annotation.apply(); + + //test that as the original servlet mapping had only 1 pathspec, then the whole + //servlet mapping should be deleted as that pathspec will be remapped to the DServlet + ServletMapping[] resultMappings = wac.getServletHandler().getServletMappings(); + assertNotNull(resultMappings); + assertEquals(1, resultMappings.length); + assertEquals(2, resultMappings[0].getPathSpecs().length); + resultMappings[0].getServletName().equals("DServlet"); + for (String s : resultMappings[0].getPathSpecs()) + { + assertThat(s, anyOf(is("/"), is("/bah/*"))); + } + } + + @Test + public void testWebServletAnnotationReplaceDefault() + { + //if the existing servlet mapping TO A DIFFERENT SERVLET IS from a default descriptor we + //DO allow the annotation to replace the mapping. + WebAppContext wac = new WebAppContext(); + ServletHolder defaultServlet = new ServletHolder(); + defaultServlet.setClassName("org.eclipse.jetty.ee11.servlet.DefaultServlet"); + defaultServlet.setName("default"); + wac.getServletHandler().addServlet(defaultServlet); + + ServletMapping m = new ServletMapping(); + m.setPathSpec("/"); + m.setServletName("default"); + m.setFromDefaultDescriptor(true); //this mapping will be from a default descriptor + wac.getServletHandler().addServletMapping(m); + + ServletMapping m2 = new ServletMapping(); + m2.setPathSpec("/other"); + m2.setServletName("default"); + m2.setFromDefaultDescriptor(true); //this mapping will be from a default descriptor + wac.getServletHandler().addServletMapping(m2); + + WebServletAnnotation annotation = new WebServletAnnotation(wac, "org.eclipse.jetty.ee11.annotations.ServletD", null); + annotation.apply(); + + //test that only the mapping for "/" was removed from the mappings to the default servlet + ServletMapping[] resultMappings = wac.getServletHandler().getServletMappings(); + assertNotNull(resultMappings); + assertEquals(2, resultMappings.length); + for (ServletMapping r : resultMappings) + { + if (r.getServletName().equals("default")) + { + assertEquals(1, r.getPathSpecs().length); + assertEquals("/other", r.getPathSpecs()[0]); + } + else if (r.getServletName().equals("DServlet")) + { + assertEquals(2, r.getPathSpecs().length); + for (String p : r.getPathSpecs()) + { + if (!p.equals("/") && !p.equals("/bah/*")) + fail("Unexpected path"); + } + } + else + fail("Unexpected servlet mapping: " + r); + } + } + + @Test + public void testWebServletAnnotationNotOverride() + { + //if the existing servlet mapping TO A DIFFERENT SERVLET IS NOT from a default descriptor we + //DO NOT allow the annotation to replace the mapping + WebAppContext wac = new WebAppContext(); + ServletHolder servlet = new ServletHolder(); + servlet.setClassName("org.eclipse.jetty.ee11.servlet.FooServlet"); + servlet.setName("foo"); + wac.getServletHandler().addServlet(servlet); + ServletMapping m = new ServletMapping(); + m.setPathSpec("/"); + m.setServletName("foo"); + wac.getServletHandler().addServletMapping(m); + + WebServletAnnotation annotation = new WebServletAnnotation(wac, "org.eclipse.jetty.ee11.annotations.ServletD", null); + annotation.apply(); + + ServletMapping[] resultMappings = wac.getServletHandler().getServletMappings(); + assertEquals(2, resultMappings.length); + for (ServletMapping r : resultMappings) + { + if (r.getServletName().equals("DServlet")) + { + assertEquals(2, r.getPathSpecs().length); + } + else if (r.getServletName().equals("foo")) + { + assertEquals(1, r.getPathSpecs().length); + } + else + fail("Unexpected servlet name: " + r); + } + } + + @Test + public void testWebServletAnnotationIgnore() + { + //an existing servlet OF THE SAME NAME has even 1 non-default mapping we can't use + //any of the url mappings in the annotation + WebAppContext wac = new WebAppContext(); + ServletHolder servlet = new ServletHolder(); + servlet.setClassName("org.eclipse.jetty.ee11.servlet.OtherDServlet"); + servlet.setName("DServlet"); + wac.getServletHandler().addServlet(servlet); + + ServletMapping m = new ServletMapping(); + m.setPathSpec("/default"); + m.setFromDefaultDescriptor(true); + m.setServletName("DServlet"); + wac.getServletHandler().addServletMapping(m); + + ServletMapping m2 = new ServletMapping(); + m2.setPathSpec("/other"); + m2.setServletName("DServlet"); + wac.getServletHandler().addServletMapping(m2); + + WebServletAnnotation annotation = new WebServletAnnotation(wac, "org.eclipse.jetty.ee11.annotations.ServletD", null); + annotation.apply(); + + ServletMapping[] resultMappings = wac.getServletHandler().getServletMappings(); + assertEquals(2, resultMappings.length); + + for (ServletMapping r : resultMappings) + { + assertEquals(1, r.getPathSpecs().length); + if (!r.getPathSpecs()[0].equals("/default") && !r.getPathSpecs()[0].equals("/other")) + fail("Unexpected path in mapping: " + r); + } + } + + @Test + public void testWebServletAnnotationNoMappings() + { + //an existing servlet OF THE SAME NAME has no mappings, therefore all mappings in the annotation + //should be accepted + WebAppContext wac = new WebAppContext(); + ServletHolder servlet = new ServletHolder(); + servlet.setName("foo"); + wac.getServletHandler().addServlet(servlet); + + WebServletAnnotation annotation = new WebServletAnnotation(wac, "org.eclipse.jetty.ee11.annotations.ServletD", null); + annotation.apply(); + + ServletMapping[] resultMappings = wac.getServletHandler().getServletMappings(); + assertEquals(1, resultMappings.length); + assertEquals(2, resultMappings[0].getPathSpecs().length); + for (String s : resultMappings[0].getPathSpecs()) + { + assertThat(s, anyOf(is("/"), is("/bah/*"))); + } + } + + @Test + public void testDeclareRoles() + { + WebAppContext wac = new WebAppContext(); + ConstraintSecurityHandler sh = new ConstraintSecurityHandler(); + wac.setSecurityHandler(sh); + sh.setRoles(Set.of("humpty", "dumpty")); + DeclareRolesAnnotationHandler handler = new DeclareRolesAnnotationHandler(wac); + handler.doHandle(ServletC.class); + assertThat(sh.getKnownRoles(), containsInAnyOrder("humpty", "alice", "dumpty")); + } + + private void copyClass(Class clazz, Path outputDir) throws IOException, URISyntaxException + { + String classRef = TypeUtil.toClassReference(clazz); + URL url = this.getClass().getResource('/' + classRef); + assertThat("URL for: " + classRef, url, notNullValue()); + + Path srcClass = Paths.get(url.toURI()); + Path dest = outputDir.resolve(classRef); + FS.ensureDirExists(dest.getParent()); + Files.copy(srcClass, dest); + } +} diff --git a/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/resources/ResourceA.java b/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/resources/ResourceA.java new file mode 100644 index 00000000000..8976ed248ee --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/resources/ResourceA.java @@ -0,0 +1,115 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.annotations.resources; + +import java.io.IOException; + +import jakarta.annotation.Resource; +import jakarta.servlet.ServletConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; + +/** + * ResourceA + */ +public class ResourceA implements jakarta.servlet.Servlet +{ + private Integer e; + private Integer h; + private Integer k; + + @Resource(name = "myf", mappedName = "resB") //test giving both a name and mapped name from the environment + private Integer f; //test a non inherited field that needs injection + + @Resource(mappedName = "resA") //test the default naming scheme but using a mapped name from the environment + private Integer g; + + @Resource(name = "resA") //test using the given name as the name from the environment + private Integer j; + + @Resource(mappedName = "resB") //test using the default name on an inherited field + protected Integer n; //TODO - if it's inherited, is it supposed to use the classname of the class it is inherited by? + + @Resource(name = "mye", mappedName = "resA", type = Integer.class) + public void setE(Integer e) + { + this.e = e; + } + + public Integer getE() + { + return this.e; + } + + public Integer getF() + { + return this.f; + } + + public Integer getG() + { + return this.g; + } + + public Integer getJ() + { + return this.j; + } + + @Resource(mappedName = "resA") + public void setH(Integer h) + { + this.h = h; + } + + @Resource(name = "resA") + public void setK(Integer k) + { + this.k = k; + } + + public void x() + { + System.err.println("ResourceA.x"); + } + + @Override + public void destroy() + { + } + + @Override + public ServletConfig getServletConfig() + { + return null; + } + + @Override + public String getServletInfo() + { + return null; + } + + @Override + public void init(ServletConfig arg0) throws ServletException + { + } + + @Override + public void service(ServletRequest arg0, ServletResponse arg1) + throws ServletException, IOException + { + } +} diff --git a/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/resources/ResourceB.java b/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/resources/ResourceB.java new file mode 100644 index 00000000000..2121d8991ad --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/resources/ResourceB.java @@ -0,0 +1,39 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.annotations.resources; + +import jakarta.annotation.Resource; +import jakarta.annotation.Resources; + +/** + * ResourceB + */ +@Resources({ + @Resource(name = "peach", mappedName = "resA"), + @Resource(name = "pear", mappedName = "resB") +}) +public class ResourceB extends ResourceA +{ + @Resource(mappedName = "resB") + private Integer f; //test no inheritance of private fields + + @Resource + private Integer p = 8; //test no injection because no value + + //test no annotation + public void z() + { + System.err.println("ResourceB.z"); + } +} diff --git a/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/resources/TestResourceAnnotations.java b/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/resources/TestResourceAnnotations.java new file mode 100644 index 00000000000..e9800c1fc78 --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/test/java/org/eclipse/jetty/ee11/annotations/resources/TestResourceAnnotations.java @@ -0,0 +1,168 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.annotations.resources; + +import java.lang.reflect.Field; +import java.util.Set; +import javax.naming.Context; +import javax.naming.InitialContext; + +import org.eclipse.jetty.ee11.annotations.AnnotationIntrospector; +import org.eclipse.jetty.ee11.annotations.ResourceAnnotationHandler; +import org.eclipse.jetty.ee11.annotations.ResourcesAnnotationHandler; +import org.eclipse.jetty.ee11.webapp.WebAppContext; +import org.eclipse.jetty.plus.annotation.Injection; +import org.eclipse.jetty.plus.annotation.InjectionCollection; +import org.eclipse.jetty.plus.jndi.EnvEntry; +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +public class TestResourceAnnotations +{ + private Server server; + private WebAppContext wac; + private InjectionCollection injections; + private Context comp; + private Context env; + private Object objA = 1000; + private Object objB = 2000; + + @BeforeEach + public void init() throws Exception + { + server = new Server(); + wac = new WebAppContext(); + wac.setServer(server); + injections = new InjectionCollection(); + wac.setAttribute(InjectionCollection.INJECTION_COLLECTION, injections); + InitialContext ic = new InitialContext(); + comp = (Context)ic.lookup("java:comp"); + env = comp.createSubcontext("env"); + } + + @AfterEach + public void destroy() throws Exception + { + comp.destroySubcontext("env"); + } + + @Test + public void testResourceAnnotations() + throws Exception + { + new EnvEntry(server, "resA", objA, false); + new EnvEntry(server, "resB", objB, false); + + AnnotationIntrospector parser = new AnnotationIntrospector(wac); + ResourceAnnotationHandler handler = new ResourceAnnotationHandler(wac); + parser.registerHandler(handler); + + ResourceA resourceA = new ResourceA(); + ResourceB resourceB = new ResourceB(); + parser.introspect(resourceA, null); + parser.introspect(resourceB, null); + + //processing classA should give us these jndi name bindings: + // java:comp/env/myf + // java:comp/env/org.eclipse.jetty.annotations.resources.ResourceA/g + // java:comp/env/mye + // java:comp/env/org.eclipse.jetty.annotations.resources.ResourceA/h + // java:comp/env/resA + // java:comp/env/org.eclipse.jetty.annotations.resources.ResourceB/f + // java:comp/env/org.eclipse.jetty.annotations.resources.ResourceA/n + // + assertEquals(objB, env.lookup("myf")); + assertEquals(objA, env.lookup("mye")); + assertEquals(objA, env.lookup("resA")); + assertEquals(objA, env.lookup("org.eclipse.jetty.ee11.annotations.resources.ResourceA/g")); + assertEquals(objA, env.lookup("org.eclipse.jetty.ee11.annotations.resources.ResourceA/h")); + assertEquals(objB, env.lookup("org.eclipse.jetty.ee11.annotations.resources.ResourceB/f")); + assertEquals(objB, env.lookup("org.eclipse.jetty.ee11.annotations.resources.ResourceA/n")); + + //we should have Injections + assertNotNull(injections); + + Set resBInjections = injections.getInjections(ResourceB.class.getName()); + assertNotNull(resBInjections); + + //only 1 field injection because the other has no Resource mapping + assertEquals(1, resBInjections.size()); + Injection fi = resBInjections.iterator().next(); + assertEquals("f", fi.getTarget().getName()); + + //3 method injections on class ResourceA, 4 field injections + Set resAInjections = injections.getInjections(ResourceA.class.getName()); + assertNotNull(resAInjections); + assertEquals(7, resAInjections.size()); + int fieldCount = 0; + int methodCount = 0; + for (Injection x : resAInjections) + { + if (x.isField()) + fieldCount++; + else + methodCount++; + } + assertEquals(4, fieldCount); + assertEquals(3, methodCount); + + //test injection + ResourceB binst = new ResourceB(); + injections.inject(binst); + + //check injected values + Field f = ResourceB.class.getDeclaredField("f"); + f.setAccessible(true); + assertEquals(objB, f.get(binst)); + + //@Resource(mappedName="resA") //test the default naming scheme but using a mapped name from the environment + f = ResourceA.class.getDeclaredField("g"); + f.setAccessible(true); + assertEquals(objA, f.get(binst)); + + //@Resource(name="resA") //test using the given name as the name from the environment + f = ResourceA.class.getDeclaredField("j"); + f.setAccessible(true); + assertEquals(objA, f.get(binst)); + + //@Resource(mappedName="resB") //test using the default name on an inherited field + f = ResourceA.class.getDeclaredField("n"); + f.setAccessible(true); + assertEquals(objB, f.get(binst)); + } + + @Test + public void testResourcesAnnotation() + throws Exception + { + new EnvEntry(server, "resA", objA, false); + new EnvEntry(server, "resB", objB, false); + + AnnotationIntrospector introspector = new AnnotationIntrospector(wac); + ResourcesAnnotationHandler handler = new ResourcesAnnotationHandler(wac); + introspector.registerHandler(handler); + ResourceA resourceA = new ResourceA(); + ResourceB resourceB = new ResourceB(); + introspector.introspect(resourceA, null); + introspector.introspect(resourceB, null); + + assertEquals(objA, env.lookup("peach")); + assertEquals(objB, env.lookup("pear")); + } +} diff --git a/jetty-ee11/jetty-ee11-annotations/src/test/resources/bad-classes.jar b/jetty-ee11/jetty-ee11-annotations/src/test/resources/bad-classes.jar new file mode 100644 index 00000000000..5538c18b552 Binary files /dev/null and b/jetty-ee11/jetty-ee11-annotations/src/test/resources/bad-classes.jar differ diff --git a/jetty-ee11/jetty-ee11-annotations/src/test/resources/jdk10/multirelease-10.jar b/jetty-ee11/jetty-ee11-annotations/src/test/resources/jdk10/multirelease-10.jar new file mode 100644 index 00000000000..a824bc10f1c Binary files /dev/null and b/jetty-ee11/jetty-ee11-annotations/src/test/resources/jdk10/multirelease-10.jar differ diff --git a/jetty-ee11/jetty-ee11-annotations/src/test/resources/jdk9/log4j-api-2.9.0.jar b/jetty-ee11/jetty-ee11-annotations/src/test/resources/jdk9/log4j-api-2.9.0.jar new file mode 100644 index 00000000000..ab98d40e9a2 Binary files /dev/null and b/jetty-ee11/jetty-ee11-annotations/src/test/resources/jdk9/log4j-api-2.9.0.jar differ diff --git a/jetty-ee11/jetty-ee11-annotations/src/test/resources/jdk9/slf4j-api-1.8.0-alpha2.jar b/jetty-ee11/jetty-ee11-annotations/src/test/resources/jdk9/slf4j-api-1.8.0-alpha2.jar new file mode 100644 index 00000000000..7a2a9b2d8e4 Binary files /dev/null and b/jetty-ee11/jetty-ee11-annotations/src/test/resources/jdk9/slf4j-api-1.8.0-alpha2.jar differ diff --git a/jetty-ee11/jetty-ee11-annotations/src/test/resources/jetty-logging.properties b/jetty-ee11/jetty-ee11-annotations/src/test/resources/jetty-logging.properties new file mode 100644 index 00000000000..0c65bb4ec58 --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/test/resources/jetty-logging.properties @@ -0,0 +1,6 @@ +# Jetty Logging using jetty-slf4j-impl +#org.eclipse.jetty.LEVEL=DEBUG +#org.eclipse.jetty.annotations.LEVEL=DEBUG +#org.eclipse.jetty.util.MultiReleaseJarFile.LEVEL=DEBUG +#org.eclipse.jetty.util.resources.LEVEL=DEBUG +#org.eclipse.jetty.ee11.annotations.AnnotationParser.LEVEL=DEBUG diff --git a/jetty-ee11/jetty-ee11-annotations/src/test/resources/tinytest.jar b/jetty-ee11/jetty-ee11-annotations/src/test/resources/tinytest.jar new file mode 100644 index 00000000000..4041f0e8a1b Binary files /dev/null and b/jetty-ee11/jetty-ee11-annotations/src/test/resources/tinytest.jar differ diff --git a/jetty-ee11/jetty-ee11-annotations/src/test/resources/tinytest_copy.jar b/jetty-ee11/jetty-ee11-annotations/src/test/resources/tinytest_copy.jar new file mode 100644 index 00000000000..db720508d0d Binary files /dev/null and b/jetty-ee11/jetty-ee11-annotations/src/test/resources/tinytest_copy.jar differ diff --git a/jetty-ee11/jetty-ee11-annotations/src/test/resources/web-fragment4false.xml b/jetty-ee11/jetty-ee11-annotations/src/test/resources/web-fragment4false.xml new file mode 100644 index 00000000000..4a8871e8c2c --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/test/resources/web-fragment4false.xml @@ -0,0 +1,11 @@ + + + + ardvaark + + + diff --git a/jetty-ee11/jetty-ee11-annotations/src/test/resources/web-fragment4true.xml b/jetty-ee11/jetty-ee11-annotations/src/test/resources/web-fragment4true.xml new file mode 100644 index 00000000000..1738993b944 --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/test/resources/web-fragment4true.xml @@ -0,0 +1,14 @@ + + + + + badger + + + + diff --git a/jetty-ee11/jetty-ee11-annotations/src/test/resources/web25.xml b/jetty-ee11/jetty-ee11-annotations/src/test/resources/web25.xml new file mode 100644 index 00000000000..da2e65b6007 --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/test/resources/web25.xml @@ -0,0 +1,10 @@ + + + + Test 2.5 WebApp + + diff --git a/jetty-ee11/jetty-ee11-annotations/src/test/resources/web31false.xml b/jetty-ee11/jetty-ee11-annotations/src/test/resources/web31false.xml new file mode 100644 index 00000000000..81750df9dda --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/test/resources/web31false.xml @@ -0,0 +1,11 @@ + + + + Test 31 WebApp + + diff --git a/jetty-ee11/jetty-ee11-annotations/src/test/resources/web31true.xml b/jetty-ee11/jetty-ee11-annotations/src/test/resources/web31true.xml new file mode 100644 index 00000000000..768c5ea212f --- /dev/null +++ b/jetty-ee11/jetty-ee11-annotations/src/test/resources/web31true.xml @@ -0,0 +1,11 @@ + + + + Test 31 WebApp + + diff --git a/jetty-ee11/jetty-ee11-apache-jsp/pom.xml b/jetty-ee11/jetty-ee11-apache-jsp/pom.xml new file mode 100644 index 00000000000..ed33316aeb7 --- /dev/null +++ b/jetty-ee11/jetty-ee11-apache-jsp/pom.xml @@ -0,0 +1,95 @@ + + + + 4.0.0 + + org.eclipse.jetty.ee11 + jetty-ee11 + 12.1.0-SNAPSHOT + + jetty-ee11-apache-jsp + EE11 :: Apache JSP + + + ${project.groupId}.apache-jsp + true + + + + + jakarta.servlet + jakarta.servlet-api + + + org.eclipse.jetty + jetty-util + + + org.mortbay.jasper + apache-jsp + + + org.slf4j + slf4j-api + + + org.eclipse.jetty + jetty-http-tools + test + + + org.eclipse.jetty + jetty-slf4j-impl + test + + + org.eclipse.jetty.ee11 + jetty-ee11-servlet + test + + + org.eclipse.jetty.toolchain + jetty-test-helper + test + + + + + + + + org.apache.felix + maven-bundle-plugin + true + + + Jetty-specific ServletContainerInitializer for Jasper + org.eclipse.jetty.ee11.apache.jsp.*;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}", org.eclipse.jetty.ee11.jsp.*;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}" + osgi.extender; filter:="(osgi.extender=osgi.serviceloader.registrar)";resolution:=optional + osgi.serviceloader;osgi.serviceloader=jakarta.servlet.ServletContainerInitializer,osgi.serviceloader;osgi.serviceloader=org.apache.juli.logging.Log + <_nouses>true + + + + + org.apache.maven.plugins + maven-jar-plugin + + + nolog-jar + + jar + + + nolog + + META-INF/services/org.apache.juli.logging.Log + + + + + + + + + diff --git a/jetty-ee11/jetty-ee11-apache-jsp/src/main/config/modules/ee11-apache-jsp.mod b/jetty-ee11/jetty-ee11-apache-jsp/src/main/config/modules/ee11-apache-jsp.mod new file mode 100644 index 00000000000..a475fe461d0 --- /dev/null +++ b/jetty-ee11/jetty-ee11-apache-jsp/src/main/config/modules/ee11-apache-jsp.mod @@ -0,0 +1,26 @@ +# DO NOT EDIT THIS FILE - See: https://eclipse.dev/jetty/documentation/ + +[description] +Enables use of the apache implementation of JSP. + +[environment] +ee11 + +[depend] +ee11-servlet +ee11-annotations + +[ini] +ee11.jakarta.el.api.version?=@jakarta.el.api.version@ +ee11.jakarta.servlet.jsp.api.version?=@jakarta.servlet.jsp.api.version@ +eclipse.jdt.ecj.version?=@eclipse.jdt.ecj.version@ +ee11.jsp.impl.version?=@jsp.impl.version@ + +[lib] +lib/ee11-apache-jsp/jakarta.el.jakarta.el-api-${ee11.jakarta.el.api.version}.jar +lib/ee11-apache-jsp/jakarta.servlet.jsp.jakarta.servlet.jsp-api-${ee11.jakarta.servlet.jsp.api.version}.jar +lib/ee11-apache-jsp/org.eclipse.jdt.ecj-${eclipse.jdt.ecj.version}.jar +lib/ee11-apache-jsp/org.mortbay.jasper.apache-el-${ee11.jsp.impl.version}.jar +lib/ee11-apache-jsp/org.mortbay.jasper.apache-jsp-${ee11.jsp.impl.version}.jar +lib/jetty-ee11-apache-jsp-${jetty.version}.jar + diff --git a/jetty-ee11/jetty-ee11-apache-jsp/src/main/java/module-info.java b/jetty-ee11/jetty-ee11-apache-jsp/src/main/java/module-info.java new file mode 100644 index 00000000000..5af260d128d --- /dev/null +++ b/jetty-ee11/jetty-ee11-apache-jsp/src/main/java/module-info.java @@ -0,0 +1,30 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +module org.eclipse.jetty.ee11.apache.jsp +{ + requires java.xml; + requires jakarta.servlet; + requires org.eclipse.jetty.util; + requires org.mortbay.apache.jasper; + requires org.slf4j; + + exports org.eclipse.jetty.ee11.apache.jsp; + exports org.eclipse.jetty.ee11.jsp; + + provides org.apache.juli.logging.Log with + org.eclipse.jetty.ee11.apache.jsp.JuliLog; + + provides jakarta.servlet.ServletContainerInitializer with + org.eclipse.jetty.ee11.apache.jsp.JettyJasperInitializer; +} diff --git a/jetty-ee11/jetty-ee11-apache-jsp/src/main/java/org/eclipse/jetty/ee11/apache/jsp/JettyJasperInitializer.java b/jetty-ee11/jetty-ee11-apache-jsp/src/main/java/org/eclipse/jetty/ee11/apache/jsp/JettyJasperInitializer.java new file mode 100644 index 00000000000..d9622b5ab94 --- /dev/null +++ b/jetty-ee11/jetty-ee11-apache-jsp/src/main/java/org/eclipse/jetty/ee11/apache/jsp/JettyJasperInitializer.java @@ -0,0 +1,98 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.apache.jsp; + +import java.io.IOException; +import java.net.URL; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import jakarta.servlet.ServletContext; +import org.apache.jasper.servlet.JasperInitializer; +import org.apache.jasper.servlet.TldScanner; +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; +import org.xml.sax.SAXException; + +/** + * JettyJasperInitializer + */ +public class JettyJasperInitializer extends JasperInitializer +{ + private static final Log LOG = LogFactory.getLog(JasperInitializer.class); + + /** + * NullTldScanner + * + * Does nothing. Used when we can tell that all jsps have been precompiled, in which case + * the tlds are not needed. + */ + private final class NullTldScanner extends TldScanner + { + /** + * + */ + private NullTldScanner(ServletContext context, boolean namespaceAware, boolean validation, boolean blockExternal) + { + super(context, namespaceAware, validation, blockExternal); + } + + @Override + public void scan() throws IOException, SAXException + { + return; //do nothing + } + + @Override + public List getListeners() + { + return Collections.emptyList(); + } + + @Override + public void scanJars() + { + return; //do nothing + } + } + + /** + * Make a TldScanner, and prefeed it the tlds that have already been discovered in jar files + * by the MetaInfConfiguration. + */ + @Override + public TldScanner newTldScanner(ServletContext context, boolean namespaceAware, boolean validate, boolean blockExternal) + { + String tmp = context.getInitParameter("org.eclipse.jetty.jsp.precompiled"); + if (tmp != null && !tmp.isEmpty() && Boolean.valueOf(tmp)) + { + if (LOG.isDebugEnabled()) + LOG.debug("Jsp precompilation detected"); + return new NullTldScanner(context, namespaceAware, validate, blockExternal); + } + + Collection tldUrls = (Collection)context.getAttribute("org.eclipse.jetty.tlds"); + if (tldUrls != null) + { + if (LOG.isDebugEnabled()) + LOG.debug("Tld pre-scan detected"); + return new JettyTldPreScanned(context, namespaceAware, validate, blockExternal, tldUrls); + } + + if (LOG.isDebugEnabled()) + LOG.debug("Defaulting to jasper tld scanning"); + return super.newTldScanner(context, namespaceAware, validate, blockExternal); + } +} diff --git a/jetty-ee11/jetty-ee11-apache-jsp/src/main/java/org/eclipse/jetty/ee11/apache/jsp/JettyTldPreScanned.java b/jetty-ee11/jetty-ee11-apache-jsp/src/main/java/org/eclipse/jetty/ee11/apache/jsp/JettyTldPreScanned.java new file mode 100644 index 00000000000..7e3808bab74 --- /dev/null +++ b/jetty-ee11/jetty-ee11-apache-jsp/src/main/java/org/eclipse/jetty/ee11/apache/jsp/JettyTldPreScanned.java @@ -0,0 +1,87 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.apache.jsp; + +import java.net.URL; +import java.util.Collection; + +import jakarta.servlet.ServletContext; +import org.apache.jasper.servlet.TldPreScanned; +import org.apache.tomcat.util.descriptor.tld.TldResourcePath; + +/** + * JettyTldPreScanned + * + * Change to TldPreScanned to not require that the tlds have been + * pre-scanned from a jar file, but rather may be files in the + * file system. + * + * This is important for running in the jetty maven plugin + * environment in multi-module builds, where modules that contain tlds + * may be in the reactor at the same time as a webapp being run with the + * plugin. That means that the tlds will be used from their location in + * the file system, rather than from their assembled jar. + */ +public class JettyTldPreScanned extends TldPreScanned +{ + private final Collection _jettyPreScannedURLs; + + public JettyTldPreScanned(ServletContext context, boolean namespaceAware, boolean validation, boolean blockExternal, Collection preScannedTlds) + { + super(context, namespaceAware, validation, blockExternal, preScannedTlds); + _jettyPreScannedURLs = preScannedTlds; + } + + @Override + public void scanJars() + { + if (_jettyPreScannedURLs != null) + { + for (URL url : _jettyPreScannedURLs) + { + String str = url.toExternalForm(); + int a = str.indexOf("jar:"); + int b = str.indexOf("META-INF"); + if (b < 0) + throw new IllegalStateException("Bad tld url: " + str); + + String path = str.substring(b); + if (a >= 0) + { + int c = str.indexOf("!/"); + String fileUrl = str.substring(a + 4, c); + try + { + parseTld(new TldResourcePath(new URL(fileUrl), null, path)); + } + catch (Exception e) + { + throw new IllegalStateException(e); + } + } + else + { + try + { + parseTld(new TldResourcePath(url, null, null)); + } + catch (Exception e) + { + throw new IllegalStateException(e); + } + } + } + } + } +} diff --git a/jetty-ee11/jetty-ee11-apache-jsp/src/main/java/org/eclipse/jetty/ee11/apache/jsp/JuliLog.java b/jetty-ee11/jetty-ee11-apache-jsp/src/main/java/org/eclipse/jetty/ee11/apache/jsp/JuliLog.java new file mode 100644 index 00000000000..0e098090ce2 --- /dev/null +++ b/jetty-ee11/jetty-ee11-apache-jsp/src/main/java/org/eclipse/jetty/ee11/apache/jsp/JuliLog.java @@ -0,0 +1,182 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.apache.jsp; + +import org.slf4j.LoggerFactory; + +public class JuliLog implements org.apache.juli.logging.Log +{ + public static org.apache.juli.logging.Log getInstance(String name) + { + return new JuliLog(name); + } + + private final org.slf4j.Logger _logger; + + public JuliLog() + { + _logger = LoggerFactory.getLogger(""); + } + + public JuliLog(String name) + { + _logger = LoggerFactory.getLogger(name); + } + + @Override + public boolean isDebugEnabled() + { + return _logger.isDebugEnabled(); + } + + @Override + public boolean isErrorEnabled() + { + return _logger.isErrorEnabled(); + } + + @Override + public boolean isFatalEnabled() + { + return _logger.isErrorEnabled(); + } + + @Override + public boolean isInfoEnabled() + { + return _logger.isInfoEnabled(); + } + + @Override + public boolean isTraceEnabled() + { + return _logger.isTraceEnabled(); + } + + @Override + public boolean isWarnEnabled() + { + return _logger.isWarnEnabled(); + } + + @Override + public void trace(Object message) + { + if (message instanceof String) + _logger.debug((String)message); + else + _logger.debug("{}", message); + } + + @Override + public void trace(Object message, Throwable t) + { + if (message instanceof String) + _logger.debug((String)message, t); + else + _logger.debug("{}", message, t); + } + + @Override + public void debug(Object message) + { + if (message instanceof String) + _logger.debug((String)message); + else + _logger.debug("{}", message); + } + + @Override + public void debug(Object message, Throwable t) + { + if (message instanceof String) + _logger.debug((String)message, t); + else + _logger.debug("{}", message, t); + } + + @Override + public void info(Object message) + { + if (message instanceof String) + _logger.info((String)message); + else + _logger.info("{}", message); + } + + @Override + public void info(Object message, Throwable t) + { + if (message instanceof String) + _logger.info((String)message, t); + else + _logger.info("{}", message, t); + } + + @Override + public void warn(Object message) + { + if (message instanceof String) + _logger.warn((String)message); + else + _logger.warn("{}", message); + } + + @Override + public void warn(Object message, Throwable t) + { + if (message instanceof String) + _logger.warn((String)message, t); + else + _logger.warn("{}", message, t); + } + + @Override + public void error(Object message) + { + if (message instanceof String) + _logger.warn((String)message); + else + _logger.warn("{}", message); + } + + @Override + public void error(Object message, Throwable t) + { + if (message instanceof String) + _logger.warn((String)message, t); + else + _logger.warn("{}", message, t); + } + + @Override + public void fatal(Object message) + { + if (message instanceof String) + _logger.warn((String)message); + else + _logger.warn("{}", message); + } + + @Override + public void fatal(Object message, Throwable t) + { + if (message instanceof String) + _logger.warn((String)message, t); + else + _logger.warn("{}", message, t); + } +} + + diff --git a/jetty-ee11/jetty-ee11-apache-jsp/src/main/java/org/eclipse/jetty/ee11/jsp/JettyJspServlet.java b/jetty-ee11/jetty-ee11-apache-jsp/src/main/java/org/eclipse/jetty/ee11/jsp/JettyJspServlet.java new file mode 100644 index 00000000000..5835a52f004 --- /dev/null +++ b/jetty-ee11/jetty-ee11-apache-jsp/src/main/java/org/eclipse/jetty/ee11/jsp/JettyJspServlet.java @@ -0,0 +1,119 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.jsp; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import jakarta.servlet.RequestDispatcher; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.jasper.servlet.JspServlet; + +/** + * JettyJspServlet + * + * Wrapper for the jsp servlet that handles receiving requests mapped from + * jsp-property-groups. Mappings could be wildcard urls like "/*", which would + * include welcome files, but we need those to be handled by the DefaultServlet. + */ +public class JettyJspServlet extends JspServlet +{ + + /** + * + */ + private static final long serialVersionUID = -5387857473125086791L; + + @Override + public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException + { + HttpServletRequest request = null; + if (req instanceof HttpServletRequest) + request = (HttpServletRequest)req; + else + throw new ServletException("Request not HttpServletRequest"); + + String servletPath = null; + String pathInfo = null; + if (request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI) != null) + { + servletPath = (String)request.getAttribute(RequestDispatcher.INCLUDE_SERVLET_PATH); + pathInfo = (String)request.getAttribute(RequestDispatcher.INCLUDE_PATH_INFO); + if (servletPath == null) + { + servletPath = request.getServletPath(); + pathInfo = request.getPathInfo(); + } + } + else + { + servletPath = request.getServletPath(); + pathInfo = request.getPathInfo(); + } + + String pathInContext = addPaths(servletPath, pathInfo); + + String jspFile = getInitParameter("jspFile"); + + //if this is a forced-path from a jsp-file, we want the jsp servlet to handle it, + //otherwise the default servlet might handle it + if (jspFile == null) + { + if (pathInContext != null && pathInContext.endsWith("/")) + { + //dispatch via forward to the default servlet + getServletContext().getNamedDispatcher("default").forward(req, resp); + return; + } + else + { + //check if it resolves to a directory + String realPath = getServletContext().getRealPath(pathInContext); + if (realPath != null) + { + Path asPath = Paths.get(realPath); + if (Files.exists(asPath) && Files.isDirectory(asPath)) + { + //dispatch via forward to the default servlet + getServletContext().getNamedDispatcher("default").forward(req, resp); + return; + } + } + } + } + + //fall through to the normal jsp servlet handling + super.service(req, resp); + } + + /** + * @param servletPath the servletPath of the request + * @param pathInfo the pathInfo of the request + * @return servletPath with pathInfo appended + */ + private String addPaths(String servletPath, String pathInfo) + { + if (servletPath.isEmpty()) + return pathInfo; + + if (pathInfo == null) + return servletPath; + + return servletPath + pathInfo; + } +} diff --git a/jetty-ee11/jetty-ee11-apache-jsp/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer b/jetty-ee11/jetty-ee11-apache-jsp/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer new file mode 100644 index 00000000000..b73014705ca --- /dev/null +++ b/jetty-ee11/jetty-ee11-apache-jsp/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer @@ -0,0 +1 @@ +org.eclipse.jetty.ee11.apache.jsp.JettyJasperInitializer diff --git a/jetty-ee11/jetty-ee11-apache-jsp/src/main/resources/META-INF/services/org.apache.juli.logging.Log b/jetty-ee11/jetty-ee11-apache-jsp/src/main/resources/META-INF/services/org.apache.juli.logging.Log new file mode 100644 index 00000000000..e1ebb1e1cd7 --- /dev/null +++ b/jetty-ee11/jetty-ee11-apache-jsp/src/main/resources/META-INF/services/org.apache.juli.logging.Log @@ -0,0 +1 @@ +org.eclipse.jetty.ee11.apache.jsp.JuliLog diff --git a/jetty-ee11/jetty-ee11-apache-jsp/src/test/java/org/eclipse/jetty/ee11/jsp/TestJettyJspServlet.java b/jetty-ee11/jetty-ee11-apache-jsp/src/test/java/org/eclipse/jetty/ee11/jsp/TestJettyJspServlet.java new file mode 100644 index 00000000000..ef4f8979771 --- /dev/null +++ b/jetty-ee11/jetty-ee11-apache-jsp/src/test/java/org/eclipse/jetty/ee11/jsp/TestJettyJspServlet.java @@ -0,0 +1,121 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.jsp; + +import java.io.IOException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Path; + +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.jsp.JspFactory; +import org.apache.jasper.runtime.JspFactoryImpl; +import org.apache.tomcat.InstanceManager; +import org.apache.tomcat.SimpleInstanceManager; +import org.eclipse.jetty.ee11.servlet.ServletContextHandler; +import org.eclipse.jetty.ee11.servlet.ServletHolder; +import org.eclipse.jetty.http.HttpTester; +import org.eclipse.jetty.server.LocalConnector; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; +import org.eclipse.jetty.util.component.LifeCycle; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.not; + +@ExtendWith(WorkDirExtension.class) +public class TestJettyJspServlet +{ + public WorkDir workdir; + private Server _server; + private LocalConnector _connector; + + public static class DfltServlet extends HttpServlet + { + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException + { + resp.setContentType("html/text"); + resp.getOutputStream().println("This.Is.The.Default."); + } + } + + @BeforeEach + public void setUp() throws Exception + { + JspFactory.setDefaultFactory(new JspFactoryImpl()); + Path baseDir = MavenTestingUtils.getTestResourcePathDir("base"); + _server = new Server(); + _connector = new LocalConnector(_server); + _server.addConnector(_connector); + ServletContextHandler context = new ServletContextHandler("/context", true, false); + _server.setHandler(context); + context.setClassLoader(new URLClassLoader(new URL[0], Thread.currentThread().getContextClassLoader())); + ServletHolder jspHolder = context.addServlet(JettyJspServlet.class, "/*"); + jspHolder.setInitParameter("scratchdir", workdir.getEmptyPathDir().toString()); + context.setBaseResourceAsPath(baseDir); + context.setAttribute(InstanceManager.class.getName(), new SimpleInstanceManager()); + ServletHolder dfltHolder = new ServletHolder(); + dfltHolder.setName("default"); + dfltHolder.setHeldClass(DfltServlet.class); + context.addServlet(dfltHolder, "/"); + _server.start(); + } + + @AfterEach + public void tearDown() + { + LifeCycle.stop(_server); + } + + @Test + public void testWithJsp() throws Exception + { + //test that an ordinary jsp is served by jsp servlet + String request = + "GET /context/foo.jsp HTTP/1.1\r\n" + + "Host: localhost\r\n" + + "Connection: close\r\n" + + "\r\n"; + + String rawResponse = _connector.getResponse(request); + HttpTester.Response response = HttpTester.parseResponse(rawResponse); + assertThat(response.toString(), response.getContent(), not(containsString("This.Is.The.Default."))); + } + + @Disabled //TODO + @Test + public void testWithDirectory() throws Exception + { + //test that a dir is served by the default servlet + String request = + "GET /context/dir HTTP/1.1\r\n" + + "Host: localhost\r\n" + + "Connection: close\r\n" + + "\r\n"; + String rawResponse = _connector.getResponse(request); + HttpTester.Response response = HttpTester.parseResponse(rawResponse); + assertThat(response.toString(), response.getContent(), containsString("This.Is.The.Default.")); + } +} diff --git a/jetty-ee11/jetty-ee11-apache-jsp/src/test/java/org/eclipse/jetty/ee11/jsp/TestJettyTldPreScanned.java b/jetty-ee11/jetty-ee11-apache-jsp/src/test/java/org/eclipse/jetty/ee11/jsp/TestJettyTldPreScanned.java new file mode 100644 index 00000000000..6ddead0482d --- /dev/null +++ b/jetty-ee11/jetty-ee11-apache-jsp/src/test/java/org/eclipse/jetty/ee11/jsp/TestJettyTldPreScanned.java @@ -0,0 +1,67 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.jsp; + +import java.io.File; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.apache.tomcat.util.descriptor.tld.TaglibXml; +import org.apache.tomcat.util.descriptor.tld.TldResourcePath; +import org.eclipse.jetty.ee11.apache.jsp.JettyTldPreScanned; +import org.eclipse.jetty.ee11.servlet.ServletContextHandler; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.fail; + +/** + * TestJettyTldPreScanned + */ +public class TestJettyTldPreScanned +{ + + /** + * Test that a tld inside a jar can be scanned, as can a tld not inside a jar. + */ + @Test + public void testIt() + throws Exception + { + File jar = MavenTestingUtils.getTestResourceFile("taglib.jar"); + File tld = MavenTestingUtils.getTestResourceFile("META-INF/foo-taglib.tld"); + + List list = new ArrayList<>(); + list.add(new URL("jar:" + jar.toURI().toURL().toString() + "!/META-INF/bar-taglib.tld")); + list.add(tld.toURI().toURL()); + + JettyTldPreScanned preScanned = new JettyTldPreScanned(new ServletContextHandler().getServletContext(), false, false, false, list); + preScanned.scanJars(); + Map map = preScanned.getTldResourcePathTaglibXmlMap(); + assertNotNull(map); + assertEquals(2, map.size()); + for (TldResourcePath p : map.keySet()) + { + URL u = p.getUrl(); + TaglibXml tlx = map.get(p); + assertNotNull(tlx); + if (!"foo".equals(tlx.getShortName()) && !"bar".equals(tlx.getShortName())) + fail("unknown tag"); + } + } +} diff --git a/jetty-ee11/jetty-ee11-apache-jsp/src/test/java/org/eclipse/jetty/ee11/jsp/TestJspFileNameToClass.java b/jetty-ee11/jetty-ee11-apache-jsp/src/test/java/org/eclipse/jetty/ee11/jsp/TestJspFileNameToClass.java new file mode 100644 index 00000000000..21c11dcdc79 --- /dev/null +++ b/jetty-ee11/jetty-ee11-apache-jsp/src/test/java/org/eclipse/jetty/ee11/jsp/TestJspFileNameToClass.java @@ -0,0 +1,52 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.jsp; + +import org.eclipse.jetty.ee11.servlet.ServletHolder; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class TestJspFileNameToClass +{ + + @Test + public void testJspFileNameToClassName() throws Exception + { + ServletHolder h = new ServletHolder(); + h.setName("test"); + + assertEquals(null, h.getClassNameForJsp(null)); + + assertEquals(null, h.getClassNameForJsp("")); + + assertEquals(null, h.getClassNameForJsp("/blah/")); + + assertEquals(null, h.getClassNameForJsp("//blah///")); + + assertEquals(null, h.getClassNameForJsp("/a/b/c/blah/")); + + assertEquals("org.apache.jsp.a.b.c.blah", h.getClassNameForJsp("/a/b/c/blah")); + + assertEquals("org.apache.jsp.blah_jsp", h.getClassNameForJsp("/blah.jsp")); + + assertEquals("org.apache.jsp.blah_jsp", h.getClassNameForJsp("//blah.jsp")); + + assertEquals("org.apache.jsp.blah_jsp", h.getClassNameForJsp("blah.jsp")); + + assertEquals("org.apache.jsp.a.b.c.blah_jsp", h.getClassNameForJsp("/a/b/c/blah.jsp")); + + assertEquals("org.apache.jsp.a.b.c.blah_jsp", h.getClassNameForJsp("a/b/c/blah.jsp")); + } +} diff --git a/jetty-ee11/jetty-ee11-apache-jsp/src/test/resources/META-INF/foo-taglib.tld b/jetty-ee11/jetty-ee11-apache-jsp/src/test/resources/META-INF/foo-taglib.tld new file mode 100644 index 00000000000..f13333980ce --- /dev/null +++ b/jetty-ee11/jetty-ee11-apache-jsp/src/test/resources/META-INF/foo-taglib.tld @@ -0,0 +1,25 @@ + + + + + + 1.0 + 1.2 + foo + http://www.foo.com/taglib + foo example + + + foodate + com.foo.DateTag + TAGDEPENDENT + Display Date + + tz + false + + + + diff --git a/jetty-ee11/jetty-ee11-apache-jsp/src/test/resources/base/dir/empty.txt b/jetty-ee11/jetty-ee11-apache-jsp/src/test/resources/base/dir/empty.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/jetty-ee11/jetty-ee11-apache-jsp/src/test/resources/base/foo.jsp b/jetty-ee11/jetty-ee11-apache-jsp/src/test/resources/base/foo.jsp new file mode 100644 index 00000000000..fb73b0b0002 --- /dev/null +++ b/jetty-ee11/jetty-ee11-apache-jsp/src/test/resources/base/foo.jsp @@ -0,0 +1,23 @@ + +<%@ page import="java.util.Enumeration" %> + +

JSP Dump

+ + + + + + +<% + Enumeration e =request.getParameterNames(); + while(e.hasMoreElements()) + { + String name = (String)e.nextElement(); +%> + + + +<% } %> + +
Request URI:<%= request.getRequestURI() %>
ServletPath:<%= request.getServletPath() %>
PathInfo:<%= request.getPathInfo() %>
getParameter("<%= name %>")<%= request.getParameter(name) %>
+ diff --git a/jetty-ee11/jetty-ee11-apache-jsp/src/test/resources/taglib.jar b/jetty-ee11/jetty-ee11-apache-jsp/src/test/resources/taglib.jar new file mode 100644 index 00000000000..af1aa0c1186 Binary files /dev/null and b/jetty-ee11/jetty-ee11-apache-jsp/src/test/resources/taglib.jar differ diff --git a/jetty-ee11/jetty-ee11-bom/pom.xml b/jetty-ee11/jetty-ee11-bom/pom.xml new file mode 100644 index 00000000000..802fe82b47f --- /dev/null +++ b/jetty-ee11/jetty-ee11-bom/pom.xml @@ -0,0 +1,176 @@ + + + 4.0.0 + + + org.eclipse.jetty.ee11 + jetty-ee11 + 12.1.0-SNAPSHOT + + + jetty-ee11-bom + pom + EE11 :: BOM + Jetty EE11 APIs BOM artifact + + + + + + org.eclipse.jetty.ee11 + jetty-ee11-annotations + 12.1.0-SNAPSHOT + + + org.eclipse.jetty.ee11 + jetty-ee11-apache-jsp + 12.1.0-SNAPSHOT + + + org.eclipse.jetty.ee11 + jetty-ee11-cdi + 12.1.0-SNAPSHOT + + + org.eclipse.jetty.ee11 + jetty-ee11-fcgi-proxy + 12.1.0-SNAPSHOT + + + org.eclipse.jetty.ee11 + jetty-ee11-glassfish-jstl + 12.1.0-SNAPSHOT + + + org.eclipse.jetty.ee11 + jetty-ee11-jaspi + 12.1.0-SNAPSHOT + + + org.eclipse.jetty.ee11 + jetty-ee11-jndi + 12.1.0-SNAPSHOT + + + org.eclipse.jetty.ee11 + jetty-ee11-jspc-maven-plugin + 12.1.0-SNAPSHOT + + + org.eclipse.jetty.ee11 + jetty-ee11-maven-plugin + 12.1.0-SNAPSHOT + + + org.eclipse.jetty.ee11 + jetty-ee11-plus + 12.1.0-SNAPSHOT + + + org.eclipse.jetty.ee11 + jetty-ee11-proxy + 12.1.0-SNAPSHOT + + + org.eclipse.jetty.ee11 + jetty-ee11-quickstart + 12.1.0-SNAPSHOT + + + org.eclipse.jetty.ee11 + jetty-ee11-runner + 12.1.0-SNAPSHOT + + + org.eclipse.jetty.ee11 + jetty-ee11-servlet + 12.1.0-SNAPSHOT + + + org.eclipse.jetty.ee11 + jetty-ee11-servlets + 12.1.0-SNAPSHOT + + + org.eclipse.jetty.ee11 + jetty-ee11-webapp + 12.1.0-SNAPSHOT + + + org.eclipse.jetty.ee11.osgi + jetty-ee11-osgi-alpn + 12.1.0-SNAPSHOT + + + org.eclipse.jetty.ee11.osgi + jetty-ee11-osgi-boot + 12.1.0-SNAPSHOT + + + org.eclipse.jetty.ee11.osgi + jetty-ee11-osgi-boot-jsp + 12.1.0-SNAPSHOT + + + org.eclipse.jetty.ee11.websocket + jetty-ee11-websocket-jakarta-client + 12.1.0-SNAPSHOT + + + org.eclipse.jetty.ee11.websocket + jetty-ee11-websocket-jakarta-client-webapp + 12.1.0-SNAPSHOT + + + org.eclipse.jetty.ee11.websocket + jetty-ee11-websocket-jakarta-common + 12.1.0-SNAPSHOT + + + org.eclipse.jetty.ee11.websocket + jetty-ee11-websocket-jakarta-server + 12.1.0-SNAPSHOT + + + org.eclipse.jetty.ee11.websocket + jetty-ee11-websocket-jetty-client-webapp + 12.1.0-SNAPSHOT + + + org.eclipse.jetty.ee11.websocket + jetty-ee11-websocket-jetty-server + 12.1.0-SNAPSHOT + + + org.eclipse.jetty.ee11.websocket + jetty-ee11-websocket-servlet + 12.1.0-SNAPSHOT + + + + + + + + org.codehaus.mojo + flatten-maven-plugin + + + flatten-clean + + clean + + clean + + + flatten + + flatten + + process-resources + + + + + + diff --git a/jetty-ee11/jetty-ee11-cdi/pom.xml b/jetty-ee11/jetty-ee11-cdi/pom.xml new file mode 100644 index 00000000000..2c9de77d3a5 --- /dev/null +++ b/jetty-ee11/jetty-ee11-cdi/pom.xml @@ -0,0 +1,61 @@ + + + 4.0.0 + + org.eclipse.jetty.ee11 + jetty-ee11 + 12.1.0-SNAPSHOT + + jetty-ee11-cdi + jar + EE11 :: CDI + + ${project.groupId}.cdi + + + + org.eclipse.jetty + jetty-util + compile + + + org.eclipse.jetty.ee11 + jetty-ee11-annotations + compile + + + org.eclipse.jetty.ee11 + jetty-ee11-webapp + compile + + + org.slf4j + slf4j-api + + + + + + org.apache.felix + maven-bundle-plugin + true + + + osgi.extender; filter:="(osgi.extender=osgi.serviceloader.registrar)" + osgi.serviceloader; osgi.serviceloader=org.eclipse.jetty.ee11.webapp.Configuration, + osgi.serviceloader; osgi.serviceloader=jakarta.servlet.ServletContainerInitializer + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven.surefire.plugin.version} + + --add-opens java.base/java.lang=ALL-UNNAMED + + + + + + diff --git a/jetty-ee11/jetty-ee11-cdi/src/main/config/etc/cdi/jetty-ee11-cdi.xml b/jetty-ee11/jetty-ee11-cdi/src/main/config/etc/cdi/jetty-ee11-cdi.xml new file mode 100644 index 00000000000..ce6c9112742 --- /dev/null +++ b/jetty-ee11/jetty-ee11-cdi/src/main/config/etc/cdi/jetty-ee11-cdi.xml @@ -0,0 +1,8 @@ + + + + + org.eclipse.jetty.ee11.cdi + + + diff --git a/jetty-ee11/jetty-ee11-cdi/src/main/config/modules/ee11-cdi-decorate.mod b/jetty-ee11/jetty-ee11-cdi/src/main/config/modules/ee11-cdi-decorate.mod new file mode 100644 index 00000000000..fbada7d3d9d --- /dev/null +++ b/jetty-ee11/jetty-ee11-cdi/src/main/config/modules/ee11-cdi-decorate.mod @@ -0,0 +1,20 @@ +# DO NOT EDIT THIS FILE - See: https://eclipse.dev/jetty/documentation/ + +[description] +Configures Jetty to use the "CdiDecoratingListener" as the default CDI mode. +This mode that allows a webapp to register it's own CDI decorator. + +[environment] +ee11 + +[tag] +cdi + +[provides] +cdi-mode + +[depend] +ee11-cdi + +[ini] +jetty.cdi.mode=CdiDecoratingListener diff --git a/jetty-ee11/jetty-ee11-cdi/src/main/config/modules/ee11-cdi-spi.mod b/jetty-ee11/jetty-ee11-cdi/src/main/config/modules/ee11-cdi-spi.mod new file mode 100644 index 00000000000..86b16715575 --- /dev/null +++ b/jetty-ee11/jetty-ee11-cdi/src/main/config/modules/ee11-cdi-spi.mod @@ -0,0 +1,20 @@ +# DO NOT EDIT THIS FILE - See: https://eclipse.dev/jetty/documentation/ + +[description] +Configures Jetty to use the "CdiSpiDecorator" as the default CDI mode. +This mode uses the CDI SPI to integrate an arbitrary CDI implementation. + +[environment] +ee11 + +[tag] +cdi + +[provides] +cdi-mode + +[depend] +ee11-cdi + +[ini] +jetty.cdi.mode=CdiSpiDecorator diff --git a/jetty-ee11/jetty-ee11-cdi/src/main/config/modules/ee11-cdi.mod b/jetty-ee11/jetty-ee11-cdi/src/main/config/modules/ee11-cdi.mod new file mode 100644 index 00000000000..7e6d18c6148 --- /dev/null +++ b/jetty-ee11/jetty-ee11-cdi/src/main/config/modules/ee11-cdi.mod @@ -0,0 +1,32 @@ +# DO NOT EDIT THIS FILE - See: https://eclipse.dev/jetty/documentation/ + +[description] +Provides integration of CDI within webapp to Jetty container object lifecycles. +This module does not provide CDI, but configures jetty to support various +integration modes with a CDI implementation on the webapp classpath. +CDI integration modes can be selected per webapp with the "org.eclipse.jetty.ee11.cdi" +init parameter or defaults to the mode set by the "org.eclipse.jetty.ee11.cdi" server +attribute (which is initialised from the "jetty.cdi.mode" start property). +Supported modes are: +CdiSpiDecorator - Jetty will call the CDI SPI within the webapp to decorate + objects (default). +CdiDecoratingLister - The webapp may register a decorator on the context attribute + "org.eclipse.jetty.ee11.cdi.decorator". + +[environment] +ee11 + +[tag] +cdi + +[provides] +cdi + +[depend] +deploy + +[xml] +etc/cdi/jetty-ee11-cdi.xml + +[lib] +lib/jetty-ee11-cdi-${jetty.version}.jar diff --git a/jetty-ee11/jetty-ee11-cdi/src/main/java/module-info.java b/jetty-ee11/jetty-ee11-cdi/src/main/java/module-info.java new file mode 100644 index 00000000000..7b1bc0bf098 --- /dev/null +++ b/jetty-ee11/jetty-ee11-cdi/src/main/java/module-info.java @@ -0,0 +1,23 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +module org.eclipse.jetty.ee11.cdi +{ + requires org.eclipse.jetty.ee11.annotations; + + requires transitive org.eclipse.jetty.ee11.servlet; + requires transitive org.eclipse.jetty.ee11.webapp; + requires static jakarta.cdi; + + exports org.eclipse.jetty.ee11.cdi; +} diff --git a/jetty-ee11/jetty-ee11-cdi/src/main/java/org/eclipse/jetty/ee11/cdi/CdiConfiguration.java b/jetty-ee11/jetty-ee11-cdi/src/main/java/org/eclipse/jetty/ee11/cdi/CdiConfiguration.java new file mode 100644 index 00000000000..a42d6dd2afb --- /dev/null +++ b/jetty-ee11/jetty-ee11-cdi/src/main/java/org/eclipse/jetty/ee11/cdi/CdiConfiguration.java @@ -0,0 +1,46 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.cdi; + +import org.eclipse.jetty.ee11.annotations.AnnotationConfiguration; +import org.eclipse.jetty.ee11.plus.webapp.PlusConfiguration; +import org.eclipse.jetty.ee11.webapp.AbstractConfiguration; + +/** + *

CDI Configuration

+ *

This configuration configures the WebAppContext server/system classes to + * be able to see the {@link CdiServletContainerInitializer}. Also hides the + * jakarta cdi classes that are on the environment/server classpath and allows + * the webapp to provide their own. + *

+ */ +public class CdiConfiguration extends AbstractConfiguration +{ + public CdiConfiguration() + { + super(new Builder() + .protectAndExpose("org.eclipse.jetty.ee11.cdi.CdiServletContainerInitializer") + .hide(getHiddenClasses()) + .addDependents(AnnotationConfiguration.class, PlusConfiguration.class)); + } + + private static String[] getHiddenClasses() + { + //Only hide the cdi api classes if there is not also an impl on the + //environment classpath - vital for embedded uses. + if (CdiConfiguration.class.getClassLoader().getResource("META-INF/services/jakarta.enterprise.inject.spi.CDIProvider") == null) + return new String[]{"jakarta.enterprise.", "jakarta.decorator."}; + return new String[0]; + } +} \ No newline at end of file diff --git a/jetty-ee11/jetty-ee11-cdi/src/main/java/org/eclipse/jetty/ee11/cdi/CdiDecoratingListener.java b/jetty-ee11/jetty-ee11-cdi/src/main/java/org/eclipse/jetty/ee11/cdi/CdiDecoratingListener.java new file mode 100644 index 00000000000..be04a0d9269 --- /dev/null +++ b/jetty-ee11/jetty-ee11-cdi/src/main/java/org/eclipse/jetty/ee11/cdi/CdiDecoratingListener.java @@ -0,0 +1,35 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.cdi; + +import org.eclipse.jetty.ee11.servlet.DecoratingListener; +import org.eclipse.jetty.ee11.servlet.ServletContextHandler; + +/** + * A DecoratingListener that listens for "org.eclipse.jetty.cdi.decorator" + */ +public class CdiDecoratingListener extends DecoratingListener +{ + public static final String MODE = "CdiDecoratingListener"; + /** + * Attribute used by Weld to communicate to Jetty that it has created a WeldDecorator + */ + public static final String ATTRIBUTE = "org.eclipse.jetty.cdi.decorator"; + + public CdiDecoratingListener(ServletContextHandler contextHandler) + { + super(contextHandler, ATTRIBUTE); + contextHandler.setAttribute(CdiServletContainerInitializer.CDI_INTEGRATION_ATTRIBUTE, MODE); + } +} diff --git a/jetty-ee11/jetty-ee11-cdi/src/main/java/org/eclipse/jetty/ee11/cdi/CdiServletContainerInitializer.java b/jetty-ee11/jetty-ee11-cdi/src/main/java/org/eclipse/jetty/ee11/cdi/CdiServletContainerInitializer.java new file mode 100644 index 00000000000..cc62cb7d2be --- /dev/null +++ b/jetty-ee11/jetty-ee11-cdi/src/main/java/org/eclipse/jetty/ee11/cdi/CdiServletContainerInitializer.java @@ -0,0 +1,92 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.cdi; + +import java.util.Objects; +import java.util.Set; + +import jakarta.servlet.ServletContainerInitializer; +import jakarta.servlet.ServletContext; +import org.eclipse.jetty.ee11.annotations.AnnotationConfiguration; +import org.eclipse.jetty.ee11.servlet.ServletContextHandler; +import org.eclipse.jetty.util.Loader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + *

A {@link ServletContainerInitializer} that introspects for a CDI API + * implementation within a web application and applies an integration + * mode if CDI is found. CDI integration modes can be selected per webapp with + * the "org.eclipse.jetty.cdi" init parameter or default to the mode set by the + * "org.eclipse.jetty.cdi" server attribute. Supported modes are:

+ *
+ *
CdiSpiDecorator
+ *
Jetty will call the CDI SPI within the webapp to decorate objects (default).
+ *
CdiDecoratingLister
+ *
The webapp may register a decorator on the context attribute + * "org.eclipse.jetty.cdi.decorator".
+ *
+ * + * @see AnnotationConfiguration.ServletContainerInitializerOrdering + */ +public class CdiServletContainerInitializer implements ServletContainerInitializer +{ + public static final String CDI_INTEGRATION_ATTRIBUTE = "org.eclipse.jetty.cdi"; + private static final Logger LOG = LoggerFactory.getLogger(CdiServletContainerInitializer.class); + + @Override + public void onStartup(Set> c, ServletContext ctx) + { + try + { + ServletContextHandler context = ServletContextHandler.getServletContextHandler(ctx); + Objects.requireNonNull(context); + + // Test if CDI is in the webapp by trying to load the CDI class. + ClassLoader loader = context.getClassLoader(); + if (loader == null) + Loader.loadClass("jakarta.enterprise.inject.spi.CDI"); + else + loader.loadClass("jakarta.enterprise.inject.spi.CDI"); + + String mode = ctx.getInitParameter(CDI_INTEGRATION_ATTRIBUTE); + if (mode == null) + { + mode = (String)context.getServer().getAttribute(CDI_INTEGRATION_ATTRIBUTE); + if (mode == null) + mode = CdiSpiDecorator.MODE; + } + + switch (mode) + { + case CdiSpiDecorator.MODE: + context.getObjectFactory().addDecorator(new CdiSpiDecorator(context)); + break; + + case CdiDecoratingListener.MODE: + context.addEventListener(new CdiDecoratingListener(context)); + break; + + default: + throw new IllegalStateException(mode); + } + LOG.info("{} enabled in {}", mode, ctx); + } + catch (UnsupportedOperationException | ClassNotFoundException e) + { + if (LOG.isDebugEnabled()) + LOG.debug("CDI not found in {}", ctx, e); + } + } +} diff --git a/jetty-ee11/jetty-ee11-cdi/src/main/java/org/eclipse/jetty/ee11/cdi/CdiSpiDecorator.java b/jetty-ee11/jetty-ee11-cdi/src/main/java/org/eclipse/jetty/ee11/cdi/CdiSpiDecorator.java new file mode 100644 index 00000000000..c67c6d9065c --- /dev/null +++ b/jetty-ee11/jetty-ee11-cdi/src/main/java/org/eclipse/jetty/ee11/cdi/CdiSpiDecorator.java @@ -0,0 +1,228 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.cdi; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.jetty.ee11.servlet.ServletContextHandler; +import org.eclipse.jetty.util.Decorator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A Decorator that invokes the CDI provider within a webapp to decorate objects created by + * the contexts {@link org.eclipse.jetty.util.DecoratedObjectFactory} + * (typically Listeners, Filters and Servlets). + * The CDI provider is invoked using reflection to avoid any CDI instance + * or dependencies within the server scope. The code invoked is equivalent to: + *
+ * public <T> T decorate(T o)
+ * {
+ *   BeanManager manager = CDI.current().getBeanManager();
+ *   manager.createInjectionTarget(manager.createAnnotatedType((Class<T>)o.getClass()))
+ *     .inject(o,manager.createCreationalContext(null));
+ *   return o;
+ * }
+ * 
+ */ +public class CdiSpiDecorator implements Decorator +{ + private static final Logger LOG = LoggerFactory.getLogger(CdiServletContainerInitializer.class); + + private static final Object NULL_SINGLETON_ARG = null; + private static final Object[] NULL_ARRAY_ARG = new Object[]{null}; + + public static final String MODE = "CdiSpiDecorator"; + + private final ServletContextHandler _context; + private final Map _decorated = new HashMap<>(); + + private final Method _current; + private final Method _getBeanManager; + private final Method _createAnnotatedType; + private final Method _createInjectionTarget; + private final Method _getInjectionTargetFactory; + private final Method _createCreationalContext; + private final Method _inject; + private final Method _dispose; + private final Method _release; + private final Set _undecorated = new HashSet<>(List.of("org.jboss.weld.environment.servlet.Listener", "org.jboss.weld.environment.servlet.EnhancedListener")); + + public CdiSpiDecorator(ServletContextHandler context) throws UnsupportedOperationException + { + _context = context; + context.setAttribute(CdiServletContainerInitializer.CDI_INTEGRATION_ATTRIBUTE, MODE); + ClassLoader classLoader = _context.getClassLoader(); + if (classLoader == null) + classLoader = this.getClass().getClassLoader(); + + try + { + Class cdiClass = classLoader.loadClass("jakarta.enterprise.inject.spi.CDI"); + Class beanClass = classLoader.loadClass("jakarta.enterprise.inject.spi.Bean"); + Class beanManagerClass = classLoader.loadClass("jakarta.enterprise.inject.spi.BeanManager"); + Class annotatedTypeClass = classLoader.loadClass("jakarta.enterprise.inject.spi.AnnotatedType"); + Class injectionTargetClass = classLoader.loadClass("jakarta.enterprise.inject.spi.InjectionTarget"); + Class injectionTargetFactoryClass = classLoader.loadClass("jakarta.enterprise.inject.spi.InjectionTargetFactory"); + Class creationalContextClass = classLoader.loadClass("jakarta.enterprise.context.spi.CreationalContext"); + Class contextualClass = classLoader.loadClass("jakarta.enterprise.context.spi.Contextual"); + + //Use reflection rather than MethodHandles. Reflection respects the classloader that loaded the class, which means + //that as it's a WebAppClassLoader it will do hiding of the cdi spi classes that are on the server classpath. MethodHandles + //see both the cdi api classes from the server classpath and the webapp classpath and throws an exception. + _current = cdiClass.getMethod("current", null); + _getBeanManager = cdiClass.getMethod("getBeanManager", null); + _createAnnotatedType = beanManagerClass.getMethod("createAnnotatedType", Class.class); + _getInjectionTargetFactory = beanManagerClass.getMethod("getInjectionTargetFactory", annotatedTypeClass); + _createInjectionTarget = injectionTargetFactoryClass.getMethod("createInjectionTarget", beanClass); + _createCreationalContext = beanManagerClass.getMethod("createCreationalContext", contextualClass); + _inject = injectionTargetClass.getMethod("inject", Object.class, creationalContextClass); + _dispose = injectionTargetClass.getMethod("dispose", Object.class); + _release = creationalContextClass.getMethod("release", null); + } + catch (Exception e) + { + throw new UnsupportedOperationException(e); + } + } + + /** + * Test if a class can be decorated. + * The default implementation checks the set from {@link #getUndecoratable()} + * on the class and all it's super classes. + * @param clazz The class to check + * @return True if the class and all it's super classes can be decorated + */ + protected boolean isDecoratable(Class clazz) + { + if (Object.class == clazz) + return true; + if (getUndecoratable().contains(clazz.getName())) + return false; + return isDecoratable(clazz.getSuperclass()); + } + + /** + * Get the set of classes that will not be decorated. The default set includes the listener from Weld that will itself + * setup decoration. + * @return The modifiable set of class names that will not be decorated (ie {@link #isDecoratable(Class)} will return false. + * @see #isDecoratable(Class) + */ + public Set getUndecoratable() + { + return _undecorated; + } + + /** + * @param classnames The set of class names that will not be decorated. + * @see #isDecoratable(Class) + */ + public void setUndecoratable(Set classnames) + { + _undecorated.clear(); + if (classnames != null) + _undecorated.addAll(classnames); + } + + /** + * @param classname A class name that will be added to the undecoratable classes set. + * @see #getUndecoratable() + * @see #isDecoratable(Class) + */ + public void addUndecoratable(String... classname) + { + _undecorated.addAll(Arrays.asList()); + } + + /** + * Decorate an object. + *

The signature of this method must match what is introspected for by the + * Jetty DecoratingListener class. It is invoked dynamically.

+ * + * @param o The object to be decorated + * @param The type of the object to be decorated + * @return The decorated object + */ + public T decorate(T o) + { + try + { + if (LOG.isDebugEnabled()) + LOG.debug("decorate {} in {}", o, _context); + + if (isDecoratable(o.getClass())) + _decorated.put(o, new Decorated(o)); + } + catch (Throwable th) + { + LOG.warn("Unable to decorate {}", o, th); + } + return o; + } + + /** + * Destroy a decorated object. + *

The signature of this method must match what is introspected for by the + * Jetty DecoratingListener class. It is invoked dynamically.

+ * + * @param o The object to be destroyed + */ + public void destroy(Object o) + { + try + { + Decorated decorated = _decorated.remove(o); + if (decorated != null) + decorated.destroy(o); + } + catch (Throwable th) + { + LOG.warn("Unable to destroy {}", o, th); + } + } + + private class Decorated + { + private final Object _injectionTarget; + private final Object _creationalContext; + + Decorated(Object o) throws Throwable + { + // BeanManager manager = CDI.current().getBeanManager(); + Object manager = _getBeanManager.invoke(_current.invoke(null)); + // AnnotatedType annotatedType = manager.createAnnotatedType((Class)o.getClass()); + Object annotatedType = _createAnnotatedType.invoke(manager, o.getClass()); + // CreationalContext creationalContext = manager.createCreationalContext(null); + _creationalContext = _createCreationalContext.invoke(manager, NULL_SINGLETON_ARG); + //InjectionTargetFactory injectionTargetFactory = manager.getInjectionTargetFactory(AnnotatedType + + + 4.0.0 + + org.eclipse.jetty.ee11.demos + jetty-ee11-demo-async-rest + 12.1.0-SNAPSHOT + + jetty-ee11-demo-async-rest-jar + jar + EE11 :: Demo :: Async Rest :: Jar + + + ${project.parent.groupId}.async.rest + + + + + org.eclipse.jetty + jetty-client + + + org.eclipse.jetty + jetty-util-ajax + + + jakarta.servlet + jakarta.servlet-api + provided + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-jar/src/main/java/org/eclipse/jetty/ee11/demos/AbstractRestServlet.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-jar/src/main/java/org/eclipse/jetty/ee11/demos/AbstractRestServlet.java new file mode 100644 index 00000000000..b69d29ba32a --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-jar/src/main/java/org/eclipse/jetty/ee11/demos/AbstractRestServlet.java @@ -0,0 +1,137 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.io.IOException; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.net.URLEncoder; +import java.util.Map; +import java.util.Queue; + +import jakarta.servlet.ServletConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +/** + * Abstract Servlet implementation class AsyncRESTServlet. + * Enquires ebay REST service for auctions by key word. + * May be configured with init parameters:
+ *
appid
The eBay application ID to use
+ *
+ * Each request examines the following request parameters:
+ *
items
The keyword to search for
+ *
+ */ +public class AbstractRestServlet extends HttpServlet +{ + protected static final String __DEFAULT_APPID = "Webtide81-adf4-4f0a-ad58-d91e41bbe85"; + protected static final String STYLE = + ""; + + protected static final String ITEMS_PARAM = "items"; + protected static final String APPID_PARAM = "appid"; + + protected String _appid; + + @Override + public void init(ServletConfig servletConfig) throws ServletException + { + if (servletConfig.getInitParameter(APPID_PARAM) == null) + _appid = __DEFAULT_APPID; + else + _appid = servletConfig.getInitParameter(APPID_PARAM); + } + + // TODO: consider using StringUtil.sanitizeFileSystemName instead of this? + // might introduce jetty-util dependency though + public static String sanitize(String str) + { + if (str == null) + return null; + + char[] chars = str.toCharArray(); + int len = chars.length; + for (int i = 0; i < len; i++) + { + char c = chars[i]; + if ((c <= 0x1F) || // control characters + (c == '<') || (c == '&')) + { + chars[i] = '?'; + } + } + return String.valueOf(chars); + } + + protected String restURL(String item) + { + try + { + return ("https://open.api.ebay.com/shopping?MaxEntries=3&appid=" + _appid + + "&version=573&siteid=0&callname=FindItems&responseencoding=JSON&QueryKeywords=" + + URLEncoder.encode(item, "UTF-8")); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + } + + protected String generateThumbs(Queue> results) + { + StringBuilder thumbs = new StringBuilder(); + for (Map m : results) + { + if (!m.containsKey("GalleryURL")) + continue; + + thumbs.append(""); + thumbs.append(""); + thumbs.append(" "); + } + return thumbs.toString(); + } + + protected String ms(long nano) + { + BigDecimal dec = new BigDecimal(nano); + return dec.divide(new BigDecimal(1000000L)).setScale(1, RoundingMode.UP).toString(); + } + + protected int width(long nano) + { + int w = (int)((nano + 999999L) / 5000000L); + if (w == 0) + w = 2; + return w; + } + + @Override + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + doGet(request, response); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-jar/src/main/java/org/eclipse/jetty/ee11/demos/AsyncRestServlet.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-jar/src/main/java/org/eclipse/jetty/ee11/demos/AsyncRestServlet.java new file mode 100644 index 00000000000..90b85dc3cce --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-jar/src/main/java/org/eclipse/jetty/ee11/demos/AsyncRestServlet.java @@ -0,0 +1,208 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.ByteBuffer; +import java.util.Map; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicInteger; + +import jakarta.servlet.AsyncContext; +import jakarta.servlet.ServletConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.Response; +import org.eclipse.jetty.client.Result; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.util.BufferUtil; +import org.eclipse.jetty.util.NanoTime; +import org.eclipse.jetty.util.Utf8StringBuilder; +import org.eclipse.jetty.util.ajax.JSON; + +/** + * Servlet implementation class AsyncRESTServlet. + * Enquires ebay REST service for auctions by key word. + * May be configured with init parameters:
+ *
appid
The eBay application ID to use
+ *
+ * Each request examines the following request parameters:
+ *
items
The keyword to search for
+ *
+ */ +public class AsyncRestServlet extends AbstractRestServlet +{ + static final String RESULTS_ATTR = "org.eclipse.jetty.demo.client"; + static final String DURATION_ATTR = "org.eclipse.jetty.demo.duration"; + static final String START_ATTR = "org.eclispe.jetty.demo.start"; + + HttpClient _client; + + @Override + public void init(ServletConfig servletConfig) throws ServletException + { + super.init(servletConfig); + + _client = new HttpClient(); + + try + { + _client.start(); + } + catch (Exception e) + { + throw new ServletException(e); + } + } + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + long start = NanoTime.now(); + + // Do we have results yet? + @SuppressWarnings("unchecked") + Queue> results = (Queue>)request.getAttribute(RESULTS_ATTR); + + // If no results, this must be the first dispatch, so send the REST request(s) + if (results == null) + { + // define results data structures + results = new ConcurrentLinkedQueue<>(); + request.setAttribute(RESULTS_ATTR, results); + + // suspend the request + // This is done before scheduling async handling to avoid race of + // dispatch before startAsync! + final AsyncContext async = request.startAsync(); + async.setTimeout(30000); + + // extract keywords to search for + String[] keywords = sanitize(request.getParameter(ITEMS_PARAM)).split(","); + final AtomicInteger outstanding = new AtomicInteger(keywords.length); + + // Send request each keyword + Queue> resultsQueue = results; + for (final String item : keywords) + { + _client.newRequest(restURL(item)).method(HttpMethod.GET).send( + new AsyncRestRequest() + { + @Override + void onAuctionFound(Map auction) + { + resultsQueue.add(auction); + } + + @Override + void onComplete() + { + if (outstanding.decrementAndGet() <= 0) + async.dispatch(); + } + }); + } + + // save timing info and return + request.setAttribute(START_ATTR, start); + request.setAttribute(DURATION_ATTR, NanoTime.since(start)); + + return; + } + + // We have results! + + // Generate the response + final String thumbs = generateThumbs(results); + + response.setContentType("text/html"); + PrintWriter out = response.getWriter(); + out.println(""); + out.println(STYLE); + out.println(""); + + long initial = (Long)request.getAttribute(DURATION_ATTR); + long start0 = (Long)request.getAttribute(START_ATTR); + + long now = NanoTime.now(); + long total = NanoTime.elapsed(start0, now); + long generate = NanoTime.elapsed(start, now); + long thread = initial + generate; + + out.print("Asynchronous: " + sanitize(request.getParameter(ITEMS_PARAM)) + "
"); + out.print("Total Time: " + ms(total) + "ms
"); + + out.print("Thread held (red): " + ms(thread) + "ms (" + ms(initial) + " initial + " + ms(generate) + " generate )
"); + out.print("Async wait (green): " + ms(total - thread) + "ms
"); + + out.println("" + + "" + + ""); + + out.println("
"); + out.println(thumbs); + out.println("
"); + out.println(""); + out.close(); + } + + private abstract static class AsyncRestRequest implements Response.Listener + { + private final Utf8StringBuilder _content = new Utf8StringBuilder(); + + AsyncRestRequest() + { + } + + @Override + public void onContent(Response response, ByteBuffer content) + { + byte[] bytes = BufferUtil.toArray(content); + _content.append(bytes, 0, bytes.length); + } + + @Override + public void onComplete(Result result) + { + // extract auctions from the results + @SuppressWarnings("unchecked") + Map query = (Map)new JSON().fromJSON(_content.toCompleteString()); + Object[] auctions = (Object[])query.get("Item"); + if (auctions != null) + { + for (Object o : auctions) + { + @SuppressWarnings("unchecked") + Map auction = (Map)o; + onAuctionFound(auction); + } + } + onComplete(); + } + + abstract void onComplete(); + + abstract void onAuctionFound(Map details); + } + + @Override + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + doGet(request, response); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-jar/src/main/java/org/eclipse/jetty/ee11/demos/SerialRestServlet.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-jar/src/main/java/org/eclipse/jetty/ee11/demos/SerialRestServlet.java new file mode 100644 index 00000000000..46cabfbc7a9 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-jar/src/main/java/org/eclipse/jetty/ee11/demos/SerialRestServlet.java @@ -0,0 +1,96 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.LinkedList; +import java.util.Map; +import java.util.Queue; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.eclipse.jetty.util.NanoTime; +import org.eclipse.jetty.util.ajax.JSON; + +/** + * Servlet implementation class SerialRestServlet + */ +public class SerialRestServlet extends AbstractRestServlet +{ + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + final long start = NanoTime.now(); + + String[] keywords = sanitize(request.getParameter(ITEMS_PARAM)).split(","); + Queue> results = new LinkedList<>(); + + // make all requests serially + for (String itemName : keywords) + { + URL url = new URL(restURL(itemName)); + + HttpURLConnection connection = (HttpURLConnection)url.openConnection(); + connection.setRequestMethod("GET"); + + @SuppressWarnings("unchecked") + Map query = (Map)new JSON().fromJSON(new BufferedReader(new InputStreamReader(connection.getInputStream()))); + Object[] auctions = (Object[])query.get("Item"); + if (auctions != null) + { + for (Object o : auctions) + { + @SuppressWarnings("unchecked") + Map auction = (Map)o; + results.add(auction); + } + } + } + + // Generate the response + final String thumbs = generateThumbs(results); + + response.setContentType("text/html"); + PrintWriter out = response.getWriter(); + out.println(""); + out.println(STYLE); + out.println(""); + + long total = NanoTime.since(start); + + out.print("Blocking: " + sanitize(request.getParameter(ITEMS_PARAM)) + "
"); + out.print("Total Time: " + ms(total) + "ms
"); + out.print("Thread held (red): " + ms(total) + "ms
"); + + out.println(""); + + out.println("
"); + out.println(thumbs); + out.println("
"); + out.println(""); + out.close(); + } + + @Override + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + doGet(request, response); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-jar/src/main/resources/META-INF/resources/asyncrest.html b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-jar/src/main/resources/META-INF/resources/asyncrest.html new file mode 100644 index 00000000000..f92f7f661d4 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-jar/src/main/resources/META-INF/resources/asyncrest.html @@ -0,0 +1,38 @@ + + + + + +

Blocking vs Asynchronous REST

+

+This demo calls the EBay WS API both synchronously and asynchronously, +to obtain items matching each of the keywords passed on the query +string. The time the request thread is head is displayed for both. +

+ + + + + + + + + + + + + +
+ + + +
+ + + +
+ + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-jar/src/main/resources/META-INF/resources/asyncrest/green.png b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-jar/src/main/resources/META-INF/resources/asyncrest/green.png new file mode 100644 index 00000000000..d0fb8420c5d Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-jar/src/main/resources/META-INF/resources/asyncrest/green.png differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-jar/src/main/resources/META-INF/resources/asyncrest/red.png b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-jar/src/main/resources/META-INF/resources/asyncrest/red.png new file mode 100644 index 00000000000..f2a79a07fbc Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-jar/src/main/resources/META-INF/resources/asyncrest/red.png differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-jar/src/main/resources/META-INF/web-fragment.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-jar/src/main/resources/META-INF/web-fragment.xml new file mode 100644 index 00000000000..abd4565faef --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-jar/src/main/resources/META-INF/web-fragment.xml @@ -0,0 +1,28 @@ + + + + + SerialRestServlet + SerialRestServlet + org.eclipse.jetty.ee11.demos.SerialRestServlet + + + SerialRestServlet + /testSerial + + + + AsyncRestServlet + AsyncRestServlet + org.eclipse.jetty.ee11.demos.AsyncRestServlet + true + + + AsyncRestServlet + /testAsync + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-server/pom.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-server/pom.xml new file mode 100644 index 00000000000..626319ed510 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-server/pom.xml @@ -0,0 +1,24 @@ + + + + 4.0.0 + + org.eclipse.jetty.ee11.demos + jetty-ee11-demo-async-rest + 12.1.0-SNAPSHOT + + jetty-ee11-demo-async-rest-server + jar + EE11 :: Demo :: Async Rest :: Server + + + ${project.parent.groupId}.async.rest.server + + + + + org.eclipse.jetty.ee11 + jetty-ee11-webapp + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-server/src/main/java/org/eclipse/jetty/ee11/demos/AsyncRestServer.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-server/src/main/java/org/eclipse/jetty/ee11/demos/AsyncRestServer.java new file mode 100644 index 00000000000..7d27fb3919f --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-server/src/main/java/org/eclipse/jetty/ee11/demos/AsyncRestServer.java @@ -0,0 +1,47 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.eclipse.jetty.ee11.webapp.WebAppContext; +import org.eclipse.jetty.server.Server; + +public class AsyncRestServer +{ + public static void main(String[] args) + throws Exception + { + // Find the async-reset webapp based on common IDE working directories + // TODO import webapp as maven artifact + Path home = FileSystems.getDefault().getPath(System.getProperty("jetty.home", ".")).toAbsolutePath(); + Path war = home.resolve("../async-rest-webapp/target/async-rest/"); + if (!Files.exists(war)) + war = home.resolve("examples/async-rest/async-rest-webapp/target/async-rest/"); + if (!Files.exists(war)) + throw new IllegalArgumentException("Cannot find async-rest webapp"); + + // Build a demo server + Server server = new Server(Integer.getInteger("jetty.http.port", 8080).intValue()); + WebAppContext webapp = new WebAppContext(); + webapp.setContextPath("/"); + webapp.setWar(war.toAbsolutePath().toString()); + server.setHandler(webapp); + + server.start(); + server.join(); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-webapp/pom.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-webapp/pom.xml new file mode 100644 index 00000000000..900f86518e5 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-webapp/pom.xml @@ -0,0 +1,34 @@ + + + + 4.0.0 + + org.eclipse.jetty.ee11.demos + jetty-ee11-demo-async-rest + 12.1.0-SNAPSHOT + + jetty-ee11-demo-async-rest-webapp + war + EE11 :: Demo :: Async Rest :: WebApp + + + + org.eclipse.jetty + jetty-slf4j-impl + compile + + + org.eclipse.jetty.ee11.demos + jetty-ee11-demo-async-rest-jar + + + org.slf4j + slf4j-api + + + jakarta.servlet + jakarta.servlet-api + provided + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-webapp/src/main/config/modules/ee11-demo-async-rest.mod b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-webapp/src/main/config/modules/ee11-demo-async-rest.mod new file mode 100644 index 00000000000..403d9c60ce0 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-webapp/src/main/config/modules/ee11-demo-async-rest.mod @@ -0,0 +1,18 @@ +# DO NOT EDIT THIS FILE - See: https://eclipse.dev/jetty/documentation/ + +[description] +Demo Async Rest webapp + +[environment] +ee11 + +[tags] +demo +webapp + +[depends] +ee11-deploy + +[files] +maven://org.eclipse.jetty.ee11.demos/jetty-ee11-demo-async-rest-webapp/${jetty.version}/war|webapps/ee11-demo-async-rest.war + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-webapp/src/main/webapp/META-INF/MANIFEST.MF b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-webapp/src/main/webapp/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..5e9495128c0 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-webapp/src/main/webapp/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Class-Path: + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-webapp/src/main/webapp/WEB-INF/jetty-web.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-webapp/src/main/webapp/WEB-INF/jetty-web.xml new file mode 100644 index 00000000000..44fcf2b806f --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-webapp/src/main/webapp/WEB-INF/jetty-web.xml @@ -0,0 +1,15 @@ + + + + + + + + The async-rest webapp is deployed. DO NOT USE IN PRODUCTION! + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-webapp/src/main/webapp/WEB-INF/web.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-webapp/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000000..9a182ff8e85 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-webapp/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,9 @@ + + + + EE11 Demo Async REST WebApp + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-webapp/src/main/webapp/demo.css b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-webapp/src/main/webapp/demo.css new file mode 100644 index 00000000000..f2b91d3365d --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-webapp/src/main/webapp/demo.css @@ -0,0 +1,83 @@ +body +{ + font-family: Arial, Verdana, Helvetica, sans-serif; +} + +.topnav +{ + overflow: hidden; + padding: 10px; + border: 1px solid #f6815c; + border-radius: 10px; + text-align: right; +} + +.menu +{ + margin-left: 3em; +} + +.content +{ + padding: 10px; +} + +.footer +{ + padding: 10px; + border-radius: 10px; + border: 1px solid #f6815c; +} + +.test +{ + background-color: #0099cc; + color: white; + padding: 10px 15px; + border: none; + font-size: 12pt; + border-radius: 10px; + box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2), 0 3px 10px 0 rgba(0,0,0,0.19); +} + +.test:hover +{ + background-color: #f6815c; + color: white; +} + +A:link +{ + color: #0099cc; + text-decoration: none; + font-weight: normal; + font-size: 11pt; + font-family:sans-serif; +} + +A:visited +{ + color: #0099cc; + text-decoration: none; + font-weight: normal; + font-size: 11pt; + font-family:sans-serif; +} + +A:hover +{ + color: #ff6600; + text-decoration: none; + font-weight: normal; + font-size: 11pt; + font-family:sans-serif; +} + +A:active +{ + color: #0099cc; + text-decoration: none; + font-weight: normal; + font-size: 11pt; + font-family:sans-serif; +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-webapp/src/main/webapp/index.html b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-webapp/src/main/webapp/index.html new file mode 100644 index 00000000000..cd023d63696 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-webapp/src/main/webapp/index.html @@ -0,0 +1,63 @@ + + + + + + + + +
+
+ Demo Web Application Only - Do NOT Deploy in Production +
+ + +

Blocking vs Asynchronous REST

+

+ This demo calls the EBay WS API both synchronously and asynchronously, to obtain items matching each of the keywords passed on the query string. The time the request thread is held by the servlet is displayed in red for both. +

+ + + + + + + + + + + + + +
+ + + +
+ + + +
+

+ By the use of Asynchronous Servlets and the Jetty Asynchronous client, the server is able to release the thread (green) while waiting for the response from Ebay. This thread goes back into the thread pool and can service many other requests during the wait. This greatly reduces the number of threads needed, which in turn greatly reduces the memory requirements of the server. +

+

+ Press your browser's reload button to see even better results after JIT and TCP/IP warmup! +

+
+ + + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-webapp/src/main/webapp/small_powered_by.gif b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-webapp/src/main/webapp/small_powered_by.gif new file mode 100644 index 00000000000..c5dd44319f0 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/jetty-ee11-demo-async-rest-webapp/src/main/webapp/small_powered_by.gif differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/pom.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/pom.xml new file mode 100644 index 00000000000..a91dd9b4294 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-async-rest/pom.xml @@ -0,0 +1,20 @@ + + + + 4.0.0 + + org.eclipse.jetty.ee11.demos + jetty-ee11-demos + 12.1.0-SNAPSHOT + + jetty-ee11-demo-async-rest + pom + EE11 :: Demo :: Async Rest + + + jetty-ee11-demo-async-rest-jar + jetty-ee11-demo-async-rest-server + jetty-ee11-demo-async-rest-webapp + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/pom.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/pom.xml new file mode 100644 index 00000000000..c673a5ee60a --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/pom.xml @@ -0,0 +1,170 @@ + + + 4.0.0 + + org.eclipse.jetty.ee11.demos + jetty-ee11-demos + 12.1.0-SNAPSHOT + + jetty-ee11-demo-embedded + EE11 :: Demo :: Embedded Jetty + Embedded Jetty Demos + + ${project.groupId}.embedded + + + + jakarta.transaction + jakarta.transaction-api + compile + + + org.eclipse.jetty + jetty-alpn-conscrypt-server + + + org.eclipse.jetty + jetty-alpn-java-server + + + org.eclipse.jetty + jetty-alpn-server + ${project.version} + + + org.eclipse.jetty + jetty-deploy + + + org.eclipse.jetty + jetty-jmx + + + org.eclipse.jetty + jetty-rewrite + + + org.eclipse.jetty + jetty-security + + + org.eclipse.jetty + jetty-util-ajax + + + org.eclipse.jetty.ee11 + jetty-ee11-annotations + + + org.eclipse.jetty.ee11 + jetty-ee11-apache-jsp + + + org.eclipse.jetty.ee11 + jetty-ee11-glassfish-jstl + + + org.eclipse.jetty.ee11 + jetty-ee11-plus + + + org.eclipse.jetty.ee11 + jetty-ee11-proxy + + + org.eclipse.jetty.ee11 + jetty-ee11-servlets + + + org.eclipse.jetty.ee11 + jetty-ee11-webapp + + + org.eclipse.jetty.ee11.demos + jetty-ee11-demo-async-rest-webapp + ${project.version} + war + + + org.eclipse.jetty.ee11.demos + jetty-ee11-demo-jndi-webapp + ${project.version} + war + + + org.eclipse.jetty.ee11.demos + jetty-ee11-demo-jsp-webapp + ${project.version} + war + + + org.eclipse.jetty.ee11.demos + jetty-ee11-demo-mock-resources + + + org.eclipse.jetty.ee11.demos + jetty-ee11-demo-simple-webapp + ${project.version} + war + + + org.eclipse.jetty.ee11.demos + jetty-ee11-demo-spec-webapp + ${project.version} + war + + + org.eclipse.jetty.ee11.websocket + jetty-ee11-websocket-jakarta-server + + + org.eclipse.jetty.ee11.websocket + jetty-ee11-websocket-jetty-server + + + org.eclipse.jetty.ee11.websocket + jetty-ee11-websocket-servlet + + + org.eclipse.jetty.http2 + jetty-http2-server + + + org.slf4j + slf4j-api + + + org.eclipse.jetty + jetty-slf4j-impl + runtime + + + org.eclipse.jetty.toolchain + jetty-test-helper + test + + + org.eclipse.jetty.websocket + jetty-websocket-jetty-client + test + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + ${project.version} + + + ${session.repositorySession.localRepository.basedir.absolutePath} + ${project.version} + + false + + + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/prodDb.properties b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/prodDb.properties new file mode 100644 index 00000000000..5130d856783 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/prodDb.properties @@ -0,0 +1,17 @@ +#HSQL Database Engine 1.8.0.10 +#Mon Nov 08 13:35:35 EST 2010 +hsqldb.script_format=0 +runtime.gc_interval=0 +sql.enforce_strict_size=false +hsqldb.cache_size_scale=8 +readonly=false +hsqldb.nio_data_file=true +hsqldb.cache_scale=14 +version=1.8.0 +hsqldb.default_table_type=memory +hsqldb.cache_file_scale=1 +hsqldb.log_size=200 +modified=no +hsqldb.cache_version=1.7.0 +hsqldb.original_version=1.8.0 +hsqldb.compatible_version=1.8.0 diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/prodDb.script b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/prodDb.script new file mode 100644 index 00000000000..382d243636c --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/prodDb.script @@ -0,0 +1,4 @@ +CREATE SCHEMA PUBLIC AUTHORIZATION DBA +CREATE USER SA PASSWORD "" +GRANT DBA TO SA +SET WRITE_DELAY 10 diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/AsyncEchoServlet.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/AsyncEchoServlet.java new file mode 100644 index 00000000000..02fe46177c5 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/AsyncEchoServlet.java @@ -0,0 +1,116 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.io.IOException; +import java.util.concurrent.atomic.AtomicBoolean; + +import jakarta.servlet.AsyncContext; +import jakarta.servlet.ReadListener; +import jakarta.servlet.ServletInputStream; +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.WriteListener; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +public class AsyncEchoServlet extends HttpServlet +{ + private static final long serialVersionUID = 1L; + + @Override + protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException + { + AsyncContext asyncContext = request.startAsync(request, response); + asyncContext.setTimeout(0); + Echoer echoer = new Echoer(asyncContext); + request.getInputStream().setReadListener(echoer); + response.getOutputStream().setWriteListener(echoer); + } + + private class Echoer implements ReadListener, WriteListener + { + private final byte[] buffer = new byte[4096]; + private final AsyncContext asyncContext; + private final ServletInputStream input; + private final ServletOutputStream output; + private final AtomicBoolean complete = new AtomicBoolean(false); + + private Echoer(AsyncContext asyncContext) throws IOException + { + this.asyncContext = asyncContext; + this.input = asyncContext.getRequest().getInputStream(); + this.output = asyncContext.getResponse().getOutputStream(); + } + + @Override + public void onDataAvailable() throws IOException + { + handleAsyncIO(); + } + + @Override + public void onAllDataRead() throws IOException + { + handleAsyncIO(); + } + + @Override + public void onWritePossible() throws IOException + { + handleAsyncIO(); + } + + private void handleAsyncIO() throws IOException + { + // This method is called: + // 1) after first registering a WriteListener (ready for first write) + // 2) after first registering a ReadListener iff write is ready + // 3) when a previous write completes after an output.isReady() returns false + // 4) from an input callback + + // We should try to read, only if we are able to write! + while (true) + { + if (!output.isReady()) + // Don't even try to read anything until it is possible to write something, + // when onWritePossible will be called + break; + + if (!input.isReady()) + // Nothing available to read, so wait for another call to onDataAvailable + break; + + int read = input.read(buffer); + if (read < 0) + { + if (complete.compareAndSet(false, true)) + asyncContext.complete(); + break; + } + else if (read > 0) + { + output.write(buffer, 0, read); + } + } + } + + @Override + public void onError(Throwable failure) + { + new Throwable("onError", failure).printStackTrace(); + asyncContext.complete(); + } + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/DumpServlet.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/DumpServlet.java new file mode 100644 index 00000000000..f8bb8b284f1 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/DumpServlet.java @@ -0,0 +1,70 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Collections; + +import jakarta.servlet.ServletContext; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +@SuppressWarnings("serial") +public class DumpServlet extends HttpServlet +{ + @Override + protected void doGet(HttpServletRequest request, + HttpServletResponse response) throws IOException + { + response.setContentType("text/html"); + response.setStatus(HttpServletResponse.SC_OK); + + PrintWriter out = response.getWriter(); + + out.println("

DumpServlet

"); + out.println("
");
+        out.println("requestURI=" + request.getRequestURI());
+        out.println("requestURL=" + request.getRequestURL().toString());
+        out.println("contextPath=" + request.getContextPath());
+        out.println("servletPath=" + request.getServletPath());
+        out.println("pathInfo=" + request.getPathInfo());
+        out.println("session=" + request.getSession(true).getId());
+
+        ServletContext servletContext = getServletContext();
+
+        String r = request.getParameter("resource");
+        if (r != null)
+        {
+            out.println("resource(" + r + ")=" + servletContext.getResource(r));
+        }
+
+        Collections.list(request.getAttributeNames())
+            .stream()
+            .filter((name) -> name.startsWith("X-"))
+            .sorted()
+            .forEach((name) ->
+                out.println("request.attribute[" + name + "]=" + request.getAttribute(name)));
+
+        Collections.list(servletContext.getAttributeNames())
+            .stream()
+            .filter((name) -> name.startsWith("X-"))
+            .sorted()
+            .forEach((name) ->
+                out.println("servletContext.attribute[" + name + "]=" + servletContext.getAttribute(name)));
+
+        out.println("
"); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/ExampleServer.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/ExampleServer.java new file mode 100644 index 00000000000..e98e9a1b196 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/ExampleServer.java @@ -0,0 +1,50 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import org.eclipse.jetty.ee11.servlet.ServletContextHandler; +import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.handler.DefaultHandler; + +public class ExampleServer +{ + public static Server createServer(int port) + { + Server server = new Server(); + server.setDefaultHandler(new DefaultHandler()); + + ServerConnector connector = new ServerConnector(server); + connector.setPort(port); + server.setConnectors(new Connector[]{connector}); + + ServletContextHandler context = new ServletContextHandler(); + context.setContextPath("/"); + context.addServlet(HelloServlet.class, "/hello"); + context.addServlet(AsyncEchoServlet.class, "/echo/*"); + + server.setHandler(context); + + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); + server.start(); + server.join(); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/ExampleServerXml.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/ExampleServerXml.java new file mode 100644 index 00000000000..054387cce38 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/ExampleServerXml.java @@ -0,0 +1,46 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.util.resource.ResourceFactory; +import org.eclipse.jetty.xml.XmlConfiguration; + +/** + * Configures and Starts a Jetty server from an XML declaration. + */ +public class ExampleServerXml +{ + public static Server createServer(int port) throws Exception + { + // Find Jetty XML (in classpath) that configures and starts Server. + // See src/main/resources/exampleserver.xml + ResourceFactory.LifeCycle resourceFactory = ResourceFactory.lifecycle(); + Resource serverXml = resourceFactory.newClassLoaderResource("exampleserver.xml"); + XmlConfiguration xml = new XmlConfiguration(serverXml); + xml.getProperties().put("http.port", Integer.toString(port)); + Server server = (Server)xml.configure(); + server.addBean(resourceFactory, true); + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); + server.start(); + server.join(); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/ExampleUtil.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/ExampleUtil.java new file mode 100644 index 00000000000..6a7fd067f28 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/ExampleUtil.java @@ -0,0 +1,81 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import org.eclipse.jetty.util.StringUtil; + +public class ExampleUtil +{ + /** + * Get a port, possibly configured from Command line or System property. + * + * @param args the command line arguments + * @param propertyName the property name + * @param defValue the default value + * @return the configured port + */ + public static int getPort(String[] args, String propertyName, int defValue) + { + for (String arg : args) + { + if (arg.startsWith(propertyName + "=")) + { + String value = arg.substring(propertyName.length() + 2); + int port = toInt(value); + if (isValidPort(port)) + return port; + } + } + + String value = System.getProperty(propertyName); + int port = toInt(value); + if (isValidPort(port)) + return port; + + return defValue; + } + + /** + * Test if port is in the valid range to be used. + * + * @param port the port to test + * @return true if valid + */ + private static boolean isValidPort(int port) + { + return (port >= 0) && (port <= 65535); + } + + /** + * Parse an int, ignoring any {@link NumberFormatException} + * + * @param value the string value to parse + * @return the int (if parsed), or -1 if not parsed. + */ + private static int toInt(String value) + { + if (StringUtil.isBlank(value)) + return -1; + + try + { + return Integer.parseInt(value); + } + catch (NumberFormatException ignored) + { + // ignored + return -1; + } + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/FastFileServer.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/FastFileServer.java new file mode 100644 index 00000000000..be4aa83c055 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/FastFileServer.java @@ -0,0 +1,195 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import org.eclipse.jetty.ee11.servlet.DefaultServlet; +import org.eclipse.jetty.server.handler.ResourceHandler; + +/** + * Fast FileServer. + *

+ * This example shows how to use the Jetty APIs for sending static as fast as + * possible using various strategies for small, medium and large content. + *

+ *

+ * The Jetty {@link DefaultServlet} does all this and more, and to a lesser + * extent so does the {@link ResourceHandler}, so unless you have exceptional + * circumstances it is best to use those classes for static content + *

+ *

+ * WARNING: This is an example on how to send content fast. + * It is not secure, is highly vulnerable, and does not contain the + * common set of mitigations for malicious requests that bypass + * your controls over what a client can access. + * + * If you want to continue this codebase, consider adding + * checks for content outside of the resourceBase, and other + * bypasses such as alias references, alternate stream references, + * filesystem case sensitivity differences, filesystem utf-8 handling + * differences, bad filename concerns, etc.. + * + * Or just use the existing {@link DefaultServlet} or + * {@link ResourceHandler} that gives you all of these protections + * (and more) built-in. + *

+ */ +public class FastFileServer +{ + //TODO + /* public static Server createServer(int port, File resourceBase) + { + Server server = new Server(port); + + server.setHandler(new Handler.Collection( + new FastFileHandler(resourceBase), + new DefaultHandler())); + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + File directory = new File(System.getProperty("user.dir")); + Server server = createServer(port, directory); + server.start(); + server.join(); + } + + static class FastFileHandler extends AbstractHandler + { + private final MimeTypes mimeTypes = new MimeTypes(); + private final File dir; + + private FastFileHandler(File dir) + { + this.dir = dir; + } + + @Override + public void handle(String target, + Request baseRequest, + HttpServletRequest request, + HttpServletResponse response) throws IOException, + ServletException + { + // define small medium and large. + // This should be turned for your content, JVM and OS, but we will + // huge HTTP response buffer size as a measure + final int SMALL = response.getBufferSize(); + final int MEDIUM = 8 * SMALL; + + // What file to serve? + final File file = new File(this.dir, request.getPathInfo()); + + // Only handle existing files + if (!file.exists()) + return; + + // we will handle this request + baseRequest.setHandled(true); + + // Handle directories + if (file.isDirectory()) + { + if (!request.getPathInfo().endsWith(URIUtil.SLASH)) + { + response.sendRedirect(response.encodeRedirectURL(request.getRequestURI() + URIUtil.SLASH)); + return; + } + String listing = Resource.newResource(file).getListHTML( + request.getRequestURI(), + request.getPathInfo().lastIndexOf("/") > 0, + request.getQueryString()); + response.setContentType("text/html; charset=utf-8"); + response.getWriter().println(listing); + return; + } + + // Set some content headers. + + // Jetty DefaultServlet will cache formatted date strings, but we + // will reformat for each request here + response.setDateHeader("Last-Modified", file.lastModified()); + response.setContentLengthLong(file.length()); + response.setContentType(mimeTypes.getMimeByExtension(file.getName())); + + // send "small" files blocking directly from an input stream + if (file.length() < SMALL) + { + // need to caste to Jetty output stream for best API + ((HttpOutput)response.getOutputStream()) + .sendContent(FileChannel.open(file.toPath(), + StandardOpenOption.READ)); + return; + } + + // send not "small" files asynchronously so we don't hold threads if + // the client is slow + final AsyncContext async = request.startAsync(); + Callback completionCB = new Callback() + { + @Override + public void succeeded() + { + // Async content write succeeded, so complete async response + async.complete(); + } + + @Override + public void failed(Throwable x) + { + // log error and complete async response; + x.printStackTrace(); + async.complete(); + } + + @Override + public InvocationType getInvocationType() + { + return InvocationType.NON_BLOCKING; + } + }; + + // send "medium" files from an input stream + if (file.length() < MEDIUM) + { + // the file channel is closed by the async send + ((HttpOutput)response.getOutputStream()) + .sendContent(FileChannel.open(file.toPath(), + StandardOpenOption.READ), completionCB); + return; + } + + // for "large" files get the file mapped buffer to send Typically + // the resulting buffer should be cached as allocating kernel memory + // can be hard to GC on some JVMs. But for this example we will + // create a new buffer per file + ByteBuffer buffer; + try (RandomAccessFile raf = new RandomAccessFile(file, "r");) + { + buffer = raf.getChannel().map(MapMode.READ_ONLY, 0, + raf.length()); + } + + // Assuming the file buffer might be shared cached version, so lets + // take our own view of it + buffer = buffer.asReadOnlyBuffer(); + + // send the content as a buffer with a callback to complete the + // async request need to caste to Jetty output stream for best API + ((HttpOutput)response.getOutputStream()).sendContent(buffer, + completionCB); + } + }*/ +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/FileServer.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/FileServer.java new file mode 100644 index 00000000000..7e333446b15 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/FileServer.java @@ -0,0 +1,69 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.DefaultHandler; +import org.eclipse.jetty.server.handler.ResourceHandler; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.util.resource.ResourceFactory; + +/** + * Simple Jetty FileServer. + * This is a simple example of Jetty configured as a FileServer. + */ +public class FileServer +{ + public static Server createServer(int port, Resource baseResource) throws Exception + { + // Create a basic Jetty server object that will listen on port 8080. Note that if you set this to port 0 + // then a randomly available port will be assigned that you can either look in the logs for the port, + // or programmatically obtain it for use in test cases. + Server server = new Server(port); + server.setDefaultHandler(new DefaultHandler()); + + // Create the ResourceHandler. It is the object that will actually handle the request for a given file. It is + // a Jetty Handler object so it is suitable for chaining with other handlers as you will see in other examples. + ResourceHandler resourceHandler = new ResourceHandler(); + + // Configure the ResourceHandler. Setting the resource base indicates where the files should be served out of. + // In this example it is the current directory but it can be configured to anything that the jvm has access to. + resourceHandler.setDirAllowed(true); + resourceHandler.setWelcomeFiles(List.of("index.html")); + resourceHandler.setBaseResource(baseResource); + + // Add the ResourceHandler to the server. + server.setHandler(resourceHandler); + + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Path userDir = Paths.get(System.getProperty("user.dir")); + Resource pathResource = ResourceFactory.root().newResource(userDir); + + Server server = createServer(port, pathResource); + + // Start things up! By using the server.join() the server thread will join with the current thread. + // See "http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Thread.html#join()" for more details. + server.start(); + server.join(); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/FileServerXml.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/FileServerXml.java new file mode 100644 index 00000000000..c776bb25c57 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/FileServerXml.java @@ -0,0 +1,56 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.util.resource.ResourceFactory; +import org.eclipse.jetty.xml.XmlConfiguration; + +/** + * A Jetty FileServer. + *

+ * This server is identical to {@link FileServer}, except that it is configured + * via an {@link XmlConfiguration} config file that does the identical work. + *

+ */ +public class FileServerXml +{ + public static Server createServer(int port, Path basePath) throws Exception + { + // Find Jetty XML (in classpath) that configures and starts Server. + // See src/main/resources/fileserver.xml + ResourceFactory.LifeCycle resourceFactory = ResourceFactory.lifecycle(); + Resource fileServerXml = resourceFactory.newClassLoaderResource("fileserver.xml"); + Resource baseResource = resourceFactory.newResource(basePath); + XmlConfiguration configuration = new XmlConfiguration(fileServerXml); + configuration.getProperties().put("fileserver.baseResource", baseResource.toString()); + configuration.getProperties().put("http.port", Integer.toString(port)); + Server server = (Server)configuration.configure(); + server.addBean(resourceFactory, true); + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Path userDir = Paths.get(System.getProperty("user.dir")); + Server server = createServer(port, userDir); + server.start(); + server.join(); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/HelloHandler.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/HelloHandler.java new file mode 100644 index 00000000000..ea9e370de3c --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/HelloHandler.java @@ -0,0 +1,54 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import jakarta.servlet.http.HttpServletResponse; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.io.Content; +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Response; +import org.eclipse.jetty.util.Callback; + +public class HelloHandler extends Handler.Abstract +{ + final String greeting; + final String body; + + public HelloHandler() + { + this("Hello World"); + } + + public HelloHandler(String greeting) + { + this(greeting, null); + } + + public HelloHandler(String greeting, String body) + { + this.greeting = greeting; + this.body = body == null ? "" : body; + } + + @Override + public boolean handle(Request request, Response response, Callback callback) throws Exception + { + response.getHeaders().put(HttpHeader.CONTENT_TYPE, "text/html; charset=utf-8"); + response.setStatus(HttpServletResponse.SC_OK); + + Content.Sink.write(response, true, "

" + greeting + "

\n" + body, callback); + return true; + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/HelloServlet.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/HelloServlet.java new file mode 100644 index 00000000000..d0ab3ae648a --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/HelloServlet.java @@ -0,0 +1,48 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.io.IOException; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +@SuppressWarnings("serial") +public class HelloServlet extends HttpServlet +{ + final String greeting; + + public HelloServlet() + { + this("Hello"); + } + + public HelloServlet(String greeting) + { + this.greeting = greeting; + } + + @Override + protected void doGet(HttpServletRequest request, + HttpServletResponse response) throws ServletException, + IOException + { + response.setContentType("text/html"); + response.setStatus(HttpServletResponse.SC_OK); + response.getWriter().println( + "

" + greeting + " from HelloServlet

"); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/HelloSessionServlet.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/HelloSessionServlet.java new file mode 100644 index 00000000000..acfb5761dae --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/HelloSessionServlet.java @@ -0,0 +1,79 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.io.IOException; +import java.io.PrintWriter; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; + +@SuppressWarnings("serial") +public class HelloSessionServlet extends HttpServlet +{ + public HelloSessionServlet() + { + } + + @Override + protected void doGet(HttpServletRequest request, + HttpServletResponse response) throws ServletException, + IOException + { + response.setContentType("text/html"); + response.setStatus(HttpServletResponse.SC_OK); + response.addHeader("Cache-Control", "no-cache"); + + HttpSession session = request.getSession(); + String message; + String link; + + String greeting = request.getParameter("greeting"); + if (greeting != null) + { + session.setAttribute("greeting", greeting); + message = "New greeting '" + greeting + "' set in session."; + link = "Click here to use the new greeting from the session."; + } + else + { + greeting = (String)session.getAttribute("greeting"); + + if (greeting != null) + { + message = "Greeting '" + greeting + "' set from session."; + } + else + { + greeting = "Hello"; + message = "Greeting '" + greeting + "' is default."; + } + + link = "Click here to set a new greeting."; + } + + PrintWriter out = response.getWriter(); + out.println("

" + greeting + " from HelloSessionServlet

"); + out.println("

" + message + "

"); + out.println("
");
+        out.println("session.getId() = " + session.getId());
+        out.println("session.isNew() = " + session.isNew());
+        out.println("
"); + out.println("

" + link + "

"); + } +} + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/HelloWorld.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/HelloWorld.java new file mode 100644 index 00000000000..dc39a9be694 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/HelloWorld.java @@ -0,0 +1,51 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import jakarta.servlet.http.HttpServletResponse; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.io.Content; +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Response; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.Callback; + +public class HelloWorld extends Handler.Abstract +{ + @Override + public boolean handle(Request request, Response response, Callback callback) throws Exception + { + + // Declare response encoding and types + response.getHeaders().put(HttpHeader.CONTENT_TYPE, "text/html; charset=utf-8"); + + // Declare response status code + response.setStatus(HttpServletResponse.SC_OK); + + // Write back response + Content.Sink.write(response, true, "

Hello World

\n", callback); + return true; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = new Server(port); + server.setHandler(new HelloWorld()); + + server.start(); + server.join(); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/Http2Server.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/Http2Server.java new file mode 100644 index 00000000000..7c58cd20b6a --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/Http2Server.java @@ -0,0 +1,191 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Date; +import java.util.EnumSet; +import java.util.Optional; +import java.util.stream.Collectors; + +import jakarta.servlet.DispatcherType; +import jakarta.servlet.Filter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.FilterConfig; +import jakarta.servlet.Servlet; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; +import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory; +import org.eclipse.jetty.ee11.servlet.DefaultServlet; +import org.eclipse.jetty.ee11.servlet.ServletContextHandler; +import org.eclipse.jetty.ee11.servlet.ServletHolder; +import org.eclipse.jetty.http2.HTTP2Cipher; +import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory; +import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory; +import org.eclipse.jetty.jmx.MBeanContainer; +import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.HttpConnectionFactory; +import org.eclipse.jetty.server.SecureRequestCustomizer; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.SslConnectionFactory; +import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.slf4j.LoggerFactory; + +public class Http2Server +{ + public static void main(String... args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + int securePort = ExampleUtil.getPort(args, "jetty.https.port", 8443); + Server server = new Server(); + + MBeanContainer mbContainer = new MBeanContainer( + ManagementFactory.getPlatformMBeanServer()); + server.addBean(mbContainer); + + server.addBean(LoggerFactory.getILoggerFactory()); + + ServletContextHandler context = new ServletContextHandler("/", ServletContextHandler.SESSIONS); + Path docroot = Paths.get("src/main/resources/docroot"); + if (!Files.exists(docroot)) + throw new FileNotFoundException(docroot.toString()); + + context.setBaseResourceAsPath(docroot); + context.addFilter(PushedTilesFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST)); + context.addServlet(new ServletHolder(servlet), "/test/*"); + context.addServlet(DefaultServlet.class, "/").setInitParameter("maxCacheSize", "81920"); + server.setHandler(context); + + // HTTP Configuration + HttpConfiguration httpConfig = new HttpConfiguration(); + httpConfig.setSecureScheme("https"); + httpConfig.setSecurePort(securePort); + httpConfig.setSendXPoweredBy(true); + httpConfig.setSendServerVersion(true); + + // HTTP Connector + ServerConnector http = new ServerConnector(server, new HttpConnectionFactory(httpConfig), new HTTP2CServerConnectionFactory(httpConfig)); + http.setPort(port); + server.addConnector(http); + + // SSL Context Factory for HTTPS and HTTP/2 + Path keystorePath = Paths.get("src/main/resources/etc/keystore.p12").toAbsolutePath(); + if (!Files.exists(keystorePath)) + throw new FileNotFoundException(keystorePath.toString()); + SslContextFactory.Server sslContextFactory = new SslContextFactory.Server(); + sslContextFactory.setKeyStorePath(keystorePath.toString()); + sslContextFactory.setKeyStorePassword("storepwd"); + sslContextFactory.setCipherComparator(HTTP2Cipher.COMPARATOR); + // sslContextFactory.setProvider("Conscrypt"); + + // HTTPS Configuration + HttpConfiguration httpsConfig = new HttpConfiguration(httpConfig); + httpsConfig.addCustomizer(new SecureRequestCustomizer()); + + // HTTP/2 Connection Factory + HTTP2ServerConnectionFactory h2 = new HTTP2ServerConnectionFactory(httpsConfig); + + ALPNServerConnectionFactory alpn = new ALPNServerConnectionFactory(); + alpn.setDefaultProtocol(http.getDefaultProtocol()); + + // SSL Connection Factory + SslConnectionFactory ssl = new SslConnectionFactory(sslContextFactory, alpn.getProtocol()); + + // HTTP/2 Connector + ServerConnector http2Connector = + new ServerConnector(server, ssl, alpn, h2, new HttpConnectionFactory(httpsConfig)); + http2Connector.setPort(securePort); + server.addConnector(http2Connector); + + server.start(); + server.join(); + } + + public static class PushedTilesFilter implements Filter + { + @Override + public void init(FilterConfig filterConfig) + { + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException + { + /* TODO + Request baseRequest = Request.getBaseRequest(request); + + if (baseRequest.isPush() && baseRequest.getRequestURI().contains("tiles")) + { + String uri = baseRequest.getRequestURI().replace("tiles", "pushed").substring(baseRequest.getContextPath().length()); + request.getRequestDispatcher(uri).forward(request, response); + return; + } + + */ + + chain.doFilter(request, response); + } + + @Override + public void destroy() + { + } + } + + static Servlet servlet = new HttpServlet() + { + private static final long serialVersionUID = 1L; + + @Override + protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException + { + String code = request.getParameter("code"); + if (code != null) + response.setStatus(Integer.parseInt(code)); + + HttpSession session = request.getSession(true); + if (session.isNew()) + response.addCookie(new Cookie("bigcookie", + "This is a test cookies that was created on " + new Date() + " and is used by the jetty http/2 test servlet.")); + response.setHeader("Custom", "Value"); + response.setContentType("text/plain"); + String content = "Hello from Jetty using " + request.getProtocol() + "\n"; + content += "uri=" + request.getRequestURI() + "\n"; + content += "session=" + session.getId() + (session.isNew() ? "(New)\n" : "\n"); + content += "date=" + new Date() + "\n"; + + content += Optional.ofNullable(request.getCookies()) + .stream() + .flatMap(Arrays::stream) + .map(cookie -> String.format("cookie %s=%s", cookie.getName(), cookie.getValue())) + .collect(Collectors.joining(System.lineSeparator())); + + response.setContentLength(content.length()); + response.getOutputStream().print(content); + } + }; +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/JarServer.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/JarServer.java new file mode 100644 index 00000000000..89371a48478 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/JarServer.java @@ -0,0 +1,70 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.io.FileNotFoundException; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Objects; + +import org.eclipse.jetty.ee11.servlet.DefaultServlet; +import org.eclipse.jetty.ee11.servlet.ServletContextHandler; +import org.eclipse.jetty.ee11.servlet.ServletHolder; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.FileID; +import org.eclipse.jetty.util.URIUtil; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.util.resource.ResourceFactory; + +/** + * Example of serving content from a JAR file. + * The JAR file in this example does not belong to any Classpath. + */ +public class JarServer +{ + public static Server createServer(int port, URI jarBase) throws Exception + { + Objects.requireNonNull(jarBase); + + URI baseUri = jarBase; + if (FileID.isArchive(baseUri)) + baseUri = URIUtil.toJarFileUri(baseUri); + + Server server = new Server(port); + Resource baseResource = ResourceFactory.of(server).newResource(baseUri); + + ServletContextHandler context = new ServletContextHandler(); + context.setBaseResource(baseResource); + ServletHolder defaultHolder = new ServletHolder("default", new DefaultServlet()); + context.addServlet(defaultHolder, "/"); + + server.setHandler(context); + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + + Path jarFile = Paths.get("src/main/other/content.jar"); + if (!Files.exists(jarFile)) + throw new FileNotFoundException(jarFile.toString()); + + Server server = createServer(port, jarFile.toUri()); + server.start(); + server.join(); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/JettyDemos.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/JettyDemos.java new file mode 100644 index 00000000000..0b75c2d21b9 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/JettyDemos.java @@ -0,0 +1,193 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.stream.Stream; + +import org.eclipse.jetty.util.StringUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A utility test class to locate the Jetty Demo build contents. + *

+ * Looking for content in the /demos/ directory. + *

+ */ +public class JettyDemos +{ + private static final Logger LOG = LoggerFactory.getLogger(JettyDemos.class); + private static final Path JETTY_DEMOS_DIR; + private static String VERSION = System.getProperty("jettyVersion", "unknown"); + + static + { + Path demosDir = asDirectory(System.getProperty("jetty.demos")); + LOG.debug("JettyDemos(prop(jetty.demos)) = {}", demosDir); + if (demosDir == null) + { + demosDir = asDirectory(System.getenv().get("JETTY_DEMOS")); + LOG.debug("JettyDemos(env(JETTY_DEMOS)) = {}", demosDir); + } + + if (demosDir == null || !Files.exists(demosDir.resolve("pom.xml"))) + { + try + { + Path working = Paths.get(System.getProperty("user.dir")); + Path dir = null; + LOG.debug("JettyDemos(prop(user.dir)) = {}", working); + while (dir == null && working != null) + { + dir = asDirectory(working.resolve("jetty-ee11-demos").toString()); + if (dir != null && Files.exists(dir.resolve("pom.xml"))) + { + demosDir = dir; + } + // try one parent up + working = working.getParent(); + } + if (LOG.isDebugEnabled()) + LOG.debug("JettyDemos(working.resolve(...)) = {}", demosDir); + } + catch (Throwable th) + { + LOG.warn("Unable to resolve Jetty Demos location", th); + } + } + JETTY_DEMOS_DIR = demosDir; + String version = "unknown"; + if (version.equals(VERSION)) + { + Path pomFile = demosDir.resolve("pom.xml"); + try (Stream lineStream = Files.lines(pomFile)) + { + String versionLine = lineStream + .filter((line) -> line.contains("")) + .findFirst() + .orElseThrow(() -> new RuntimeException("Unable to find in " + pomFile)); + + version = versionLine.replaceAll("<[^>]*>", "").trim(); + } + catch (IOException e) + { + LOG.warn("Unable to find in " + pomFile, e); + } + + VERSION = version; + } + } + + private static Path asDirectory(String path) + { + try + { + if (path == null) + { + return null; + } + + if (StringUtil.isBlank(path)) + { + LOG.debug("asDirectory {} is blank", path); + return null; + } + + Path dir = Paths.get(path); + if (!Files.exists(dir)) + { + LOG.debug("asDirectory {} does not exist", path); + return null; + } + + if (!Files.isDirectory(dir)) + { + LOG.debug("asDirectory {} is not a directory", path); + return null; + } + + LOG.debug("asDirectory {}", dir); + return dir.toAbsolutePath(); + } + catch (Exception e) + { + LOG.trace("IGNORED", e); + } + return null; + } + + private static Path get() + { + if (JETTY_DEMOS_DIR == null) + throw new RuntimeException("jetty /demos/ dir not found"); + return JETTY_DEMOS_DIR; + } + + public static Path find(String path, MavenCoordinate mavenCoordinate) throws FileNotFoundException + { + try + { + return find(path); + } + catch (FileNotFoundException e) + { + //could be not in the target directory if restored from build cache + // but as the build always runs install the artifact should be there in local repository + } + //.repository/org/eclipse/jetty/ee9/demos/jetty-ee9-demo-jndi-webapp/12.0.0-SNAPSHOT/jetty-ee9-demo-jndi-webapp-12.0.0-SNAPSHOT.war + String version = mavenCoordinate.version().isEmpty() ? VERSION : mavenCoordinate.version(); + Path result = Paths.get(System.getProperty("mavenRepoPath", System.getProperty("user.home") + "/.m2/repository"), + mavenCoordinate.groupId().replaceAll("\\.", "/"), + mavenCoordinate.artifactId(), + version, + mavenCoordinate.artifactId() + "-" + version + "." + mavenCoordinate.packaging() + ); + if (!Files.exists(result)) + { + throw new FileNotFoundException(result.toString()); + } + return result; + } + + @Deprecated + public static Path find(String path) throws FileNotFoundException + { + String expandedPath = path.replaceAll("@VER@", VERSION); + Path result = get().resolve(expandedPath); + if (!Files.exists(result)) + { + throw new FileNotFoundException(result.toString()); + } + return result; + } + + /** + * + * @param groupId Maven groupId + * @param artifactId Maven artifactId + * @param version can be null and default current project version will be used + * @param packaging Maven packaging (war, jar) + */ + public record MavenCoordinate(String groupId, String artifactId, String version, String packaging){} + + public static void main(String... arg) + { + System.err.println("Jetty Demos Dir is " + JETTY_DEMOS_DIR); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/LikeJettyXml.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/LikeJettyXml.java new file mode 100644 index 00000000000..5344e7961e8 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/LikeJettyXml.java @@ -0,0 +1,250 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.eclipse.jetty.deploy.DeploymentManager; +import org.eclipse.jetty.deploy.providers.ContextProvider; +import org.eclipse.jetty.ee11.annotations.AnnotationConfiguration; +import org.eclipse.jetty.ee11.plus.webapp.EnvConfiguration; +import org.eclipse.jetty.ee11.plus.webapp.PlusConfiguration; +import org.eclipse.jetty.ee11.servlet.DebugListener; +import org.eclipse.jetty.ee11.webapp.Configurations; +import org.eclipse.jetty.http.HttpVersion; +import org.eclipse.jetty.io.ConnectionStatistics; +import org.eclipse.jetty.jmx.MBeanContainer; +import org.eclipse.jetty.rewrite.handler.InvalidURIRule; +import org.eclipse.jetty.rewrite.handler.RewriteHandler; +import org.eclipse.jetty.security.HashLoginService; +import org.eclipse.jetty.server.AsyncRequestLogWriter; +import org.eclipse.jetty.server.CustomRequestLog; +import org.eclipse.jetty.server.Deployable; +import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.HttpConnectionFactory; +import org.eclipse.jetty.server.LowResourceMonitor; +import org.eclipse.jetty.server.SecureRequestCustomizer; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.SslConnectionFactory; +import org.eclipse.jetty.server.handler.ContextHandlerCollection; +import org.eclipse.jetty.server.handler.DefaultHandler; +import org.eclipse.jetty.server.handler.StatisticsHandler; +import org.eclipse.jetty.util.component.Environment; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.util.resource.ResourceFactory; +import org.eclipse.jetty.util.resource.Resources; +import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.eclipse.jetty.util.thread.QueuedThreadPool; +import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler; +import org.eclipse.jetty.xml.EnvironmentBuilder; +import org.slf4j.LoggerFactory; + +/** + * Starts the Jetty Distribution's demo-base directory using entirely + * embedded jetty techniques. + */ +public class LikeJettyXml +{ + public static Server createServer(int port, int securePort, boolean addDebugListener) throws Exception + { + Path configDir = Path.of("src/main/resources/demo").toAbsolutePath(); + Path runtimeDir = Path.of("target/embedded/" + LikeJettyXml.class.getSimpleName()).toAbsolutePath(); + mkdir(runtimeDir); + + // === jetty.xml === + // Setup Threadpool + QueuedThreadPool threadPool = new QueuedThreadPool(); + threadPool.setMaxThreads(500); + + // Server + Server server = new Server(threadPool); + server.setDefaultHandler(new DefaultHandler()); + + // Scheduler + server.addBean(new ScheduledExecutorScheduler(null, false, -1)); + + // HTTP Configuration + HttpConfiguration httpConfig = new HttpConfiguration(); + httpConfig.setSecureScheme("https"); + httpConfig.setSecurePort(securePort); + httpConfig.setOutputBufferSize(32768); + httpConfig.setRequestHeaderSize(8192); + httpConfig.setResponseHeaderSize(8192); + httpConfig.setSendServerVersion(true); + httpConfig.setSendDateHeader(false); + // httpConfig.addCustomizer(new ForwardedRequestCustomizer()); + + // Handler Structure + ContextHandlerCollection contexts = new ContextHandlerCollection(); + server.setHandler(contexts); + + // === jetty-jmx.xml === + MBeanContainer mbContainer = new MBeanContainer(ManagementFactory.getPlatformMBeanServer()); + mbContainer.beanAdded(null, LoggerFactory.getILoggerFactory()); + server.addBean(mbContainer); + + // === jetty-http.xml === + ServerConnector http = new ServerConnector(server, new HttpConnectionFactory(httpConfig)); + http.setHost("0.0.0.0"); + http.setPort(port); + http.setIdleTimeout(30000); + server.addConnector(http); + + // === jetty-ssl-context.xml === + // SSL Context Factory + Path keystorePath = Paths.get("src/main/resources/etc/keystore.p12").toAbsolutePath(); + if (!Files.exists(keystorePath)) + throw new FileNotFoundException(keystorePath.toString()); + SslContextFactory.Server sslContextFactory = new SslContextFactory.Server(); + sslContextFactory.setKeyStorePath(keystorePath.toString()); + sslContextFactory.setKeyStorePassword("storepwd"); + sslContextFactory.setTrustStorePath(keystorePath.toString()); + sslContextFactory.setTrustStorePassword("storepwd"); + + // === jetty-ssl.xml === + // SSL HTTP Configuration + HttpConfiguration httpsConfig = new HttpConfiguration(httpConfig); + httpsConfig.addCustomizer(new SecureRequestCustomizer()); + + // === jetty-https.xml === + // SSL Connector + ServerConnector sslConnector = new ServerConnector(server, + new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()), + new HttpConnectionFactory(httpsConfig)); + sslConnector.setPort(securePort); + server.addConnector(sslConnector); + + // === jetty-deploy.xml === + DeploymentManager deployer = new DeploymentManager(); + if (addDebugListener) + { + DebugListener debug = new DebugListener(System.err, true, true, true); + server.addBean(debug); + } + deployer.setContexts(contexts); + + Path webappsDir = runtimeDir.resolve("webapps"); + mkdir(webappsDir); + + Path testWebapp = webappsDir.resolve("test.war"); + if (!Files.exists(testWebapp)) + { + JettyDemos.MavenCoordinate mavenCoordinate = new JettyDemos.MavenCoordinate("org.eclipse.jetty.ee11.demos", + "jetty-ee11-demo-simple-webapp", "", "war"); + Path testWebappSrc = JettyDemos.find("jetty-ee11-demo-simple-webapp/target/jetty-ee11-demo-simple-webapp-@VER@.war", mavenCoordinate); + Files.copy(testWebappSrc, testWebapp); + } + + // == Build ee11 Environment == + // Support for environment specific classpath / modulepath goes here + String environmentName = "ee11"; + Environment ee11 = Environment.get(environmentName); + if (ee11 == null) + { + ee11 = new EnvironmentBuilder(environmentName).build(); + Environment.set(ee11); + } + + // === jetty-ee11-deploy.xml === + ee11.setAttribute("contextHandlerClass", org.eclipse.jetty.ee11.webapp.WebAppContext.class.getName()); + ContextProvider webAppProvider = new ContextProvider(); + webAppProvider.setEnvironmentName(environmentName); + webAppProvider.setMonitoredDirName(webappsDir.toString()); + webAppProvider.setDefaultsDescriptor(configDir.resolve("webdefault-ee11.xml").toString()); + webAppProvider.setScanInterval(1); + webAppProvider.setExtractWars(true); + webAppProvider.getProperties().put(Deployable.CONTAINER_SCAN_JARS, + ".*/jakarta.servlet-api-[^/]*\\.jar$|.*jakarta.servlet.jsp.jstl-.*\\.jar$|.*/[^/]*taglibs.*\\.jar$"); + + deployer.addAppProvider(webAppProvider); + server.addBean(deployer); + + // === setup jetty plus == + Configurations.setServerDefault(server) + .add(new EnvConfiguration(), new PlusConfiguration(), new AnnotationConfiguration()); + + // === jetty-stats.xml === + StatisticsHandler stats = new StatisticsHandler(); + stats.setHandler(server.getHandler()); + server.setHandler(stats); + server.addBeanToAllConnectors(new ConnectionStatistics()); + + // === Rewrite Handler + RewriteHandler rewrite = new RewriteHandler(); + rewrite.setHandler(server.getHandler()); + server.setHandler(rewrite); + rewrite.addRule(new InvalidURIRule()); + + // === jetty-requestlog.xml === + Path logsDir = runtimeDir.resolve("logs"); + mkdir(logsDir); + AsyncRequestLogWriter logWriter = new AsyncRequestLogWriter(logsDir.resolve("yyyy_mm_dd.request.log").toString()); + logWriter.setFilenameDateFormat("yyyy_MM_dd"); + logWriter.setRetainDays(90); + logWriter.setTimeZone("GMT"); + CustomRequestLog requestLog = new CustomRequestLog(logWriter, CustomRequestLog.EXTENDED_NCSA_FORMAT + " \"%C\""); + server.setRequestLog(requestLog); + + // === jetty-lowresources.xml === + LowResourceMonitor lowResourcesMonitor = new LowResourceMonitor(server); + lowResourcesMonitor.setPeriod(1000); + lowResourcesMonitor.setLowResourcesIdleTimeout(200); + lowResourcesMonitor.setMonitorThreads(true); + lowResourcesMonitor.setMaxMemory(0); + lowResourcesMonitor.setMaxLowResourcesTime(5000); + server.addBean(lowResourcesMonitor); + + // === Realm === + HashLoginService login = new HashLoginService(); + login.setName("Test Realm"); + Path realmFile = configDir.resolve("ee11-demo-realm.properties"); + Resource realmResource = ResourceFactory.of(server).newResource(realmFile); + if (!Resources.isReadableFile(realmResource)) + throw new FileNotFoundException("Unable to find config: " + realmFile); + login.setConfig(realmResource); + login.setReloadInterval(0); + server.addBean(login); + + return server; + } + + private static void mkdir(Path path) throws IOException + { + if (Files.exists(path)) + return; + Files.createDirectories(path); + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + int securePort = ExampleUtil.getPort(args, "jetty.https.port", 8443); + Server server = createServer(port, securePort, true); + + // Extra options + server.setDumpAfterStart(true); + server.setDumpBeforeStop(false); + server.setStopAtShutdown(true); + + // Start the server + server.start(); + server.join(); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/ManyConnectors.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/ManyConnectors.java new file mode 100644 index 00000000000..43eb0168a14 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/ManyConnectors.java @@ -0,0 +1,134 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.io.FileNotFoundException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.eclipse.jetty.http.HttpVersion; +import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.HttpConnectionFactory; +import org.eclipse.jetty.server.SecureRequestCustomizer; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.SslConnectionFactory; +import org.eclipse.jetty.util.ssl.SslContextFactory; + +/** + * A Jetty server with multiple connectors. + */ +public class ManyConnectors +{ + public static Server createServer(int plainPort, int securePort) throws Exception + { + // Since this example shows off SSL configuration, we need a keystore + // with the appropriate key. + Path keystorePath = Paths.get("src/main/resources/etc/keystore.p12").toAbsolutePath(); + if (!Files.exists(keystorePath)) + throw new FileNotFoundException(keystorePath.toString()); + + // Create a basic jetty server object without declaring the port. Since + // we are configuring connectors directly we'll be setting ports on + // those connectors. + Server server = new Server(); + + // HTTP Configuration + // HttpConfiguration is a collection of configuration information + // appropriate for http and https. The default scheme for http is + // http of course, as the default for secured http is + // https but we show setting the scheme to show it can be + // done. The port for secured communication is also set here. + HttpConfiguration httpConfig = new HttpConfiguration(); + httpConfig.setSecureScheme("https"); + httpConfig.setSecurePort(securePort); + httpConfig.setOutputBufferSize(32768); + + // HTTP connector + // The first server connector we create is the one for http, passing in + // the http configuration we configured above so it can get things like + // the output buffer size, etc. We also set the port (8080) and + // configure an idle timeout. + ServerConnector http = new ServerConnector(server, + new HttpConnectionFactory(httpConfig)); + http.setPort(plainPort); + http.setIdleTimeout(30000); + + // SSL Context Factory for HTTPS + // SSL requires a certificate so we configure a factory for ssl contents + // with information pointing to what keystore the ssl connection needs + // to know about. Much more configuration is available the ssl context, + // including things like choosing the particular certificate out of a + // keystore to be used. + + SslContextFactory.Server sslContextFactory = new SslContextFactory.Server(); + sslContextFactory.setKeyStorePath(keystorePath.toString()); + sslContextFactory.setKeyStorePassword("storepwd"); + + // OPTIONAL: Un-comment the following to use Conscrypt for SSL instead of + // the native JSSE implementation. + + //Security.addProvider(new OpenSSLProvider()); + //sslContextFactory.setProvider("Conscrypt"); + + // HTTPS Configuration + // A new HttpConfiguration object is needed for the next connector and + // you can pass the old one as an argument to effectively clone the + // contents. On this HttpConfiguration object we add a + // SecureRequestCustomizer which is how a new connector is able to + // resolve the https connection before handing control over to the Jetty + // Server. + HttpConfiguration httpsConfig = new HttpConfiguration(httpConfig); + SecureRequestCustomizer src = new SecureRequestCustomizer(); + src.setStsMaxAge(2000); + src.setStsIncludeSubDomains(true); + httpsConfig.addCustomizer(src); + + // HTTPS connector + // We create a second ServerConnector, passing in the http configuration + // we just made along with the previously created ssl context factory. + // Next we set the port and a longer idle timeout. + ServerConnector https = new ServerConnector(server, + new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()), + new HttpConnectionFactory(httpsConfig)); + https.setPort(securePort); + https.setIdleTimeout(500000); + + // Here you see the server having multiple connectors registered with + // it, now requests can flow into the server from both http and https + // urls to their respective ports and be processed accordingly by jetty. + // A simple handler is also registered with the server so the example + // has something to pass requests off to. + + // Set the connectors + server.setConnectors(new Connector[]{http, https}); + + // Set a handler + server.setHandler(new HelloHandler()); + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + int securePort = ExampleUtil.getPort(args, "jetty.https.port", 8443); + Server server = createServer(port, securePort); + // Start the server + server.start(); + server.dumpStdErr(); + server.join(); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/ManyContexts.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/ManyContexts.java new file mode 100644 index 00000000000..46a6f36054a --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/ManyContexts.java @@ -0,0 +1,58 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.util.List; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.server.handler.ContextHandlerCollection; + +public class ManyContexts +{ + public static Server createServer(int port) + { + Server server = new Server(port); + + ContextHandler context = new ContextHandler("/"); + context.setContextPath("/"); + context.setHandler(new HelloHandler("Root Hello")); + + ContextHandler contextFR = new ContextHandler("/fr"); + contextFR.setHandler(new HelloHandler("Bonjour")); + + ContextHandler contextIT = new ContextHandler("/it"); + contextIT.setHandler(new HelloHandler("Buongiorno")); + + ContextHandler contextV = new ContextHandler("/"); + contextV.setVirtualHosts(List.of("127.0.0.2")); + contextV.setHandler(new HelloHandler("Virtual Hello")); + + ContextHandlerCollection contexts = new ContextHandlerCollection( + context, contextFR, contextIT, contextV + ); + + server.setHandler(contexts); + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); + server.start(); + server.dumpStdErr(); + server.join(); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/ManyHandlers.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/ManyHandlers.java new file mode 100644 index 00000000000..ab0768f6471 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/ManyHandlers.java @@ -0,0 +1,158 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.MimeTypes; +import org.eclipse.jetty.server.CustomRequestLog; +import org.eclipse.jetty.server.FormFields; +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Response; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.server.handler.ContextHandlerCollection; +import org.eclipse.jetty.server.handler.DefaultHandler; +import org.eclipse.jetty.server.handler.gzip.GzipHandler; +import org.eclipse.jetty.util.Callback; +import org.eclipse.jetty.util.Fields; +import org.eclipse.jetty.util.ajax.JSON; + +/** + * Frequently many handlers are combined together to handle different aspects of + * a request. A handler may: + *
    + *
  • handle the request and completely generate the response + *
  • partially handle the request, but defer response generation to another + * handler. + *
  • select another handler to pass the request to. + *
  • use business logic to decide to do one of the above. + *
+ * Multiple handlers may be combined with: + *
    + *
  • {@link Handler.Wrapper} which will nest one handler inside another. In + * this example, the HelloHandler is nested inside a HandlerWrapper that sets + * the greeting as a request attribute. + *
  • {@link Handler.Sequence} which will call a collection of handlers until the + * request is marked as handled. In this example, a list is used to combine the + * param handler (which only handles the request if there are parameters) and + * the wrapper handler. Frequently handler lists are terminated with the + * {@link DefaultHandler}, which will generate a suitable 404 response if the + * request has not been handled. + *
  • {@link Handler.Sequence} which will call each handler regardless if the + * request has been handled or not. Typically this is used to always pass a + * request to the logging handler. + *
+ */ +public class ManyHandlers +{ + /** + * Produce output that lists all of the request parameters + */ + public static class ParamHandler extends Handler.Abstract + { + @Override + public boolean handle(Request request, Response response, Callback callback) throws Exception + { + Fields queryFields = Request.extractQueryParameters(request); + Fields formFields = FormFields.from(request).get(); + + response.getHeaders().put(HttpHeader.CONTENT_TYPE, MimeTypes.Type.TEXT_JSON.asString()); + response.write(true, + ByteBuffer.wrap(new JSON().toJSON(List.of(queryFields, formFields)).getBytes(StandardCharsets.UTF_8)), + callback); + + return true; + } + } + + /** + * Add a request attribute, but produce no output. + */ + public static class WelcomeWrapHandler extends Handler.Wrapper + { + @Override + public boolean handle(Request request, Response response, Callback callback) throws Exception + { + response.getHeaders().add("X-Welcome", "Greetings from WelcomeWrapHandler"); + return super.handle(request, response, callback); + } + } + + public static Server createServer(int port) throws IOException + { + Server server = new Server(port); + server.setDefaultHandler(new DefaultHandler()); + + // create the handlers + Handler param = new ParamHandler(); + Handler.Singleton wrapper = new WelcomeWrapHandler(); + Handler hello = new HelloHandler(); + GzipHandler gzipHandler = new GzipHandler(); + gzipHandler.setMinGzipSize(10); + gzipHandler.addIncludedMimeTypes("text/plain"); + gzipHandler.addIncludedMimeTypes("text/html"); + + // configure request logging + Path requestLogFile = Files.createTempFile("demo", "log"); + CustomRequestLog ncsaLog = new CustomRequestLog(requestLogFile.toString()); + server.setRequestLog(ncsaLog); + + // wrap contexts around specific handlers + wrapper.setHandler(hello); + ContextHandler helloContext = new ContextHandler("/hello"); + helloContext.setHandler(wrapper); + + ContextHandler paramContext = new ContextHandler("/params"); + paramContext.setHandler(param); + + ContextHandlerCollection contexts = new ContextHandlerCollection(helloContext, paramContext); + + // Wrap Contexts with GZIP + gzipHandler.setHandler(contexts); + + // Set the top level Handler List + server.setHandler(gzipHandler); + + /* + * At this point you have the following handler hierarchy: + * Server.handler: + * \- GzipHandler + * | \- ContextHandlerCollection + * | \- ContextHandler ("/hello") + * | | \- WelcomeWrapHandler + * | | \- HelloHandler + * | \- ContextHandler ("/params") + * | \- ParamHandler + * \- DefaultHandler + */ + + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); + server.start(); + server.join(); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/ManyServletContexts.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/ManyServletContexts.java new file mode 100644 index 00000000000..328afe15ab5 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/ManyServletContexts.java @@ -0,0 +1,67 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.lang.management.ManagementFactory; + +import org.eclipse.jetty.ee11.servlet.DefaultServlet; +import org.eclipse.jetty.ee11.servlet.ServletContextHandler; +import org.eclipse.jetty.ee11.servlet.ServletHolder; +import org.eclipse.jetty.jmx.MBeanContainer; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.ContextHandlerCollection; + +public class ManyServletContexts +{ + public static Server createServer(int port) + { + Server server = new Server(port); + + // Setup JMX + MBeanContainer mbContainer = new MBeanContainer( + ManagementFactory.getPlatformMBeanServer()); + server.addBean(mbContainer, true); + + // Declare server handler collection + ContextHandlerCollection contexts = new ContextHandlerCollection(); + server.setHandler(contexts); + + // Configure context "/" (root) for servlets + ServletContextHandler root = new ServletContextHandler("/", + ServletContextHandler.SESSIONS); + contexts.addHandler(root); + // Add servlets to root context + root.addServlet(new ServletHolder(new HelloServlet("Hello")), "/"); + root.addServlet(new ServletHolder(new HelloServlet("Ciao")), "/it/*"); + root.addServlet(new ServletHolder(new HelloServlet("Bonjour")), "/fr/*"); + + // Configure context "/other" for servlets + ServletContextHandler other = new ServletContextHandler("/other", ServletContextHandler.SESSIONS); + contexts.addHandler(other); + // Add servlets to /other context + other.addServlet(DefaultServlet.class.getCanonicalName(), "/"); + other.addServlet(new ServletHolder(new HelloServlet("YO!")), "*.yo"); + + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); + server.start(); + server.dumpStdErr(); + server.join(); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/MinimalServlets.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/MinimalServlets.java new file mode 100644 index 00000000000..ee089c57329 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/MinimalServlets.java @@ -0,0 +1,76 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.io.IOException; + +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.eclipse.jetty.ee11.servlet.ServletContextHandler; +import org.eclipse.jetty.server.Server; + +public class MinimalServlets +{ + + public static Server createServer(int port) + { + // Note that if you set this to port 0 then a randomly available port + // will be assigned that you can either look in the logs for the port, + // or programmatically obtain it for use in test cases. + Server server = new Server(port); + + + ServletContextHandler handler = new ServletContextHandler(); + server.setHandler(handler); + + // Passing in the class for the Servlet allows jetty to instantiate an + // instance of that Servlet and mount it on a given context path. + + // IMPORTANT: + // This is a raw Servlet, not a Servlet that has been configured + // through a web.xml @WebServlet annotation, or anything similar. + handler.getServletHandler().addServletWithMapping(HelloServlet.class, "/*"); + + return server; + } + + public static void main(String[] args) throws Exception + { + // Create a basic jetty server object that will listen on port 8080. + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); + + // Start things up! + server.start(); + + // The use of server.join() the will make the current thread join and + // wait until the server thread is done executing. + server.join(); + } + + @SuppressWarnings("serial") + public static class HelloServlet extends HttpServlet + { + @Override + protected void doGet(HttpServletRequest request, + HttpServletResponse response) throws IOException + { + response.setStatus(HttpServletResponse.SC_OK); + response.setContentType("text/html"); + response.setCharacterEncoding("utf-8"); + response.getWriter().println("

Hello from HelloServlet

"); + } + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/OneConnector.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/OneConnector.java new file mode 100644 index 00000000000..0d9e40339aa --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/OneConnector.java @@ -0,0 +1,52 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; + +/** + * A Jetty server with one connectors. + */ +public class OneConnector +{ + public static Server createServer(int port) throws Exception + { + // The Server + Server server = new Server(); + + // HTTP connector + ServerConnector http = new ServerConnector(server); + http.setHost("localhost"); + http.setPort(port); + http.setIdleTimeout(30000); + + // Set the connector + server.addConnector(http); + + // Set a handler + server.setHandler(new HelloHandler()); + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); + + // Start the server + server.start(); + server.join(); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/OneContext.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/OneContext.java new file mode 100644 index 00000000000..38b330dde8a --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/OneContext.java @@ -0,0 +1,45 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.ContextHandler; + +public class OneContext +{ + public static Server createServer(int port) + { + Server server = new Server(port); + + // Add a single handler on context "/hello" + ContextHandler context = new ContextHandler(); + context.setContextPath("/hello"); + context.setHandler(new HelloHandler()); + + // Can be accessed using http://localhost:8080/hello + + server.setHandler(context); + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); + + // Start the server + server.start(); + server.join(); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/OneHandler.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/OneHandler.java new file mode 100644 index 00000000000..520a5e6f6e0 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/OneHandler.java @@ -0,0 +1,34 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import org.eclipse.jetty.server.Server; + +public class OneHandler +{ + public static Server createServer(int port) + { + Server server = new Server(port); + server.setHandler(new HelloHandler()); + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); + server.start(); + server.join(); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/OneServletContext.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/OneServletContext.java new file mode 100644 index 00000000000..a549077962d --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/OneServletContext.java @@ -0,0 +1,140 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.EnumSet; + +import jakarta.servlet.Filter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.FilterConfig; +import jakarta.servlet.ServletContextEvent; +import jakarta.servlet.ServletContextListener; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletRequestEvent; +import jakarta.servlet.ServletRequestListener; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServletResponse; +import org.eclipse.jetty.ee11.servlet.DefaultServlet; +import org.eclipse.jetty.ee11.servlet.ListenerHolder; +import org.eclipse.jetty.ee11.servlet.ServletContextHandler; +import org.eclipse.jetty.ee11.servlet.ServletHolder; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.util.resource.ResourceFactory; + +import static jakarta.servlet.DispatcherType.ASYNC; +import static jakarta.servlet.DispatcherType.REQUEST; + +public class OneServletContext +{ + public static Server createServer(int port, Resource baseResource) + { + Server server = new Server(port); + + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); + context.setContextPath("/"); + context.setBaseResource(baseResource); + server.setHandler(context); + + // add hello servlet + context.addServlet(HelloServlet.class, "/hello/*"); + + // Add dump servlet on multiple url-patterns + ServletHolder debugHolder = new ServletHolder("debug", DumpServlet.class); + context.addServlet(debugHolder, "/dump/*"); + context.addServlet(debugHolder, "*.dump"); + + // add default servlet (for error handling and static resources) + context.addServlet(DefaultServlet.class, "/"); + + // sprinkle in a few filters to demonstrate behaviors + context.addFilter(TestFilter.class, "/test/*", EnumSet.of(REQUEST)); + context.addFilter(TestFilter.class, "*.test", EnumSet.of(REQUEST, ASYNC)); + + // and a few listeners to show other ways of working with servlets + context.getServletHandler().addListener(new ListenerHolder(InitListener.class)); + context.getServletHandler().addListener(new ListenerHolder(RequestListener.class)); + + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Path tempDir = Paths.get(System.getProperty("java.io.tmpdir")); + + Server server = createServer(port, ResourceFactory.root().newResource(tempDir)); + + server.start(); + server.dumpStdErr(); + server.join(); + } + + public static class TestFilter implements Filter + { + @Override + public void init(FilterConfig filterConfig) + { + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException + { + if (response instanceof HttpServletResponse) + { + HttpServletResponse httpServletResponse = (HttpServletResponse)response; + httpServletResponse.setHeader("X-TestFilter", "true"); + } + chain.doFilter(request, response); + } + + @Override + public void destroy() + { + + } + } + + public static class InitListener implements ServletContextListener + { + @Override + public void contextInitialized(ServletContextEvent sce) + { + sce.getServletContext().setAttribute("X-Init", "true"); + } + + @Override + public void contextDestroyed(ServletContextEvent sce) + { + } + } + + public static class RequestListener implements ServletRequestListener + { + @Override + public void requestInitialized(ServletRequestEvent sre) + { + sre.getServletRequest().setAttribute("X-ReqListener", "true"); + } + + @Override + public void requestDestroyed(ServletRequestEvent sre) + { + } + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/OneServletContextJmxStats.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/OneServletContextJmxStats.java new file mode 100644 index 00000000000..80cfdb6a53d --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/OneServletContextJmxStats.java @@ -0,0 +1,55 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.lang.management.ManagementFactory; + +import org.eclipse.jetty.ee11.servlet.DefaultServlet; +import org.eclipse.jetty.ee11.servlet.ServletContextHandler; +import org.eclipse.jetty.io.ConnectionStatistics; +import org.eclipse.jetty.jmx.MBeanContainer; +import org.eclipse.jetty.server.Server; + +public class OneServletContextJmxStats +{ + public static Server createServer(int port) + { + Server server = new Server(port); + + // Add JMX tracking to Server + server.addBean(new MBeanContainer(ManagementFactory + .getPlatformMBeanServer())); + + ServletContextHandler context = new ServletContextHandler( + ServletContextHandler.SESSIONS); + context.setContextPath("/"); + server.setHandler(context); + + context.addServlet(DumpServlet.class, "/dump/*"); + context.addServlet(DefaultServlet.class, "/"); + + // Add Connector Statistics tracking to all connectors + server.addBeanToAllConnectors(new ConnectionStatistics()); + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); + + server.start(); + server.join(); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/OneServletContextWithSession.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/OneServletContextWithSession.java new file mode 100644 index 00000000000..a843af1efbd --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/OneServletContextWithSession.java @@ -0,0 +1,72 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.eclipse.jetty.ee11.servlet.ServletContextHandler; +import org.eclipse.jetty.ee11.servlet.SessionHandler; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.session.DefaultSessionCache; +import org.eclipse.jetty.session.NullSessionDataStore; +import org.eclipse.jetty.session.SessionCache; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.util.resource.ResourceFactory; + +public class OneServletContextWithSession +{ + public static Server createServer(int port, Resource baseResource) + { + Server server = new Server(port); + + // Create a ServletContext, with a session handler enabled. + ServletContextHandler context = new ServletContextHandler( + ServletContextHandler.SESSIONS); + context.setContextPath("/"); + context.setBaseResource(baseResource); + server.setHandler(context); + + // Access the SessionHandler from the context. + SessionHandler sessions = context.getSessionHandler(); + + // Explicitly set Session Cache and null Datastore. + // This is normally done by default, + // but is done explicitly here for demonstration. + // If more than one context is to be deployed, it is + // simpler to use SessionCacheFactory and/or + // SessionDataStoreFactory instances set as beans on + // the server. + SessionCache cache = new DefaultSessionCache(sessions); + cache.setSessionDataStore(new NullSessionDataStore()); + sessions.setSessionCache(cache); + + // Servlet to read/set the greeting stored in the session. + // Can be accessed using http://localhost:8080/hello + context.addServlet(HelloSessionServlet.class, "/"); + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Path dir = Paths.get(System.getProperty("user.dir")); + Resource baseResource = ResourceFactory.root().newResource(dir); + Server server = createServer(port, baseResource); + + server.start(); + server.dumpStdErr(); + server.join(); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/OneWebApp.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/OneWebApp.java new file mode 100644 index 00000000000..d165af7d9fc --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/OneWebApp.java @@ -0,0 +1,69 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.io.IOException; +import java.nio.file.Path; + +import org.eclipse.jetty.ee11.webapp.Configurations; +import org.eclipse.jetty.ee11.webapp.WebAppContext; +import org.eclipse.jetty.server.Server; + +public class OneWebApp +{ + public static Server createServer(int port) throws IOException + { + // Create a basic jetty server object that will listen on port 8080. + // Note that if you set this to port 0 then a randomly available port + // will be assigned that you can either look in the logs for the port, + // or programmatically obtain it for use in test cases. + Server server = new Server(port); + + // The WebAppContext is the entity that controls the environment in + // which a web application lives and breathes. In this example the + // context path is being set to "/" so it is suitable for serving root + // context requests and then we see it setting the location of the war. + // A whole host of other configurations are available, ranging from + // configuring to support annotation scanning in the webapp (through + // PlusConfiguration) to choosing where the webapp will unpack itself. + WebAppContext webapp = new WebAppContext(); + webapp.setContextPath("/"); + JettyDemos.MavenCoordinate mavenCoordinate = new JettyDemos.MavenCoordinate("org.eclipse.jetty.ee11.demos", + "jetty-ee11-demo-async-rest-webapp", "", "war"); + Path warFile = JettyDemos.find("demo-async-rest/demo-async-rest-webapp/target/demo-async-rest-webapp-@VER@.war", mavenCoordinate); + webapp.setWar(warFile.toString()); + + // A WebAppContext is a ContextHandler as well so it needs to be set to + // the server so it is aware of where to send the appropriate requests. + server.setHandler(webapp); + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); + + Configurations.setServerDefault(server); + + // Start things up! + server.start(); + + server.dumpStdErr(); + + // The use of server.join() the will make the current thread join and + // wait until the server is done executing. + server.join(); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/OneWebAppWithJsp.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/OneWebAppWithJsp.java new file mode 100644 index 00000000000..e76c7979670 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/OneWebAppWithJsp.java @@ -0,0 +1,106 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.io.FileNotFoundException; +import java.nio.file.Path; + +import org.eclipse.jetty.ee11.annotations.AnnotationConfiguration; +import org.eclipse.jetty.ee11.webapp.WebAppContext; +import org.eclipse.jetty.security.HashLoginService; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.resource.Resource; + +public class OneWebAppWithJsp +{ + public static Server createServer(int port) throws FileNotFoundException + { + // Create a basic jetty server object that will listen on port 8080. + // Note that if you set this to port 0 then + // a randomly available port will be assigned that you can either look + // in the logs for the port, + // or programmatically obtain it for use in test cases. + Server server = new Server(port); + + // The WebAppContext is the entity that controls the environment in + // which a web application lives and breathes. + // In this example the context path is being set to "/" so it + // is suitable for serving root context + // requests and then we see it setting the location of the war. + // A whole host of other configurations are + // available, ranging from configuring to support annotation scanning in + // the webapp (through PlusConfiguration), to choosing where + // the webapp will unpack itself. + WebAppContext webapp = new WebAppContext(); + webapp.setContextPath("/"); + JettyDemos.MavenCoordinate mavenCoordinate = new JettyDemos.MavenCoordinate("org.eclipse.jetty.ee11.demos", + "jetty-ee11-demo-jsp-webapp", "", "war"); + Path warFile = JettyDemos.find("demo-jsp-webapp/target/demo-jsp-webapp-@VER@.war", mavenCoordinate); + webapp.setWarResource(webapp.getResourceFactory().newResource(warFile)); + webapp.setExtractWAR(true); + + // This webapp will use jsps and jstl. We need to enable the + // AnnotationConfiguration in order to correctly + // set up the jsp container + webapp.addConfiguration(new AnnotationConfiguration()); + + // Set the ContainerIncludeJarPattern so that jetty examines these + // container-path jars for tlds, web-fragments etc. + // If you omit the jar that contains the jstl .tlds, the jsp engine will + // scan for them instead. + webapp.setAttribute( + "org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern", + ".*/jetty-jakarta-servlet-api-[^/]*\\.jar$|.*/jakarta.servlet.jsp.jstl-.*\\.jar$|.*/[^/]*taglibs.*\\.jar$"); + + // A WebAppContext is a ContextHandler as well so it needs to be set to + // the server so it is aware of where to + // send the appropriate requests. + server.setHandler(webapp); + + // Configure a LoginService. + // Since this example is for our test webapp, we need to setup a + // LoginService so this shows how to create a very simple hashmap based + // one. The name of the LoginService needs to correspond to what is + // configured in the webapp's web.xml and since it has a lifecycle of + // its own we register it as a bean with the Jetty server object so it + // can be started and stopped according to the lifecycle of the server + // itself. + String realmResourceName = "etc/realm.properties"; + Resource realmResource = webapp.getResourceFactory().newClassLoaderResource(realmResourceName, false); + if (realmResource == null) + throw new FileNotFoundException("Unable to find " + realmResourceName); + + HashLoginService loginService = new HashLoginService(); + loginService.setName("Test Realm"); + loginService.setConfig(realmResource); + server.addBean(loginService); + + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); + + // Start things up! + server.start(); + + server.dumpStdErr(); + + // The use of server.join() the will make the current thread join and + // wait until the server is done executing. + server.join(); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/ProxyServer.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/ProxyServer.java new file mode 100644 index 00000000000..c0ad67e1ff2 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/ProxyServer.java @@ -0,0 +1,57 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import org.eclipse.jetty.ee11.proxy.ProxyServlet; +import org.eclipse.jetty.ee11.servlet.ServletContextHandler; +import org.eclipse.jetty.ee11.servlet.ServletHolder; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.handler.ConnectHandler; + +public class ProxyServer +{ + public static Server createServer(int port) + { + Server server = new Server(); + + // Establish listening connector + ServerConnector connector = new ServerConnector(server); + connector.setPort(port); + server.addConnector(connector); + + // Setup proxy handler to handle CONNECT methods + ConnectHandler proxy = new ConnectHandler(); + server.setHandler(proxy); + + // Setup proxy servlet + ServletContextHandler context = new ServletContextHandler("/", + ServletContextHandler.SESSIONS); + proxy.setHandler(context); + ServletHolder proxyServlet = new ServletHolder(ProxyServlet.class); + proxyServlet.setInitParameter("blackList", "www.eclipse.org"); + context.addServlet(proxyServlet, "/*"); + + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); + + server.start(); + server.join(); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/RewriteServer.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/RewriteServer.java new file mode 100644 index 00000000000..5feddcd5e00 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/RewriteServer.java @@ -0,0 +1,57 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.util.Arrays; + +import org.eclipse.jetty.ee11.servlet.ServletContextHandler; +import org.eclipse.jetty.rewrite.RewriteCustomizer; +import org.eclipse.jetty.rewrite.handler.CompactPathRule; +import org.eclipse.jetty.rewrite.handler.RewriteRegexRule; +import org.eclipse.jetty.server.HttpConnectionFactory; +import org.eclipse.jetty.server.Server; + +public class RewriteServer +{ + public static Server createServer(int port) + { + Server server = new Server(port); + + RewriteCustomizer rewrite = new RewriteCustomizer(); + rewrite.addRule(new CompactPathRule()); + rewrite.addRule(new RewriteRegexRule("(.*)foo(.*)", "$1FOO$2")); + + Arrays.stream(server.getConnectors()) + .forEach((connector) -> connector.getConnectionFactory(HttpConnectionFactory.class) + .getHttpConfiguration().addCustomizer(rewrite)); + + ServletContextHandler context = new ServletContextHandler( + ServletContextHandler.SESSIONS); + context.setContextPath("/"); + server.setHandler(context); + + context.addServlet(DumpServlet.class, "/*"); + + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); + + server.start(); + server.join(); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/SecuredHelloHandler.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/SecuredHelloHandler.java new file mode 100644 index 00000000000..c4e2eaeb260 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/SecuredHelloHandler.java @@ -0,0 +1,113 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.io.FileNotFoundException; +import java.util.Collections; + +import org.eclipse.jetty.ee11.servlet.ServletContextHandler; +import org.eclipse.jetty.ee11.servlet.ServletHolder; +import org.eclipse.jetty.ee11.servlet.security.ConstraintMapping; +import org.eclipse.jetty.ee11.servlet.security.ConstraintSecurityHandler; +import org.eclipse.jetty.security.Constraint; +import org.eclipse.jetty.security.HashLoginService; +import org.eclipse.jetty.security.LoginService; +import org.eclipse.jetty.security.authentication.BasicAuthenticator; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.util.resource.ResourceFactory; + +public class SecuredHelloHandler +{ + public static Server createServer(int port) throws FileNotFoundException + { + // Create a basic jetty server object that will listen on port 8080. + // Note that if you set this to port 0 then a randomly available port + // will be assigned that you can either look in the logs for the port, + // or programmatically obtain it for use in test cases. + Server server = new Server(port); + + // Since this example is for our test webapp, we need to setup a + // LoginService so this shows how to create a very simple hashmap based + // one. The name of the LoginService needs to correspond to what is + // configured a webapp's web.xml and since it has a lifecycle of its own + // we register it as a bean with the Jetty server object so it can be + // started and stopped according to the lifecycle of the server itself. + // In this example the name can be whatever you like since we are not + // dealing with webapp realms. + String realmResourceName = "etc/realm.properties"; + Resource realmResource = ResourceFactory.of(server).newClassLoaderResource("etc/realm.properties", false); + if (realmResource == null) + throw new FileNotFoundException("Unable to find " + realmResourceName); + + LoginService loginService = new HashLoginService("MyRealm", realmResource); + server.addBean(loginService); + + ServletContextHandler context = new ServletContextHandler(); + server.setHandler(context); + + // A security handler is a jetty handler that secures content behind a + // particular portion of a url space. The ConstraintSecurityHandler is a + // more specialized handler that allows matching of urls to different + // constraints. The server sets this as the first handler in the chain, + // effectively applying these constraints to all subsequent handlers in + // the chain. + ConstraintSecurityHandler security = new ConstraintSecurityHandler(); + context.setSecurityHandler(security); + + // This constraint requires authentication and in addition that an + // authenticated user be a member of a given set of roles for + // authorization purposes. + Constraint constraint = new Constraint.Builder() + .name("auth") + .roles("user", "admin") + .build(); + + // Binds a url pattern with the previously created constraint. The roles + // for this constraint mapping are mined from the Constraint itself + // although methods exist to declare and bind roles separately as well. + ConstraintMapping mapping = new ConstraintMapping(); + mapping.setPathSpec("/*"); + mapping.setConstraint(constraint); + + // First you see the constraint mapping being applied to the handler as + // a singleton list, however you can passing in as many security + // constraint mappings as you like so long as they follow the mapping + // requirements of the servlet api. Next we set a BasicAuthenticator + // instance which is the object that actually checks the credentials + // followed by the LoginService which is the store of known users, etc. + security.setConstraintMappings(Collections.singletonList(mapping)); + security.setAuthenticator(new BasicAuthenticator()); + security.setLoginService(loginService); + + ServletHolder holder = new ServletHolder(); + holder.setServlet(new HelloServlet("Hello World")); + context.getServletHandler().addServletWithMapping(holder, "/"); + + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); + + // Start things up! + server.start(); + + // The use of server.join() the will make the current thread join and + // wait until the server is done executing. + server.join(); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/ServerWithAnnotations.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/ServerWithAnnotations.java new file mode 100644 index 00000000000..45464438ce2 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/ServerWithAnnotations.java @@ -0,0 +1,94 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.io.FileNotFoundException; +import java.nio.file.Path; +import javax.naming.NamingException; + +import org.eclipse.jetty.ee11.annotations.AnnotationConfiguration; +import org.eclipse.jetty.ee11.plus.jndi.Transaction; +import org.eclipse.jetty.ee11.plus.webapp.EnvConfiguration; +import org.eclipse.jetty.ee11.plus.webapp.PlusConfiguration; +import org.eclipse.jetty.ee11.webapp.WebAppContext; +import org.eclipse.jetty.plus.jndi.EnvEntry; +import org.eclipse.jetty.plus.jndi.Resource; +import org.eclipse.jetty.security.HashLoginService; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.jndi.NamingDump; + +/** + * ServerWithAnnotations + */ +public class ServerWithAnnotations +{ + public static Server createServer(int port) throws NamingException, FileNotFoundException + { + // Create the server + Server server = new Server(port); + + // Create a WebApp + WebAppContext webapp = new WebAppContext(); + + // Enable parsing of jndi-related parts of web.xml and jetty-env.xml + webapp.addConfiguration(new EnvConfiguration(), new PlusConfiguration(), new AnnotationConfiguration()); + + webapp.setContextPath("/"); + JettyDemos.MavenCoordinate mavenCoordinate = new JettyDemos.MavenCoordinate("org.eclipse.jetty.ee11.demos", + "jetty-ee11-demo-spec-webapp", "", "war"); + Path warFile = JettyDemos.find("ee11-demo-spec/ee11-demo-spec-webapp/target/demo-spec-webapp-@VER@.war", mavenCoordinate); + webapp.setWar(warFile.toString()); + webapp.setAttribute( + "org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern", + ".*/jetty-jakarta-servlet-api-[^/]*\\.jar$"); + server.setHandler(webapp); + + // Register new transaction manager in JNDI + // At runtime, the webapp accesses this as java:comp/UserTransaction + new Transaction("ee11", new org.example.MockUserTransaction()); + + // Define an env entry with webapp scope. + // THIS ENTRY IS OVERRIDDEN BY THE ENTRY IN jetty-env.xml + new EnvEntry(webapp, "maxAmount", 100d, true); + + // Register a mock DataSource scoped to the webapp + new Resource(server, "jdbc/mydatasource", new org.example.MockDataSource()); + + // Add JNDI context to server for dump + server.addBean(new NamingDump()); + + // Configure a LoginService + String realmResourceName = "etc/realm.properties"; + + org.eclipse.jetty.util.resource.Resource realmResource = webapp.getResourceFactory().newClassLoaderResource(realmResourceName, false); + if (realmResource == null) + throw new FileNotFoundException("Unable to find " + realmResourceName); + + HashLoginService loginService = new HashLoginService(); + loginService.setName("Test Realm"); + loginService.setConfig(realmResource); + server.addBean(loginService); + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); + + server.start(); + server.dumpStdErr(); + server.join(); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/ServerWithJMX.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/ServerWithJMX.java new file mode 100644 index 00000000000..7d371d352cc --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/ServerWithJMX.java @@ -0,0 +1,58 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.lang.management.ManagementFactory; +import java.net.MalformedURLException; +import javax.management.remote.JMXServiceURL; + +import org.eclipse.jetty.jmx.ConnectorServer; +import org.eclipse.jetty.jmx.MBeanContainer; +import org.eclipse.jetty.server.Server; + +/** + * A Jetty Server with JMX enabled for remote connections + */ +public class ServerWithJMX +{ + public static Server createServer(int port) throws MalformedURLException + { + Server server = new Server(port); + + MBeanContainer mbContainer = new MBeanContainer( + ManagementFactory.getPlatformMBeanServer()); + server.addBean(mbContainer); + + ConnectorServer jmx = new ConnectorServer( + new JMXServiceURL( + "rmi", + null, + 1999, + "/jndi/rmi://localhost:1999/jmxrmi"), + "org.eclipse.jetty.jmx:name=rmiconnectorserver"); + server.addBean(jmx); + + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); + + server.start(); + server.dumpStdErr(); + server.join(); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/ServerWithJNDI.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/ServerWithJNDI.java new file mode 100644 index 00000000000..6eae209ccf4 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/ServerWithJNDI.java @@ -0,0 +1,97 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.io.FileNotFoundException; +import java.nio.file.Path; +import javax.naming.NamingException; + +import org.eclipse.jetty.ee11.plus.webapp.EnvConfiguration; +import org.eclipse.jetty.ee11.plus.webapp.PlusConfiguration; +import org.eclipse.jetty.ee11.webapp.WebAppContext; +import org.eclipse.jetty.server.Server; + +/** + * ServerWithJNDI + */ +public class ServerWithJNDI +{ + public static Server createServer(int port) throws NamingException, FileNotFoundException + { + // Create the server + Server server = new Server(port); + + // Create a WebApp + WebAppContext webapp = new WebAppContext(); + webapp.setContextPath("/"); + JettyDemos.MavenCoordinate mavenCoordinate = new JettyDemos.MavenCoordinate("org.eclipse.jetty.ee11.demos", + "jetty-ee11-demo-jndi-webapp", "", "war"); + Path testJndiWar = JettyDemos.find("jetty-ee11-demo-jndi-webapp/target/jetty-ee11-demo-jndi-webapp-@VER@.war", mavenCoordinate); + webapp.setWarResource(webapp.getResourceFactory().newResource(testJndiWar)); + server.setHandler(webapp); + + // Enable parsing of jndi-related parts of web.xml and jetty-env.xml + webapp.addConfiguration(new EnvConfiguration(), new PlusConfiguration()); + + // Register new transaction manager in JNDI + // At runtime, the webapp accesses this as java:comp/UserTransaction + new org.eclipse.jetty.ee11.plus.jndi.Transaction("ee11", + new org.example.MockUserTransaction()); + + // Define an env entry with ee11 scope. + // At runtime, the webapp accesses this as java:comp/env/woggle + // This is equivalent to putting an env-entry in web.xml: + // + // woggle + // java.lang.Integer + // 4000 + // + new org.eclipse.jetty.plus.jndi.EnvEntry("ee11", "woggle", 4000, false); + + // Define an env entry with webapp scope. + // At runtime, the webapp accesses this as java:comp/env/wiggle + // This is equivalent to putting a web.xml entry in web.xml: + // + // wiggle + // 100 + // java.lang.Double + // + // Note that the last arg of "true" means that this definition for + // "wiggle" would override an entry of the + // same name in web.xml + new org.eclipse.jetty.plus.jndi.EnvEntry(webapp, "wiggle", 100d, true); + + // Register a mock DataSource scoped to the webapp + // This must be linked to the webapp via an entry in web.xml: + // + // jdbc/mydatasource + // javax.sql.DataSource + // Container + // + // At runtime the webapp accesses this as + // java:comp/env/jdbc/mydatasource + new org.eclipse.jetty.plus.jndi.Resource( + webapp, "jdbc/mydatasource", new org.example.MockDataSource()); + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); + + server.start(); + server.join(); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/SimplestServer.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/SimplestServer.java new file mode 100644 index 00000000000..45c785589e6 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/SimplestServer.java @@ -0,0 +1,40 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import org.eclipse.jetty.server.Server; + +/** + * The simplest possible Jetty server. + */ +// TODO: remove this class, only used in documentation. +public class SimplestServer +{ + public static Server createServer(int port) + { + Server server = new Server(port); + // This has a connector listening on port specified + // and no handlers, meaning all requests will result + // in a 404 response + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); + server.start(); + server.join(); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/SplitFileServer.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/SplitFileServer.java new file mode 100644 index 00000000000..8c13bfd2791 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/SplitFileServer.java @@ -0,0 +1,97 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.nio.file.Paths; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.server.handler.ContextHandlerCollection; +import org.eclipse.jetty.server.handler.ResourceHandler; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.util.resource.ResourceFactory; + +/** + * A {@link ContextHandlerCollection} handler may be used to direct a request to + * a specific Context. The URI path prefix and optional virtual host is used to + * select the context. + */ +public class SplitFileServer +{ + public static Server createServer(int port, Resource baseResource0, Resource baseResource1) + { + // Create the Server object and a corresponding ServerConnector and then + // set the port for the connector. In this example the server will + // listen on port 8080. If you set this to port 0 then when the server + // has been started you can called connector.getLocalPort() to + // programmatically get the port the server started on. + Server server = new Server(); + ServerConnector connector = new ServerConnector(server); + connector.setPort(port); + server.addConnector(connector); + + // Create a Context Handler and ResourceHandler. The ContextHandler is + // getting set to "/" path but this could be anything you like for + // building out your url. Note how we are setting the ResourceBase using + // our jetty maven testing utilities to get the proper resource + // directory, you needn't use these, you simply need to supply the paths + // you are looking to serve content from. + ResourceHandler rh0 = new ResourceHandler(); + rh0.setDirAllowed(false); + + ContextHandler context0 = new ContextHandler(); + context0.setContextPath("/"); + context0.setBaseResource(baseResource0); + context0.setHandler(rh0); + + // Rinse and repeat the previous item, only specifying a different + // resource base. + ResourceHandler rh1 = new ResourceHandler(); + rh1.setDirAllowed(false); + + ContextHandler context1 = new ContextHandler(); + context1.setContextPath("/"); + context1.setBaseResource(baseResource1); + context1.setHandler(rh1); + + // Create a ContextHandlerCollection and set the context handlers to it. + // This will let jetty process urls against the declared contexts in + // order to match up content. + ContextHandlerCollection contexts = new ContextHandlerCollection( + context0, context1 + ); + server.setHandler(contexts); + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Resource resource0 = ResourceFactory.root().newResource(Paths.get("src/test/resources/dir0")); + Resource resource1 = ResourceFactory.root().newResource(Paths.get("src/test/resources/dir1")); + + Server server = createServer(port, resource0, resource1); + + // Dump the server state + server.setDumpAfterStart(true); + + // Start things up! + server.start(); + + // The use of server.join() the will make the current thread join and + // wait until the server is done executing. + server.join(); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/WebSocketServer.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/WebSocketServer.java new file mode 100644 index 00000000000..c212bba048f --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/java/org/eclipse/jetty/ee11/demos/WebSocketServer.java @@ -0,0 +1,84 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import org.eclipse.jetty.ee11.servlet.ServletContextHandler; +import org.eclipse.jetty.ee11.websocket.server.JettyWebSocketServlet; +import org.eclipse.jetty.ee11.websocket.server.JettyWebSocketServletFactory; +import org.eclipse.jetty.ee11.websocket.server.config.JettyWebSocketServletContainerInitializer; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.websocket.api.Callback; +import org.eclipse.jetty.websocket.api.Session; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; +import org.eclipse.jetty.websocket.api.annotations.WebSocket; + +/** + * Example of setting up a Jetty WebSocket server + *

+ * Note: this uses the Jetty WebSocket API, not the jakarta.websocket API. + */ +public class WebSocketServer +{ + /** + * Example of a Jetty API WebSocket Echo Socket + */ + @WebSocket + public static class EchoSocket + { + @OnWebSocketMessage + public void onMessage(Session session, String message) + { + session.sendText(message, Callback.NOOP); + } + } + + /** + * Servlet layer + */ + @SuppressWarnings("serial") + public static class EchoServlet extends JettyWebSocketServlet + { + @Override + public void configure(JettyWebSocketServletFactory factory) + { + factory.addMapping("/", (req, res) -> new EchoSocket()); + } + } + + public static Server createServer(int port) + { + Server server = new Server(port); + + ServletContextHandler context = new ServletContextHandler(); + context.setContextPath("/"); + server.setHandler(context); + + // Add the echo socket servlet to the /echo path map + context.addServlet(EchoServlet.class, "/echo"); + + // Configure context to support WebSocket + JettyWebSocketServletContainerInitializer.configure(context, null); + + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); + + server.start(); + server.join(); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/other/content.jar b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/other/content.jar new file mode 100644 index 00000000000..846f71fa3c4 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/other/content.jar differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/demo/ee11-demo-realm.properties b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/demo/ee11-demo-realm.properties new file mode 100644 index 00000000000..9d88b852b7f --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/demo/ee11-demo-realm.properties @@ -0,0 +1,21 @@ +# +# This file defines users passwords and roles for a HashUserRealm +# +# The format is +# : [, ...] +# +# Passwords may be clear text, obfuscated or checksummed. The class +# org.eclipse.util.Password should be used to generate obfuscated +# passwords or password checksums +# +# If DIGEST Authentication is used, the password must be in a recoverable +# format, either plain text or OBF:. +# +jetty: MD5:164c88b302622e17050af52c89945d44,user +admin: CRYPT:adpexzg3FUZAk,server-administrator,content-administrator,admin,user +other: OBF:1xmk1w261u9r1w1c1xmq,user +plain: plain,user +user: password,user + +# This entry is for digest auth. The credential is a MD5 hash of username:realmname:password +digest: MD5:6e120743ad67abfbc385bc2bb754e297,user diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/demo/webdefault-ee11.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/demo/webdefault-ee11.xml new file mode 100644 index 00000000000..a40048c908e --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/demo/webdefault-ee11.xml @@ -0,0 +1,433 @@ + + + + + + + + + + + + + + + + + + + + + + + Default web.xml file. + This file is applied to a Web application before its own WEB_INF/web.xml file + + + + + + + + org.eclipse.jetty.ee11.servlet.listener.IntrospectorCleaner + + + + + + + + + + + + + + + + + default + org.eclipse.jetty.ee11.servlet.DefaultServlet + + acceptRanges + true + + + dirAllowed + true + + + welcomeServlets + false + + + redirectWelcome + false + + + maxCacheSize + 256000000 + + + maxCachedFileSize + 200000000 + + + maxCachedFiles + 2048 + + + etags + false + + + useFileMappedBuffer + true + + 0 + + + + default + / + + + + + + + + + + + + + + + jsp + org.eclipse.jetty.jsp.JettyJspServlet + + xpoweredBy + false + + + compilerTargetVM + 1.8 + + + compilerSourceVM + 1.8 + + 0 + + + + jsp + *.jsp + *.jspf + *.jspx + *.xsp + *.JSP + *.JSPF + *.JSPX + *.XSP + + + + + + + + 30 + + + + + + + + + + + + + + + index.html + index.htm + index.jsp + + + + + + + + ar + ISO-8859-6 + + + be + ISO-8859-5 + + + bg + ISO-8859-5 + + + ca + ISO-8859-1 + + + cs + ISO-8859-2 + + + da + ISO-8859-1 + + + de + ISO-8859-1 + + + el + ISO-8859-7 + + + en + ISO-8859-1 + + + es + ISO-8859-1 + + + et + ISO-8859-1 + + + fi + ISO-8859-1 + + + fr + ISO-8859-1 + + + hr + ISO-8859-2 + + + hu + ISO-8859-2 + + + is + ISO-8859-1 + + + it + ISO-8859-1 + + + iw + ISO-8859-8 + + + ja + Shift_JIS + + + ko + EUC-KR + + + lt + ISO-8859-2 + + + lv + ISO-8859-2 + + + mk + ISO-8859-5 + + + nl + ISO-8859-1 + + + no + ISO-8859-1 + + + pl + ISO-8859-2 + + + pt + ISO-8859-1 + + + ro + ISO-8859-2 + + + ru + ISO-8859-5 + + + sh + ISO-8859-5 + + + sk + ISO-8859-2 + + + sl + ISO-8859-2 + + + sq + ISO-8859-2 + + + sr + ISO-8859-5 + + + sv + ISO-8859-1 + + + tr + ISO-8859-9 + + + uk + ISO-8859-5 + + + zh + GB2312 + + + zh_TW + Big5 + + + + + + + + + Disable TRACE + / + TRACE + + + + + + Enable everything but TRACE + / + TRACE + + + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/push.html b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/push.html new file mode 100644 index 00000000000..f6807e7dd47 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/push.html @@ -0,0 +1,100 @@ + + + + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile00.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile00.jpg new file mode 100644 index 00000000000..dc303075c63 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile00.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile01.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile01.jpg new file mode 100644 index 00000000000..66c2e252532 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile01.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile02.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile02.jpg new file mode 100644 index 00000000000..f281dfd3f6c Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile02.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile03.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile03.jpg new file mode 100644 index 00000000000..4787e69e903 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile03.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile04.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile04.jpg new file mode 100644 index 00000000000..64e6d1026c6 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile04.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile05.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile05.jpg new file mode 100644 index 00000000000..1530c7687b3 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile05.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile06.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile06.jpg new file mode 100644 index 00000000000..cd59c5f7eef Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile06.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile07.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile07.jpg new file mode 100644 index 00000000000..7f852875de5 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile07.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile08.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile08.jpg new file mode 100644 index 00000000000..31ebcb0db88 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile08.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile09.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile09.jpg new file mode 100644 index 00000000000..b9cfb1a4875 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile09.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile10.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile10.jpg new file mode 100644 index 00000000000..11c6faeb2e9 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile10.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile11.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile11.jpg new file mode 100644 index 00000000000..fee67603106 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile11.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile12.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile12.jpg new file mode 100644 index 00000000000..a5032464e24 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile12.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile13.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile13.jpg new file mode 100644 index 00000000000..4227207ddba Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile13.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile14.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile14.jpg new file mode 100644 index 00000000000..6071c0ffbf0 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile14.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile15.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile15.jpg new file mode 100644 index 00000000000..64a0bf99b85 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile15.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile16.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile16.jpg new file mode 100644 index 00000000000..f5995168c29 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile16.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile17.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile17.jpg new file mode 100644 index 00000000000..b4183b3ea8f Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile17.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile18.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile18.jpg new file mode 100644 index 00000000000..5a568e28011 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile18.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile19.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile19.jpg new file mode 100644 index 00000000000..126c3c64043 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile19.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile20.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile20.jpg new file mode 100644 index 00000000000..5423ac18112 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile20.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile21.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile21.jpg new file mode 100644 index 00000000000..0b6edb0d772 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile21.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile22.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile22.jpg new file mode 100644 index 00000000000..f788a4849a1 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile22.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile23.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile23.jpg new file mode 100644 index 00000000000..1fbc64d0ba3 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile23.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile24.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile24.jpg new file mode 100644 index 00000000000..f565b8fff2a Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile24.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile25.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile25.jpg new file mode 100644 index 00000000000..18a4d0697cb Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile25.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile26.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile26.jpg new file mode 100644 index 00000000000..dde86ecf64b Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile26.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile27.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile27.jpg new file mode 100644 index 00000000000..eaf0ce8a766 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile27.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile28.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile28.jpg new file mode 100644 index 00000000000..7e596304f30 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile28.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile29.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile29.jpg new file mode 100644 index 00000000000..1eb25b7dce4 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile29.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile30.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile30.jpg new file mode 100644 index 00000000000..ea68292a07d Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile30.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile31.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile31.jpg new file mode 100644 index 00000000000..31455ca3217 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile31.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile32.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile32.jpg new file mode 100644 index 00000000000..f6a2a2947df Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile32.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile33.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile33.jpg new file mode 100644 index 00000000000..8362cf44a5d Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile33.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile34.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile34.jpg new file mode 100644 index 00000000000..2b856a0dc77 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile34.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile35.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile35.jpg new file mode 100644 index 00000000000..4e59e21a912 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile35.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile36.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile36.jpg new file mode 100644 index 00000000000..7df53fdd734 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile36.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile37.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile37.jpg new file mode 100644 index 00000000000..31844113b96 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile37.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile38.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile38.jpg new file mode 100644 index 00000000000..52b44b8c982 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile38.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile39.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile39.jpg new file mode 100644 index 00000000000..2f7a636b4a6 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile39.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile40.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile40.jpg new file mode 100644 index 00000000000..61cfdfacb0e Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile40.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile41.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile41.jpg new file mode 100644 index 00000000000..d25a4e4db4e Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile41.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile42.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile42.jpg new file mode 100644 index 00000000000..029e0f6731a Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile42.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile43.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile43.jpg new file mode 100644 index 00000000000..5ea4f5582d4 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile43.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile44.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile44.jpg new file mode 100644 index 00000000000..ba18b30f5a6 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile44.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile45.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile45.jpg new file mode 100644 index 00000000000..b856d66a3f9 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile45.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile46.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile46.jpg new file mode 100644 index 00000000000..f474cc2f9fd Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile46.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile47.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile47.jpg new file mode 100644 index 00000000000..6b2f58e2ca7 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile47.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile48.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile48.jpg new file mode 100644 index 00000000000..e5d14265425 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile48.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile49.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile49.jpg new file mode 100644 index 00000000000..6a33cf37251 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/pushed/tile49.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/readme.txt b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/readme.txt new file mode 100644 index 00000000000..e0df187bd7e --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/readme.txt @@ -0,0 +1 @@ +This is a test resouce for the Http2Server example diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile00.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile00.jpg new file mode 100644 index 00000000000..b2e52fbb8b1 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile00.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile01.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile01.jpg new file mode 100644 index 00000000000..4d98637b23e Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile01.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile02.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile02.jpg new file mode 100644 index 00000000000..06b289b5a40 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile02.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile03.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile03.jpg new file mode 100644 index 00000000000..fcb7b194af9 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile03.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile04.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile04.jpg new file mode 100644 index 00000000000..0cc4933277d Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile04.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile05.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile05.jpg new file mode 100644 index 00000000000..6a6d66fb418 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile05.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile06.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile06.jpg new file mode 100644 index 00000000000..89ddd6406e6 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile06.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile07.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile07.jpg new file mode 100644 index 00000000000..967931796ed Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile07.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile08.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile08.jpg new file mode 100644 index 00000000000..aa895c49695 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile08.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile09.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile09.jpg new file mode 100644 index 00000000000..9c234cacf35 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile09.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile10.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile10.jpg new file mode 100644 index 00000000000..8bc306c4633 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile10.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile11.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile11.jpg new file mode 100644 index 00000000000..85b62b6b701 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile11.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile12.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile12.jpg new file mode 100644 index 00000000000..448faa25330 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile12.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile13.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile13.jpg new file mode 100644 index 00000000000..4f4f9012e06 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile13.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile14.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile14.jpg new file mode 100644 index 00000000000..e03120b27ca Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile14.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile15.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile15.jpg new file mode 100644 index 00000000000..64bc2383dc7 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile15.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile16.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile16.jpg new file mode 100644 index 00000000000..5e858b9ecd6 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile16.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile17.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile17.jpg new file mode 100644 index 00000000000..2da3aa8ef0a Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile17.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile18.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile18.jpg new file mode 100644 index 00000000000..e934ce5ec1a Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile18.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile19.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile19.jpg new file mode 100644 index 00000000000..15707bc73e2 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile19.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile20.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile20.jpg new file mode 100644 index 00000000000..4b940f13d4b Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile20.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile21.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile21.jpg new file mode 100644 index 00000000000..b340585ab07 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile21.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile22.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile22.jpg new file mode 100644 index 00000000000..39b08249da7 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile22.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile23.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile23.jpg new file mode 100644 index 00000000000..42adae167e7 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile23.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile24.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile24.jpg new file mode 100644 index 00000000000..a8281ddb533 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile24.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile25.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile25.jpg new file mode 100644 index 00000000000..43692f07952 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile25.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile26.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile26.jpg new file mode 100644 index 00000000000..f8de0f7f361 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile26.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile27.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile27.jpg new file mode 100644 index 00000000000..f6c210ac9d8 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile27.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile28.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile28.jpg new file mode 100644 index 00000000000..39f262204c9 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile28.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile29.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile29.jpg new file mode 100644 index 00000000000..952ce0bf67e Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile29.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile30.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile30.jpg new file mode 100644 index 00000000000..e699a53cd49 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile30.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile31.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile31.jpg new file mode 100644 index 00000000000..d6256fe6538 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile31.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile32.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile32.jpg new file mode 100644 index 00000000000..fde5e8b9b34 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile32.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile33.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile33.jpg new file mode 100644 index 00000000000..2ba66aa343c Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile33.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile34.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile34.jpg new file mode 100644 index 00000000000..37e24f05c20 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile34.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile35.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile35.jpg new file mode 100644 index 00000000000..b147697ed6c Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile35.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile36.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile36.jpg new file mode 100644 index 00000000000..6501650b180 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile36.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile37.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile37.jpg new file mode 100644 index 00000000000..e2d40eeb9d2 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile37.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile38.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile38.jpg new file mode 100644 index 00000000000..1363781678b Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile38.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile39.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile39.jpg new file mode 100644 index 00000000000..790cfd220a7 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile39.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile40.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile40.jpg new file mode 100644 index 00000000000..3027238b9ec Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile40.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile41.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile41.jpg new file mode 100644 index 00000000000..d085e38e429 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile41.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile42.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile42.jpg new file mode 100644 index 00000000000..b7ec60fed90 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile42.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile43.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile43.jpg new file mode 100644 index 00000000000..6230abd253c Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile43.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile44.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile44.jpg new file mode 100644 index 00000000000..46ad6a1f20d Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile44.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile45.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile45.jpg new file mode 100644 index 00000000000..89d2d325d1a Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile45.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile46.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile46.jpg new file mode 100644 index 00000000000..9f3451f8810 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile46.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile47.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile47.jpg new file mode 100644 index 00000000000..b7021789268 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile47.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile48.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile48.jpg new file mode 100644 index 00000000000..9defd9a30e4 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile48.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile49.jpg b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile49.jpg new file mode 100644 index 00000000000..26f2a44a56b Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/docroot/tiles/tile49.jpg differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/etc/keystore.p12 b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/etc/keystore.p12 new file mode 100644 index 00000000000..e4657280ca5 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/etc/keystore.p12 differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/etc/realm.properties b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/etc/realm.properties new file mode 100644 index 00000000000..f4b3490e910 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/etc/realm.properties @@ -0,0 +1,20 @@ +# +# This file defines users passwords and roles for a HashUserRealm +# +# The format is +# : [, ...] +# +# Passwords may be clear text, obfuscated or checksummed. The class +# org.eclipse.jetty.util.security.Password should be used to generate obfuscated +# passwords or password checksums +# +# If DIGEST Authentication is used, the password must be in a recoverable +# format, either plain text or OBF:. +# +jetty:MD5:164c88b302622e17050af52c89945d44,user +admin:CRYPT:adpexzg3FUZAk,server-administrator,content-administrator,admin,user +other:OBF:1xmk1w261u9r1w1c1xmq,user +plain:plain,user +user:password,user +# This entry is for digest auth. The credential is a MD5 hash of username:realmname:password +digest:MD5:6e120743ad67abfbc385bc2bb754e297,user diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/exampleserver.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/exampleserver.xml new file mode 100644 index 00000000000..c9ce4991c99 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/exampleserver.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + /hello + + org.eclipse.jetty.ee11.demos.HelloServlet + / + + + + + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/fileserver.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/fileserver.xml new file mode 100644 index 00000000000..2fcadfe77b0 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/fileserver.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + true + + index.html + + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/java-util-logging.properties b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/java-util-logging.properties new file mode 100644 index 00000000000..4aaa236d96c --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/java-util-logging.properties @@ -0,0 +1,9 @@ + +# Logging +handlers = java.util.logging.ConsoleHandler +.level = INFO + +java.util.logging.SimpleFormatter.format=%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS %4$-6s %2$s %5$s%6$s%n + +# Console Logging +java.util.logging.ConsoleHandler.level = ALL \ No newline at end of file diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/jetty-logging.properties b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/jetty-logging.properties new file mode 100644 index 00000000000..8dd7e7c2a23 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/jetty-logging.properties @@ -0,0 +1,12 @@ +## Jetty Logging using jetty-slf4j-impl +#org.eclipse.jetty.util.log.javautil.PROPERTIES=java-util-logging.properties +#org.eclipse.jetty.util.log.SOURCE=true +#org.eclipse.jetty.LEVEL=INFO +#org.eclipse.jetty.STACKS=true +#org.eclipse.jetty.STACKS=false +#org.eclipse.jetty.io.LEVEL=DEBUG +#org.eclipse.jetty.io.ssl.LEVEL=DEBUG +#org.eclipse.jetty.server.LEVEL=DEBUG +#org.eclipse.jetty.ee11.servlets.LEVEL=DEBUG +#org.eclipse.jetty.alpn.LEVEL=DEBUG +#org.eclipse.jetty.jmx.LEVEL=DEBUG diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/logback-access.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/logback-access.xml new file mode 100644 index 00000000000..6cd90623677 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/main/resources/logback-access.xml @@ -0,0 +1,16 @@ + + + + + + logs/access.log + + logs/access.%d{yyyy-MM-dd}.log.zip + + + combined + + + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/AbstractEmbeddedTest.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/AbstractEmbeddedTest.java new file mode 100644 index 00000000000..d6ab8af43ee --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/AbstractEmbeddedTest.java @@ -0,0 +1,59 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import org.eclipse.jetty.client.ContentResponse; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.transport.HttpClientTransportOverHTTP; +import org.eclipse.jetty.io.ClientConnector; +import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.eclipse.jetty.util.thread.QueuedThreadPool; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; + +public abstract class AbstractEmbeddedTest +{ + public HttpClient client; + + @BeforeEach + public void startClient() throws Exception + { + SslContextFactory.Client sslContextFactory = new SslContextFactory.Client(); + sslContextFactory.setTrustAll(true); + + ClientConnector clientConnector = new ClientConnector(); + clientConnector.setSelectors(1); + clientConnector.setSslContextFactory(sslContextFactory); + + QueuedThreadPool clientThreads = new QueuedThreadPool(); + clientThreads.setName("client"); + + clientConnector.setExecutor(clientThreads); + + client = new HttpClient(new HttpClientTransportOverHTTP(clientConnector)); + client.start(); + } + + @AfterEach + public void stopClient() throws Exception + { + client.stop(); + } + + protected void dumpResponseHeaders(ContentResponse response) + { + System.out.printf("%s %s %s%n", response.getVersion(), response.getStatus(), response.getReason()); + System.out.println(response.getHeaders()); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/ExampleServerTest.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/ExampleServerTest.java new file mode 100644 index 00000000000..f8bdeec64d6 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/ExampleServerTest.java @@ -0,0 +1,85 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.net.URI; + +import org.eclipse.jetty.client.ContentResponse; +import org.eclipse.jetty.client.StringRequestContent; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class ExampleServerTest extends AbstractEmbeddedTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = ExampleServer.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetHello() throws Exception + { + URI uri = server.getURI().resolve("/hello"); + + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("Hello")); + } + + @Test + public void testGetEcho() throws Exception + { + URI uri = server.getURI().resolve("/echo/a/greeting"); + + String postBody = "Greetings from " + ExampleServerTest.class; + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.POST) + .body(new StringRequestContent(postBody)) + .send(); + + // Check the response status code + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString(postBody)); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/ExampleServerXmlTest.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/ExampleServerXmlTest.java new file mode 100644 index 00000000000..00a528d71b7 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/ExampleServerXmlTest.java @@ -0,0 +1,62 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.net.URI; + +import org.eclipse.jetty.client.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class ExampleServerXmlTest extends AbstractEmbeddedTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = ExampleServerXml.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetHello() throws Exception + { + URI uri = server.getURI().resolve("/hello"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("Hello")); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/FastFileServerTest.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/FastFileServerTest.java new file mode 100644 index 00000000000..8b67c6b192a --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/FastFileServerTest.java @@ -0,0 +1,90 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.io.BufferedWriter; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.eclipse.jetty.client.ContentResponse; +import org.eclipse.jetty.http.HttpFields; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +@ExtendWith(WorkDirExtension.class) +public class FastFileServerTest extends AbstractEmbeddedTest +{ + private static final String TEXT_CONTENT = "I am an old man and I have known a great " + + "many troubles, but most of them never happened. - Mark Twain"; + + private WorkDir workDir; + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + Path textFile = workDir.getEmptyPathDir().resolve("simple.txt"); + try (BufferedWriter writer = Files.newBufferedWriter(textFile, UTF_8)) + { + writer.write(TEXT_CONTENT); + } + + //TODO fix me + //server = FastFileServer.createServer(0, baseDir.toFile()); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + // FIXME + @Disabled + @Test + public void testGetSimpleText() throws Exception + { + URI uri = server.getURI().resolve("/simple.txt"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + HttpFields responseHeaders = response.getHeaders(); + + assertThat("Content-Type", responseHeaders.get("Content-Type"), is("text/plain")); + assertThat("Content-Length", responseHeaders.getLongField("Content-Length"), + is((long)TEXT_CONTENT.length())); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response body", responseBody, is(TEXT_CONTENT)); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/FileServerTest.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/FileServerTest.java new file mode 100644 index 00000000000..a0ea221bdd4 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/FileServerTest.java @@ -0,0 +1,87 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.io.BufferedWriter; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.eclipse.jetty.client.ContentResponse; +import org.eclipse.jetty.http.HttpFields; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; +import org.eclipse.jetty.util.resource.ResourceFactory; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +@ExtendWith(WorkDirExtension.class) +public class FileServerTest extends AbstractEmbeddedTest +{ + private static final String TEXT_CONTENT = "I am an old man and I have known a great " + + "many troubles, but most of them never happened. - Mark Twain"; + public Path baseDir; + private Server server; + + @BeforeEach + public void startServer(WorkDir workDir) throws Exception + { + baseDir = workDir.getEmptyPathDir(); + Path textFile = baseDir.resolve("simple.txt"); + try (BufferedWriter writer = Files.newBufferedWriter(textFile, UTF_8)) + { + writer.write(TEXT_CONTENT); + } + + server = FileServer.createServer(0, ResourceFactory.root().newResource(baseDir)); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetSimpleText() throws Exception + { + URI uri = server.getURI().resolve("/simple.txt"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + HttpFields responseHeaders = response.getHeaders(); + + assertThat("Content-Type", responseHeaders.get("Content-Type"), is("text/plain")); + assertThat("Content-Length", responseHeaders.getLongField("Content-Length"), + is((long)TEXT_CONTENT.length())); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response body", responseBody, is(TEXT_CONTENT)); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/FileServerXmlTest.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/FileServerXmlTest.java new file mode 100644 index 00000000000..a6b951a7523 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/FileServerXmlTest.java @@ -0,0 +1,86 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.io.BufferedWriter; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.eclipse.jetty.client.ContentResponse; +import org.eclipse.jetty.http.HttpFields; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +@ExtendWith(WorkDirExtension.class) +public class FileServerXmlTest extends AbstractEmbeddedTest +{ + private static final String TEXT_CONTENT = "I am an old man and I have known a great " + + "many troubles, but most of them never happened. - Mark Twain"; + + private Server server; + + @BeforeEach + public void startServer(WorkDir workDir) throws Exception + { + Path baseDir = workDir.getEmptyPathDir(); + Path textFile = baseDir.resolve("simple.txt"); + try (BufferedWriter writer = Files.newBufferedWriter(textFile, UTF_8)) + { + writer.write(TEXT_CONTENT); + } + + server = FileServerXml.createServer(0, baseDir); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetSimpleText() throws Exception + { + URI uri = server.getURI().resolve("/simple.txt"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + HttpFields responseHeaders = response.getHeaders(); + + assertThat("Content-Type", responseHeaders.get("Content-Type"), is("text/plain")); + assertThat("Content-Length", responseHeaders.getLongField("Content-Length"), + is((long)TEXT_CONTENT.length())); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response body", responseBody, is(TEXT_CONTENT)); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/JarServerTest.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/JarServerTest.java new file mode 100644 index 00000000000..5a8609db726 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/JarServerTest.java @@ -0,0 +1,86 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.io.FileNotFoundException; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.eclipse.jetty.client.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class JarServerTest extends AbstractEmbeddedTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + Path jarFile = Paths.get("src/main/other/content.jar"); + if (!Files.exists(jarFile)) + throw new FileNotFoundException(jarFile.toString()); + + server = JarServer.createServer(0, jarFile.toUri()); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetDir0Test0() throws Exception + { + URI uri = server.getURI().resolve("/dir0/test0.txt"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("test0")); + } + + @Test + public void testGetDir1Test1() throws Exception + { + URI uri = server.getURI().resolve("/dir1/test1.txt"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("test1")); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/LikeJettyXmlTest.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/LikeJettyXmlTest.java new file mode 100644 index 00000000000..df1713dbbe6 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/LikeJettyXmlTest.java @@ -0,0 +1,89 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.net.URI; +import java.util.Map; + +import org.eclipse.jetty.client.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.component.LifeCycle; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class LikeJettyXmlTest extends AbstractEmbeddedTest +{ + private Server server; + private URI serverPlainUri; + private URI serverSslUri; + + @BeforeEach + public void startServer() throws Exception + { + server = LikeJettyXml.createServer(0, 0, false); + server.start(); + + Map ports = ServerUtil.fixDynamicPortConfigurations(server); + + // Establish base URI's that use "localhost" to prevent tripping over + // the "REMOTE ACCESS" warnings in demo-base + serverPlainUri = URI.create("http://localhost:" + ports.get("plain") + "/"); + serverSslUri = URI.create("https://localhost:" + ports.get("secure") + "/"); + } + + @AfterEach + public void stopServer() throws Exception + { + LifeCycle.stop(server); + } + + @Test + public void testGetTest() throws Exception + { + URI uri = serverPlainUri.resolve("/test/"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("Hello")); + } + + @Test + public void testGetTestSsl() throws Exception + { + URI uri = serverSslUri.resolve("/test/"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("Hello")); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/ManyConnectorsTest.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/ManyConnectorsTest.java new file mode 100644 index 00000000000..f13c1a3f0dd --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/ManyConnectorsTest.java @@ -0,0 +1,90 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.net.URI; +import java.util.Map; + +import org.eclipse.jetty.client.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class ManyConnectorsTest extends AbstractEmbeddedTest +{ + private Server server; + private URI serverPlainUri; + private URI serverSslUri; + + @BeforeEach + public void startServer() throws Exception + { + server = ManyConnectors.createServer(0, 0); + server.start(); + + Map ports = ServerUtil.fixDynamicPortConfigurations(server); + + // Establish base URI's that use "localhost" to prevent tripping over + // the "REMOTE ACCESS" warnings in demo-base + serverPlainUri = URI.create("http://localhost:" + ports.get("plain") + "/"); + serverSslUri = URI.create("https://localhost:" + ports.get("secure") + "/"); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testPlainGetHello() throws Exception + { + URI uri = serverPlainUri.resolve("/hello"); + + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("Hello")); + } + + @Test + public void testSecureGetHello() throws Exception + { + URI uri = serverSslUri.resolve("/hello"); + + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("Hello")); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/ManyContextsTest.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/ManyContextsTest.java new file mode 100644 index 00000000000..dc395c114c7 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/ManyContextsTest.java @@ -0,0 +1,112 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.net.URI; + +import org.eclipse.jetty.client.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class ManyContextsTest extends AbstractEmbeddedTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = ManyContexts.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetRootHello() throws Exception + { + URI uri = server.getURI().resolve("/"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("Root Hello")); + } + + @Test + public void testGetFrenchHello() throws Exception + { + URI uri = server.getURI().resolve("/fr"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("Bonjour")); + } + + @Test + public void testGetItalianGoodMorning() throws Exception + { + URI uri = server.getURI().resolve("/it"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("Buongiorno")); + } + + @Test + public void testGetVirtualHostHello() throws Exception + { + int port = server.getURI().getPort(); + + URI uri = URI.create("http://127.0.0.2:" + port + "/"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("Virtual Hello")); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/ManyHandlersTest.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/ManyHandlersTest.java new file mode 100644 index 00000000000..adbdbe7b9b9 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/ManyHandlersTest.java @@ -0,0 +1,117 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.net.URI; +import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; + +import org.eclipse.jetty.client.ContentResponse; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.ajax.JSON; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +@Disabled //TODO +public class ManyHandlersTest extends AbstractEmbeddedTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + //TODO fix me + //server = ManyHandlers.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetParams() throws Exception + { + URI uri = server.getURI().resolve("/params?a=b&foo=bar"); + + AtomicReference contentEncoding = new AtomicReference<>(); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .onResponseHeader((r, field) -> + { + if (field.getHeader() == HttpHeader.CONTENT_ENCODING) + contentEncoding.set(field.getValue()); + return true; + }) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test gzip + // Test that Gzip was used to produce the response + assertThat("Content-Encoding", contentEncoding.get(), containsString("gzip")); + + // test response content + String responseBody = response.getContentAsString(); + Object jsonObj = new JSON().fromJSON(responseBody); + @SuppressWarnings("unchecked") + Map jsonMap = (Map)jsonObj; + assertThat("Response JSON keys.size", jsonMap.keySet().size(), is(2)); + } + + @Test + public void testGetHello() throws Exception + { + URI uri = server.getURI().resolve("/hello"); + + AtomicReference contentEncoding = new AtomicReference<>(); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .onResponseHeader((r, field) -> + { + if (field.getHeader() == HttpHeader.CONTENT_ENCODING) + contentEncoding.set(field.getValue()); + return true; + }) + .send(); + + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test gzip + // Test that Gzip was used to produce the response + assertThat("Content-Encoding", contentEncoding.get(), containsString("gzip")); + + // test expected header from wrapper + String welcome = response.getHeaders().get("X-Welcome"); + assertThat("X-Welcome header", welcome, containsString("Greetings from WelcomeWrapHandler")); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("Hello")); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/ManyServletContextsTest.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/ManyServletContextsTest.java new file mode 100644 index 00000000000..ba269510842 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/ManyServletContextsTest.java @@ -0,0 +1,110 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.net.URI; + +import org.eclipse.jetty.client.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class ManyServletContextsTest extends AbstractEmbeddedTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = ManyServletContexts.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetHello() throws Exception + { + URI uri = server.getURI().resolve("/hello"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("Hello")); + } + + @Test + public void testGetItalianHello() throws Exception + { + URI uri = server.getURI().resolve("/it/hello"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("Ciao")); + } + + @Test + public void testGetFrenchHello() throws Exception + { + URI uri = server.getURI().resolve("/fr/hello"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("Bonjour")); + } + + @Test + public void testGetOtherYo() throws Exception + { + URI uri = server.getURI().resolve("/other/hello.yo"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("YO!")); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/MinimalServletsTest.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/MinimalServletsTest.java new file mode 100644 index 00000000000..90239f71f66 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/MinimalServletsTest.java @@ -0,0 +1,62 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.net.URI; + +import org.eclipse.jetty.client.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class MinimalServletsTest extends AbstractEmbeddedTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = MinimalServlets.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetHello() throws Exception + { + URI uri = server.getURI().resolve("/hello"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("Hello")); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/OneConnectorTest.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/OneConnectorTest.java new file mode 100644 index 00000000000..b4bda5f0335 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/OneConnectorTest.java @@ -0,0 +1,62 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.net.URI; + +import org.eclipse.jetty.client.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class OneConnectorTest extends AbstractEmbeddedTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = OneConnector.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetHello() throws Exception + { + URI uri = server.getURI().resolve("/hello"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("Hello")); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/OneContextTest.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/OneContextTest.java new file mode 100644 index 00000000000..b4664ef22a5 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/OneContextTest.java @@ -0,0 +1,62 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.net.URI; + +import org.eclipse.jetty.client.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class OneContextTest extends AbstractEmbeddedTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = OneContext.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetHello() throws Exception + { + URI uri = server.getURI().resolve("/hello"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("Hello")); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/OneHandlerTest.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/OneHandlerTest.java new file mode 100644 index 00000000000..344e4c50037 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/OneHandlerTest.java @@ -0,0 +1,62 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.net.URI; + +import org.eclipse.jetty.client.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class OneHandlerTest extends AbstractEmbeddedTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = OneHandler.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetHello() throws Exception + { + URI uri = server.getURI().resolve("/hello"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("Hello")); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/OneServletContextJmxStatsTest.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/OneServletContextJmxStatsTest.java new file mode 100644 index 00000000000..17b4de3b216 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/OneServletContextJmxStatsTest.java @@ -0,0 +1,93 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.net.URI; +import java.util.Set; +import javax.management.MBeanServer; +import javax.management.ObjectInstance; +import javax.management.ObjectName; + +import org.eclipse.jetty.client.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.io.ConnectionStatistics; +import org.eclipse.jetty.jmx.MBeanContainer; +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.opentest4j.AssertionFailedError; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.greaterThanOrEqualTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; + +public class OneServletContextJmxStatsTest extends AbstractEmbeddedTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = OneServletContextJmxStats.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetDumpViaPathInfo() throws Exception + { + URI uri = server.getURI().resolve("/dump/something"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, + allOf( + containsString("DumpServlet"), + containsString("servletPath=/dump"), + containsString("pathInfo=/something") + ) + ); + } + + @Test + public void testJmxConnectStatsPresent() throws Exception + { + MBeanContainer mbeanContainer = server.getBean(MBeanContainer.class); + MBeanServer mbeanServer = mbeanContainer.getMBeanServer(); + + String domain = ConnectionStatistics.class.getPackage().getName(); + Set mbeanNames = mbeanServer.queryNames(ObjectName.getInstance(domain + ":type=connectionstatistics,*"), null); + ObjectName connStatsName = mbeanNames.stream().findFirst().orElseThrow(AssertionFailedError::new); + ObjectInstance mbeanConnStats = mbeanServer.getObjectInstance(connStatsName); + Number connections = (Number)mbeanServer.getAttribute(connStatsName, "connections"); + assertThat("stats[connections]", connections, is(notNullValue())); + assertThat("stats[connections]", connections.longValue(), greaterThanOrEqualTo(0L)); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/OneServletContextTest.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/OneServletContextTest.java new file mode 100644 index 00000000000..e32bf3e56f2 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/OneServletContextTest.java @@ -0,0 +1,154 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.io.BufferedWriter; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.eclipse.jetty.client.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; +import org.eclipse.jetty.util.resource.ResourceFactory; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +@ExtendWith(WorkDirExtension.class) +public class OneServletContextTest extends AbstractEmbeddedTest +{ + private static final String TEXT_CONTENT = "The secret of getting ahead is getting started. - Mark Twain"; + + public WorkDir workDir; + public Path baseDir; + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + baseDir = workDir.getEmptyPathDir(); + Path textFile = baseDir.resolve("simple.txt"); + try (BufferedWriter writer = Files.newBufferedWriter(textFile, UTF_8)) + { + writer.write(TEXT_CONTENT); + } + + server = OneServletContext.createServer(0, ResourceFactory.root().newResource(baseDir)); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetHello() throws Exception + { + URI uri = server.getURI().resolve("/hello/there"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("Hello")); + } + + @Test + public void testGetDumpViaPathInfo() throws Exception + { + URI uri = server.getURI().resolve("/dump/something"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, + allOf( + containsString("DumpServlet"), + containsString("servletPath=/dump"), + containsString("pathInfo=/something") + ) + ); + } + + @Test + public void testGetDumpSuffix() throws Exception + { + URI uri = server.getURI().resolve("/another.dump"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, + allOf( + containsString("DumpServlet"), + containsString("servletPath=/another.dump"), + containsString("pathInfo=null") + ) + ); + } + + @Test + public void testGetTestDumpSuffix() throws Exception + { + URI uri = server.getURI().resolve("/test/another.dump"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + String filterResponseHeader = response.getHeaders().get("X-TestFilter"); + assertThat("X-TestFilter header", filterResponseHeader, is("true")); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, + allOf( + containsString("DumpServlet"), + containsString("servletPath=/test/another.dump"), + containsString("pathInfo=null"), + containsString("request.attribute[X-ReqListener]=true"), + containsString("servletContext.attribute[X-Init]=true") + ) + ); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/OneServletContextWithSessionTest.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/OneServletContextWithSessionTest.java new file mode 100644 index 00000000000..e15ff85a437 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/OneServletContextWithSessionTest.java @@ -0,0 +1,91 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.io.BufferedWriter; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.eclipse.jetty.client.ContentResponse; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; +import org.eclipse.jetty.util.resource.ResourceFactory; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +@ExtendWith(WorkDirExtension.class) +public class OneServletContextWithSessionTest extends AbstractEmbeddedTest +{ + private static final String TEXT_CONTENT = "Do the right thing. It will gratify some people and astonish the rest. - Mark Twain"; + private Server server; + + public WorkDir workDir; + private Path baseDir; + + @BeforeEach + public void startServer() throws Exception + { + baseDir = workDir.getEmptyPathDir(); + Path textFile = baseDir.resolve("simple.txt"); + try (BufferedWriter writer = Files.newBufferedWriter(textFile, UTF_8)) + { + writer.write(TEXT_CONTENT); + } + + server = OneServletContextWithSession.createServer(0, ResourceFactory.root().newResource(baseDir)); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetHello() throws Exception + { + URI uri = server.getURI().resolve("/"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + String setCookieValue = response.getHeaders().get(HttpHeader.SET_COOKIE); + assertThat("Set-Cookie value", setCookieValue, containsString("JSESSIONID=")); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, + allOf( + containsString("session.getId() = "), + containsString("session.isNew() = true") + ) + ); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/OneWebAppTest.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/OneWebAppTest.java new file mode 100644 index 00000000000..320cab285a0 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/OneWebAppTest.java @@ -0,0 +1,68 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.net.URI; + +import org.eclipse.jetty.client.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.component.LifeCycle; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +@Disabled //TODO +public class OneWebAppTest extends AbstractEmbeddedTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = OneWebApp.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + LifeCycle.stop(server); + } + + @Test + @Tag("external") + public void testGetAsyncRest() throws Exception + { + // The async rest webapp forwards the call to ebay.com. + URI uri = server.getURI().resolve("/testAsync?items=mouse,beer,gnome"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("Asynchronous: mouse,beer,gnome")); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/OneWebAppWithJspTest.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/OneWebAppWithJspTest.java new file mode 100644 index 00000000000..4d216d31b1e --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/OneWebAppWithJspTest.java @@ -0,0 +1,106 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.net.URI; + +import org.eclipse.jetty.client.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.component.LifeCycle; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +@Disabled //TODO +public class OneWebAppWithJspTest extends AbstractEmbeddedTest +{ + private Server server; + private URI serverLocalUri; + + @BeforeEach + public void startServer() throws Exception + { + server = OneWebAppWithJsp.createServer(0); + server.start(); + + // Use URI based on "localhost" to get past "REMOTE ACCESS!" protection of demo war + serverLocalUri = URI.create("http://localhost:" + server.getURI().getPort() + "/"); + } + + @AfterEach + public void stopServer() throws Exception + { + LifeCycle.stop(server); + } + + @Test + public void testGetDumpInfo() throws Exception + { + URI uri = serverLocalUri.resolve("/dump.jsp"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("Protocol:HTTP/1.1")); + } + + @Test + public void testGetJspExpr() throws Exception + { + URI uri = serverLocalUri.resolve("/expr.jsp?A=1"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + String userAgent = client.getUserAgentField().getValue(); + assertThat("Response Content", responseBody, containsString("" + userAgent + "")); + } + + @Test + public void testGetJstlExpr() throws Exception + { + URI uri = serverLocalUri.resolve("/jstl.jsp"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("

JSTL Example

")); + for (int i = 1; i <= 10; i++) + { + assertThat("Response content (counting)", responseBody, containsString("" + i)); + } + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/ProxyServerTest.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/ProxyServerTest.java new file mode 100644 index 00000000000..d9a092a10b7 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/ProxyServerTest.java @@ -0,0 +1,71 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.net.URI; + +import org.eclipse.jetty.client.ContentResponse; +import org.eclipse.jetty.client.HttpProxy; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +@Disabled //TODO +public class ProxyServerTest extends AbstractEmbeddedTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = ProxyServer.createServer(0); + server.start(); + + URI uri = server.getURI(); + client.getProxyConfiguration().addProxy(new HttpProxy("localhost", uri.getPort())); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Tag("external") + @Test + public void testGetProxiedRFC() throws Exception + { + URI uri = URI.create("https://tools.ietf.org/rfc/rfc7230.txt"); + + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("Hypertext Transfer Protocol (HTTP/1.1): Message Syntax and Routing")); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/RewriteServerTest.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/RewriteServerTest.java new file mode 100644 index 00000000000..8d6eb59a6c9 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/RewriteServerTest.java @@ -0,0 +1,78 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.net.URI; + +import org.eclipse.jetty.client.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class RewriteServerTest extends AbstractEmbeddedTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = RewriteServer.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetRewriteFooInName() throws Exception + { + URI uri = server.getURI().resolve("/do-be-foo-be-do"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("requestURI=/do-be-FOO-be-do")); + } + + @Test + public void testGetRewriteFooInPath() throws Exception + { + URI uri = server.getURI().resolve("/do/be/foo/be/do.it"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("requestURI=/do/be/FOO/be/do.it")); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/SecuredHelloHandlerTest.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/SecuredHelloHandlerTest.java new file mode 100644 index 00000000000..80709bcb21e --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/SecuredHelloHandlerTest.java @@ -0,0 +1,80 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.net.URI; +import java.util.Base64; + +import org.eclipse.jetty.client.ContentResponse; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class SecuredHelloHandlerTest extends AbstractEmbeddedTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = SecuredHelloHandler.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetWithoutAuth() throws Exception + { + URI uri = server.getURI().resolve("/hello"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.UNAUTHORIZED_401)); + + // dumpResponseHeaders(response); + } + + @Test + public void testGetWithAuth() throws Exception + { + URI uri = server.getURI().resolve("/hello"); + + String authEncoded = Base64.getEncoder().encodeToString("user:password".getBytes(UTF_8)); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .headers(headers -> headers.put(HttpHeader.AUTHORIZATION, "Basic " + authEncoded)) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("

Hello World from HelloServlet

")); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/ServerUtil.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/ServerUtil.java new file mode 100644 index 00000000000..5ff2a02edc3 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/ServerUtil.java @@ -0,0 +1,83 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.HttpConnectionFactory; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.SslConnectionFactory; + +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +public class ServerUtil +{ + /** + * Fix the HttpConfiguration entries for securePort after the dynamic ports have been bound. + * + * @param server the server to correct. + */ + public static Map fixDynamicPortConfigurations(Server server) + { + // Fix ports in HttpConfiguration (since we are using dynamic port assignment for this testcase) + HttpConfiguration plainHttpConfiguration = null; + HttpConfiguration secureHttpConfiguration = null; + int plainHttpPort = -1; + int secureHttpPort = -1; + + for (Connector connector : server.getConnectors()) + { + if (connector instanceof ServerConnector) + { + ServerConnector serverConnector = (ServerConnector)connector; + SslConnectionFactory sslConnectionFactory = serverConnector.getConnectionFactory(SslConnectionFactory.class); + HttpConnectionFactory httpConnectionFactory = serverConnector.getConnectionFactory(HttpConnectionFactory.class); + if (httpConnectionFactory != null) + { + HttpConfiguration configuration = httpConnectionFactory.getHttpConfiguration(); + if (sslConnectionFactory != null) + { + secureHttpConfiguration = configuration; + secureHttpPort = serverConnector.getLocalPort(); + } + else + { + plainHttpConfiguration = configuration; + plainHttpPort = serverConnector.getLocalPort(); + } + } + } + } + + assertNotNull(plainHttpConfiguration, "Plain HTTP Configuration"); + assertNotEquals(plainHttpPort, -1, "Dynamic Plain HTTP Port"); + + assertNotNull(secureHttpConfiguration, "Secure HTTP Configuration"); + assertNotEquals(secureHttpPort, -1, "Dynamic Secure Port"); + + plainHttpConfiguration.setSecurePort(secureHttpPort); + secureHttpConfiguration.setSecurePort(secureHttpPort); + + Map ports = new HashMap<>(); + ports.put("plain", plainHttpPort); + ports.put("secure", secureHttpPort); + + return ports; + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/ServerWithAnnotationsTest.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/ServerWithAnnotationsTest.java new file mode 100644 index 00000000000..42977757708 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/ServerWithAnnotationsTest.java @@ -0,0 +1,64 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.net.URI; + +import org.eclipse.jetty.client.ContentResponse; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.component.LifeCycle; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; + +@Disabled //TODO +public class ServerWithAnnotationsTest extends AbstractEmbeddedTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = ServerWithAnnotations.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + LifeCycle.stop(server); + } + + @Test + public void testGetTest() throws Exception + { + URI uri = server.getURI().resolve("/test"); + ContentResponse response = client.GET(uri); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("maxAmount=55.0")); + assertThat("Response Content", responseBody, not(containsString(""))); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/ServerWithJMXTest.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/ServerWithJMXTest.java new file mode 100644 index 00000000000..7a92cfef06e --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/ServerWithJMXTest.java @@ -0,0 +1,59 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.util.Optional; +import java.util.Set; +import javax.management.MBeanServer; +import javax.management.ObjectName; + +import org.eclipse.jetty.jmx.MBeanContainer; +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +@Disabled(value = "ServerWithJMX uses registry on fixed port 1999 which can be already in-use") +public class ServerWithJMXTest extends AbstractEmbeddedTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = ServerWithJMX.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetTest() throws Exception + { + MBeanContainer mbeanContainer = server.getBean(MBeanContainer.class); + MBeanServer mbeanServer = mbeanContainer.getMBeanServer(); + + String name = "org.eclipse.jetty.jmx:name=rmiconnectorserver,*"; + Set mbeanNames = mbeanServer.queryNames(ObjectName.getInstance(name), null); + Optional rmiConnectorNameOptional = mbeanNames.stream().findFirst(); + assertTrue(rmiConnectorNameOptional.isPresent(), "Has RMI Connector Server"); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/ServerWithJNDITest.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/ServerWithJNDITest.java new file mode 100644 index 00000000000..f4c540c5969 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/ServerWithJNDITest.java @@ -0,0 +1,70 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.net.URI; + +import org.eclipse.jetty.client.ContentResponse; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.component.LifeCycle; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; + +public class ServerWithJNDITest extends AbstractEmbeddedTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = ServerWithJNDI.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + LifeCycle.stop(server); + } + + @Test + public void testGetTest() throws Exception + { + URI uri = server.getURI().resolve("/test"); + ContentResponse response = client.GET(uri); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, + allOf( + containsString("java:comp/env/woggle"), + containsString("java:comp/env/gargle"), + containsString("java:comp/env/wiggle") + ) + ); + + assertThat("Response Content", responseBody, not(containsString(""))); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/SimplestServerTest.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/SimplestServerTest.java new file mode 100644 index 00000000000..9fe945202d7 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/SimplestServerTest.java @@ -0,0 +1,52 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.net.URI; + +import org.eclipse.jetty.client.ContentResponse; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +public class SimplestServerTest extends AbstractEmbeddedTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = SimplestServer.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetTest() throws Exception + { + URI uri = server.getURI().resolve("/test"); + ContentResponse response = client.GET(uri); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.NOT_FOUND_404)); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/SplitFileServerTest.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/SplitFileServerTest.java new file mode 100644 index 00000000000..51984b308db --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/SplitFileServerTest.java @@ -0,0 +1,92 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.net.URI; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.eclipse.jetty.client.ContentResponse; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.util.resource.ResourceFactory; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +@Disabled //TODO +public class SplitFileServerTest extends AbstractEmbeddedTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + Path path0 = Paths.get("src/test/resources/dir0"); + Path path1 = Paths.get("src/test/resources/dir1"); + Resource resource0 = ResourceFactory.root().newResource(path0); + Resource resource1 = ResourceFactory.root().newResource(path1); + + server = SplitFileServer.createServer(0, resource0, resource1); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetTest0() throws Exception + { + URI uri = server.getURI().resolve("/test0.txt"); + ContentResponse response = client.GET(uri); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("test0")); + } + + @Test + public void testGetTest1() throws Exception + { + URI uri = server.getURI().resolve("/test1.txt"); + ContentResponse response = client.GET(uri); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("test1")); + } + + @Test + public void testGetTest2() throws Exception + { + URI uri = server.getURI().resolve("/test2.txt"); + ContentResponse response = client.GET(uri); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.NOT_FOUND_404)); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/WebSocketServerTest.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/WebSocketServerTest.java new file mode 100644 index 00000000000..1192dcb4ecf --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/java/org/eclipse/jetty/ee11/demos/WebSocketServerTest.java @@ -0,0 +1,107 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.net.URI; +import java.time.Duration; +import java.util.concurrent.Future; +import java.util.concurrent.LinkedBlockingQueue; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.component.LifeCycle; +import org.eclipse.jetty.websocket.api.Callback; +import org.eclipse.jetty.websocket.api.Session; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; +import org.eclipse.jetty.websocket.api.annotations.WebSocket; +import org.eclipse.jetty.websocket.api.util.WSURI; +import org.eclipse.jetty.websocket.client.WebSocketClient; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +public class WebSocketServerTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = WebSocketServer.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetEcho() throws Exception + { + WebSocketClient webSocketClient = new WebSocketClient(); + webSocketClient.setIdleTimeout(Duration.ofSeconds(2)); + try + { + webSocketClient.start(); + URI wsUri = WSURI.toWebsocket(server.getURI().resolve("/echo")); + + TrackingClientEndpoint clientEndpoint = new TrackingClientEndpoint(); + + Future sessionFut = webSocketClient.connect(clientEndpoint, wsUri); + Session session = sessionFut.get(2, SECONDS); + session.sendText("Hello World", Callback.NOOP); + + String response = clientEndpoint.messages.poll(2, SECONDS); + assertThat("Response", response, is("Hello World")); + } + finally + { + LifeCycle.stop(webSocketClient); + } + } + + @WebSocket + public static class TrackingClientEndpoint + { + private static final Logger LOG = LoggerFactory.getLogger(TrackingClientEndpoint.class); + public LinkedBlockingQueue messages = new LinkedBlockingQueue<>(); + + @OnWebSocketMessage + public void onMessage(String message) + { + messages.offer(message); + } + + @OnWebSocketError + public void onError(Throwable cause) + { + LOG.warn("TrackingClientEndpoint Error", cause); + } + + @OnWebSocketClose + public void onClose(int statusCode, String reason) + { + LOG.debug("Closed({}, {})", statusCode, reason); + } + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/resources/dir0/test0.txt b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/resources/dir0/test0.txt new file mode 100644 index 00000000000..a39c44cc614 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/resources/dir0/test0.txt @@ -0,0 +1 @@ +test0 \ No newline at end of file diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/resources/dir1/test1.txt b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/resources/dir1/test1.txt new file mode 100644 index 00000000000..f079749c42f --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/resources/dir1/test1.txt @@ -0,0 +1 @@ +test1 \ No newline at end of file diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/resources/jetty-logging.properties b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/resources/jetty-logging.properties new file mode 100644 index 00000000000..948c82a590f --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/resources/jetty-logging.properties @@ -0,0 +1,11 @@ +# Jetty Logging using jetty-slf4j-impl +org.eclipse.jetty.LEVEL=INFO +org.eclipse.jetty.demos.JettyHome.LEVEL=DEBUG +#org.eclipse.jetty.STACKS=true +#org.eclipse.jetty.STACKS=false +#org.eclipse.jetty.io.LEVEL=DEBUG +#org.eclipse.jetty.io.ssl.LEVEL=DEBUG +#org.eclipse.jetty.server.LEVEL=DEBUG +#org.eclipse.jetty.ee11.servlets.LEVEL=DEBUG +#org.eclipse.jetty.alpn.LEVEL=DEBUG +#org.eclipse.jetty.jmx.LEVEL=DEBUG diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/resources/realm.properties b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/resources/realm.properties new file mode 100644 index 00000000000..556117fcdbc --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-embedded/src/test/resources/realm.properties @@ -0,0 +1,21 @@ +# +# This file defines users passwords and roles for a HashUserRealm +# +# The format is +# : [, ...] +# +# Passwords may be clear text, obfuscated or checksummed. The class +# org.eclipse.jetty.util.security.Password should be used to generate obfuscated +# passwords or password checksums +# +# If DIGEST Authentication is used, the password must be in a recoverable +# format, either plain text or OBF:. +# +jetty: MD5:164c88b302622e17050af52c89945d44,user +admin: CRYPT:adpexzg3FUZAk,server-administrator,content-administrator,admin,user +other: OBF:1xmk1w261u9r1w1c1xmq,user +plain: plain,user +user: password,user + +# This entry is for digest auth. The credential is a MD5 hash of username:realmname:password +digest: MD5:6e120743ad67abfbc385bc2bb754e297,user diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jaas-webapp/pom.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jaas-webapp/pom.xml new file mode 100644 index 00000000000..206071ed47f --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jaas-webapp/pom.xml @@ -0,0 +1,42 @@ + + + 4.0.0 + + org.eclipse.jetty.ee11.demos + jetty-ee11-demos + 12.1.0-SNAPSHOT + + jetty-ee11-demo-jaas-webapp + war + EE11 :: Demo :: JAAS WebApp + + ${project.groupId}.jaas + + + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jaas-webapp/src/main/config/modules/demo.d/ee11-demo-jaas.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jaas-webapp/src/main/config/modules/demo.d/ee11-demo-jaas.xml new file mode 100644 index 00000000000..fe153f8ba44 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jaas-webapp/src/main/config/modules/demo.d/ee11-demo-jaas.xml @@ -0,0 +1,26 @@ + + + + + + + + + /ee11-test-jaas + /ee11-demo-jaas.war + + true + + + + + + Demo JAAS Realm + demo + + + + + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jaas-webapp/src/main/config/modules/ee11-demo-jaas.mod b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jaas-webapp/src/main/config/modules/ee11-demo-jaas.mod new file mode 100644 index 00000000000..db3e11dedc6 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jaas-webapp/src/main/config/modules/ee11-demo-jaas.mod @@ -0,0 +1,24 @@ +# DO NOT EDIT THIS FILE - See: https://eclipse.dev/jetty/documentation/ + +[description] +Demo EE11 JAAS webapp + +[environment] +ee11 + +[tags] +demo +webapp + +[depends] +demo-jaas +ee11-deploy +jaas +jdbc +ee11-jsp +ee11-annotations +ext + +[files] +basehome:modules/demo.d/ee11-demo-jaas.xml|webapps/ee11-demo-jaas.xml +maven://org.eclipse.jetty.ee11.demos/jetty-ee11-demo-jaas-webapp/${jetty.version}/war|webapps/ee11-demo-jaas.war diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jaas-webapp/src/main/etc/ee11-demo-login.conf b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jaas-webapp/src/main/etc/ee11-demo-login.conf new file mode 100644 index 00000000000..970f5cf1652 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jaas-webapp/src/main/etc/ee11-demo-login.conf @@ -0,0 +1,5 @@ +ee11-xyz { +org.eclipse.jetty.ee11.jaas.spi.PropertyFileLoginModule required +debug="true" +file="${jetty.base}/etc/ee11-demo-login.properties"; +}; diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jaas-webapp/src/main/webapp/WEB-INF/jetty-web.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jaas-webapp/src/main/webapp/WEB-INF/jetty-web.xml new file mode 100644 index 00000000000..b4475ea2c09 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jaas-webapp/src/main/webapp/WEB-INF/jetty-web.xml @@ -0,0 +1,8 @@ + + + + + + The test-jaas webapp is deployed. DO NOT USE IN PRODUCTION! + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jaas-webapp/src/main/webapp/WEB-INF/web.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jaas-webapp/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000000..9fdea59d670 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jaas-webapp/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,41 @@ + + + EE11 Demo JAAS WebApp + + + index.html + + + + + + JAAS Role + /auth.html + + + roleA + + + + + + FORM + Test JAAS Realm + + + /login.html + + + /authfail.html + + + + + + roleA + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jaas-webapp/src/main/webapp/auth.html b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jaas-webapp/src/main/webapp/auth.html new file mode 100644 index 00000000000..2041c6dce03 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jaas-webapp/src/main/webapp/auth.html @@ -0,0 +1,17 @@ + + + + + + + +

SUCCESS! You are AUTHENTICATED and AUTHORIZED

+In order to see this page, you must have been JAAS authenticated using the +configured Login Module. You have also been authorized according to this webapp's role-based web security constraints. +

+ To logout click: +

+ LOGOUT +

+ + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jaas-webapp/src/main/webapp/authfail.html b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jaas-webapp/src/main/webapp/authfail.html new file mode 100644 index 00000000000..62d7e5efec8 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jaas-webapp/src/main/webapp/authfail.html @@ -0,0 +1,10 @@ + + + Authentication Failure + + +

Authentication Failure

+

Sorry, either your login or password were incorrect, please try again.

+ LOGIN + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jaas-webapp/src/main/webapp/demo.css b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jaas-webapp/src/main/webapp/demo.css new file mode 100644 index 00000000000..f2b91d3365d --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jaas-webapp/src/main/webapp/demo.css @@ -0,0 +1,83 @@ +body +{ + font-family: Arial, Verdana, Helvetica, sans-serif; +} + +.topnav +{ + overflow: hidden; + padding: 10px; + border: 1px solid #f6815c; + border-radius: 10px; + text-align: right; +} + +.menu +{ + margin-left: 3em; +} + +.content +{ + padding: 10px; +} + +.footer +{ + padding: 10px; + border-radius: 10px; + border: 1px solid #f6815c; +} + +.test +{ + background-color: #0099cc; + color: white; + padding: 10px 15px; + border: none; + font-size: 12pt; + border-radius: 10px; + box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2), 0 3px 10px 0 rgba(0,0,0,0.19); +} + +.test:hover +{ + background-color: #f6815c; + color: white; +} + +A:link +{ + color: #0099cc; + text-decoration: none; + font-weight: normal; + font-size: 11pt; + font-family:sans-serif; +} + +A:visited +{ + color: #0099cc; + text-decoration: none; + font-weight: normal; + font-size: 11pt; + font-family:sans-serif; +} + +A:hover +{ + color: #ff6600; + text-decoration: none; + font-weight: normal; + font-size: 11pt; + font-family:sans-serif; +} + +A:active +{ + color: #0099cc; + text-decoration: none; + font-weight: normal; + font-size: 11pt; + font-family:sans-serif; +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jaas-webapp/src/main/webapp/index.html b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jaas-webapp/src/main/webapp/index.html new file mode 100644 index 00000000000..c27be05c62b --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jaas-webapp/src/main/webapp/index.html @@ -0,0 +1,43 @@ + + + JAAS Authentication and Authorization Test + + + + + + + + +
+
+ Demo Web Application Only - Do NOT Deploy in Production +
+

JAAS Demo

+

+ This is a demo webapp for the Eclipse Jetty HTTP Server and Servlet Container. It was added into your $JETTY_BASE/webapps directory. + + It uses a simple login module that stores its configuration in a properties file. +

+ +

Using the Demo

+

+ Click on the link below to test JAAS authentication and role-based web security constraint authorization. Use + username=me with password=me. All other usernames and passwords should result in authentication + failure. +

+ START +
+ + + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jaas-webapp/src/main/webapp/login.html b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jaas-webapp/src/main/webapp/login.html new file mode 100644 index 00000000000..b4732c6ad74 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jaas-webapp/src/main/webapp/login.html @@ -0,0 +1,35 @@ + + +JAAS Authentication and Authorization Test + + + + +
+
+ Demo Web Application Only - Do NOT Deploy in Production +
+ +

Enter your username and password to login

+ Enter login=me and password=me in order to authenticate successfully +
+ Login: +

+ Password: +

+ +

+
+ + + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jaas-webapp/src/main/webapp/logout.jsp b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jaas-webapp/src/main/webapp/logout.jsp new file mode 100644 index 00000000000..7f780c55cc7 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jaas-webapp/src/main/webapp/logout.jsp @@ -0,0 +1,18 @@ +<%@ page contentType="text/html; charset=UTF-8" %> + + + Logout + + + +<% + HttpSession s = request.getSession(false); + s.invalidate(); + %> +

Logout

+ +

You are now logged out.

+ Login + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jaas-webapp/src/main/webapp/small_powered_by.gif b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jaas-webapp/src/main/webapp/small_powered_by.gif new file mode 100644 index 00000000000..c5dd44319f0 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jaas-webapp/src/main/webapp/small_powered_by.gif differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jaas-webapp/src/main/webapp/stylesheet.css b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jaas-webapp/src/main/webapp/stylesheet.css new file mode 100644 index 00000000000..4ecc2cb4ae1 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jaas-webapp/src/main/webapp/stylesheet.css @@ -0,0 +1,7 @@ +body {color: #2E2E2E; font-family:sans-serif; font-size:90%;} +h1 {font-variant: small-caps; font-size:130%; letter-spacing: 0.1em;} +h2 {font-variant: small-caps; font-size:100%; letter-spacing: 0.1em;} +h3 {font-size:100%; letter-spacing: 0.1em;} + +span.pass { color: green; } +span.fail { color:red; } diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/jetty-chat.jmx b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/jetty-chat.jmx new file mode 100644 index 00000000000..088b7e0c485 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/jetty-chat.jmx @@ -0,0 +1,318 @@ + + + + + + + + false + false + + + + + 1165945030000 + + + 400 + false + + 1 + false + + 1165945030000 + continue + 10 + + + + + + = + 50 + rooms + + + = + + + + + + + + /test/chat/ + localhost + http + + + + 8080 + + + + true + + + + + + GET + true + + true + + + + + = + poll + true + ajax + false + + + = + poll + true + message + false + + + = + 0 + true + timeout + false + + + = + ${__javaScript(${__threadNum}%${rooms},room)} + true + room + false + + + + + + false + + + false + + + + + POST + true + + false + + + + + = + join + true + ajax + false + + + = + Elvis${__threadNum} + true + message + false + + + = + ${__javaScript(${__threadNum}%${rooms},room)} + true + room + false + + + + + + false + + + false + + + + -1 + true + + + + ${__Random(3,20,random)} + true + + + + + GET + true + + true + + + + + = + poll + true + ajax + false + + + = + poll + true + message + false + + + = + ${__Random(3000,10000,poll)} + true + timeout + false + + + = + ${__javaScript(${__threadNum}%${rooms},room)} + true + room + false + + + + + + false + + + false + + + + + + POST + true + + false + + + + + = + chat + true + ajax + false + + + = + Give me ${__Random(1,200,mars)} deep fried mars bars + true + message + false + + + = + ${__javaScript(${__threadNum}%${rooms},room)} + true + room + false + + + + + + false + + + false + + + + + + POST + true + + false + + + + + = + leave + true + ajax + false + + + = + Elvis${__threadNum} + true + message + false + + + = + ${__javaScript(${__threadNum}%${rooms},room)} + true + room + false + + + + + + false + + + false + + + + + + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + false + false + false + false + false + 0 + + saveConfig + + + false + + + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/pom.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/pom.xml new file mode 100644 index 00000000000..71250f197c3 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/pom.xml @@ -0,0 +1,200 @@ + + + 4.0.0 + + org.eclipse.jetty.ee11.demos + jetty-ee11-demos + 12.1.0-SNAPSHOT + + jetty-ee11-demo-jetty-webapp + war + EE11 :: Demo :: Jetty WebApp + + ${project.groupId}.webapp + + + + jakarta.annotation + jakarta.annotation-api + provided + + + jakarta.servlet + jakarta.servlet-api + provided + + + jakarta.servlet.jsp + jakarta.servlet.jsp-api + provided + + + jakarta.servlet.jsp.jstl + jakarta.servlet.jsp.jstl-api + provided + + + jakarta.websocket + jakarta.websocket-api + provided + + + jakarta.websocket + jakarta.websocket-client-api + provided + + + org.eclipse.jetty + jetty-server + provided + + + org.eclipse.jetty.ee11 + jetty-ee11-servlets + provided + + + org.eclipse.jetty.ee11.websocket + jetty-ee11-websocket-jetty-server + provided + + + org.eclipse.jetty.websocket + jetty-websocket-jetty-api + provided + + + org.eclipse.jetty + jetty-jmx + test + + + org.eclipse.jetty.ee11 + jetty-ee11-webapp + test + + + org.eclipse.jetty.ee11.websocket + jetty-ee11-websocket-jakarta-server + test + + + org.eclipse.jetty.toolchain + jetty-test-helper + test + + + + + + + org.eclipse.jetty + jetty-maven-plugin + ${project.version} + + 8087 + foo + 1 + + 222 + + + /test + ${project.build.directory}/work + + + + Test Realm + src/test/resources/test-realm.properties + + + + + + org.eclipse.jetty + jetty-client + ${project.version} + + + org.eclipse.jetty.ee11 + jetty-servlets + ${project.version} + + + + + + + + org.apache.felix + maven-bundle-plugin + true + + + war + + + jakarta.servlet.jsp.*;version="[3,4)",org.eclipse.jetty.*;version="[$(version;===;${parsedVersion.osgiVersion}),$(version;==+;${parsedVersion.osgiVersion}))",* + !org.example* + + /ee11-demo-jetty + + .,WEB-INF/classes + ee11 + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + web-bundle-assembly + + single + + package + + + src/main/assembly/web-bundle.xml + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + **/WebAppTest.java + **/Test*.java + + + + + test + test + + + + + + maven-war-plugin + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/assembly/embedded-jetty-web-for-webbundle.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/assembly/embedded-jetty-web-for-webbundle.xml new file mode 100644 index 00000000000..fcd6fc104c7 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/assembly/embedded-jetty-web-for-webbundle.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + / + /webapps/test.war + + + + + true + false + /etc/webdefault-ee11.xml + /etc/override-web.xml + + + + + + + + + Test Realm + + /realm.properties + + + + + + true + + + + + + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/assembly/web-bundle.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/assembly/web-bundle.xml new file mode 100644 index 00000000000..92951efa91e --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/assembly/web-bundle.xml @@ -0,0 +1,38 @@ + + webbundle + + jar + + false + + + + ${basedir}/${project.build.directory}/${project.build.finalName}/ + + + **/*.* + + + WEB-INF/lib/** + WEB-INF/jetty-web.xml + + + + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/config/modules/demo.d/ee11-demo-jetty-override-web.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/config/modules/demo.d/ee11-demo-jetty-override-web.xml new file mode 100644 index 00000000000..4d71431ce0a --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/config/modules/demo.d/ee11-demo-jetty-override-web.xml @@ -0,0 +1,64 @@ + + + + + + + + + context-override-example + a context value + + + + + default + + precompressed + true + + + + + + Dump + + servlet-override-example + a servlet value + + + + + + Dump + *.more + + + + + Session + org.example.SessionDump + 5 + + + + + + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/config/modules/demo.d/ee11-demo-jetty.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/config/modules/demo.d/ee11-demo-jetty.xml new file mode 100644 index 00000000000..6f06907fbc3 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/config/modules/demo.d/ee11-demo-jetty.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + /ee11-test + /ee11-demo-jetty.war + + + + + + true + false + /etc/webdefault-ee11.xml + /ee11-demo-jetty.d/ee11-demo-jetty-override-web.xml + + + + + org.eclipse.jetty.websocket.jakarta + true + + + + + + 2048 + + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/config/modules/demo.d/ee11-demo-rewrite-rules.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/config/modules/demo.d/ee11-demo-rewrite-rules.xml new file mode 100644 index 00000000000..5589484aa0d --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/config/modules/demo.d/ee11-demo-rewrite-rules.xml @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + /favicon.ico + Cache-Control + Max-Age=3600,public + true + + + + + + + + + /ee11-test/rewrite/ + /ee11-test/rewrite/info.html + + + + + + + + + /ee11-test/some/old/context + /ee11-test/rewritten/newcontext + + + + + + + + + /ee11-test/rewrite/for/* + /ee11-test/rewritten/ + + + + + + + + + (.*?)/reverse/([^/]*)/(.*) + $1/reverse/$3/$2 + + + + + + + + + /* + visited + yes + + + + + + + + + /ee11-test/redirect/* + /ee11-test/redirected + + + + + + + + + /400Error + 400 + ResponsePatternRuleDemo + + + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/config/modules/ee11-demo-jetty.mod b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/config/modules/ee11-demo-jetty.mod new file mode 100644 index 00000000000..b0a7467983e --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/config/modules/ee11-demo-jetty.mod @@ -0,0 +1,30 @@ +# DO NOT EDIT THIS FILE - See: https://eclipse.dev/jetty/documentation/ + +[description] +Demo Jetty Webapp + +[tags] +demo +webapp + +[environment] +ee11 + +[depends] +ee11-deploy +jdbc +ee11-jsp +ee11-jstl +ee11-annotations +ext +ee11-servlets +ee11-websocket-jakarta +ee11-websocket-jetty +demo-realm +ee11-demo-rewrite + +[files] +webapps/ee11-demo-jetty.d/ +basehome:modules/demo.d/ee11-demo-jetty.xml|webapps/ee11-demo-jetty.xml +basehome:modules/demo.d/ee11-demo-jetty-override-web.xml|webapps/ee11-demo-jetty.d/ee11-demo-jetty-override-web.xml +maven://org.eclipse.jetty.ee11.demos/jetty-ee11-demo-jetty-webapp/${jetty.version}/war|webapps/ee11-demo-jetty.war diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/config/modules/ee11-demo-rewrite.mod b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/config/modules/ee11-demo-rewrite.mod new file mode 100644 index 00000000000..71421571905 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/config/modules/ee11-demo-rewrite.mod @@ -0,0 +1,20 @@ +# DO NOT EDIT THIS FILE - See: https://eclipse.dev/jetty/documentation/ + +[description] +Demonstrate the rewrite module. + +[environment] +ee11 + +[tags] +demo + +[depends] +rewrite + +[xml] +etc/ee11-demo-rewrite-rules.xml + +[files] +basehome:modules/demo.d/ee11-demo-rewrite-rules.xml|etc/ee11-demo-rewrite-rules.xml + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/AddListServletRequestListener.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/AddListServletRequestListener.java new file mode 100644 index 00000000000..b28f51b8c28 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/AddListServletRequestListener.java @@ -0,0 +1,45 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.example; + +import java.util.ArrayList; +import java.util.List; + +import jakarta.servlet.ServletRequestEvent; +import jakarta.servlet.ServletRequestListener; + +public final class AddListServletRequestListener + implements ServletRequestListener +{ + + public void requestDestroyed(ServletRequestEvent event) + { + List al = (List)event.getServletContext().getAttribute("arraylist"); + if (al != null) + { + event.getServletContext().removeAttribute("arraylist"); + } + } + + public void requestInitialized(ServletRequestEvent event) + { + List al = (List)event.getServletContext().getAttribute("arraylist"); + if (al == null) + { + al = new ArrayList(); + } + al.add("in requestInitialized method of " + getClass().getName()); + event.getServletContext().setAttribute("arraylist", al); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/ChatServlet.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/ChatServlet.java new file mode 100644 index 00000000000..205aa0229ba --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/ChatServlet.java @@ -0,0 +1,218 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.example; + +import java.io.IOException; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; +import java.util.Queue; +import java.util.concurrent.atomic.AtomicReference; + +import jakarta.servlet.AsyncContext; +import jakarta.servlet.AsyncEvent; +import jakarta.servlet.AsyncListener; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +// Simple asynchronous Chat room. +// This does not handle duplicate usernames or multiple frames/tabs from the same browser +// Some code is duplicated for clarity. +@SuppressWarnings("serial") +public class ChatServlet extends HttpServlet +{ + private long asyncTimeout = 10000; + + @Override + public void init() + { + String parameter = getServletConfig().getInitParameter("asyncTimeout"); + if (parameter != null) + asyncTimeout = Long.parseLong(parameter); + } + + // inner class to hold message queue for each chat room member + class Member implements AsyncListener + { + final String _name; + final AtomicReference _async = new AtomicReference<>(); + final Queue _queue = new LinkedList<>(); + + Member(String name) + { + _name = name; + } + + @Override + public void onTimeout(AsyncEvent event) throws IOException + { + getServletContext().log("resume request"); + AsyncContext async = _async.get(); + if (async != null && _async.compareAndSet(async, null)) + { + HttpServletResponse response = (HttpServletResponse)async.getResponse(); + response.setContentType("text/json;charset=utf-8"); + response.getOutputStream().write("{action:\"poll\"}".getBytes()); + async.complete(); + } + } + + @Override + public void onStartAsync(AsyncEvent event) throws IOException + { + event.getAsyncContext().addListener(this); + } + + @Override + public void onError(AsyncEvent event) throws IOException + { + } + + @Override + public void onComplete(AsyncEvent event) throws IOException + { + } + } + + Map> _rooms = new HashMap<>(); + + // Handle Ajax calls from browser + @Override + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + // Ajax calls are form encoded + boolean join = Boolean.parseBoolean(request.getParameter("join")); + String message = request.getParameter("message"); + String username = request.getParameter("user"); + + getServletContext().log("doPost called. join=" + join + " message=" + message + " username=" + username); + if (username == null) + { + getServletContext().log("no parameter user set, sending 503"); + response.sendError(503, "user==null"); + return; + } + + Map room = getRoom(request.getPathInfo()); + Member member = getMember(username, room); + + if (message != null) + { + sendMessageToAllMembers(message, username, room); + } + // If a message is set, we only want to enter poll mode if the user is a new user. This is necessary to avoid + // two parallel requests per user (one is already in async wait and the new one). Sending a message will + // dispatch to an existing poll request if necessary and the client will issue a new request to receive the + // next message or long poll again. + if (message == null || join) + { + synchronized (member) + { + getServletContext().log("Queue size: " + member._queue.size()); + if (!member._queue.isEmpty()) + { + sendSingleMessage(response, member); + } + else + { + getServletContext().log("starting async"); + AsyncContext async = request.startAsync(); + async.setTimeout(asyncTimeout); + async.addListener(member); + member._async.set(async); + } + } + } + } + + private Member getMember(String username, Map room) + { + Member member = room.get(username); + if (member == null) + { + getServletContext().log("user: " + username + " in room: " + room + " doesn't exist. Creating new user."); + member = new Member(username); + room.put(username, member); + } + return member; + } + + private Map getRoom(String path) + { + Map room = _rooms.get(path); + if (room == null) + { + getServletContext().log("room: " + path + " doesn't exist. Creating new room."); + room = new HashMap<>(); + _rooms.put(path, room); + } + return room; + } + + private void sendSingleMessage(HttpServletResponse response, Member member) throws IOException + { + response.setContentType("text/json;charset=utf-8"); + StringBuilder buf = new StringBuilder(); + + buf.append("{\"from\":\""); + buf.append(member._queue.poll()); + buf.append("\","); + + String returnMessage = member._queue.poll(); + int quote = returnMessage.indexOf('"'); + while (quote >= 0) + { + returnMessage = returnMessage.substring(0, quote) + '\\' + returnMessage.substring(quote); + quote = returnMessage.indexOf('"', quote + 2); + } + buf.append("\"chat\":\""); + buf.append(returnMessage); + buf.append("\"}"); + byte[] bytes = buf.toString().getBytes("utf-8"); + response.setContentLength(bytes.length); + response.getOutputStream().write(bytes); + } + + private void sendMessageToAllMembers(String message, String username, Map room) + { + for (Member m : room.values()) + { + synchronized (m) + { + m._queue.add(username); // from + m._queue.add(message); // chat + + // wakeup member if polling + AsyncContext async = m._async.get(); + if (async != null & m._async.compareAndSet(async, null)) + { + async.dispatch(); + } + } + } + } + + // Serve the HTML with embedded CSS and Javascript. + // This should be static content and should use real JS libraries. + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + if (request.getParameter("action") != null) + doPost(request, response); + else + getServletContext().getNamedDispatcher("default").forward(request, response); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/CookieDump.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/CookieDump.java new file mode 100644 index 00000000000..14d779194f7 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/CookieDump.java @@ -0,0 +1,130 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.example; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.concurrent.TimeUnit; + +import jakarta.servlet.RequestDispatcher; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +/** + * Test Servlet Cookies. + */ +@SuppressWarnings("serial") +public class CookieDump extends HttpServlet +{ + int redirectCount = 0; + + protected void handleForm(HttpServletRequest request, + HttpServletResponse response) + { + String name = request.getParameter("Name"); + String value = request.getParameter("Value"); + String age = request.getParameter("Age"); + + if (name != null && !name.isEmpty()) + { + Cookie cookie = new Cookie(name, value); + if (age != null && !age.isEmpty()) + cookie.setMaxAge(Integer.parseInt(age)); + response.addCookie(cookie); + } + } + + @Override + public void doPost(HttpServletRequest request, + HttpServletResponse response) + throws ServletException, IOException + { + handleForm(request, response); + String nextUrl = getURI(request) + "?R=" + redirectCount++; + String encodedUrl = response.encodeRedirectURL(nextUrl); + response.sendRedirect(encodedUrl); + } + + @Override + public void doGet(HttpServletRequest request, + HttpServletResponse response) + throws ServletException, IOException + { + handleForm(request, response); + + response.setContentType("text/html"); + + PrintWriter out = response.getWriter(); + out.println("

Cookie Dump Servlet:

"); + + Cookie[] cookies = request.getCookies(); + + for (int i = 0; cookies != null && i < cookies.length; i++) + { + out.println("" + deScript(cookies[i].getName()) + "=" + deScript(cookies[i].getValue()) + "
"); + } + + out.println("
"); + + out.println("Name:
"); + out.println("Value:
"); + out.println("Max-Age:
"); + out.println(""); + } + + @Override + public String getServletInfo() + { + return "Session Dump Servlet"; + } + + private String getURI(HttpServletRequest request) + { + String uri = (String)request.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI); + if (uri == null) + uri = request.getRequestURI(); + return uri; + } + + protected String deScript(String string) + { + if (string == null) + return null; + string = string.replace("&", "&"); + string = string.replace("<", "<"); + string = string.replace(">", ">"); + return string; + } + + @Override + public void destroy() + { + // For testing --stop with STOP.WAIT handling of the jetty-start behavior. + if (Boolean.getBoolean("test.slow.destroy")) + { + try + { + TimeUnit.SECONDS.sleep(10); + } + catch (InterruptedException e) + { + // ignore + } + } + super.destroy(); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/DispatchServlet.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/DispatchServlet.java new file mode 100644 index 00000000000..027d6b4ab6e --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/DispatchServlet.java @@ -0,0 +1,254 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.example; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintWriter; + +import jakarta.servlet.RequestDispatcher; +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequestWrapper; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletResponseWrapper; + +/** + * Test Servlet RequestDispatcher. + */ +@SuppressWarnings("serial") +public class DispatchServlet extends HttpServlet +{ + + String pageType; + + @Override + public void doPost(HttpServletRequest sreq, HttpServletResponse sres) throws ServletException, IOException + { + doGet(sreq, sres); + } + + @Override + public void doGet(HttpServletRequest sreq, HttpServletResponse sres) throws ServletException, IOException + { + if (sreq.getParameter("wrap") != null) + { + sreq = new HttpServletRequestWrapper(sreq); + sres = new HttpServletResponseWrapper(sres); + } + + if (sreq.getParameter("session") != null) + sreq.getSession(true); + + String prefix = + sreq.getContextPath() != null ? sreq.getContextPath() + sreq.getServletPath() : sreq.getServletPath(); + + String info; + + if (sreq.getAttribute(RequestDispatcher.INCLUDE_SERVLET_PATH) != null) + info = (String)sreq.getAttribute(RequestDispatcher.INCLUDE_PATH_INFO); + else + info = sreq.getPathInfo(); + + if (info == null) + info = "NULL"; + + if (info.indexOf(sreq.getServletPath()) > 0) + { + sres.sendError(403, "Nested " + sreq.getServletPath() + " forbidden."); + return; + } + + if (info.indexOf(getServletName()) > 0) + { + sres.sendError(403, "Nested " + getServletName() + " forbidden."); + return; + } + + if (info.startsWith("/includeW/")) + { + sres.setContentType("text/html"); + info = info.substring(9); + if (info.indexOf('?') < 0) + info += "?Dispatch=include"; + else + info += "&Dispatch=include"; + + PrintWriter pout = null; + pout = sres.getWriter(); + pout.write("

Include (writer): " + info + "


"); + + RequestDispatcher dispatch = getServletContext().getRequestDispatcher(info); + if (dispatch == null) + { + pout = sres.getWriter(); + pout.write("

Null dispatcher

"); + } + else + dispatch.include(sreq, sres); + + pout.write("

-- Included (writer)

"); + } + else if (info.startsWith("/includeS/")) + { + sres.setContentType("text/html"); + info = info.substring(9); + if (info.indexOf('?') < 0) + info += "?Dispatch=include"; + else + info += "&Dispatch=include"; + + OutputStream out = null; + out = sres.getOutputStream(); + out.write(("

Include (outputstream): " + info + "


").getBytes()); + + RequestDispatcher dispatch = getServletContext().getRequestDispatcher(info); + if (dispatch == null) + { + out = sres.getOutputStream(); + out.write("

Null dispatcher

".getBytes()); + } + else + dispatch.include(sreq, sres); + + out.write("

-- Included (outputstream)

".getBytes()); + } + else if (info.startsWith("/forward/")) + { + info = info.substring(8); + if (info.indexOf('?') < 0) + info += "?Dispatch=forward"; + else + info += "&Dispatch=forward"; + + RequestDispatcher dispatch = getServletContext().getRequestDispatcher(info); + if (dispatch != null) + { + ServletOutputStream out = sres.getOutputStream(); + out.print("Can't see this"); + dispatch.forward(sreq, sres); + try + { + // should be closed + out.println("IOException"); + // should not get here + throw new IllegalStateException(); + } + catch (IOException e) + { + // getServletContext().log("ignore",e); + } + } + else + { + sres.setContentType("text/html"); + PrintWriter pout = sres.getWriter(); + pout.write("

No dispatcher for: " + info + "


"); + pout.flush(); + } + } + else if (info.startsWith("/forwardC/")) + { + info = info.substring(9); + if (info.indexOf('?') < 0) + info += "?Dispatch=forward"; + else + info += "&Dispatch=forward"; + + String cpath = info.substring(0, info.indexOf('/', 1)); + info = info.substring(cpath.length()); + + ServletContext context = getServletContext().getContext(cpath); + RequestDispatcher dispatch = context.getRequestDispatcher(info); + + if (dispatch != null) + { + dispatch.forward(sreq, sres); + } + else + { + sres.setContentType("text/html"); + PrintWriter pout = sres.getWriter(); + pout.write("

No dispatcher for: " + cpath + "/" + info + "


"); + pout.flush(); + } + } + else if (info.startsWith("/includeN/")) + { + sres.setContentType("text/html"); + info = info.substring(10); + if (info.indexOf("/") >= 0) + info = info.substring(0, info.indexOf("/")); + + PrintWriter pout; + if (info.startsWith("/null")) + info = info.substring(5); + else + { + pout = sres.getWriter(); + pout.write("

Include named: " + info + "


"); + } + + RequestDispatcher dispatch = getServletContext().getNamedDispatcher(info); + if (dispatch != null) + dispatch.include(sreq, sres); + else + { + pout = sres.getWriter(); + pout.write("

No servlet named: " + info + "

"); + } + + pout = sres.getWriter(); + pout.write("

Included "); + } + else if (info.startsWith("/forwardN/")) + { + info = info.substring(10); + if (info.indexOf("/") >= 0) + info = info.substring(0, info.indexOf("/")); + RequestDispatcher dispatch = getServletContext().getNamedDispatcher(info); + if (dispatch != null) + dispatch.forward(sreq, sres); + else + { + sres.setContentType("text/html"); + PrintWriter pout = sres.getWriter(); + pout.write("

No servlet named: " + info + "

"); + pout.flush(); + } + } + else + { + sres.setContentType("text/html"); + PrintWriter pout = sres.getWriter(); + pout.write( + "

Dispatch URL must be of the form:

" + + "
" +
+                    prefix + "/includeW/path\n" +
+                    prefix + "/includeS/path\n" +
+                    prefix + "/forward/path\n" +
+                    prefix + "/includeN/name\n" +
+                    prefix + "/forwardC/_context/path\n
"); + } + } + + @Override + public String getServletInfo() + { + return "Include Servlet"; + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/Dump.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/Dump.java new file mode 100644 index 00000000000..50a4ee400df --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/Dump.java @@ -0,0 +1,1082 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.example; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.Reader; +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.net.URL; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.Enumeration; +import java.util.Locale; +import java.util.Timer; +import java.util.TimerTask; + +import jakarta.servlet.AsyncContext; +import jakarta.servlet.AsyncEvent; +import jakarta.servlet.AsyncListener; +import jakarta.servlet.RequestDispatcher; +import jakarta.servlet.ServletConfig; +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletRequestWrapper; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.ServletResponseWrapper; +import jakarta.servlet.UnavailableException; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequestWrapper; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletResponseWrapper; +import jakarta.servlet.http.Part; + +/** + * Dump Servlet Request. + */ +@SuppressWarnings("serial") +public class Dump extends HttpServlet +{ + /** + * Zero Width Space, to allow text to be wrapped at designated spots + */ + private static final String ZWSP = "​"; + boolean fixed; + Timer _timer; + + @Override + public void init(ServletConfig config) throws ServletException + { + super.init(config); + + if (config.getInitParameter("unavailable") != null && !fixed) + { + + fixed = true; + throw new UnavailableException("Unavailable test", Integer.parseInt(config.getInitParameter("unavailable"))); + } + + _timer = new Timer(true); + } + + @Override + public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + doGet(request, response); + } + + @Override + public void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + byte[] buffer = new byte[8192]; + int len = request.getContentLength(); + int c = 0; + InputStream in = request.getInputStream(); + while (c < len) + { + int l = in.read(buffer); + if (l < 0) + break; + c += l; + } + request.setAttribute("PUT", c + "bytes"); + doGet(request, response); + } + + @Override + public void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException + { + if (request.getRemoteUser() == null) + { + try + { + request.login("user", "password"); + } + catch (ServletException e) + { + getServletContext().log("Login fail", e); + } + } + + // Handle a dump of data + final String data = request.getParameter("data"); + final String chars = request.getParameter("chars"); + final String block = request.getParameter("block"); + final String dribble = request.getParameter("dribble"); + final boolean flush = request.getParameter("flush") != null ? Boolean.parseBoolean(request.getParameter("flush")) : false; + + if (request.getPathInfo() != null && request.getPathInfo().toLowerCase(Locale.ENGLISH).indexOf("script") != -1) + { + response.sendRedirect(response.encodeRedirectURL(getServletContext().getContextPath() + "/dump/info")); + return; + } + + request.setCharacterEncoding("UTF-8"); + + if (request.getParameter("busy") != null) + { + long end = System.currentTimeMillis() + Long.parseLong(request.getParameter("busy")); + while (System.currentTimeMillis() < end) + { + } + } + + if (request.getParameter("empty") != null) + { + response.setStatus(200); + response.flushBuffer(); + return; + } + + if (request.getParameter("sleep") != null) + { + try + { + long s = Long.parseLong(request.getParameter("sleep")); + if (request.getHeader("Expect") != null && request.getHeader("Expect").indexOf("102") >= 0) + { + Thread.sleep(s / 2); + response.sendError(102); + Thread.sleep(s / 2); + } + else + Thread.sleep(s); + } + catch (InterruptedException e) + { + return; + } + catch (Exception e) + { + throw new ServletException(e); + } + } + + if (request.getParameter("startAsync") != null && request.getAttribute("ASYNC") != Boolean.TRUE) + { + request.setAttribute("ASYNC", Boolean.TRUE); + try + { + final AsyncContext async = request.startAsync(request, response); + async.setTimeout(Long.parseLong(request.getParameter("startAsync"))); + async.addListener(new AsyncListener() + { + + @Override + public void onTimeout(AsyncEvent event) throws IOException + { + response.addHeader("Dump", "onTimeout"); + try + { + if (!dump(response, data, chars, block, dribble, flush)) + { + response.setContentType("text/plain"); + response.getOutputStream().println("EXPIRED"); + } + async.complete(); + } + catch (IOException e) + { + getServletContext().log("", e); + } + } + + @Override + public void onStartAsync(AsyncEvent event) throws IOException + { + response.addHeader("Dump", "onStartAsync"); + } + + @Override + public void onError(AsyncEvent event) throws IOException + { + response.addHeader("Dump", "onError"); + } + + @Override + public void onComplete(AsyncEvent event) throws IOException + { + response.addHeader("Dump", "onComplete"); + } + }); + + if (request.getParameter("dispatch") != null) + { + request.setAttribute("RESUME", Boolean.TRUE); + + final long resume = Long.parseLong(request.getParameter("dispatch")); + _timer.schedule(new TimerTask() + { + @Override + public void run() + { + async.dispatch(); + } + }, resume); + } + + if (request.getParameter("complete") != null) + { + final long complete = Long.parseLong(request.getParameter("complete")); + _timer.schedule(new TimerTask() + { + @Override + public void run() + { + try + { + response.setContentType("text/html"); + response.getOutputStream().println("

COMPLETED

"); + async.complete(); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + }, complete); + } + + return; + } + catch (Exception e) + { + throw new ServletException(e); + } + } + + request.setAttribute("Dump", this); + getServletContext().setAttribute("Dump", this); + // getServletContext().log("dump "+request.getRequestURI()); + + // Force a content length response + String length = request.getParameter("length"); + if (length != null && length.length() > 0) + { + response.setContentLength(Integer.parseInt(length)); + } + + // Handle a dump of data + if (dump(response, data, chars, block, dribble, flush)) + return; + + // handle an exception + String info = request.getPathInfo(); + if (info != null && info.endsWith("Exception")) + { + try + { + throw (Throwable)Thread.currentThread().getContextClassLoader() + .loadClass(info.substring(1)).getDeclaredConstructor().newInstance(); + } + catch (Throwable th) + { + throw new ServletException(th); + } + } + + // test a reset + String reset = request.getParameter("reset"); + if (reset != null && reset.length() > 0) + { + response.getOutputStream().println("THIS SHOULD NOT BE SEEN!"); + response.setHeader("SHOULD_NOT", "BE SEEN"); + response.reset(); + } + + // handle an redirect + String redirect = request.getParameter("redirect"); + if (redirect != null && redirect.length() > 0) + { + response.getOutputStream().println("THIS SHOULD NOT BE SEEN!"); + response.sendRedirect(response.encodeRedirectURL(redirect)); + try + { + response.getOutputStream().println("THIS SHOULD NOT BE SEEN!"); + } + catch (IOException e) + { + // ignored as stream is closed. + } + return; + } + + // handle an error + String error = request.getParameter("error"); + if (error != null && error.length() > 0 && request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE) == null) + { + response.getOutputStream().println("THIS SHOULD NOT BE SEEN!"); + response.sendError(Integer.parseInt(error)); + try + { + response.getOutputStream().println("THIS SHOULD NOT BE SEEN!"); + } + catch (IllegalStateException e) + { + try + { + response.getWriter().println("NOR THIS!!"); + } + catch (IOException e2) + { + getServletContext().log("Write fail", e2); + } + } + catch (IOException e) + { + getServletContext().log("Output fail", e); + } + return; + } + + // Handle a extra headers + String headers = request.getParameter("headers"); + if (headers != null && headers.length() > 0) + { + long h = Long.parseLong(headers); + for (int i = 0; i < h; i++) + { + response.addHeader("Header" + i, "Value" + i); + } + } + + String buffer = request.getParameter("buffer"); + if (buffer != null && buffer.length() > 0) + response.setBufferSize(Integer.parseInt(buffer)); + + String charset = request.getParameter("charset"); + if (charset == null) + charset = "UTF-8"; + response.setCharacterEncoding(charset); + response.setContentType("text/html"); + + if (info != null && info.indexOf("Locale/") >= 0) + { + try + { + String localeName = info.substring(info.indexOf("Locale/") + 7); + Field f = java.util.Locale.class.getField(localeName); + response.setLocale((Locale)f.get(null)); + } + catch (Exception e) + { + e.printStackTrace(); + response.setLocale(Locale.getDefault()); + } + } + + String cn = request.getParameter("cookie"); + String cv = request.getParameter("cookiev"); + if (cn != null && cv != null) + { + Cookie cookie = new Cookie(cn, cv); + if (request.getParameter("version") != null) + cookie.setVersion(Integer.parseInt(request.getParameter("version"))); + cookie.setComment("Cookie from dump servlet"); + response.addCookie(cookie); + } + + String pi = request.getPathInfo(); + if (pi != null && pi.startsWith("/ex")) + { + OutputStream out = response.getOutputStream(); + out.write("This text should be reset".getBytes()); + if ("/ex0".equals(pi)) + throw new ServletException("test ex0", new Throwable()); + else if ("/ex1".equals(pi)) + throw new IOException("test ex1"); + else if ("/ex2".equals(pi)) + throw new UnavailableException("test ex2"); + else if (pi.startsWith("/ex3/")) + throw new UnavailableException("test ex3", Integer.parseInt(pi.substring(5))); + throw new RuntimeException("test"); + } + + if ("true".equals(request.getParameter("close"))) + response.setHeader("Connection", "close"); + + String buffered = request.getParameter("buffered"); + + PrintWriter pout = null; + + try + { + pout = response.getWriter(); + } + catch (IllegalStateException e) + { + pout = new PrintWriter(new OutputStreamWriter(response.getOutputStream(), charset)); + } + if (buffered != null) + pout = new PrintWriter(new BufferedWriter(pout, Integer.parseInt(buffered))); + + try + { + pout.write("\n\n"); + pout.write("

Dump Servlet

\n"); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + + Enumeration locales = request.getLocales(); + while (locales.hasMoreElements()) + { + pout.write("\n"); + pout.write(""); + pout.write(""); + } + pout.write("\n"); + + pout.write(""); + Enumeration h = request.getHeaderNames(); + String name; + while (h.hasMoreElements()) + { + name = (String)h.nextElement(); + + Enumeration h2 = request.getHeaders(name); + while (h2.hasMoreElements()) + { + String hv = (String)h2.nextElement(); + pout.write("\n"); + pout.write(""); + pout.write(""); + } + } + + pout.write("\n"); + pout.write(""); + h = request.getParameterNames(); + while (h.hasMoreElements()) + { + name = (String)h.nextElement(); + pout.write("\n"); + pout.write(""); + pout.write(""); + String[] values = request.getParameterValues(name); + if (values == null) + { + pout.write("\n"); + pout.write(""); + pout.write(""); + } + else if (values.length > 1) + { + for (int i = 0; i < values.length; i++) + { + pout.write("\n"); + pout.write(""); + pout.write(""); + } + } + } + + try + { + Collection parts = request.getParts(); + if (parts != null && !parts.isEmpty()) + { + pout.write("\n"); + pout.write(""); + for (Part p : parts) + { + pout.write("\n"); + pout.write(""); + pout.write(""); + } + } + } + catch (ServletException e) + { + pout.write("\n"); + pout.write(""); + } + + pout.write("\n"); + pout.write(""); + Cookie[] cookies = request.getCookies(); + for (int i = 0; cookies != null && i < cookies.length; i++) + { + Cookie cookie = cookies[i]; + + pout.write("\n"); + pout.write(""); + pout.write(""); + } + + String contentType = request.getContentType(); + if (contentType != null && + !contentType.startsWith("application/x-www-form-urlencoded") && + !contentType.startsWith("multipart/form-data")) + { + pout.write("\n"); + pout.write(""); + pout.write("\n"); + pout.write(""); + } + + pout.write("\n"); + pout.write(""); + Enumeration a = request.getAttributeNames(); + while (a.hasMoreElements()) + { + name = (String)a.nextElement(); + pout.write("\n"); + pout.write(""); + Object value = request.getAttribute(name); + if (value instanceof File) + { + File file = (File)value; + pout.write(""); + } + else + pout.write(""); + } + request.setAttribute("org.eclipse.jetty.ee11.servlet.MultiPartFilter.files", null); + + pout.write("\n"); + pout.write(""); + a = getInitParameterNames(); + while (a.hasMoreElements()) + { + name = (String)a.nextElement(); + pout.write("\n"); + pout.write(""); + pout.write(""); + } + + pout.write("\n"); + pout.write(""); + a = getServletContext().getInitParameterNames(); + while (a.hasMoreElements()) + { + name = (String)a.nextElement(); + pout.write("\n"); + pout.write(""); + pout.write(""); + } + + pout.write("\n"); + pout.write(""); + a = getServletContext().getAttributeNames(); + while (a.hasMoreElements()) + { + name = (String)a.nextElement(); + pout.write("\n"); + pout.write(""); + pout.write(""); + } + + String res = request.getParameter("resource"); + if (res != null && res.length() > 0) + { + pout.write("\n"); + pout.write(""); + + pout.write("\n"); + pout.write(""); + try + { + pout.write(""); + } + catch (Exception e) + { + pout.write(""); + } + + pout.write("\n"); + pout.write(""); + try + { + pout.write(""); + } + catch (Exception e) + { + pout.write(""); + } + + pout.write("\n"); + pout.write(""); + try + { + pout.write(""); + } + catch (Exception e) + { + pout.write(""); + } + + pout.write("\n"); + pout.write(""); + + ServletContext context = getServletContext().getContext(res); + pout.write(""); + + if (context != null) + { + pout.write("\n"); + pout.write(""); + try + { + pout.write(""); + } + catch (Exception e) + { + pout.write(""); + } + + pout.write("\n"); + pout.write(""); + try + { + pout.write(""); + } + catch (Exception e) + { + pout.write(""); + } + + String cp = context.getContextPath(); + if (cp == null || "/".equals(cp)) + cp = ""; + pout.write("\n"); + pout.write(""); + pout.write(""); + + pout.write("\n"); + pout.write(""); + pout.write(""); + } + + pout.write("\n"); + pout.write(""); + pout.write(""); + + pout.write("\n"); + pout.write(""); + pout.write(""); + + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + Enumeration urls = Thread.currentThread().getContextClassLoader().getResources(res); + if (urls == null) + pout.write(""); + else + pout.write(""); + } + + pout.write("
getContentLength: " + Integer.toString(request.getContentLength()) + "
getContentType: " + notag(request.getContentType()) + "
getContextPath: " + request.getContextPath() + "
getDispatcherType: " + request.getDispatcherType() + "
getLocale: " + request.getLocale() + "
getLocalName: " + request.getLocalName() + "
getLocalAddr: " + request.getLocalAddr() + "
getLocalPort: " + Integer.toString(request.getLocalPort()) + "
getMethod: " + notag(request.getMethod()) + "
getPathInfo: " + notag(request.getPathInfo()) + "
getPathTranslated: " + notag(request.getPathTranslated()) + "
getProtocol: " + request.getProtocol() + "
getQueryString: " + notag(request.getQueryString()) + "
getRemoteAddr: " + request.getRemoteAddr() + "
getRemoteHost: " + request.getRemoteHost() + "
getRemotePort: " + request.getRemotePort() + "
getRemoteUser: " + request.getRemoteUser() + "
getRequestedSessionId: " + request.getRequestedSessionId() + "
getRequestURI: " + notag(request.getRequestURI()) + "
getRequestURL: " + notag(request.getRequestURL().toString()) + "
getScheme: " + request.getScheme() + "
getServerName: " + notag(request.getServerName()) + "
getServletPath: " + notag(request.getServletPath()) + "
getServerPort: " + Integer.toString(request.getServerPort()) + "
getUserPrincipal: " + request.getUserPrincipal() + "
isAsyncStarted(): " + request.isAsyncStarted() + "
isAsyncSupported(): " + request.isAsyncSupported() + "
isSecure(): " + request.isSecure() + "
isUserInRole(admin): " + request.isUserInRole("admin") + "
encodeRedirectURL(/foo?bar): " + response.encodeRedirectURL("/foo?bar") + "
getLocales: " + locales.nextElement() + "

Other HTTP Headers:
" + notag(name) + ": " + notag(hv) + "

Request Parameters:
" + notag(name) + ": " + notag(request.getParameter(name)) + "
" + notag(name) + " Values: " + "NULL!" + "
" + notag(name) + "[" + i + "]: " + notag(values[i]) + "

Parts:
" + notag(p.getName()) + ": " + p + "

No Parts!

Cookies:
" + notag(cookie.getName()) + ": " + notag(cookie.getValue()) + "

Content:
");
+                char[] content = new char[4096];
+                int len;
+                try
+                {
+                    Reader in = request.getReader();
+
+                    while ((len = in.read(content)) >= 0)
+                    {
+                        pout.write(notag(new String(content, 0, len)));
+                    }
+                }
+                catch (IOException e)
+                {
+                    pout.write(e.toString());
+                }
+
+                pout.write("

Request Attributes:
" + name.replace(".", ZWSP + ".") + ": " + "
" + file.getName() + " (" + file.length() + " " + new Date(file.lastModified()) + ")
" + "
" + "
" + toString(request.getAttribute(name)) + "
" + "

Servlet InitParameters:
" + name + ": " + toString(getInitParameter(name)) + "

Context InitParameters:
" + name.replace(".", ZWSP + ".") + ": " + toString(getServletContext().getInitParameter(name)) + "

Context Attributes:
" + name.replace(".", ZWSP + ".") + ": " + "
" + toString(getServletContext().getAttribute(name)) + "
" + "

Get Resource: \"" + res + "\"
getServletContext().getResource(...): " + getServletContext().getResource(res) + "" + "" + e + "
getServletContext().getResourcePaths(...): " + getServletContext().getResourcePaths(res) + "" + "" + e + "
getServletContext().getRealPath(...): " + getServletContext().getRealPath(res) + "" + "" + e + "
getServletContext().getContext(...): " + context + "
getServletContext().getContext(...).getResource(...): " + context.getResource(res) + "" + "" + e + "
getServletContext().getContext(...).getResourcePaths(...): " + context.getResourcePaths(res) + "" + "" + e + "
getServletContext().getContext(...).getRequestDispatcher(...): " + context.getRequestDispatcher(res.substring(cp.length())) + "
getServletContext().getContext(...).getRealPath(...): " + context.getRealPath(res.substring(cp.length())) + "
this.getClass().getResource(...): " + this.getClass().getResource(res) + "
this.getClass().getClassLoader().getResource(...): " + this.getClass().getClassLoader().getResource(res) + "
Thread.currentThread().getContextClassLoader().getResource(...): " + Thread.currentThread().getContextClassLoader().getResource(res) + "
Thread.currentThread().getContextClassLoader().getResources(...): null" + Collections.list(urls) + "
\n"); + + pout.write("

Request Wrappers

\n"); + ServletRequest rw = request; + int w = 0; + while (rw != null) + { + pout.write((w++) + ": " + rw.getClass().getName() + "
"); + if (rw instanceof HttpServletRequestWrapper) + rw = ((HttpServletRequestWrapper)rw).getRequest(); + else if (rw instanceof ServletRequestWrapper) + rw = ((ServletRequestWrapper)rw).getRequest(); + else + rw = null; + } + + pout.write("

Response Wrappers

\n"); + ServletResponse rsw = response; + w = 0; + while (rsw != null) + { + pout.write((w++) + ": " + rsw.getClass().getName() + "
"); + if (rsw instanceof HttpServletResponseWrapper) + rsw = ((HttpServletResponseWrapper)rsw).getResponse(); + else if (rsw instanceof ServletResponseWrapper) + rsw = ((ServletResponseWrapper)rsw).getResponse(); + else + rsw = null; + } + + pout.write("
"); + pout.write("

International Characters (UTF-8)

"); + pout.write("LATIN LETTER SMALL CAPITAL AE
\n"); + pout.write("Directly uni encoded(\\u1d01): \u1d01
"); // uni encoded + pout.write("HTML reference (&AElig;): Æ
"); + pout.write("Decimal (&#7425;): ᴁ
"); + pout.write("Javascript unicode (\\u1d01) :
"); // uni encoded + pout.write("
"); + pout.write("

Form to generate GET content

"); + pout.write(""); + pout.write("TextField:
\n"); + pout.write(""); + pout.write("
"); + + pout.write("
"); + + pout.write("

Form to generate POST content

"); + pout.write("
"); + pout.write("TextField:
\n"); + pout.write("Select:
"); + pout.write("
"); + pout.write("
"); + pout.write("
"); + + pout.write("

Form to generate UPLOAD content

"); + pout.write("
"); + pout.write("TextField:
\n"); + pout.write("File 1:
\n"); + pout.write("File 2:
\n"); + pout.write("
"); + pout.write("
"); + + pout.write("

Form to set Cookie

"); + pout.write("
"); + pout.write("cookie:
\n"); + pout.write("value:
\n"); + pout.write(""); + pout.write("
\n"); + + pout.write("

Form to get Resource

"); + pout.write("
"); + pout.write("resource:
\n"); + pout.write(""); + pout.write("
\n"); + } + catch (Exception e) + { + getServletContext().log("dump " + e); + } + + String lines = request.getParameter("lines"); + if (lines != null) + { + char[] line = "A line of characters. Blah blah blah blah. blooble blooble
\n".toCharArray(); + for (int l = Integer.parseInt(lines); l-- > 0; ) + { + pout.write("" + l + " "); + pout.write(line); + } + } + + pout.write("\n\n"); + + pout.close(); + + if (pi != null) + { + if ("/ex4".equals(pi)) + throw new ServletException("test ex4", new Throwable()); + if ("/ex5".equals(pi)) + throw new IOException("test ex5"); + if ("/ex6".equals(pi)) + throw new UnavailableException("test ex6"); + } + } + + @Override + public String getServletInfo() + { + return "Dump Servlet"; + } + + @Override + public void destroy() + { + _timer.cancel(); + } + + private String getURI(HttpServletRequest request) + { + String uri = (String)request.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI); + if (uri == null) + uri = request.getRequestURI(); + return uri; + } + + private static String toString(Object o) + { + if (o == null) + return null; + + try + { + if (o.getClass().isArray()) + { + StringBuffer sb = new StringBuffer(); + if (!o.getClass().getComponentType().isPrimitive()) + { + Object[] array = (Object[])o; + for (int i = 0; i < array.length; i++) + { + if (i > 0) + sb.append("\n"); + sb.append(array.getClass().getComponentType().getName()); + sb.append("["); + sb.append(i); + sb.append("]="); + sb.append(toString(array[i])); + } + return sb.toString(); + } + else + { + int length = Array.getLength(o); + for (int i = 0; i < length; i++) + { + if (i > 0) + sb.append("\n"); + sb.append(o.getClass().getComponentType().getName()); + sb.append("["); + sb.append(i); + sb.append("]="); + sb.append(toString(Array.get(o, i))); + } + return sb.toString(); + } + } + else + return o.toString(); + } + catch (Exception e) + { + return e.toString(); + } + } + + private boolean dump(HttpServletResponse response, String data, String chars, String block, String dribble, boolean flush) throws IOException + { + if (data != null && data.length() > 0) + { + int b = (block != null && block.length() > 0) ? Integer.parseInt(block) : 50; + byte[] buf = new byte[b]; + for (int i = 0; i < b; i++) + { + + buf[i] = (byte)('0' + (i % 10)); + if (i % 10 == 9) + buf[i] = (byte)'\n'; + } + buf[0] = 'o'; + OutputStream out = response.getOutputStream(); + response.setContentType("text/plain"); + long d = Long.parseLong(data); + while (d > 0) + { + if (b == 1) + { + out.write(d % 80 == 0 ? '\n' : '.'); + d--; + } + else if (d >= b) + { + out.write(buf); + d = d - b; + } + else + { + out.write(buf, 0, (int)d); + d = 0; + } + + if (dribble != null) + { + out.flush(); + try + { + Thread.sleep(Long.parseLong(dribble)); + } + catch (Exception e) + { + e.printStackTrace(); + break; + } + } + } + + if (flush) + out.flush(); + + return true; + } + + // Handle a dump of data + if (chars != null && chars.length() > 0) + { + int b = (block != null && block.length() > 0) ? Integer.parseInt(block) : 50; + char[] buf = new char[b]; + for (int i = 0; i < b; i++) + { + buf[i] = (char)('0' + (i % 10)); + if (i % 10 == 9) + buf[i] = '\n'; + } + buf[0] = 'o'; + response.setContentType("text/plain"); + PrintWriter out = response.getWriter(); + long d = Long.parseLong(chars); + while (d > 0 && !out.checkError()) + { + if (b == 1) + { + out.write(d % 80 == 0 ? '\n' : '.'); + d--; + } + else if (d >= b) + { + out.write(buf); + d = d - b; + } + else + { + out.write(buf, 0, (int)d); + d = 0; + } + } + return true; + } + return false; + } + + private String notag(String s) + { + if (s == null) + return "null"; + s = s.replace("&", "&"); + s = s.replace("<", "<"); + s = s.replace(">", ">"); + return s; + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/HelloWorld.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/HelloWorld.java new file mode 100644 index 00000000000..78526cd75d4 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/HelloWorld.java @@ -0,0 +1,54 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.example; + +import java.io.IOException; + +import jakarta.servlet.ServletConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +/** + * Dump Servlet Request. + */ +@SuppressWarnings("serial") +public class HelloWorld extends HttpServlet +{ + + @Override + public void init(ServletConfig config) throws ServletException + { + super.init(config); + } + + @Override + public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + doGet(request, response); + } + + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + response.setContentType("text/html"); + ServletOutputStream out = response.getOutputStream(); + out.println(""); + out.println("

Hello World

"); + out.println(""); + out.flush(); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/JakartaWebSocketChat.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/JakartaWebSocketChat.java new file mode 100644 index 00000000000..2d24578dbd3 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/JakartaWebSocketChat.java @@ -0,0 +1,83 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.example; + +import java.io.IOException; +import java.util.List; +import java.util.ListIterator; +import java.util.concurrent.CopyOnWriteArrayList; + +import jakarta.websocket.CloseReason; +import jakarta.websocket.OnClose; +import jakarta.websocket.OnMessage; +import jakarta.websocket.OnOpen; +import jakarta.websocket.RemoteEndpoint; +import jakarta.websocket.Session; +import jakarta.websocket.server.ServerEndpoint; + +@ServerEndpoint(value = "/jakarta.websocket/", subprotocols = {"chat"}) +public class JakartaWebSocketChat +{ + private static final List members = new CopyOnWriteArrayList<>(); + + volatile Session session; + volatile RemoteEndpoint.Async remote; + + @OnOpen + public void onOpen(Session sess) + { + this.session = sess; + this.remote = this.session.getAsyncRemote(); + members.add(this); + } + + @OnMessage + public void onMessage(String data) + { + if (data.contains("disconnect")) + { + try + { + session.close(); + } + catch (IOException ignore) + { + /* ignore */ + } + return; + } + + ListIterator iter = members.listIterator(); + while (iter.hasNext()) + { + JakartaWebSocketChat member = iter.next(); + + // Test if member is now disconnected + if (!member.session.isOpen()) + { + iter.remove(); + continue; + } + + // Async write the message back + member.remote.sendText(data); + } + } + + @OnClose + public void onClose(CloseReason reason) + { + members.remove(this); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/LoginServlet.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/LoginServlet.java new file mode 100644 index 00000000000..28228e3d5f5 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/LoginServlet.java @@ -0,0 +1,71 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.example; + +import java.io.IOException; + +import jakarta.servlet.ServletConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +/** + * Dump Servlet Request. + */ +public class LoginServlet extends HttpServlet +{ + + @Override + public void init(ServletConfig config) throws ServletException + { + super.init(config); + } + + @Override + public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + doGet(request, response); + } + + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + response.setContentType("text/html"); + ServletOutputStream out = response.getOutputStream(); + out.println(""); + out.println("
Before getUserPrincipal=" + request.getUserPrincipal()); + out.println("
Before getRemoteUser=" + request.getRemoteUser()); + String param = request.getParameter("action"); + + if ("login".equals(param)) + { + request.login("jetty", "jetty"); + } + else if ("logout".equals(param)) + { + request.logout(); + } + else if ("wrong".equals(param)) + { + request.login("jetty", "123"); + } + + out.println("
After getUserPrincipal=" + request.getUserPrincipal()); + out.println("
After getRemoteUser=" + request.getRemoteUser()); + out.println(""); + out.flush(); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/RegTest.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/RegTest.java new file mode 100644 index 00000000000..0bf2d2df9c9 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/RegTest.java @@ -0,0 +1,171 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.example; + +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +import jakarta.servlet.ServletConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.eclipse.jetty.util.StringUtil; + +/** + * Rego Servlet - tests being accessed from servlet 3.0 programmatic + * configuration. + */ +public class RegTest extends HttpServlet +{ + + @Override + public void init(ServletConfig config) throws ServletException + { + super.init(config); + } + + @Override + public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + doGet(request, response); + } + + @Override + public void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException + { + request.setCharacterEncoding("UTF-8"); + PrintWriter pout = null; + + try + { + pout = response.getWriter(); + } + catch (IllegalStateException e) + { + pout = new PrintWriter(new OutputStreamWriter(response.getOutputStream(), "UTF-8")); + } + + try + { + pout.write("\n\n"); + pout.write("

Rego Servlet

\n"); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + pout.write("\n"); + pout.write(""); + pout.write(""); + + pout.write("\n"); + pout.write(""); + pout.write(""); + + pout.write("
getMethod: " + notag(request.getMethod()) + "
getContentLength: " + Integer.toString(request.getContentLength()) + "
getContentType: " + notag(request.getContentType()) + "
getRequestURI: " + notag(request.getRequestURI()) + "
getRequestURL: " + notag(request.getRequestURL().toString()) + "
getContextPath: " + request.getContextPath() + "
getServletPath: " + notag(request.getServletPath()) + "
getPathInfo: " + notag(request.getPathInfo()) + "
getPathTranslated: " + notag(request.getPathTranslated()) + "
getQueryString: " + notag(request.getQueryString()) + "
getProtocol: " + request.getProtocol() + "
getScheme: " + request.getScheme() + "
getServerName: " + notag(request.getServerName()) + "
getServerPort: " + Integer.toString(request.getServerPort()) + "
getLocalName: " + request.getLocalName() + "
getLocalAddr: " + request.getLocalAddr() + "
getLocalPort: " + Integer.toString(request.getLocalPort()) + "
getRemoteUser: " + request.getRemoteUser() + "
getUserPrincipal: " + request.getUserPrincipal() + "
getRemoteAddr: " + request.getRemoteAddr() + "
getRemoteHost: " + request.getRemoteHost() + "
getRemotePort: " + request.getRemotePort() + "
getRequestedSessionId: " + request.getRequestedSessionId() + "
isSecure(): " + request.isSecure() + "
isUserInRole(admin): " + request.isUserInRole("admin") + "
"); + } + catch (Exception e) + { + getServletContext().log("dump " + e); + } + + pout.write("\n\n"); + + pout.close(); + } + + @Override + public String getServletInfo() + { + return "Rego Servlet"; + } + + private String notag(String s) + { + if (s == null) + return "null"; + s = StringUtil.replace(s, "&", "&"); + s = StringUtil.replace(s, "<", "<"); + s = StringUtil.replace(s, ">", ">"); + return s; + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/RewriteServlet.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/RewriteServlet.java new file mode 100644 index 00000000000..ff147b573f9 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/RewriteServlet.java @@ -0,0 +1,70 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.example; + +import java.io.IOException; + +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +/** + * Test Servlet Rewrite + */ +@SuppressWarnings("serial") +public class RewriteServlet extends HttpServlet +{ + + @Override + public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException + { + doGet(req, res); + } + + @Override + public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException + { + ServletOutputStream out = res.getOutputStream(); + out.println(""); + out.println(""); + out.println(""); + + Cookie cookie = null; + Cookie[] cookies = req.getCookies(); + if (cookies != null) + { + for (Cookie c : cookies) + { + if (c.getName().equals("visited")) + { + cookie = c; + break; + } + } + } + if (cookie != null) + out.println(""); + + out.println("
Original request URI: " + req.getAttribute("requestedPath") + "
Rewritten request URI: " + req.getRequestURI() + "
Previously visited: " + cookie.getValue() + "
"); + } + + @Override + public String getServletInfo() + { + return "Rewrite Servlet"; + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/SecureModeServlet.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/SecureModeServlet.java new file mode 100644 index 00000000000..8f40cd04329 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/SecureModeServlet.java @@ -0,0 +1,366 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.example; + +import java.io.File; +import java.io.IOException; +import java.io.PrintStream; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Calendar; +import java.util.GregorianCalendar; + +import jakarta.servlet.ServletConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +/** + * Dump Servlet Request. + */ +@SuppressWarnings("serial") +public class SecureModeServlet extends HttpServlet +{ + @Override + public void init(ServletConfig config) throws ServletException + { + super.init(config); + } + + @Override + public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + doGet(request, response); + } + + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + + response.setContentType("text/html"); + ServletOutputStream out = response.getOutputStream(); + out.println(""); + out.println(" Secure Jetty Test Webapp"); + + try + { + runPropertyChecks(out); + + runFileSystemChecks(out); + + runLoggingChecks(out); + + runClassloaderChecks(out); + } + catch (Exception e) + { + e.printStackTrace(new PrintStream(out)); + } + out.println(""); + out.flush(); + + try + { + Thread.sleep(200); + } + catch (InterruptedException e) + { + getServletContext().log("exception", e); + } + } + + private void runClassloaderChecks(ServletOutputStream out) throws Exception + { + out.println("

Checking Classloader Setup

"); + out.println("

"); + + System.getProperty("user.dir"); + try + { + out.println("check ability to create classloader
"); + URL url = new URL("http://not.going.to.work"); + new URLClassLoader(new URL[]{url}); + out.println("status: SUCCESS - unexpected
"); + } + catch (SecurityException e) + { + out.println("status: FAILURE - expected
"); + } + + out.println("



"); + } + + private void runLoggingChecks(ServletOutputStream out) throws Exception + { + out.println("

Checking File System

"); + out.println("

"); + + String userDir = System.getProperty("user.dir"); + try + { + out.println("check ability to log
"); + getServletContext().log("testing logging"); + out.println("status: SUCCESS - expected
"); + } + catch (SecurityException e) + { + out.println("status: FAILURE - unexpected
"); + out.println("
"); + e.printStackTrace(new PrintStream(out)); + out.println("
"); + } + + try + { + Calendar c = new GregorianCalendar(); + + String logFile = c.get(Calendar.YEAR) + "_" + c.get(Calendar.MONTH) + "_" + c.get(Calendar.DAY_OF_MONTH) + ".request.log"; + + out.println("check ability to access log file directly
"); + File jettyHomeFile = new File(userDir + File.separator + "logs" + File.separator + logFile); + jettyHomeFile.canRead(); + out.println("status: SUCCESS - unexpected
"); + } + catch (SecurityException e) + { + out.println("status: FAILURE - expected
"); + } + + out.println("



"); + } + + private void runFileSystemChecks(ServletOutputStream out) throws Exception + { + out.println("

Checking File System

"); + + /* + * test the reading and writing of a read only permission + */ + out.println("

"); + + String userDir = System.getProperty("user.dir"); + try + { + out.println("check read for $jetty.home/lib/policy/jetty.policy
"); + + File jettyHomeFile = new File(userDir + File.separator + "lib" + File.separator + "policy" + File.separator + "jetty.policy"); + jettyHomeFile.canRead(); + out.println("status: SUCCESS - expected
"); + } + catch (SecurityException e) + { + out.println("status: FAILURE - unexpected
"); + out.println("
"); + e.printStackTrace(new PrintStream(out)); + out.println("
"); + } + + try + { + out.println("check write permission for $jetty.home/lib/policy/jetty.policy
"); + + File jettyHomeFile = new File(userDir + File.separator + "lib" + File.separator + "policy" + File.separator + "jetty.policy"); + jettyHomeFile.canWrite(); + out.println("status: SUCCESS - unexpected
"); + } + catch (SecurityException e) + { + out.println("status: FAILURE - expected
"); + } + + try + { + out.println("check read permission for $jetty.home/lib
"); + + File jettyHomeFile = new File(userDir + File.separator + "lib"); + jettyHomeFile.canRead(); + out.println("status: SUCCESS - unexpected
"); + } + catch (SecurityException e) + { + out.println("status: FAILURE - expected
"); + } + + try + { + out.println("check write permission for $jetty.home/lib
"); + + File jettyHomeFile = new File(userDir + File.separator + "lib"); + jettyHomeFile.canWrite(); + out.println("status: SUCCESS - unexpected
"); + } + catch (SecurityException e) + { + out.println("status: FAILURE - expected
"); + } + + try + { + out.println("check read permission for $jetty.home
"); + + File jettyHomeFile = new File(userDir + File.separator); + jettyHomeFile.canRead(); + out.println("status: SUCCESS - unexpected
"); + } + catch (SecurityException e) + { + out.println("status: FAILURE - expected
"); + } + + try + { + out.println("check write permission for $jetty.home
"); + + File jettyHomeFile = new File(userDir + File.separator); + jettyHomeFile.canWrite(); + out.println("status: SUCCESS - unexpected
"); + } + catch (SecurityException e) + { + out.println("status: FAILURE - expected
"); + } + + try + { + out.println("check read permission for $jetty.home/logs
"); + + File jettyHomeFile = new File(userDir + File.separator + "logs" + File.separator); + jettyHomeFile.canRead(); + out.println("status: SUCCESS - unexpected
"); + } + catch (SecurityException e) + { + out.println("status: FAILURE - expected
"); + } + + try + { + out.println("check read permission for $jetty.home/logs
"); + + File jettyHomeFile = new File(userDir + File.separator + "logs"); + jettyHomeFile.canWrite(); + out.println("status: SUCCESS - unexpected
"); + } + catch (SecurityException e) + { + out.println("status: FAILURE - expected
"); + } + + out.println("



"); + } + + private void runPropertyChecks(ServletOutputStream out) throws IOException + { + + out.println("

Checking Properties

"); + + /* + * test the reading and writing of a read only permission + */ + out.println("

Declared Property - read

"); + out.println("

"); + try + { + out.println("check read permission for __ALLOWED_READ_PROPERTY
"); + System.getProperty("__ALLOWED_READ_PROPERTY"); + out.println("status: SUCCESS - expected
"); + } + catch (SecurityException e) + { + out.println("status: FAILURE - unexpected
"); + out.println("
"); + e.printStackTrace(new PrintStream(out)); + out.println("
"); + } + try + { + out.println("check write permission for __ALLOWED_READ_PROPERTY
"); + System.setProperty("__ALLOWED_READ_PROPERTY", "SUCCESS - unexpected"); + String value = System.getProperty("__ALLOWED_READ_PROPERTY"); + out.println("status: " + value + "
"); + } + catch (SecurityException e) + { + out.println("status: FAILURE - expected
"); + } + + out.println("



"); + + /* + * test the reading and writing of a read/write permission + */ + out.println("

Declared Property - read/write

"); + out.println("

"); + try + { + out.println("check read permission for __ALLOWED_WRITE_PROPERTY
"); + System.getProperty("__ALLOWED_WRITE_PROPERTY"); + out.println("Status: SUCCESS - expected
"); + } + catch (SecurityException e) + { + out.println("status: FAILURE - unexpected
"); + out.println("
"); + e.printStackTrace(new PrintStream(out)); + out.println("
"); + } + try + { + out.println("check write permission for __ALLOWED_WRITE_PROPERTY
"); + System.setProperty("__ALLOWED_WRITE_PROPERTY", "SUCCESS - expected"); + String value = System.getProperty("__ALLOWED_WRITE_PROPERTY"); + out.println("status: " + value + "
"); + } + catch (SecurityException e) + { + out.println("status: FAILURE - unexpected
"); + out.println("
"); + e.printStackTrace(new PrintStream(out)); + out.println("
"); + } + + out.println("



"); + + /* + * test the reading and writing of an undeclared property + */ + out.println("

checking forbidden properties

"); + out.println("

"); + try + { + out.println("check read permission for __UNDECLARED_PROPERTY:
"); + System.getProperty("__UNDECLARED_PROPERTY"); + out.println("status: SUCCESS - expected
"); + } + catch (SecurityException e) + { + out.println("status: FAILURE - expected
"); + } + try + { + out.println("check write permission for __UNDECLARED_PROPERTY:
"); + System.setProperty("__UNDECLARED_PROPERTY", "SUCCESS - unexpected"); + String value = System.getProperty("__UNDECLARED_PROPERTY"); + out.println("status: " + value + "
"); + } + catch (SecurityException e) + { + out.println("status: FAILURE - expected
"); + } + + out.println("



"); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/SessionDump.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/SessionDump.java new file mode 100644 index 00000000000..aee724dd930 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/SessionDump.java @@ -0,0 +1,194 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.example; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Date; +import java.util.Enumeration; +import java.util.UUID; + +import jakarta.servlet.RequestDispatcher; +import jakarta.servlet.ServletConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; +import org.eclipse.jetty.util.MultiMap; + +/** + * Test Servlet Sessions. + */ +@SuppressWarnings("serial") +public class SessionDump extends HttpServlet +{ + /** + * Simple object attribute to test serialization + */ + public static class ObjectAttributeValue implements java.io.Serializable + { + long l; + + public ObjectAttributeValue(long l) + { + this.l = l; + } + + public long getValue() + { + return l; + } + } + + int redirectCount = 0; + + @Override + public void init(ServletConfig config) + throws ServletException + { + super.init(config); + } + + protected void handleForm(HttpServletRequest request) + { + HttpSession session = request.getSession(false); + String action = request.getParameter("Action"); + String name = request.getParameter("Name"); + String value = request.getParameter("Value"); + + if (action != null) + { + if (action.equals("New Session")) + { + session = request.getSession(true); + session.setAttribute("test", "value"); + session.setAttribute("obj", new ObjectAttributeValue(System.currentTimeMillis())); + session.setAttribute("WEBCL", new MultiMap<>()); + UUID uuid = UUID.randomUUID(); + session.setAttribute("uuid", uuid); + } + else if (session != null) + { + if (action.equals("Invalidate")) + session.invalidate(); + else if (action.equals("Set") && name != null && !name.isEmpty()) + session.setAttribute(name, value); + else if (action.equals("Remove")) + session.removeAttribute(name); + } + } + } + + @Override + public void doPost(HttpServletRequest request, + HttpServletResponse response) + throws IOException + { + handleForm(request); + String nextUrl = getURI(request) + "?R=" + redirectCount++; + String encodedUrl = response.encodeRedirectURL(nextUrl); + response.sendRedirect(encodedUrl); + } + + @Override + public void doGet(HttpServletRequest request, + HttpServletResponse response) + throws IOException + { + handleForm(request); + + response.setContentType("text/html"); + + HttpSession session = request.getSession(getURI(request).indexOf("new") > 0); + try + { + if (session != null) + session.isNew(); + } + catch (IllegalStateException e) + { + log("Session already invalidated", e); + session = null; + } + + PrintWriter out = response.getWriter(); + out.println("

Session Dump Servlet:

"); + out.println("
"); + + if (session == null) + { + out.println("

No Session

"); + out.println(""); + } + else + { + if (session.getAttribute("WEBCL") == null) + session.setAttribute("WEBCL", new MultiMap<>()); + try + { + out.println("ID: " + session.getId() + "
"); + out.println("New: " + session.isNew() + "
"); + out.println("Created: " + new Date(session.getCreationTime()) + "
"); + out.println("Last: " + new Date(session.getLastAccessedTime()) + "
"); + out.println("Max Inactive: " + session.getMaxInactiveInterval() + "
"); + out.println("Context: " + session.getServletContext() + "
"); + + Enumeration keys = session.getAttributeNames(); + while (keys.hasMoreElements()) + { + String name = (String)keys.nextElement(); + String value = "" + session.getAttribute(name); + + out.println("" + name + ": " + value + "
"); + } + + out.println("Name:
"); + out.println("Value:
"); + + out.println(""); + out.println(""); + out.println(""); + out.println("
"); + + out.println("
"); + + if (request.isRequestedSessionIdFromCookie()) + out.println("

Turn off cookies in your browser to try url encoding
"); + + if (request.isRequestedSessionIdFromURL()) + out.println("

Turn on cookies in your browser to try cookie encoding
"); + out.println("Encoded Link
"); + } + catch (IllegalStateException e) + { + e.printStackTrace(); + } + } + } + + @Override + public String getServletInfo() + { + return "Session Dump Servlet"; + } + + private String getURI(HttpServletRequest request) + { + String uri = (String)request.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI); + if (uri == null) + uri = request.getRequestURI(); + return uri; + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/TestFilter.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/TestFilter.java new file mode 100644 index 00000000000..3a3a1e8ae5d --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/TestFilter.java @@ -0,0 +1,105 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.example; + +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; + +import jakarta.servlet.Filter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.FilterConfig; +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletRequestWrapper; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequestWrapper; + +/** + * TestFilter. + * + * This filter checks for a none local request, and if the init parameter + * "remote" is not set to true, then all non local requests are forwarded + * to /remote.html + */ +public class TestFilter implements Filter +{ + private boolean _remote; + private ServletContext _context; + private final Set _allowed = new HashSet(); + + @Override + public void init(FilterConfig filterConfig) throws ServletException + { + _context = filterConfig.getServletContext(); + _remote = Boolean.parseBoolean(filterConfig.getInitParameter("remote")); + _allowed.add("/favicon.ico"); + _allowed.add("/jetty_banner.gif"); + _allowed.add("/remote.html"); + + filterConfig.getServletContext().log("TestFilter#remote=" + _remote); + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException + { + String from = request.getRemoteAddr(); + String to = request.getLocalAddr(); + String path = ((HttpServletRequest)request).getServletPath(); + + if (!_remote && !_allowed.contains(path) && !from.equals(to)) + { + _context.getRequestDispatcher("/remote.html").forward(request, response); + return; + } + + Integer oldValue = null; + ServletRequest r = request; + while (r instanceof ServletRequestWrapper) + { + r = ((ServletRequestWrapper)r).getRequest(); + } + + try + { + oldValue = (Integer)request.getAttribute("testFilter"); + + Integer value = (oldValue == null) ? 1 : oldValue + 1; + + request.setAttribute("testFilter", value); + + String qString = ((HttpServletRequest)request).getQueryString(); + if (qString != null && qString.indexOf("wrap") >= 0) + { + request = new HttpServletRequestWrapper((HttpServletRequest)request); + } + _context.setAttribute("request" + r.hashCode(), value); + + chain.doFilter(request, response); + } + finally + { + request.setAttribute("testFilter", oldValue); + _context.setAttribute("request" + r.hashCode(), oldValue); + } + } + + @Override + public void destroy() + { + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/TestListener.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/TestListener.java new file mode 100644 index 00000000000..9d5ad746230 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/TestListener.java @@ -0,0 +1,232 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.example; + +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; +import jakarta.servlet.DispatcherType; +import jakarta.servlet.FilterRegistration; +import jakarta.servlet.HttpConstraintElement; +import jakarta.servlet.ServletContextAttributeEvent; +import jakarta.servlet.ServletContextAttributeListener; +import jakarta.servlet.ServletContextEvent; +import jakarta.servlet.ServletContextListener; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRegistration; +import jakarta.servlet.ServletRequestAttributeEvent; +import jakarta.servlet.ServletRequestAttributeListener; +import jakarta.servlet.ServletRequestEvent; +import jakarta.servlet.ServletRequestListener; +import jakarta.servlet.ServletSecurityElement; +import jakarta.servlet.annotation.ServletSecurity; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpSessionActivationListener; +import jakarta.servlet.http.HttpSessionAttributeListener; +import jakarta.servlet.http.HttpSessionBindingEvent; +import jakarta.servlet.http.HttpSessionEvent; +import jakarta.servlet.http.HttpSessionListener; + +public class TestListener implements HttpSessionListener, HttpSessionAttributeListener, HttpSessionActivationListener, ServletContextListener, ServletContextAttributeListener, ServletRequestListener, ServletRequestAttributeListener +{ + Map _called = new HashMap<>(); + + public TestListener() + { + _called.put("TestListener", new Throwable()); + } + + @Override + public void attributeAdded(HttpSessionBindingEvent se) + { + // System.err.println("attributedAdded "+se); + + _called.put("attributeAdded", new Throwable()); + } + + @Override + public void attributeAdded(ServletContextAttributeEvent scab) + { + _called.put("attributeAdded", new Throwable()); + // System.err.println("attributeAdded "+scab); + } + + @Override + public void attributeAdded(ServletRequestAttributeEvent srae) + { + _called.put("attributeAdded", new Throwable()); + // System.err.println("attributeAdded "+srae); + } + + @Override + public void attributeRemoved(HttpSessionBindingEvent se) + { + // System.err.println("attributeRemoved "+se); + _called.put("attributeRemoved", new Throwable()); + } + + @Override + public void attributeRemoved(ServletContextAttributeEvent scab) + { + _called.put("attributeRemoved", new Throwable()); + // System.err.println("attributeRemoved "+scab); + } + + @Override + public void attributeRemoved(ServletRequestAttributeEvent srae) + { + _called.put("attributeRemoved", new Throwable()); + // System.err.println("attributeRemoved "+srae); + } + + @Override + public void attributeReplaced(HttpSessionBindingEvent se) + { + // System.err.println("attributeReplaced "+se); + _called.put("attributeReplaced", new Throwable()); + } + + @Override + public void attributeReplaced(ServletContextAttributeEvent scab) + { + _called.put("attributeReplaced", new Throwable()); + // System.err.println("attributeReplaced "+scab); + } + + @Override + public void attributeReplaced(ServletRequestAttributeEvent srae) + { + _called.put("attributeReplaced", new Throwable()); + // System.err.println("attributeReplaced "+srae); + } + + @Override + public void contextDestroyed(ServletContextEvent sce) + { + _called.put("contextDestroyed", new Throwable()); + // System.err.println("contextDestroyed "+sce); + } + + @Override + public void contextInitialized(ServletContextEvent sce) + { + + // System.err.println("contextInitialized "+sce); + _called.put("contextInitialized", new Throwable()); + + //configure programmatic security + ServletRegistration.Dynamic rego = sce.getServletContext().addServlet("RegoTest", RegTest.class.getName()); + rego.addMapping("/rego/*"); + HttpConstraintElement constraintElement = new HttpConstraintElement(ServletSecurity.EmptyRoleSemantic.PERMIT, + ServletSecurity.TransportGuarantee.NONE, new String[]{"admin"}); + ServletSecurityElement securityElement = new ServletSecurityElement(constraintElement, null); + Set unchanged = rego.setServletSecurity(securityElement); + //// System.err.println("Security constraints registered: "+unchanged.isEmpty()); + + //Test that a security constraint from web.xml can't be overridden programmatically + ServletRegistration.Dynamic rego2 = sce.getServletContext().addServlet("RegoTest2", RegTest.class.getName()); + rego2.addMapping("/rego2/*"); + securityElement = new ServletSecurityElement(constraintElement, null); + unchanged = rego2.setServletSecurity(securityElement); + //// System.err.println("Overridding web.xml constraints not possible:" +!unchanged.isEmpty()); + + /* For servlet 3.0 */ + FilterRegistration registration = sce.getServletContext().addFilter("TestFilter", TestFilter.class.getName()); + if (registration != null) //otherwise defined in web.xml + { + ((FilterRegistration.Dynamic)registration).setAsyncSupported(true); + } + else + { + registration = sce.getServletContext().getFilterRegistration("TestFilter"); + } + registration.setInitParameter("remote", "false"); + registration.addMappingForUrlPatterns( + EnumSet.of(DispatcherType.ERROR, DispatcherType.ASYNC, DispatcherType.FORWARD, DispatcherType.INCLUDE, DispatcherType.REQUEST), + true, + new String[]{"/*"}); + + try + { + AddListServletRequestListener listenerClass = + sce.getServletContext().createListener(AddListServletRequestListener.class); + sce.getServletContext().addListener(listenerClass); + } + catch (ServletException e) + { + throw new RuntimeException(e.getMessage(), e); + } + } + + @PostConstruct + public void postConstruct() + { + _called.put("postConstruct", new Throwable()); + } + + @PreDestroy + public void preDestroy() + { + _called.put("preDestroy", new Throwable()); + } + + @Override + public void requestDestroyed(ServletRequestEvent sre) + { + _called.put("requestDestroyed", new Throwable()); + ((HttpServletRequest)sre.getServletRequest()).getSession(false); + sre.getServletRequest().setAttribute("requestInitialized", null); + // System.err.println("requestDestroyed "+sre); + } + + @Override + public void requestInitialized(ServletRequestEvent sre) + { + _called.put("requestInitialized", new Throwable()); + sre.getServletRequest().setAttribute("requestInitialized", "'" + sre.getServletContext().getContextPath() + "'"); + // System.err.println("requestInitialized "+sre); + } + + @Override + public void sessionCreated(HttpSessionEvent se) + { + _called.put("sessionCreated", new Throwable()); + // System.err.println("sessionCreated "+se); + } + + @Override + public void sessionDestroyed(HttpSessionEvent se) + { + _called.put("sessionDestroyed", new Throwable()); + // System.err.println("sessionDestroyed "+se); + } + + @Override + public void sessionDidActivate(HttpSessionEvent se) + { + // System.err.println("sessionDidActivate "+se); + _called.put("sessionDidActivate", new Throwable()); + } + + @Override + public void sessionWillPassivate(HttpSessionEvent se) + { + // System.err.println("sessionWillPassivate "+se); + _called.put("sessionWillPassivate", new Throwable()); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/TestServlet.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/TestServlet.java new file mode 100644 index 00000000000..ba872fa4e14 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/TestServlet.java @@ -0,0 +1,34 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.example; + +import java.io.IOException; +import java.util.List; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +public class TestServlet extends HttpServlet +{ + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException + { + List l = (List)getServletContext().getAttribute("arraylist"); + + resp.getOutputStream().println("All Good " + l.toString()); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/WebSocketChatServlet.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/WebSocketChatServlet.java new file mode 100644 index 00000000000..2828053e6c1 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/java/org/example/WebSocketChatServlet.java @@ -0,0 +1,113 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.example; + +import java.io.IOException; +import java.util.List; +import java.util.ListIterator; +import java.util.concurrent.CopyOnWriteArrayList; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.eclipse.jetty.ee11.websocket.server.JettyServerUpgradeRequest; +import org.eclipse.jetty.ee11.websocket.server.JettyServerUpgradeResponse; +import org.eclipse.jetty.ee11.websocket.server.JettyWebSocketCreator; +import org.eclipse.jetty.ee11.websocket.server.JettyWebSocketServlet; +import org.eclipse.jetty.ee11.websocket.server.JettyWebSocketServletFactory; +import org.eclipse.jetty.websocket.api.Session; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketOpen; +import org.eclipse.jetty.websocket.api.annotations.WebSocket; + +@SuppressWarnings("serial") +public class WebSocketChatServlet extends JettyWebSocketServlet implements JettyWebSocketCreator +{ + /** + * Holds active sockets to other members of the chat + */ + private final List members = new CopyOnWriteArrayList(); + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + getServletContext().getNamedDispatcher("default").forward(request, response); + } + + @Override + public Object createWebSocket(JettyServerUpgradeRequest req, JettyServerUpgradeResponse resp) + { + if (req.hasSubProtocol("chat")) + { + resp.setAcceptedSubProtocol("chat"); + return new ChatWebSocket(); + } + return null; + } + + @Override + public void configure(JettyWebSocketServletFactory factory) + { + factory.addMapping("/", this); + } + + /** + * Create a WebSocket that echo's back the message to all other members of the servlet. + */ + @WebSocket + public class ChatWebSocket + { + volatile Session session; + + @OnWebSocketOpen + public void onOpen(Session session) + { + this.session = session; + members.add(this); + } + + @OnWebSocketMessage + public void onMessage(String data) + { + if (data.contains("disconnect")) + { + session.close(); + return; + } + + ListIterator iter = members.listIterator(); + while (iter.hasNext()) + { + ChatWebSocket member = iter.next(); + + // Test if member is now disconnected + if (!member.session.isOpen()) + { + iter.remove(); + continue; + } + + // Async write the message back. + member.session.sendText(data, null); + } + } + + @OnWebSocketClose + public void onClose(int code, String message) + { + members.remove(this); + } + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/WEB-INF/jetty-web.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/WEB-INF/jetty-web.xml new file mode 100644 index 00000000000..6764c0ff1a2 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/WEB-INF/jetty-web.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + org.eclipse.jetty.util. + org.eclipse.jetty.ee11.servlets. + + + + + + + The test-jetty webapp is deployed. DO NOT USE IN PRODUCTION! + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/WEB-INF/web.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000000..e03ded38ce7 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,282 @@ + + + + EE11 Demo Jetty WebApp + + + org.eclipse.jetty.server.context.ManagedAttributes + PushFilter,QoSFilter,TransparentProxy.ThreadPool,TransparentProxy.HttpClient + + + + + org.example.TestListener + + + + QoSFilter + org.eclipse.jetty.ee11.servlets.QoSFilter + true + + maxRequests + 10000 + + + managedAttr + true + + + + QoSFilter + /* + + + + Login + org.example.LoginServlet + 1 + + + + Login + /login/* + + + + + Hello + org.example.HelloWorld + 1 + + + + Hello + /hello/* + + + + Dump + org.example.Dump + 1 + true + admin + + upload + 4096 + + + + + Dump + /dump/* + *.dump + + + + Session + org.example.SessionDump + 1 + + + + Session + /session/* + + + + Cookie + org.example.CookieDump + 1 + + + + Cookie + /cookie/* + + + + Dispatch + org.example.DispatchServlet + 1 + true + + + + Dispatch + /dispatch/* + + + + Chat + org.example.ChatServlet + 1 + true + + + + Chat + /chat/* + + + + WSChat + org.example.WebSocketChatServlet + 1 + + + + WSChat + /jetty.websocket/* + + + + + Rewrite + org.example.RewriteServlet + + + + Rewrite + /rewritten/* + /redirected/* + + + + + SecureMode + org.example.SecureModeServlet + 1 + + + + SecureMode + /secureMode/* + + + + + TestServlet + org.example.TestServlet + 10 + + + + TestServlet + /testservlet/* + + + + 404 + /error404.html + + + + + Rego2 + /rego2/* + + + server-administrator + + + + + + Auth2 + /auth2/* + + + * + + + + + + Any User + /dump/auth/* + *.htm + + + * + + + + + + relax + /dump/auth/relax/* + /auth/relax.txt + + + + + + Admin Role + /dump/auth/admin/* + + + admin + + + + + + Forbidden + /dump/auth/noaccess/* + /auth/* + + + + + + + SSL + /dump/auth/ssl/* + + + CONFIDENTIAL + + + + + + + + + FORM + Test Realm + + /logon.html?param=test + /logonError.html?param=test + + + + + 54 + + + + admin + + + user + + + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/auth.html b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/auth.html new file mode 100644 index 00000000000..e7893cb2dc5 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/auth.html @@ -0,0 +1,47 @@ + + + Powered By Jetty - Auth Links + + + + + + +

+ +
+ +

Jetty Authentication Links

+

+ This page contains several links to test the authentication constraints: +

+

+ Usernames/Passwords are jetty/jetty admin/admin & user/password +

+ + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/auth/file.txt b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/auth/file.txt new file mode 100644 index 00000000000..cb74356f3b0 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/auth/file.txt @@ -0,0 +1,10 @@ +0000 0000000000000000000000000000000000000000000000000000000 +0001 0000000000000000000000000000000000000000000000000000000 +0002 0000000000000000000000000000000000000000000000000000000 +0003 0000000000000000000000000000000000000000000000000000000 +0004 0000000000000000000000000000000000000000000000000000000 +0005 0000000000000000000000000000000000000000000000000000000 +0006 0000000000000000000000000000000000000000000000000000000 +0007 0000000000000000000000000000000000000000000000000000000 +0008 0000000000000000000000000000000000000000000000000000000 +0009 0000000000000000000000000000000000000000000000000000000 diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/auth/relax.txt b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/auth/relax.txt new file mode 100644 index 00000000000..cb74356f3b0 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/auth/relax.txt @@ -0,0 +1,10 @@ +0000 0000000000000000000000000000000000000000000000000000000 +0001 0000000000000000000000000000000000000000000000000000000 +0002 0000000000000000000000000000000000000000000000000000000 +0003 0000000000000000000000000000000000000000000000000000000 +0004 0000000000000000000000000000000000000000000000000000000 +0005 0000000000000000000000000000000000000000000000000000000 +0006 0000000000000000000000000000000000000000000000000000000 +0007 0000000000000000000000000000000000000000000000000000000 +0008 0000000000000000000000000000000000000000000000000000000 +0009 0000000000000000000000000000000000000000000000000000000 diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/auth2/index.html b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/auth2/index.html new file mode 100644 index 00000000000..f46164c410a --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/auth2/index.html @@ -0,0 +1,6 @@ + + +

YAY!

+

You have successfully authenticated. You can use this url in conjunction with any of the other urls that lead to a login form to test which urls are saved on entry to the login form.

+ + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/cgi-bin/hello.sh b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/cgi-bin/hello.sh new file mode 100644 index 00000000000..1ded600bc48 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/cgi-bin/hello.sh @@ -0,0 +1,4 @@ +#!/bin/sh +echo "Content-Type: text/html" +echo +echo "

Hello World

" diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/chat/index.html b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/chat/index.html new file mode 100644 index 00000000000..4b72641b032 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/chat/index.html @@ -0,0 +1,165 @@ + + + Async Chat + + + + +
+
+
+ Username:  +
+ +
+ + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/d.txt b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/d.txt new file mode 100644 index 00000000000..cb74356f3b0 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/d.txt @@ -0,0 +1,10 @@ +0000 0000000000000000000000000000000000000000000000000000000 +0001 0000000000000000000000000000000000000000000000000000000 +0002 0000000000000000000000000000000000000000000000000000000 +0003 0000000000000000000000000000000000000000000000000000000 +0004 0000000000000000000000000000000000000000000000000000000 +0005 0000000000000000000000000000000000000000000000000000000 +0006 0000000000000000000000000000000000000000000000000000000 +0007 0000000000000000000000000000000000000000000000000000000 +0008 0000000000000000000000000000000000000000000000000000000 +0009 0000000000000000000000000000000000000000000000000000000 diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/da.txt b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/da.txt new file mode 100644 index 00000000000..39101db7ccf --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/da.txt @@ -0,0 +1,1000 @@ +0000 1111111111111111111111111111111111111111111111111111111 +0001 1111111111111111111111111111111111111111111111111111111 +0002 1111111111111111111111111111111111111111111111111111111 +0003 1111111111111111111111111111111111111111111111111111111 +0004 1111111111111111111111111111111111111111111111111111111 +0005 1111111111111111111111111111111111111111111111111111111 +0006 1111111111111111111111111111111111111111111111111111111 +0007 1111111111111111111111111111111111111111111111111111111 +0008 1111111111111111111111111111111111111111111111111111111 +0009 1111111111111111111111111111111111111111111111111111111 +0010 1111111111111111111111111111111111111111111111111111111 +0011 1111111111111111111111111111111111111111111111111111111 +0012 1111111111111111111111111111111111111111111111111111111 +0013 1111111111111111111111111111111111111111111111111111111 +0014 1111111111111111111111111111111111111111111111111111111 +0015 1111111111111111111111111111111111111111111111111111111 +0016 1111111111111111111111111111111111111111111111111111111 +0017 1111111111111111111111111111111111111111111111111111111 +0018 1111111111111111111111111111111111111111111111111111111 +0019 1111111111111111111111111111111111111111111111111111111 +0020 1111111111111111111111111111111111111111111111111111111 +0021 1111111111111111111111111111111111111111111111111111111 +0022 1111111111111111111111111111111111111111111111111111111 +0023 1111111111111111111111111111111111111111111111111111111 +0024 1111111111111111111111111111111111111111111111111111111 +0025 1111111111111111111111111111111111111111111111111111111 +0026 1111111111111111111111111111111111111111111111111111111 +0027 1111111111111111111111111111111111111111111111111111111 +0028 1111111111111111111111111111111111111111111111111111111 +0029 1111111111111111111111111111111111111111111111111111111 +0030 1111111111111111111111111111111111111111111111111111111 +0031 1111111111111111111111111111111111111111111111111111111 +0032 1111111111111111111111111111111111111111111111111111111 +0033 1111111111111111111111111111111111111111111111111111111 +0034 1111111111111111111111111111111111111111111111111111111 +0035 1111111111111111111111111111111111111111111111111111111 +0036 1111111111111111111111111111111111111111111111111111111 +0037 1111111111111111111111111111111111111111111111111111111 +0038 1111111111111111111111111111111111111111111111111111111 +0039 1111111111111111111111111111111111111111111111111111111 +0040 1111111111111111111111111111111111111111111111111111111 +0041 1111111111111111111111111111111111111111111111111111111 +0042 1111111111111111111111111111111111111111111111111111111 +0043 1111111111111111111111111111111111111111111111111111111 +0044 1111111111111111111111111111111111111111111111111111111 +0045 1111111111111111111111111111111111111111111111111111111 +0046 1111111111111111111111111111111111111111111111111111111 +0047 1111111111111111111111111111111111111111111111111111111 +0048 1111111111111111111111111111111111111111111111111111111 +0049 1111111111111111111111111111111111111111111111111111111 +0050 1111111111111111111111111111111111111111111111111111111 +0051 1111111111111111111111111111111111111111111111111111111 +0052 1111111111111111111111111111111111111111111111111111111 +0053 1111111111111111111111111111111111111111111111111111111 +0054 1111111111111111111111111111111111111111111111111111111 +0055 1111111111111111111111111111111111111111111111111111111 +0056 1111111111111111111111111111111111111111111111111111111 +0057 1111111111111111111111111111111111111111111111111111111 +0058 1111111111111111111111111111111111111111111111111111111 +0059 1111111111111111111111111111111111111111111111111111111 +0060 1111111111111111111111111111111111111111111111111111111 +0061 1111111111111111111111111111111111111111111111111111111 +0062 1111111111111111111111111111111111111111111111111111111 +0063 1111111111111111111111111111111111111111111111111111111 +0064 1111111111111111111111111111111111111111111111111111111 +0065 1111111111111111111111111111111111111111111111111111111 +0066 1111111111111111111111111111111111111111111111111111111 +0067 1111111111111111111111111111111111111111111111111111111 +0068 1111111111111111111111111111111111111111111111111111111 +0069 1111111111111111111111111111111111111111111111111111111 +0070 1111111111111111111111111111111111111111111111111111111 +0071 1111111111111111111111111111111111111111111111111111111 +0072 1111111111111111111111111111111111111111111111111111111 +0073 1111111111111111111111111111111111111111111111111111111 +0074 1111111111111111111111111111111111111111111111111111111 +0075 1111111111111111111111111111111111111111111111111111111 +0076 1111111111111111111111111111111111111111111111111111111 +0077 1111111111111111111111111111111111111111111111111111111 +0078 1111111111111111111111111111111111111111111111111111111 +0079 1111111111111111111111111111111111111111111111111111111 +0080 1111111111111111111111111111111111111111111111111111111 +0081 1111111111111111111111111111111111111111111111111111111 +0082 1111111111111111111111111111111111111111111111111111111 +0083 1111111111111111111111111111111111111111111111111111111 +0084 1111111111111111111111111111111111111111111111111111111 +0085 1111111111111111111111111111111111111111111111111111111 +0086 1111111111111111111111111111111111111111111111111111111 +0087 1111111111111111111111111111111111111111111111111111111 +0088 1111111111111111111111111111111111111111111111111111111 +0089 1111111111111111111111111111111111111111111111111111111 +0090 1111111111111111111111111111111111111111111111111111111 +0091 1111111111111111111111111111111111111111111111111111111 +0092 1111111111111111111111111111111111111111111111111111111 +0093 1111111111111111111111111111111111111111111111111111111 +0094 1111111111111111111111111111111111111111111111111111111 +0095 1111111111111111111111111111111111111111111111111111111 +0096 1111111111111111111111111111111111111111111111111111111 +0097 1111111111111111111111111111111111111111111111111111111 +0098 1111111111111111111111111111111111111111111111111111111 +0099 1111111111111111111111111111111111111111111111111111111 +0100 1111111111111111111111111111111111111111111111111111111 +0101 1111111111111111111111111111111111111111111111111111111 +0102 1111111111111111111111111111111111111111111111111111111 +0103 1111111111111111111111111111111111111111111111111111111 +0104 1111111111111111111111111111111111111111111111111111111 +0105 1111111111111111111111111111111111111111111111111111111 +0106 1111111111111111111111111111111111111111111111111111111 +0107 1111111111111111111111111111111111111111111111111111111 +0108 1111111111111111111111111111111111111111111111111111111 +0109 1111111111111111111111111111111111111111111111111111111 +0110 1111111111111111111111111111111111111111111111111111111 +0111 1111111111111111111111111111111111111111111111111111111 +0112 1111111111111111111111111111111111111111111111111111111 +0113 1111111111111111111111111111111111111111111111111111111 +0114 1111111111111111111111111111111111111111111111111111111 +0115 1111111111111111111111111111111111111111111111111111111 +0116 1111111111111111111111111111111111111111111111111111111 +0117 1111111111111111111111111111111111111111111111111111111 +0118 1111111111111111111111111111111111111111111111111111111 +0119 1111111111111111111111111111111111111111111111111111111 +0120 1111111111111111111111111111111111111111111111111111111 +0121 1111111111111111111111111111111111111111111111111111111 +0122 1111111111111111111111111111111111111111111111111111111 +0123 1111111111111111111111111111111111111111111111111111111 +0124 1111111111111111111111111111111111111111111111111111111 +0125 1111111111111111111111111111111111111111111111111111111 +0126 1111111111111111111111111111111111111111111111111111111 +0127 1111111111111111111111111111111111111111111111111111111 +0128 1111111111111111111111111111111111111111111111111111111 +0129 1111111111111111111111111111111111111111111111111111111 +0130 1111111111111111111111111111111111111111111111111111111 +0131 1111111111111111111111111111111111111111111111111111111 +0132 1111111111111111111111111111111111111111111111111111111 +0133 1111111111111111111111111111111111111111111111111111111 +0134 1111111111111111111111111111111111111111111111111111111 +0135 1111111111111111111111111111111111111111111111111111111 +0136 1111111111111111111111111111111111111111111111111111111 +0137 1111111111111111111111111111111111111111111111111111111 +0138 1111111111111111111111111111111111111111111111111111111 +0139 1111111111111111111111111111111111111111111111111111111 +0140 1111111111111111111111111111111111111111111111111111111 +0141 1111111111111111111111111111111111111111111111111111111 +0142 1111111111111111111111111111111111111111111111111111111 +0143 1111111111111111111111111111111111111111111111111111111 +0144 1111111111111111111111111111111111111111111111111111111 +0145 1111111111111111111111111111111111111111111111111111111 +0146 1111111111111111111111111111111111111111111111111111111 +0147 1111111111111111111111111111111111111111111111111111111 +0148 1111111111111111111111111111111111111111111111111111111 +0149 1111111111111111111111111111111111111111111111111111111 +0150 1111111111111111111111111111111111111111111111111111111 +0151 1111111111111111111111111111111111111111111111111111111 +0152 1111111111111111111111111111111111111111111111111111111 +0153 1111111111111111111111111111111111111111111111111111111 +0154 1111111111111111111111111111111111111111111111111111111 +0155 1111111111111111111111111111111111111111111111111111111 +0156 1111111111111111111111111111111111111111111111111111111 +0157 1111111111111111111111111111111111111111111111111111111 +0158 1111111111111111111111111111111111111111111111111111111 +0159 1111111111111111111111111111111111111111111111111111111 +0160 1111111111111111111111111111111111111111111111111111111 +0161 1111111111111111111111111111111111111111111111111111111 +0162 1111111111111111111111111111111111111111111111111111111 +0163 1111111111111111111111111111111111111111111111111111111 +0164 1111111111111111111111111111111111111111111111111111111 +0165 1111111111111111111111111111111111111111111111111111111 +0166 1111111111111111111111111111111111111111111111111111111 +0167 1111111111111111111111111111111111111111111111111111111 +0168 1111111111111111111111111111111111111111111111111111111 +0169 1111111111111111111111111111111111111111111111111111111 +0170 1111111111111111111111111111111111111111111111111111111 +0171 1111111111111111111111111111111111111111111111111111111 +0172 1111111111111111111111111111111111111111111111111111111 +0173 1111111111111111111111111111111111111111111111111111111 +0174 1111111111111111111111111111111111111111111111111111111 +0175 1111111111111111111111111111111111111111111111111111111 +0176 1111111111111111111111111111111111111111111111111111111 +0177 1111111111111111111111111111111111111111111111111111111 +0178 1111111111111111111111111111111111111111111111111111111 +0179 1111111111111111111111111111111111111111111111111111111 +0180 1111111111111111111111111111111111111111111111111111111 +0181 1111111111111111111111111111111111111111111111111111111 +0182 1111111111111111111111111111111111111111111111111111111 +0183 1111111111111111111111111111111111111111111111111111111 +0184 1111111111111111111111111111111111111111111111111111111 +0185 1111111111111111111111111111111111111111111111111111111 +0186 1111111111111111111111111111111111111111111111111111111 +0187 1111111111111111111111111111111111111111111111111111111 +0188 1111111111111111111111111111111111111111111111111111111 +0189 1111111111111111111111111111111111111111111111111111111 +0190 1111111111111111111111111111111111111111111111111111111 +0191 1111111111111111111111111111111111111111111111111111111 +0192 1111111111111111111111111111111111111111111111111111111 +0193 1111111111111111111111111111111111111111111111111111111 +0194 1111111111111111111111111111111111111111111111111111111 +0195 1111111111111111111111111111111111111111111111111111111 +0196 1111111111111111111111111111111111111111111111111111111 +0197 1111111111111111111111111111111111111111111111111111111 +0198 1111111111111111111111111111111111111111111111111111111 +0199 1111111111111111111111111111111111111111111111111111111 +0200 1111111111111111111111111111111111111111111111111111111 +0201 1111111111111111111111111111111111111111111111111111111 +0202 1111111111111111111111111111111111111111111111111111111 +0203 1111111111111111111111111111111111111111111111111111111 +0204 1111111111111111111111111111111111111111111111111111111 +0205 1111111111111111111111111111111111111111111111111111111 +0206 1111111111111111111111111111111111111111111111111111111 +0207 1111111111111111111111111111111111111111111111111111111 +0208 1111111111111111111111111111111111111111111111111111111 +0209 1111111111111111111111111111111111111111111111111111111 +0210 1111111111111111111111111111111111111111111111111111111 +0211 1111111111111111111111111111111111111111111111111111111 +0212 1111111111111111111111111111111111111111111111111111111 +0213 1111111111111111111111111111111111111111111111111111111 +0214 1111111111111111111111111111111111111111111111111111111 +0215 1111111111111111111111111111111111111111111111111111111 +0216 1111111111111111111111111111111111111111111111111111111 +0217 1111111111111111111111111111111111111111111111111111111 +0218 1111111111111111111111111111111111111111111111111111111 +0219 1111111111111111111111111111111111111111111111111111111 +0220 1111111111111111111111111111111111111111111111111111111 +0221 1111111111111111111111111111111111111111111111111111111 +0222 1111111111111111111111111111111111111111111111111111111 +0223 1111111111111111111111111111111111111111111111111111111 +0224 1111111111111111111111111111111111111111111111111111111 +0225 1111111111111111111111111111111111111111111111111111111 +0226 1111111111111111111111111111111111111111111111111111111 +0227 1111111111111111111111111111111111111111111111111111111 +0228 1111111111111111111111111111111111111111111111111111111 +0229 1111111111111111111111111111111111111111111111111111111 +0230 1111111111111111111111111111111111111111111111111111111 +0231 1111111111111111111111111111111111111111111111111111111 +0232 1111111111111111111111111111111111111111111111111111111 +0233 1111111111111111111111111111111111111111111111111111111 +0234 1111111111111111111111111111111111111111111111111111111 +0235 1111111111111111111111111111111111111111111111111111111 +0236 1111111111111111111111111111111111111111111111111111111 +0237 1111111111111111111111111111111111111111111111111111111 +0238 1111111111111111111111111111111111111111111111111111111 +0239 1111111111111111111111111111111111111111111111111111111 +0240 1111111111111111111111111111111111111111111111111111111 +0241 1111111111111111111111111111111111111111111111111111111 +0242 1111111111111111111111111111111111111111111111111111111 +0243 1111111111111111111111111111111111111111111111111111111 +0244 1111111111111111111111111111111111111111111111111111111 +0245 1111111111111111111111111111111111111111111111111111111 +0246 1111111111111111111111111111111111111111111111111111111 +0247 1111111111111111111111111111111111111111111111111111111 +0248 1111111111111111111111111111111111111111111111111111111 +0249 1111111111111111111111111111111111111111111111111111111 +0250 1111111111111111111111111111111111111111111111111111111 +0251 1111111111111111111111111111111111111111111111111111111 +0252 1111111111111111111111111111111111111111111111111111111 +0253 1111111111111111111111111111111111111111111111111111111 +0254 1111111111111111111111111111111111111111111111111111111 +0255 1111111111111111111111111111111111111111111111111111111 +0256 1111111111111111111111111111111111111111111111111111111 +0257 1111111111111111111111111111111111111111111111111111111 +0258 1111111111111111111111111111111111111111111111111111111 +0259 1111111111111111111111111111111111111111111111111111111 +0260 1111111111111111111111111111111111111111111111111111111 +0261 1111111111111111111111111111111111111111111111111111111 +0262 1111111111111111111111111111111111111111111111111111111 +0263 1111111111111111111111111111111111111111111111111111111 +0264 1111111111111111111111111111111111111111111111111111111 +0265 1111111111111111111111111111111111111111111111111111111 +0266 1111111111111111111111111111111111111111111111111111111 +0267 1111111111111111111111111111111111111111111111111111111 +0268 1111111111111111111111111111111111111111111111111111111 +0269 1111111111111111111111111111111111111111111111111111111 +0270 1111111111111111111111111111111111111111111111111111111 +0271 1111111111111111111111111111111111111111111111111111111 +0272 1111111111111111111111111111111111111111111111111111111 +0273 1111111111111111111111111111111111111111111111111111111 +0274 1111111111111111111111111111111111111111111111111111111 +0275 1111111111111111111111111111111111111111111111111111111 +0276 1111111111111111111111111111111111111111111111111111111 +0277 1111111111111111111111111111111111111111111111111111111 +0278 1111111111111111111111111111111111111111111111111111111 +0279 1111111111111111111111111111111111111111111111111111111 +0280 1111111111111111111111111111111111111111111111111111111 +0281 1111111111111111111111111111111111111111111111111111111 +0282 1111111111111111111111111111111111111111111111111111111 +0283 1111111111111111111111111111111111111111111111111111111 +0284 1111111111111111111111111111111111111111111111111111111 +0285 1111111111111111111111111111111111111111111111111111111 +0286 1111111111111111111111111111111111111111111111111111111 +0287 1111111111111111111111111111111111111111111111111111111 +0288 1111111111111111111111111111111111111111111111111111111 +0289 1111111111111111111111111111111111111111111111111111111 +0290 1111111111111111111111111111111111111111111111111111111 +0291 1111111111111111111111111111111111111111111111111111111 +0292 1111111111111111111111111111111111111111111111111111111 +0293 1111111111111111111111111111111111111111111111111111111 +0294 1111111111111111111111111111111111111111111111111111111 +0295 1111111111111111111111111111111111111111111111111111111 +0296 1111111111111111111111111111111111111111111111111111111 +0297 1111111111111111111111111111111111111111111111111111111 +0298 1111111111111111111111111111111111111111111111111111111 +0299 1111111111111111111111111111111111111111111111111111111 +0300 1111111111111111111111111111111111111111111111111111111 +0301 1111111111111111111111111111111111111111111111111111111 +0302 1111111111111111111111111111111111111111111111111111111 +0303 1111111111111111111111111111111111111111111111111111111 +0304 1111111111111111111111111111111111111111111111111111111 +0305 1111111111111111111111111111111111111111111111111111111 +0306 1111111111111111111111111111111111111111111111111111111 +0307 1111111111111111111111111111111111111111111111111111111 +0308 1111111111111111111111111111111111111111111111111111111 +0309 1111111111111111111111111111111111111111111111111111111 +0310 1111111111111111111111111111111111111111111111111111111 +0311 1111111111111111111111111111111111111111111111111111111 +0312 1111111111111111111111111111111111111111111111111111111 +0313 1111111111111111111111111111111111111111111111111111111 +0314 1111111111111111111111111111111111111111111111111111111 +0315 1111111111111111111111111111111111111111111111111111111 +0316 1111111111111111111111111111111111111111111111111111111 +0317 1111111111111111111111111111111111111111111111111111111 +0318 1111111111111111111111111111111111111111111111111111111 +0319 1111111111111111111111111111111111111111111111111111111 +0320 1111111111111111111111111111111111111111111111111111111 +0321 1111111111111111111111111111111111111111111111111111111 +0322 1111111111111111111111111111111111111111111111111111111 +0323 1111111111111111111111111111111111111111111111111111111 +0324 1111111111111111111111111111111111111111111111111111111 +0325 1111111111111111111111111111111111111111111111111111111 +0326 1111111111111111111111111111111111111111111111111111111 +0327 1111111111111111111111111111111111111111111111111111111 +0328 1111111111111111111111111111111111111111111111111111111 +0329 1111111111111111111111111111111111111111111111111111111 +0330 1111111111111111111111111111111111111111111111111111111 +0331 1111111111111111111111111111111111111111111111111111111 +0332 1111111111111111111111111111111111111111111111111111111 +0333 1111111111111111111111111111111111111111111111111111111 +0334 1111111111111111111111111111111111111111111111111111111 +0335 1111111111111111111111111111111111111111111111111111111 +0336 1111111111111111111111111111111111111111111111111111111 +0337 1111111111111111111111111111111111111111111111111111111 +0338 1111111111111111111111111111111111111111111111111111111 +0339 1111111111111111111111111111111111111111111111111111111 +0340 1111111111111111111111111111111111111111111111111111111 +0341 1111111111111111111111111111111111111111111111111111111 +0342 1111111111111111111111111111111111111111111111111111111 +0343 1111111111111111111111111111111111111111111111111111111 +0344 1111111111111111111111111111111111111111111111111111111 +0345 1111111111111111111111111111111111111111111111111111111 +0346 1111111111111111111111111111111111111111111111111111111 +0347 1111111111111111111111111111111111111111111111111111111 +0348 1111111111111111111111111111111111111111111111111111111 +0349 1111111111111111111111111111111111111111111111111111111 +0350 1111111111111111111111111111111111111111111111111111111 +0351 1111111111111111111111111111111111111111111111111111111 +0352 1111111111111111111111111111111111111111111111111111111 +0353 1111111111111111111111111111111111111111111111111111111 +0354 1111111111111111111111111111111111111111111111111111111 +0355 1111111111111111111111111111111111111111111111111111111 +0356 1111111111111111111111111111111111111111111111111111111 +0357 1111111111111111111111111111111111111111111111111111111 +0358 1111111111111111111111111111111111111111111111111111111 +0359 1111111111111111111111111111111111111111111111111111111 +0360 1111111111111111111111111111111111111111111111111111111 +0361 1111111111111111111111111111111111111111111111111111111 +0362 1111111111111111111111111111111111111111111111111111111 +0363 1111111111111111111111111111111111111111111111111111111 +0364 1111111111111111111111111111111111111111111111111111111 +0365 1111111111111111111111111111111111111111111111111111111 +0366 1111111111111111111111111111111111111111111111111111111 +0367 1111111111111111111111111111111111111111111111111111111 +0368 1111111111111111111111111111111111111111111111111111111 +0369 1111111111111111111111111111111111111111111111111111111 +0370 1111111111111111111111111111111111111111111111111111111 +0371 1111111111111111111111111111111111111111111111111111111 +0372 1111111111111111111111111111111111111111111111111111111 +0373 1111111111111111111111111111111111111111111111111111111 +0374 1111111111111111111111111111111111111111111111111111111 +0375 1111111111111111111111111111111111111111111111111111111 +0376 1111111111111111111111111111111111111111111111111111111 +0377 1111111111111111111111111111111111111111111111111111111 +0378 1111111111111111111111111111111111111111111111111111111 +0379 1111111111111111111111111111111111111111111111111111111 +0380 1111111111111111111111111111111111111111111111111111111 +0381 1111111111111111111111111111111111111111111111111111111 +0382 1111111111111111111111111111111111111111111111111111111 +0383 1111111111111111111111111111111111111111111111111111111 +0384 1111111111111111111111111111111111111111111111111111111 +0385 1111111111111111111111111111111111111111111111111111111 +0386 1111111111111111111111111111111111111111111111111111111 +0387 1111111111111111111111111111111111111111111111111111111 +0388 1111111111111111111111111111111111111111111111111111111 +0389 1111111111111111111111111111111111111111111111111111111 +0390 1111111111111111111111111111111111111111111111111111111 +0391 1111111111111111111111111111111111111111111111111111111 +0392 1111111111111111111111111111111111111111111111111111111 +0393 1111111111111111111111111111111111111111111111111111111 +0394 1111111111111111111111111111111111111111111111111111111 +0395 1111111111111111111111111111111111111111111111111111111 +0396 1111111111111111111111111111111111111111111111111111111 +0397 1111111111111111111111111111111111111111111111111111111 +0398 1111111111111111111111111111111111111111111111111111111 +0399 1111111111111111111111111111111111111111111111111111111 +0400 1111111111111111111111111111111111111111111111111111111 +0401 1111111111111111111111111111111111111111111111111111111 +0402 1111111111111111111111111111111111111111111111111111111 +0403 1111111111111111111111111111111111111111111111111111111 +0404 1111111111111111111111111111111111111111111111111111111 +0405 1111111111111111111111111111111111111111111111111111111 +0406 1111111111111111111111111111111111111111111111111111111 +0407 1111111111111111111111111111111111111111111111111111111 +0408 1111111111111111111111111111111111111111111111111111111 +0409 1111111111111111111111111111111111111111111111111111111 +0410 1111111111111111111111111111111111111111111111111111111 +0411 1111111111111111111111111111111111111111111111111111111 +0412 1111111111111111111111111111111111111111111111111111111 +0413 1111111111111111111111111111111111111111111111111111111 +0414 1111111111111111111111111111111111111111111111111111111 +0415 1111111111111111111111111111111111111111111111111111111 +0416 1111111111111111111111111111111111111111111111111111111 +0417 1111111111111111111111111111111111111111111111111111111 +0418 1111111111111111111111111111111111111111111111111111111 +0419 1111111111111111111111111111111111111111111111111111111 +0420 1111111111111111111111111111111111111111111111111111111 +0421 1111111111111111111111111111111111111111111111111111111 +0422 1111111111111111111111111111111111111111111111111111111 +0423 1111111111111111111111111111111111111111111111111111111 +0424 1111111111111111111111111111111111111111111111111111111 +0425 1111111111111111111111111111111111111111111111111111111 +0426 1111111111111111111111111111111111111111111111111111111 +0427 1111111111111111111111111111111111111111111111111111111 +0428 1111111111111111111111111111111111111111111111111111111 +0429 1111111111111111111111111111111111111111111111111111111 +0430 1111111111111111111111111111111111111111111111111111111 +0431 1111111111111111111111111111111111111111111111111111111 +0432 1111111111111111111111111111111111111111111111111111111 +0433 1111111111111111111111111111111111111111111111111111111 +0434 1111111111111111111111111111111111111111111111111111111 +0435 1111111111111111111111111111111111111111111111111111111 +0436 1111111111111111111111111111111111111111111111111111111 +0437 1111111111111111111111111111111111111111111111111111111 +0438 1111111111111111111111111111111111111111111111111111111 +0439 1111111111111111111111111111111111111111111111111111111 +0440 1111111111111111111111111111111111111111111111111111111 +0441 1111111111111111111111111111111111111111111111111111111 +0442 1111111111111111111111111111111111111111111111111111111 +0443 1111111111111111111111111111111111111111111111111111111 +0444 1111111111111111111111111111111111111111111111111111111 +0445 1111111111111111111111111111111111111111111111111111111 +0446 1111111111111111111111111111111111111111111111111111111 +0447 1111111111111111111111111111111111111111111111111111111 +0448 1111111111111111111111111111111111111111111111111111111 +0449 1111111111111111111111111111111111111111111111111111111 +0450 1111111111111111111111111111111111111111111111111111111 +0451 1111111111111111111111111111111111111111111111111111111 +0452 1111111111111111111111111111111111111111111111111111111 +0453 1111111111111111111111111111111111111111111111111111111 +0454 1111111111111111111111111111111111111111111111111111111 +0455 1111111111111111111111111111111111111111111111111111111 +0456 1111111111111111111111111111111111111111111111111111111 +0457 1111111111111111111111111111111111111111111111111111111 +0458 1111111111111111111111111111111111111111111111111111111 +0459 1111111111111111111111111111111111111111111111111111111 +0460 1111111111111111111111111111111111111111111111111111111 +0461 1111111111111111111111111111111111111111111111111111111 +0462 1111111111111111111111111111111111111111111111111111111 +0463 1111111111111111111111111111111111111111111111111111111 +0464 1111111111111111111111111111111111111111111111111111111 +0465 1111111111111111111111111111111111111111111111111111111 +0466 1111111111111111111111111111111111111111111111111111111 +0467 1111111111111111111111111111111111111111111111111111111 +0468 1111111111111111111111111111111111111111111111111111111 +0469 1111111111111111111111111111111111111111111111111111111 +0470 1111111111111111111111111111111111111111111111111111111 +0471 1111111111111111111111111111111111111111111111111111111 +0472 1111111111111111111111111111111111111111111111111111111 +0473 1111111111111111111111111111111111111111111111111111111 +0474 1111111111111111111111111111111111111111111111111111111 +0475 1111111111111111111111111111111111111111111111111111111 +0476 1111111111111111111111111111111111111111111111111111111 +0477 1111111111111111111111111111111111111111111111111111111 +0478 1111111111111111111111111111111111111111111111111111111 +0479 1111111111111111111111111111111111111111111111111111111 +0480 1111111111111111111111111111111111111111111111111111111 +0481 1111111111111111111111111111111111111111111111111111111 +0482 1111111111111111111111111111111111111111111111111111111 +0483 1111111111111111111111111111111111111111111111111111111 +0484 1111111111111111111111111111111111111111111111111111111 +0485 1111111111111111111111111111111111111111111111111111111 +0486 1111111111111111111111111111111111111111111111111111111 +0487 1111111111111111111111111111111111111111111111111111111 +0488 1111111111111111111111111111111111111111111111111111111 +0489 1111111111111111111111111111111111111111111111111111111 +0490 1111111111111111111111111111111111111111111111111111111 +0491 1111111111111111111111111111111111111111111111111111111 +0492 1111111111111111111111111111111111111111111111111111111 +0493 1111111111111111111111111111111111111111111111111111111 +0494 1111111111111111111111111111111111111111111111111111111 +0495 1111111111111111111111111111111111111111111111111111111 +0496 1111111111111111111111111111111111111111111111111111111 +0497 1111111111111111111111111111111111111111111111111111111 +0498 1111111111111111111111111111111111111111111111111111111 +0499 1111111111111111111111111111111111111111111111111111111 +0500 1111111111111111111111111111111111111111111111111111111 +0501 1111111111111111111111111111111111111111111111111111111 +0502 1111111111111111111111111111111111111111111111111111111 +0503 1111111111111111111111111111111111111111111111111111111 +0504 1111111111111111111111111111111111111111111111111111111 +0505 1111111111111111111111111111111111111111111111111111111 +0506 1111111111111111111111111111111111111111111111111111111 +0507 1111111111111111111111111111111111111111111111111111111 +0508 1111111111111111111111111111111111111111111111111111111 +0509 1111111111111111111111111111111111111111111111111111111 +0510 1111111111111111111111111111111111111111111111111111111 +0511 1111111111111111111111111111111111111111111111111111111 +0512 1111111111111111111111111111111111111111111111111111111 +0513 1111111111111111111111111111111111111111111111111111111 +0514 1111111111111111111111111111111111111111111111111111111 +0515 1111111111111111111111111111111111111111111111111111111 +0516 1111111111111111111111111111111111111111111111111111111 +0517 1111111111111111111111111111111111111111111111111111111 +0518 1111111111111111111111111111111111111111111111111111111 +0519 1111111111111111111111111111111111111111111111111111111 +0520 1111111111111111111111111111111111111111111111111111111 +0521 1111111111111111111111111111111111111111111111111111111 +0522 1111111111111111111111111111111111111111111111111111111 +0523 1111111111111111111111111111111111111111111111111111111 +0524 1111111111111111111111111111111111111111111111111111111 +0525 1111111111111111111111111111111111111111111111111111111 +0526 1111111111111111111111111111111111111111111111111111111 +0527 1111111111111111111111111111111111111111111111111111111 +0528 1111111111111111111111111111111111111111111111111111111 +0529 1111111111111111111111111111111111111111111111111111111 +0530 1111111111111111111111111111111111111111111111111111111 +0531 1111111111111111111111111111111111111111111111111111111 +0532 1111111111111111111111111111111111111111111111111111111 +0533 1111111111111111111111111111111111111111111111111111111 +0534 1111111111111111111111111111111111111111111111111111111 +0535 1111111111111111111111111111111111111111111111111111111 +0536 1111111111111111111111111111111111111111111111111111111 +0537 1111111111111111111111111111111111111111111111111111111 +0538 1111111111111111111111111111111111111111111111111111111 +0539 1111111111111111111111111111111111111111111111111111111 +0540 1111111111111111111111111111111111111111111111111111111 +0541 1111111111111111111111111111111111111111111111111111111 +0542 1111111111111111111111111111111111111111111111111111111 +0543 1111111111111111111111111111111111111111111111111111111 +0544 1111111111111111111111111111111111111111111111111111111 +0545 1111111111111111111111111111111111111111111111111111111 +0546 1111111111111111111111111111111111111111111111111111111 +0547 1111111111111111111111111111111111111111111111111111111 +0548 1111111111111111111111111111111111111111111111111111111 +0549 1111111111111111111111111111111111111111111111111111111 +0550 1111111111111111111111111111111111111111111111111111111 +0551 1111111111111111111111111111111111111111111111111111111 +0552 1111111111111111111111111111111111111111111111111111111 +0553 1111111111111111111111111111111111111111111111111111111 +0554 1111111111111111111111111111111111111111111111111111111 +0555 1111111111111111111111111111111111111111111111111111111 +0556 1111111111111111111111111111111111111111111111111111111 +0557 1111111111111111111111111111111111111111111111111111111 +0558 1111111111111111111111111111111111111111111111111111111 +0559 1111111111111111111111111111111111111111111111111111111 +0560 1111111111111111111111111111111111111111111111111111111 +0561 1111111111111111111111111111111111111111111111111111111 +0562 1111111111111111111111111111111111111111111111111111111 +0563 1111111111111111111111111111111111111111111111111111111 +0564 1111111111111111111111111111111111111111111111111111111 +0565 1111111111111111111111111111111111111111111111111111111 +0566 1111111111111111111111111111111111111111111111111111111 +0567 1111111111111111111111111111111111111111111111111111111 +0568 1111111111111111111111111111111111111111111111111111111 +0569 1111111111111111111111111111111111111111111111111111111 +0570 1111111111111111111111111111111111111111111111111111111 +0571 1111111111111111111111111111111111111111111111111111111 +0572 1111111111111111111111111111111111111111111111111111111 +0573 1111111111111111111111111111111111111111111111111111111 +0574 1111111111111111111111111111111111111111111111111111111 +0575 1111111111111111111111111111111111111111111111111111111 +0576 1111111111111111111111111111111111111111111111111111111 +0577 1111111111111111111111111111111111111111111111111111111 +0578 1111111111111111111111111111111111111111111111111111111 +0579 1111111111111111111111111111111111111111111111111111111 +0580 1111111111111111111111111111111111111111111111111111111 +0581 1111111111111111111111111111111111111111111111111111111 +0582 1111111111111111111111111111111111111111111111111111111 +0583 1111111111111111111111111111111111111111111111111111111 +0584 1111111111111111111111111111111111111111111111111111111 +0585 1111111111111111111111111111111111111111111111111111111 +0586 1111111111111111111111111111111111111111111111111111111 +0587 1111111111111111111111111111111111111111111111111111111 +0588 1111111111111111111111111111111111111111111111111111111 +0589 1111111111111111111111111111111111111111111111111111111 +0590 1111111111111111111111111111111111111111111111111111111 +0591 1111111111111111111111111111111111111111111111111111111 +0592 1111111111111111111111111111111111111111111111111111111 +0593 1111111111111111111111111111111111111111111111111111111 +0594 1111111111111111111111111111111111111111111111111111111 +0595 1111111111111111111111111111111111111111111111111111111 +0596 1111111111111111111111111111111111111111111111111111111 +0597 1111111111111111111111111111111111111111111111111111111 +0598 1111111111111111111111111111111111111111111111111111111 +0599 1111111111111111111111111111111111111111111111111111111 +0600 1111111111111111111111111111111111111111111111111111111 +0601 1111111111111111111111111111111111111111111111111111111 +0602 1111111111111111111111111111111111111111111111111111111 +0603 1111111111111111111111111111111111111111111111111111111 +0604 1111111111111111111111111111111111111111111111111111111 +0605 1111111111111111111111111111111111111111111111111111111 +0606 1111111111111111111111111111111111111111111111111111111 +0607 1111111111111111111111111111111111111111111111111111111 +0608 1111111111111111111111111111111111111111111111111111111 +0609 1111111111111111111111111111111111111111111111111111111 +0610 1111111111111111111111111111111111111111111111111111111 +0611 1111111111111111111111111111111111111111111111111111111 +0612 1111111111111111111111111111111111111111111111111111111 +0613 1111111111111111111111111111111111111111111111111111111 +0614 1111111111111111111111111111111111111111111111111111111 +0615 1111111111111111111111111111111111111111111111111111111 +0616 1111111111111111111111111111111111111111111111111111111 +0617 1111111111111111111111111111111111111111111111111111111 +0618 1111111111111111111111111111111111111111111111111111111 +0619 1111111111111111111111111111111111111111111111111111111 +0620 1111111111111111111111111111111111111111111111111111111 +0621 1111111111111111111111111111111111111111111111111111111 +0622 1111111111111111111111111111111111111111111111111111111 +0623 1111111111111111111111111111111111111111111111111111111 +0624 1111111111111111111111111111111111111111111111111111111 +0625 1111111111111111111111111111111111111111111111111111111 +0626 1111111111111111111111111111111111111111111111111111111 +0627 1111111111111111111111111111111111111111111111111111111 +0628 1111111111111111111111111111111111111111111111111111111 +0629 1111111111111111111111111111111111111111111111111111111 +0630 1111111111111111111111111111111111111111111111111111111 +0631 1111111111111111111111111111111111111111111111111111111 +0632 1111111111111111111111111111111111111111111111111111111 +0633 1111111111111111111111111111111111111111111111111111111 +0634 1111111111111111111111111111111111111111111111111111111 +0635 1111111111111111111111111111111111111111111111111111111 +0636 1111111111111111111111111111111111111111111111111111111 +0637 1111111111111111111111111111111111111111111111111111111 +0638 1111111111111111111111111111111111111111111111111111111 +0639 1111111111111111111111111111111111111111111111111111111 +0640 1111111111111111111111111111111111111111111111111111111 +0641 1111111111111111111111111111111111111111111111111111111 +0642 1111111111111111111111111111111111111111111111111111111 +0643 1111111111111111111111111111111111111111111111111111111 +0644 1111111111111111111111111111111111111111111111111111111 +0645 1111111111111111111111111111111111111111111111111111111 +0646 1111111111111111111111111111111111111111111111111111111 +0647 1111111111111111111111111111111111111111111111111111111 +0648 1111111111111111111111111111111111111111111111111111111 +0649 1111111111111111111111111111111111111111111111111111111 +0650 1111111111111111111111111111111111111111111111111111111 +0651 1111111111111111111111111111111111111111111111111111111 +0652 1111111111111111111111111111111111111111111111111111111 +0653 1111111111111111111111111111111111111111111111111111111 +0654 1111111111111111111111111111111111111111111111111111111 +0655 1111111111111111111111111111111111111111111111111111111 +0656 1111111111111111111111111111111111111111111111111111111 +0657 1111111111111111111111111111111111111111111111111111111 +0658 1111111111111111111111111111111111111111111111111111111 +0659 1111111111111111111111111111111111111111111111111111111 +0660 1111111111111111111111111111111111111111111111111111111 +0661 1111111111111111111111111111111111111111111111111111111 +0662 1111111111111111111111111111111111111111111111111111111 +0663 1111111111111111111111111111111111111111111111111111111 +0664 1111111111111111111111111111111111111111111111111111111 +0665 1111111111111111111111111111111111111111111111111111111 +0666 1111111111111111111111111111111111111111111111111111111 +0667 1111111111111111111111111111111111111111111111111111111 +0668 1111111111111111111111111111111111111111111111111111111 +0669 1111111111111111111111111111111111111111111111111111111 +0670 1111111111111111111111111111111111111111111111111111111 +0671 1111111111111111111111111111111111111111111111111111111 +0672 1111111111111111111111111111111111111111111111111111111 +0673 1111111111111111111111111111111111111111111111111111111 +0674 1111111111111111111111111111111111111111111111111111111 +0675 1111111111111111111111111111111111111111111111111111111 +0676 1111111111111111111111111111111111111111111111111111111 +0677 1111111111111111111111111111111111111111111111111111111 +0678 1111111111111111111111111111111111111111111111111111111 +0679 1111111111111111111111111111111111111111111111111111111 +0680 1111111111111111111111111111111111111111111111111111111 +0681 1111111111111111111111111111111111111111111111111111111 +0682 1111111111111111111111111111111111111111111111111111111 +0683 1111111111111111111111111111111111111111111111111111111 +0684 1111111111111111111111111111111111111111111111111111111 +0685 1111111111111111111111111111111111111111111111111111111 +0686 1111111111111111111111111111111111111111111111111111111 +0687 1111111111111111111111111111111111111111111111111111111 +0688 1111111111111111111111111111111111111111111111111111111 +0689 1111111111111111111111111111111111111111111111111111111 +0690 1111111111111111111111111111111111111111111111111111111 +0691 1111111111111111111111111111111111111111111111111111111 +0692 1111111111111111111111111111111111111111111111111111111 +0693 1111111111111111111111111111111111111111111111111111111 +0694 1111111111111111111111111111111111111111111111111111111 +0695 1111111111111111111111111111111111111111111111111111111 +0696 1111111111111111111111111111111111111111111111111111111 +0697 1111111111111111111111111111111111111111111111111111111 +0698 1111111111111111111111111111111111111111111111111111111 +0699 1111111111111111111111111111111111111111111111111111111 +0700 1111111111111111111111111111111111111111111111111111111 +0701 1111111111111111111111111111111111111111111111111111111 +0702 1111111111111111111111111111111111111111111111111111111 +0703 1111111111111111111111111111111111111111111111111111111 +0704 1111111111111111111111111111111111111111111111111111111 +0705 1111111111111111111111111111111111111111111111111111111 +0706 1111111111111111111111111111111111111111111111111111111 +0707 1111111111111111111111111111111111111111111111111111111 +0708 1111111111111111111111111111111111111111111111111111111 +0709 1111111111111111111111111111111111111111111111111111111 +0710 1111111111111111111111111111111111111111111111111111111 +0711 1111111111111111111111111111111111111111111111111111111 +0712 1111111111111111111111111111111111111111111111111111111 +0713 1111111111111111111111111111111111111111111111111111111 +0714 1111111111111111111111111111111111111111111111111111111 +0715 1111111111111111111111111111111111111111111111111111111 +0716 1111111111111111111111111111111111111111111111111111111 +0717 1111111111111111111111111111111111111111111111111111111 +0718 1111111111111111111111111111111111111111111111111111111 +0719 1111111111111111111111111111111111111111111111111111111 +0720 1111111111111111111111111111111111111111111111111111111 +0721 1111111111111111111111111111111111111111111111111111111 +0722 1111111111111111111111111111111111111111111111111111111 +0723 1111111111111111111111111111111111111111111111111111111 +0724 1111111111111111111111111111111111111111111111111111111 +0725 1111111111111111111111111111111111111111111111111111111 +0726 1111111111111111111111111111111111111111111111111111111 +0727 1111111111111111111111111111111111111111111111111111111 +0728 1111111111111111111111111111111111111111111111111111111 +0729 1111111111111111111111111111111111111111111111111111111 +0730 1111111111111111111111111111111111111111111111111111111 +0731 1111111111111111111111111111111111111111111111111111111 +0732 1111111111111111111111111111111111111111111111111111111 +0733 1111111111111111111111111111111111111111111111111111111 +0734 1111111111111111111111111111111111111111111111111111111 +0735 1111111111111111111111111111111111111111111111111111111 +0736 1111111111111111111111111111111111111111111111111111111 +0737 1111111111111111111111111111111111111111111111111111111 +0738 1111111111111111111111111111111111111111111111111111111 +0739 1111111111111111111111111111111111111111111111111111111 +0740 1111111111111111111111111111111111111111111111111111111 +0741 1111111111111111111111111111111111111111111111111111111 +0742 1111111111111111111111111111111111111111111111111111111 +0743 1111111111111111111111111111111111111111111111111111111 +0744 1111111111111111111111111111111111111111111111111111111 +0745 1111111111111111111111111111111111111111111111111111111 +0746 1111111111111111111111111111111111111111111111111111111 +0747 1111111111111111111111111111111111111111111111111111111 +0748 1111111111111111111111111111111111111111111111111111111 +0749 1111111111111111111111111111111111111111111111111111111 +0750 1111111111111111111111111111111111111111111111111111111 +0751 1111111111111111111111111111111111111111111111111111111 +0752 1111111111111111111111111111111111111111111111111111111 +0753 1111111111111111111111111111111111111111111111111111111 +0754 1111111111111111111111111111111111111111111111111111111 +0755 1111111111111111111111111111111111111111111111111111111 +0756 1111111111111111111111111111111111111111111111111111111 +0757 1111111111111111111111111111111111111111111111111111111 +0758 1111111111111111111111111111111111111111111111111111111 +0759 1111111111111111111111111111111111111111111111111111111 +0760 1111111111111111111111111111111111111111111111111111111 +0761 1111111111111111111111111111111111111111111111111111111 +0762 1111111111111111111111111111111111111111111111111111111 +0763 1111111111111111111111111111111111111111111111111111111 +0764 1111111111111111111111111111111111111111111111111111111 +0765 1111111111111111111111111111111111111111111111111111111 +0766 1111111111111111111111111111111111111111111111111111111 +0767 1111111111111111111111111111111111111111111111111111111 +0768 1111111111111111111111111111111111111111111111111111111 +0769 1111111111111111111111111111111111111111111111111111111 +0770 1111111111111111111111111111111111111111111111111111111 +0771 1111111111111111111111111111111111111111111111111111111 +0772 1111111111111111111111111111111111111111111111111111111 +0773 1111111111111111111111111111111111111111111111111111111 +0774 1111111111111111111111111111111111111111111111111111111 +0775 1111111111111111111111111111111111111111111111111111111 +0776 1111111111111111111111111111111111111111111111111111111 +0777 1111111111111111111111111111111111111111111111111111111 +0778 1111111111111111111111111111111111111111111111111111111 +0779 1111111111111111111111111111111111111111111111111111111 +0780 1111111111111111111111111111111111111111111111111111111 +0781 1111111111111111111111111111111111111111111111111111111 +0782 1111111111111111111111111111111111111111111111111111111 +0783 1111111111111111111111111111111111111111111111111111111 +0784 1111111111111111111111111111111111111111111111111111111 +0785 1111111111111111111111111111111111111111111111111111111 +0786 1111111111111111111111111111111111111111111111111111111 +0787 1111111111111111111111111111111111111111111111111111111 +0788 1111111111111111111111111111111111111111111111111111111 +0789 1111111111111111111111111111111111111111111111111111111 +0790 1111111111111111111111111111111111111111111111111111111 +0791 1111111111111111111111111111111111111111111111111111111 +0792 1111111111111111111111111111111111111111111111111111111 +0793 1111111111111111111111111111111111111111111111111111111 +0794 1111111111111111111111111111111111111111111111111111111 +0795 1111111111111111111111111111111111111111111111111111111 +0796 1111111111111111111111111111111111111111111111111111111 +0797 1111111111111111111111111111111111111111111111111111111 +0798 1111111111111111111111111111111111111111111111111111111 +0799 1111111111111111111111111111111111111111111111111111111 +0800 1111111111111111111111111111111111111111111111111111111 +0801 1111111111111111111111111111111111111111111111111111111 +0802 1111111111111111111111111111111111111111111111111111111 +0803 1111111111111111111111111111111111111111111111111111111 +0804 1111111111111111111111111111111111111111111111111111111 +0805 1111111111111111111111111111111111111111111111111111111 +0806 1111111111111111111111111111111111111111111111111111111 +0807 1111111111111111111111111111111111111111111111111111111 +0808 1111111111111111111111111111111111111111111111111111111 +0809 1111111111111111111111111111111111111111111111111111111 +0810 1111111111111111111111111111111111111111111111111111111 +0811 1111111111111111111111111111111111111111111111111111111 +0812 1111111111111111111111111111111111111111111111111111111 +0813 1111111111111111111111111111111111111111111111111111111 +0814 1111111111111111111111111111111111111111111111111111111 +0815 1111111111111111111111111111111111111111111111111111111 +0816 1111111111111111111111111111111111111111111111111111111 +0817 1111111111111111111111111111111111111111111111111111111 +0818 1111111111111111111111111111111111111111111111111111111 +0819 1111111111111111111111111111111111111111111111111111111 +0820 1111111111111111111111111111111111111111111111111111111 +0821 1111111111111111111111111111111111111111111111111111111 +0822 1111111111111111111111111111111111111111111111111111111 +0823 1111111111111111111111111111111111111111111111111111111 +0824 1111111111111111111111111111111111111111111111111111111 +0825 1111111111111111111111111111111111111111111111111111111 +0826 1111111111111111111111111111111111111111111111111111111 +0827 1111111111111111111111111111111111111111111111111111111 +0828 1111111111111111111111111111111111111111111111111111111 +0829 1111111111111111111111111111111111111111111111111111111 +0830 1111111111111111111111111111111111111111111111111111111 +0831 1111111111111111111111111111111111111111111111111111111 +0832 1111111111111111111111111111111111111111111111111111111 +0833 1111111111111111111111111111111111111111111111111111111 +0834 1111111111111111111111111111111111111111111111111111111 +0835 1111111111111111111111111111111111111111111111111111111 +0836 1111111111111111111111111111111111111111111111111111111 +0837 1111111111111111111111111111111111111111111111111111111 +0838 1111111111111111111111111111111111111111111111111111111 +0839 1111111111111111111111111111111111111111111111111111111 +0840 1111111111111111111111111111111111111111111111111111111 +0841 1111111111111111111111111111111111111111111111111111111 +0842 1111111111111111111111111111111111111111111111111111111 +0843 1111111111111111111111111111111111111111111111111111111 +0844 1111111111111111111111111111111111111111111111111111111 +0845 1111111111111111111111111111111111111111111111111111111 +0846 1111111111111111111111111111111111111111111111111111111 +0847 1111111111111111111111111111111111111111111111111111111 +0848 1111111111111111111111111111111111111111111111111111111 +0849 1111111111111111111111111111111111111111111111111111111 +0850 1111111111111111111111111111111111111111111111111111111 +0851 1111111111111111111111111111111111111111111111111111111 +0852 1111111111111111111111111111111111111111111111111111111 +0853 1111111111111111111111111111111111111111111111111111111 +0854 1111111111111111111111111111111111111111111111111111111 +0855 1111111111111111111111111111111111111111111111111111111 +0856 1111111111111111111111111111111111111111111111111111111 +0857 1111111111111111111111111111111111111111111111111111111 +0858 1111111111111111111111111111111111111111111111111111111 +0859 1111111111111111111111111111111111111111111111111111111 +0860 1111111111111111111111111111111111111111111111111111111 +0861 1111111111111111111111111111111111111111111111111111111 +0862 1111111111111111111111111111111111111111111111111111111 +0863 1111111111111111111111111111111111111111111111111111111 +0864 1111111111111111111111111111111111111111111111111111111 +0865 1111111111111111111111111111111111111111111111111111111 +0866 1111111111111111111111111111111111111111111111111111111 +0867 1111111111111111111111111111111111111111111111111111111 +0868 1111111111111111111111111111111111111111111111111111111 +0869 1111111111111111111111111111111111111111111111111111111 +0870 1111111111111111111111111111111111111111111111111111111 +0871 1111111111111111111111111111111111111111111111111111111 +0872 1111111111111111111111111111111111111111111111111111111 +0873 1111111111111111111111111111111111111111111111111111111 +0874 1111111111111111111111111111111111111111111111111111111 +0875 1111111111111111111111111111111111111111111111111111111 +0876 1111111111111111111111111111111111111111111111111111111 +0877 1111111111111111111111111111111111111111111111111111111 +0878 1111111111111111111111111111111111111111111111111111111 +0879 1111111111111111111111111111111111111111111111111111111 +0880 1111111111111111111111111111111111111111111111111111111 +0881 1111111111111111111111111111111111111111111111111111111 +0882 1111111111111111111111111111111111111111111111111111111 +0883 1111111111111111111111111111111111111111111111111111111 +0884 1111111111111111111111111111111111111111111111111111111 +0885 1111111111111111111111111111111111111111111111111111111 +0886 1111111111111111111111111111111111111111111111111111111 +0887 1111111111111111111111111111111111111111111111111111111 +0888 1111111111111111111111111111111111111111111111111111111 +0889 1111111111111111111111111111111111111111111111111111111 +0890 1111111111111111111111111111111111111111111111111111111 +0891 1111111111111111111111111111111111111111111111111111111 +0892 1111111111111111111111111111111111111111111111111111111 +0893 1111111111111111111111111111111111111111111111111111111 +0894 1111111111111111111111111111111111111111111111111111111 +0895 1111111111111111111111111111111111111111111111111111111 +0896 1111111111111111111111111111111111111111111111111111111 +0897 1111111111111111111111111111111111111111111111111111111 +0898 1111111111111111111111111111111111111111111111111111111 +0899 1111111111111111111111111111111111111111111111111111111 +0900 1111111111111111111111111111111111111111111111111111111 +0901 1111111111111111111111111111111111111111111111111111111 +0902 1111111111111111111111111111111111111111111111111111111 +0903 1111111111111111111111111111111111111111111111111111111 +0904 1111111111111111111111111111111111111111111111111111111 +0905 1111111111111111111111111111111111111111111111111111111 +0906 1111111111111111111111111111111111111111111111111111111 +0907 1111111111111111111111111111111111111111111111111111111 +0908 1111111111111111111111111111111111111111111111111111111 +0909 1111111111111111111111111111111111111111111111111111111 +0910 1111111111111111111111111111111111111111111111111111111 +0911 1111111111111111111111111111111111111111111111111111111 +0912 1111111111111111111111111111111111111111111111111111111 +0913 1111111111111111111111111111111111111111111111111111111 +0914 1111111111111111111111111111111111111111111111111111111 +0915 1111111111111111111111111111111111111111111111111111111 +0916 1111111111111111111111111111111111111111111111111111111 +0917 1111111111111111111111111111111111111111111111111111111 +0918 1111111111111111111111111111111111111111111111111111111 +0919 1111111111111111111111111111111111111111111111111111111 +0920 1111111111111111111111111111111111111111111111111111111 +0921 1111111111111111111111111111111111111111111111111111111 +0922 1111111111111111111111111111111111111111111111111111111 +0923 1111111111111111111111111111111111111111111111111111111 +0924 1111111111111111111111111111111111111111111111111111111 +0925 1111111111111111111111111111111111111111111111111111111 +0926 1111111111111111111111111111111111111111111111111111111 +0927 1111111111111111111111111111111111111111111111111111111 +0928 1111111111111111111111111111111111111111111111111111111 +0929 1111111111111111111111111111111111111111111111111111111 +0930 1111111111111111111111111111111111111111111111111111111 +0931 1111111111111111111111111111111111111111111111111111111 +0932 1111111111111111111111111111111111111111111111111111111 +0933 1111111111111111111111111111111111111111111111111111111 +0934 1111111111111111111111111111111111111111111111111111111 +0935 1111111111111111111111111111111111111111111111111111111 +0936 1111111111111111111111111111111111111111111111111111111 +0937 1111111111111111111111111111111111111111111111111111111 +0938 1111111111111111111111111111111111111111111111111111111 +0939 1111111111111111111111111111111111111111111111111111111 +0940 1111111111111111111111111111111111111111111111111111111 +0941 1111111111111111111111111111111111111111111111111111111 +0942 1111111111111111111111111111111111111111111111111111111 +0943 1111111111111111111111111111111111111111111111111111111 +0944 1111111111111111111111111111111111111111111111111111111 +0945 1111111111111111111111111111111111111111111111111111111 +0946 1111111111111111111111111111111111111111111111111111111 +0947 1111111111111111111111111111111111111111111111111111111 +0948 1111111111111111111111111111111111111111111111111111111 +0949 1111111111111111111111111111111111111111111111111111111 +0950 1111111111111111111111111111111111111111111111111111111 +0951 1111111111111111111111111111111111111111111111111111111 +0952 1111111111111111111111111111111111111111111111111111111 +0953 1111111111111111111111111111111111111111111111111111111 +0954 1111111111111111111111111111111111111111111111111111111 +0955 1111111111111111111111111111111111111111111111111111111 +0956 1111111111111111111111111111111111111111111111111111111 +0957 1111111111111111111111111111111111111111111111111111111 +0958 1111111111111111111111111111111111111111111111111111111 +0959 1111111111111111111111111111111111111111111111111111111 +0960 1111111111111111111111111111111111111111111111111111111 +0961 1111111111111111111111111111111111111111111111111111111 +0962 1111111111111111111111111111111111111111111111111111111 +0963 1111111111111111111111111111111111111111111111111111111 +0964 1111111111111111111111111111111111111111111111111111111 +0965 1111111111111111111111111111111111111111111111111111111 +0966 1111111111111111111111111111111111111111111111111111111 +0967 1111111111111111111111111111111111111111111111111111111 +0968 1111111111111111111111111111111111111111111111111111111 +0969 1111111111111111111111111111111111111111111111111111111 +0970 1111111111111111111111111111111111111111111111111111111 +0971 1111111111111111111111111111111111111111111111111111111 +0972 1111111111111111111111111111111111111111111111111111111 +0973 1111111111111111111111111111111111111111111111111111111 +0974 1111111111111111111111111111111111111111111111111111111 +0975 1111111111111111111111111111111111111111111111111111111 +0976 1111111111111111111111111111111111111111111111111111111 +0977 1111111111111111111111111111111111111111111111111111111 +0978 1111111111111111111111111111111111111111111111111111111 +0979 1111111111111111111111111111111111111111111111111111111 +0980 1111111111111111111111111111111111111111111111111111111 +0981 1111111111111111111111111111111111111111111111111111111 +0982 1111111111111111111111111111111111111111111111111111111 +0983 1111111111111111111111111111111111111111111111111111111 +0984 1111111111111111111111111111111111111111111111111111111 +0985 1111111111111111111111111111111111111111111111111111111 +0986 1111111111111111111111111111111111111111111111111111111 +0987 1111111111111111111111111111111111111111111111111111111 +0988 1111111111111111111111111111111111111111111111111111111 +0989 1111111111111111111111111111111111111111111111111111111 +0990 1111111111111111111111111111111111111111111111111111111 +0991 1111111111111111111111111111111111111111111111111111111 +0992 1111111111111111111111111111111111111111111111111111111 +0993 1111111111111111111111111111111111111111111111111111111 +0994 1111111111111111111111111111111111111111111111111111111 +0995 1111111111111111111111111111111111111111111111111111111 +0996 1111111111111111111111111111111111111111111111111111111 +0997 1111111111111111111111111111111111111111111111111111111 +0998 1111111111111111111111111111111111111111111111111111111 +0999 1111111111111111111111111111111111111111111111111111111 diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/da.txt.gz b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/da.txt.gz new file mode 100644 index 00000000000..9ee9be82f0e Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/da.txt.gz differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/dat.txt b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/dat.txt new file mode 100644 index 00000000000..c6f093c6931 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/dat.txt @@ -0,0 +1,4000 @@ +0000 2222222222222222222222222222222222222222222222222222222 +0001 2222222222222222222222222222222222222222222222222222222 +0002 2222222222222222222222222222222222222222222222222222222 +0003 2222222222222222222222222222222222222222222222222222222 +0004 2222222222222222222222222222222222222222222222222222222 +0005 2222222222222222222222222222222222222222222222222222222 +0006 2222222222222222222222222222222222222222222222222222222 +0007 2222222222222222222222222222222222222222222222222222222 +0008 2222222222222222222222222222222222222222222222222222222 +0009 2222222222222222222222222222222222222222222222222222222 +0010 2222222222222222222222222222222222222222222222222222222 +0011 2222222222222222222222222222222222222222222222222222222 +0012 2222222222222222222222222222222222222222222222222222222 +0013 2222222222222222222222222222222222222222222222222222222 +0014 2222222222222222222222222222222222222222222222222222222 +0015 2222222222222222222222222222222222222222222222222222222 +0016 2222222222222222222222222222222222222222222222222222222 +0017 2222222222222222222222222222222222222222222222222222222 +0018 2222222222222222222222222222222222222222222222222222222 +0019 2222222222222222222222222222222222222222222222222222222 +0020 2222222222222222222222222222222222222222222222222222222 +0021 2222222222222222222222222222222222222222222222222222222 +0022 2222222222222222222222222222222222222222222222222222222 +0023 2222222222222222222222222222222222222222222222222222222 +0024 2222222222222222222222222222222222222222222222222222222 +0025 2222222222222222222222222222222222222222222222222222222 +0026 2222222222222222222222222222222222222222222222222222222 +0027 2222222222222222222222222222222222222222222222222222222 +0028 2222222222222222222222222222222222222222222222222222222 +0029 2222222222222222222222222222222222222222222222222222222 +0030 2222222222222222222222222222222222222222222222222222222 +0031 2222222222222222222222222222222222222222222222222222222 +0032 2222222222222222222222222222222222222222222222222222222 +0033 2222222222222222222222222222222222222222222222222222222 +0034 2222222222222222222222222222222222222222222222222222222 +0035 2222222222222222222222222222222222222222222222222222222 +0036 2222222222222222222222222222222222222222222222222222222 +0037 2222222222222222222222222222222222222222222222222222222 +0038 2222222222222222222222222222222222222222222222222222222 +0039 2222222222222222222222222222222222222222222222222222222 +0040 2222222222222222222222222222222222222222222222222222222 +0041 2222222222222222222222222222222222222222222222222222222 +0042 2222222222222222222222222222222222222222222222222222222 +0043 2222222222222222222222222222222222222222222222222222222 +0044 2222222222222222222222222222222222222222222222222222222 +0045 2222222222222222222222222222222222222222222222222222222 +0046 2222222222222222222222222222222222222222222222222222222 +0047 2222222222222222222222222222222222222222222222222222222 +0048 2222222222222222222222222222222222222222222222222222222 +0049 2222222222222222222222222222222222222222222222222222222 +0050 2222222222222222222222222222222222222222222222222222222 +0051 2222222222222222222222222222222222222222222222222222222 +0052 2222222222222222222222222222222222222222222222222222222 +0053 2222222222222222222222222222222222222222222222222222222 +0054 2222222222222222222222222222222222222222222222222222222 +0055 2222222222222222222222222222222222222222222222222222222 +0056 2222222222222222222222222222222222222222222222222222222 +0057 2222222222222222222222222222222222222222222222222222222 +0058 2222222222222222222222222222222222222222222222222222222 +0059 2222222222222222222222222222222222222222222222222222222 +0060 2222222222222222222222222222222222222222222222222222222 +0061 2222222222222222222222222222222222222222222222222222222 +0062 2222222222222222222222222222222222222222222222222222222 +0063 2222222222222222222222222222222222222222222222222222222 +0064 2222222222222222222222222222222222222222222222222222222 +0065 2222222222222222222222222222222222222222222222222222222 +0066 2222222222222222222222222222222222222222222222222222222 +0067 2222222222222222222222222222222222222222222222222222222 +0068 2222222222222222222222222222222222222222222222222222222 +0069 2222222222222222222222222222222222222222222222222222222 +0070 2222222222222222222222222222222222222222222222222222222 +0071 2222222222222222222222222222222222222222222222222222222 +0072 2222222222222222222222222222222222222222222222222222222 +0073 2222222222222222222222222222222222222222222222222222222 +0074 2222222222222222222222222222222222222222222222222222222 +0075 2222222222222222222222222222222222222222222222222222222 +0076 2222222222222222222222222222222222222222222222222222222 +0077 2222222222222222222222222222222222222222222222222222222 +0078 2222222222222222222222222222222222222222222222222222222 +0079 2222222222222222222222222222222222222222222222222222222 +0080 2222222222222222222222222222222222222222222222222222222 +0081 2222222222222222222222222222222222222222222222222222222 +0082 2222222222222222222222222222222222222222222222222222222 +0083 2222222222222222222222222222222222222222222222222222222 +0084 2222222222222222222222222222222222222222222222222222222 +0085 2222222222222222222222222222222222222222222222222222222 +0086 2222222222222222222222222222222222222222222222222222222 +0087 2222222222222222222222222222222222222222222222222222222 +0088 2222222222222222222222222222222222222222222222222222222 +0089 2222222222222222222222222222222222222222222222222222222 +0090 2222222222222222222222222222222222222222222222222222222 +0091 2222222222222222222222222222222222222222222222222222222 +0092 2222222222222222222222222222222222222222222222222222222 +0093 2222222222222222222222222222222222222222222222222222222 +0094 2222222222222222222222222222222222222222222222222222222 +0095 2222222222222222222222222222222222222222222222222222222 +0096 2222222222222222222222222222222222222222222222222222222 +0097 2222222222222222222222222222222222222222222222222222222 +0098 2222222222222222222222222222222222222222222222222222222 +0099 2222222222222222222222222222222222222222222222222222222 +0100 2222222222222222222222222222222222222222222222222222222 +0101 2222222222222222222222222222222222222222222222222222222 +0102 2222222222222222222222222222222222222222222222222222222 +0103 2222222222222222222222222222222222222222222222222222222 +0104 2222222222222222222222222222222222222222222222222222222 +0105 2222222222222222222222222222222222222222222222222222222 +0106 2222222222222222222222222222222222222222222222222222222 +0107 2222222222222222222222222222222222222222222222222222222 +0108 2222222222222222222222222222222222222222222222222222222 +0109 2222222222222222222222222222222222222222222222222222222 +0110 2222222222222222222222222222222222222222222222222222222 +0111 2222222222222222222222222222222222222222222222222222222 +0112 2222222222222222222222222222222222222222222222222222222 +0113 2222222222222222222222222222222222222222222222222222222 +0114 2222222222222222222222222222222222222222222222222222222 +0115 2222222222222222222222222222222222222222222222222222222 +0116 2222222222222222222222222222222222222222222222222222222 +0117 2222222222222222222222222222222222222222222222222222222 +0118 2222222222222222222222222222222222222222222222222222222 +0119 2222222222222222222222222222222222222222222222222222222 +0120 2222222222222222222222222222222222222222222222222222222 +0121 2222222222222222222222222222222222222222222222222222222 +0122 2222222222222222222222222222222222222222222222222222222 +0123 2222222222222222222222222222222222222222222222222222222 +0124 2222222222222222222222222222222222222222222222222222222 +0125 2222222222222222222222222222222222222222222222222222222 +0126 2222222222222222222222222222222222222222222222222222222 +0127 2222222222222222222222222222222222222222222222222222222 +0128 2222222222222222222222222222222222222222222222222222222 +0129 2222222222222222222222222222222222222222222222222222222 +0130 2222222222222222222222222222222222222222222222222222222 +0131 2222222222222222222222222222222222222222222222222222222 +0132 2222222222222222222222222222222222222222222222222222222 +0133 2222222222222222222222222222222222222222222222222222222 +0134 2222222222222222222222222222222222222222222222222222222 +0135 2222222222222222222222222222222222222222222222222222222 +0136 2222222222222222222222222222222222222222222222222222222 +0137 2222222222222222222222222222222222222222222222222222222 +0138 2222222222222222222222222222222222222222222222222222222 +0139 2222222222222222222222222222222222222222222222222222222 +0140 2222222222222222222222222222222222222222222222222222222 +0141 2222222222222222222222222222222222222222222222222222222 +0142 2222222222222222222222222222222222222222222222222222222 +0143 2222222222222222222222222222222222222222222222222222222 +0144 2222222222222222222222222222222222222222222222222222222 +0145 2222222222222222222222222222222222222222222222222222222 +0146 2222222222222222222222222222222222222222222222222222222 +0147 2222222222222222222222222222222222222222222222222222222 +0148 2222222222222222222222222222222222222222222222222222222 +0149 2222222222222222222222222222222222222222222222222222222 +0150 2222222222222222222222222222222222222222222222222222222 +0151 2222222222222222222222222222222222222222222222222222222 +0152 2222222222222222222222222222222222222222222222222222222 +0153 2222222222222222222222222222222222222222222222222222222 +0154 2222222222222222222222222222222222222222222222222222222 +0155 2222222222222222222222222222222222222222222222222222222 +0156 2222222222222222222222222222222222222222222222222222222 +0157 2222222222222222222222222222222222222222222222222222222 +0158 2222222222222222222222222222222222222222222222222222222 +0159 2222222222222222222222222222222222222222222222222222222 +0160 2222222222222222222222222222222222222222222222222222222 +0161 2222222222222222222222222222222222222222222222222222222 +0162 2222222222222222222222222222222222222222222222222222222 +0163 2222222222222222222222222222222222222222222222222222222 +0164 2222222222222222222222222222222222222222222222222222222 +0165 2222222222222222222222222222222222222222222222222222222 +0166 2222222222222222222222222222222222222222222222222222222 +0167 2222222222222222222222222222222222222222222222222222222 +0168 2222222222222222222222222222222222222222222222222222222 +0169 2222222222222222222222222222222222222222222222222222222 +0170 2222222222222222222222222222222222222222222222222222222 +0171 2222222222222222222222222222222222222222222222222222222 +0172 2222222222222222222222222222222222222222222222222222222 +0173 2222222222222222222222222222222222222222222222222222222 +0174 2222222222222222222222222222222222222222222222222222222 +0175 2222222222222222222222222222222222222222222222222222222 +0176 2222222222222222222222222222222222222222222222222222222 +0177 2222222222222222222222222222222222222222222222222222222 +0178 2222222222222222222222222222222222222222222222222222222 +0179 2222222222222222222222222222222222222222222222222222222 +0180 2222222222222222222222222222222222222222222222222222222 +0181 2222222222222222222222222222222222222222222222222222222 +0182 2222222222222222222222222222222222222222222222222222222 +0183 2222222222222222222222222222222222222222222222222222222 +0184 2222222222222222222222222222222222222222222222222222222 +0185 2222222222222222222222222222222222222222222222222222222 +0186 2222222222222222222222222222222222222222222222222222222 +0187 2222222222222222222222222222222222222222222222222222222 +0188 2222222222222222222222222222222222222222222222222222222 +0189 2222222222222222222222222222222222222222222222222222222 +0190 2222222222222222222222222222222222222222222222222222222 +0191 2222222222222222222222222222222222222222222222222222222 +0192 2222222222222222222222222222222222222222222222222222222 +0193 2222222222222222222222222222222222222222222222222222222 +0194 2222222222222222222222222222222222222222222222222222222 +0195 2222222222222222222222222222222222222222222222222222222 +0196 2222222222222222222222222222222222222222222222222222222 +0197 2222222222222222222222222222222222222222222222222222222 +0198 2222222222222222222222222222222222222222222222222222222 +0199 2222222222222222222222222222222222222222222222222222222 +0200 2222222222222222222222222222222222222222222222222222222 +0201 2222222222222222222222222222222222222222222222222222222 +0202 2222222222222222222222222222222222222222222222222222222 +0203 2222222222222222222222222222222222222222222222222222222 +0204 2222222222222222222222222222222222222222222222222222222 +0205 2222222222222222222222222222222222222222222222222222222 +0206 2222222222222222222222222222222222222222222222222222222 +0207 2222222222222222222222222222222222222222222222222222222 +0208 2222222222222222222222222222222222222222222222222222222 +0209 2222222222222222222222222222222222222222222222222222222 +0210 2222222222222222222222222222222222222222222222222222222 +0211 2222222222222222222222222222222222222222222222222222222 +0212 2222222222222222222222222222222222222222222222222222222 +0213 2222222222222222222222222222222222222222222222222222222 +0214 2222222222222222222222222222222222222222222222222222222 +0215 2222222222222222222222222222222222222222222222222222222 +0216 2222222222222222222222222222222222222222222222222222222 +0217 2222222222222222222222222222222222222222222222222222222 +0218 2222222222222222222222222222222222222222222222222222222 +0219 2222222222222222222222222222222222222222222222222222222 +0220 2222222222222222222222222222222222222222222222222222222 +0221 2222222222222222222222222222222222222222222222222222222 +0222 2222222222222222222222222222222222222222222222222222222 +0223 2222222222222222222222222222222222222222222222222222222 +0224 2222222222222222222222222222222222222222222222222222222 +0225 2222222222222222222222222222222222222222222222222222222 +0226 2222222222222222222222222222222222222222222222222222222 +0227 2222222222222222222222222222222222222222222222222222222 +0228 2222222222222222222222222222222222222222222222222222222 +0229 2222222222222222222222222222222222222222222222222222222 +0230 2222222222222222222222222222222222222222222222222222222 +0231 2222222222222222222222222222222222222222222222222222222 +0232 2222222222222222222222222222222222222222222222222222222 +0233 2222222222222222222222222222222222222222222222222222222 +0234 2222222222222222222222222222222222222222222222222222222 +0235 2222222222222222222222222222222222222222222222222222222 +0236 2222222222222222222222222222222222222222222222222222222 +0237 2222222222222222222222222222222222222222222222222222222 +0238 2222222222222222222222222222222222222222222222222222222 +0239 2222222222222222222222222222222222222222222222222222222 +0240 2222222222222222222222222222222222222222222222222222222 +0241 2222222222222222222222222222222222222222222222222222222 +0242 2222222222222222222222222222222222222222222222222222222 +0243 2222222222222222222222222222222222222222222222222222222 +0244 2222222222222222222222222222222222222222222222222222222 +0245 2222222222222222222222222222222222222222222222222222222 +0246 2222222222222222222222222222222222222222222222222222222 +0247 2222222222222222222222222222222222222222222222222222222 +0248 2222222222222222222222222222222222222222222222222222222 +0249 2222222222222222222222222222222222222222222222222222222 +0250 2222222222222222222222222222222222222222222222222222222 +0251 2222222222222222222222222222222222222222222222222222222 +0252 2222222222222222222222222222222222222222222222222222222 +0253 2222222222222222222222222222222222222222222222222222222 +0254 2222222222222222222222222222222222222222222222222222222 +0255 2222222222222222222222222222222222222222222222222222222 +0256 2222222222222222222222222222222222222222222222222222222 +0257 2222222222222222222222222222222222222222222222222222222 +0258 2222222222222222222222222222222222222222222222222222222 +0259 2222222222222222222222222222222222222222222222222222222 +0260 2222222222222222222222222222222222222222222222222222222 +0261 2222222222222222222222222222222222222222222222222222222 +0262 2222222222222222222222222222222222222222222222222222222 +0263 2222222222222222222222222222222222222222222222222222222 +0264 2222222222222222222222222222222222222222222222222222222 +0265 2222222222222222222222222222222222222222222222222222222 +0266 2222222222222222222222222222222222222222222222222222222 +0267 2222222222222222222222222222222222222222222222222222222 +0268 2222222222222222222222222222222222222222222222222222222 +0269 2222222222222222222222222222222222222222222222222222222 +0270 2222222222222222222222222222222222222222222222222222222 +0271 2222222222222222222222222222222222222222222222222222222 +0272 2222222222222222222222222222222222222222222222222222222 +0273 2222222222222222222222222222222222222222222222222222222 +0274 2222222222222222222222222222222222222222222222222222222 +0275 2222222222222222222222222222222222222222222222222222222 +0276 2222222222222222222222222222222222222222222222222222222 +0277 2222222222222222222222222222222222222222222222222222222 +0278 2222222222222222222222222222222222222222222222222222222 +0279 2222222222222222222222222222222222222222222222222222222 +0280 2222222222222222222222222222222222222222222222222222222 +0281 2222222222222222222222222222222222222222222222222222222 +0282 2222222222222222222222222222222222222222222222222222222 +0283 2222222222222222222222222222222222222222222222222222222 +0284 2222222222222222222222222222222222222222222222222222222 +0285 2222222222222222222222222222222222222222222222222222222 +0286 2222222222222222222222222222222222222222222222222222222 +0287 2222222222222222222222222222222222222222222222222222222 +0288 2222222222222222222222222222222222222222222222222222222 +0289 2222222222222222222222222222222222222222222222222222222 +0290 2222222222222222222222222222222222222222222222222222222 +0291 2222222222222222222222222222222222222222222222222222222 +0292 2222222222222222222222222222222222222222222222222222222 +0293 2222222222222222222222222222222222222222222222222222222 +0294 2222222222222222222222222222222222222222222222222222222 +0295 2222222222222222222222222222222222222222222222222222222 +0296 2222222222222222222222222222222222222222222222222222222 +0297 2222222222222222222222222222222222222222222222222222222 +0298 2222222222222222222222222222222222222222222222222222222 +0299 2222222222222222222222222222222222222222222222222222222 +0300 2222222222222222222222222222222222222222222222222222222 +0301 2222222222222222222222222222222222222222222222222222222 +0302 2222222222222222222222222222222222222222222222222222222 +0303 2222222222222222222222222222222222222222222222222222222 +0304 2222222222222222222222222222222222222222222222222222222 +0305 2222222222222222222222222222222222222222222222222222222 +0306 2222222222222222222222222222222222222222222222222222222 +0307 2222222222222222222222222222222222222222222222222222222 +0308 2222222222222222222222222222222222222222222222222222222 +0309 2222222222222222222222222222222222222222222222222222222 +0310 2222222222222222222222222222222222222222222222222222222 +0311 2222222222222222222222222222222222222222222222222222222 +0312 2222222222222222222222222222222222222222222222222222222 +0313 2222222222222222222222222222222222222222222222222222222 +0314 2222222222222222222222222222222222222222222222222222222 +0315 2222222222222222222222222222222222222222222222222222222 +0316 2222222222222222222222222222222222222222222222222222222 +0317 2222222222222222222222222222222222222222222222222222222 +0318 2222222222222222222222222222222222222222222222222222222 +0319 2222222222222222222222222222222222222222222222222222222 +0320 2222222222222222222222222222222222222222222222222222222 +0321 2222222222222222222222222222222222222222222222222222222 +0322 2222222222222222222222222222222222222222222222222222222 +0323 2222222222222222222222222222222222222222222222222222222 +0324 2222222222222222222222222222222222222222222222222222222 +0325 2222222222222222222222222222222222222222222222222222222 +0326 2222222222222222222222222222222222222222222222222222222 +0327 2222222222222222222222222222222222222222222222222222222 +0328 2222222222222222222222222222222222222222222222222222222 +0329 2222222222222222222222222222222222222222222222222222222 +0330 2222222222222222222222222222222222222222222222222222222 +0331 2222222222222222222222222222222222222222222222222222222 +0332 2222222222222222222222222222222222222222222222222222222 +0333 2222222222222222222222222222222222222222222222222222222 +0334 2222222222222222222222222222222222222222222222222222222 +0335 2222222222222222222222222222222222222222222222222222222 +0336 2222222222222222222222222222222222222222222222222222222 +0337 2222222222222222222222222222222222222222222222222222222 +0338 2222222222222222222222222222222222222222222222222222222 +0339 2222222222222222222222222222222222222222222222222222222 +0340 2222222222222222222222222222222222222222222222222222222 +0341 2222222222222222222222222222222222222222222222222222222 +0342 2222222222222222222222222222222222222222222222222222222 +0343 2222222222222222222222222222222222222222222222222222222 +0344 2222222222222222222222222222222222222222222222222222222 +0345 2222222222222222222222222222222222222222222222222222222 +0346 2222222222222222222222222222222222222222222222222222222 +0347 2222222222222222222222222222222222222222222222222222222 +0348 2222222222222222222222222222222222222222222222222222222 +0349 2222222222222222222222222222222222222222222222222222222 +0350 2222222222222222222222222222222222222222222222222222222 +0351 2222222222222222222222222222222222222222222222222222222 +0352 2222222222222222222222222222222222222222222222222222222 +0353 2222222222222222222222222222222222222222222222222222222 +0354 2222222222222222222222222222222222222222222222222222222 +0355 2222222222222222222222222222222222222222222222222222222 +0356 2222222222222222222222222222222222222222222222222222222 +0357 2222222222222222222222222222222222222222222222222222222 +0358 2222222222222222222222222222222222222222222222222222222 +0359 2222222222222222222222222222222222222222222222222222222 +0360 2222222222222222222222222222222222222222222222222222222 +0361 2222222222222222222222222222222222222222222222222222222 +0362 2222222222222222222222222222222222222222222222222222222 +0363 2222222222222222222222222222222222222222222222222222222 +0364 2222222222222222222222222222222222222222222222222222222 +0365 2222222222222222222222222222222222222222222222222222222 +0366 2222222222222222222222222222222222222222222222222222222 +0367 2222222222222222222222222222222222222222222222222222222 +0368 2222222222222222222222222222222222222222222222222222222 +0369 2222222222222222222222222222222222222222222222222222222 +0370 2222222222222222222222222222222222222222222222222222222 +0371 2222222222222222222222222222222222222222222222222222222 +0372 2222222222222222222222222222222222222222222222222222222 +0373 2222222222222222222222222222222222222222222222222222222 +0374 2222222222222222222222222222222222222222222222222222222 +0375 2222222222222222222222222222222222222222222222222222222 +0376 2222222222222222222222222222222222222222222222222222222 +0377 2222222222222222222222222222222222222222222222222222222 +0378 2222222222222222222222222222222222222222222222222222222 +0379 2222222222222222222222222222222222222222222222222222222 +0380 2222222222222222222222222222222222222222222222222222222 +0381 2222222222222222222222222222222222222222222222222222222 +0382 2222222222222222222222222222222222222222222222222222222 +0383 2222222222222222222222222222222222222222222222222222222 +0384 2222222222222222222222222222222222222222222222222222222 +0385 2222222222222222222222222222222222222222222222222222222 +0386 2222222222222222222222222222222222222222222222222222222 +0387 2222222222222222222222222222222222222222222222222222222 +0388 2222222222222222222222222222222222222222222222222222222 +0389 2222222222222222222222222222222222222222222222222222222 +0390 2222222222222222222222222222222222222222222222222222222 +0391 2222222222222222222222222222222222222222222222222222222 +0392 2222222222222222222222222222222222222222222222222222222 +0393 2222222222222222222222222222222222222222222222222222222 +0394 2222222222222222222222222222222222222222222222222222222 +0395 2222222222222222222222222222222222222222222222222222222 +0396 2222222222222222222222222222222222222222222222222222222 +0397 2222222222222222222222222222222222222222222222222222222 +0398 2222222222222222222222222222222222222222222222222222222 +0399 2222222222222222222222222222222222222222222222222222222 +0400 2222222222222222222222222222222222222222222222222222222 +0401 2222222222222222222222222222222222222222222222222222222 +0402 2222222222222222222222222222222222222222222222222222222 +0403 2222222222222222222222222222222222222222222222222222222 +0404 2222222222222222222222222222222222222222222222222222222 +0405 2222222222222222222222222222222222222222222222222222222 +0406 2222222222222222222222222222222222222222222222222222222 +0407 2222222222222222222222222222222222222222222222222222222 +0408 2222222222222222222222222222222222222222222222222222222 +0409 2222222222222222222222222222222222222222222222222222222 +0410 2222222222222222222222222222222222222222222222222222222 +0411 2222222222222222222222222222222222222222222222222222222 +0412 2222222222222222222222222222222222222222222222222222222 +0413 2222222222222222222222222222222222222222222222222222222 +0414 2222222222222222222222222222222222222222222222222222222 +0415 2222222222222222222222222222222222222222222222222222222 +0416 2222222222222222222222222222222222222222222222222222222 +0417 2222222222222222222222222222222222222222222222222222222 +0418 2222222222222222222222222222222222222222222222222222222 +0419 2222222222222222222222222222222222222222222222222222222 +0420 2222222222222222222222222222222222222222222222222222222 +0421 2222222222222222222222222222222222222222222222222222222 +0422 2222222222222222222222222222222222222222222222222222222 +0423 2222222222222222222222222222222222222222222222222222222 +0424 2222222222222222222222222222222222222222222222222222222 +0425 2222222222222222222222222222222222222222222222222222222 +0426 2222222222222222222222222222222222222222222222222222222 +0427 2222222222222222222222222222222222222222222222222222222 +0428 2222222222222222222222222222222222222222222222222222222 +0429 2222222222222222222222222222222222222222222222222222222 +0430 2222222222222222222222222222222222222222222222222222222 +0431 2222222222222222222222222222222222222222222222222222222 +0432 2222222222222222222222222222222222222222222222222222222 +0433 2222222222222222222222222222222222222222222222222222222 +0434 2222222222222222222222222222222222222222222222222222222 +0435 2222222222222222222222222222222222222222222222222222222 +0436 2222222222222222222222222222222222222222222222222222222 +0437 2222222222222222222222222222222222222222222222222222222 +0438 2222222222222222222222222222222222222222222222222222222 +0439 2222222222222222222222222222222222222222222222222222222 +0440 2222222222222222222222222222222222222222222222222222222 +0441 2222222222222222222222222222222222222222222222222222222 +0442 2222222222222222222222222222222222222222222222222222222 +0443 2222222222222222222222222222222222222222222222222222222 +0444 2222222222222222222222222222222222222222222222222222222 +0445 2222222222222222222222222222222222222222222222222222222 +0446 2222222222222222222222222222222222222222222222222222222 +0447 2222222222222222222222222222222222222222222222222222222 +0448 2222222222222222222222222222222222222222222222222222222 +0449 2222222222222222222222222222222222222222222222222222222 +0450 2222222222222222222222222222222222222222222222222222222 +0451 2222222222222222222222222222222222222222222222222222222 +0452 2222222222222222222222222222222222222222222222222222222 +0453 2222222222222222222222222222222222222222222222222222222 +0454 2222222222222222222222222222222222222222222222222222222 +0455 2222222222222222222222222222222222222222222222222222222 +0456 2222222222222222222222222222222222222222222222222222222 +0457 2222222222222222222222222222222222222222222222222222222 +0458 2222222222222222222222222222222222222222222222222222222 +0459 2222222222222222222222222222222222222222222222222222222 +0460 2222222222222222222222222222222222222222222222222222222 +0461 2222222222222222222222222222222222222222222222222222222 +0462 2222222222222222222222222222222222222222222222222222222 +0463 2222222222222222222222222222222222222222222222222222222 +0464 2222222222222222222222222222222222222222222222222222222 +0465 2222222222222222222222222222222222222222222222222222222 +0466 2222222222222222222222222222222222222222222222222222222 +0467 2222222222222222222222222222222222222222222222222222222 +0468 2222222222222222222222222222222222222222222222222222222 +0469 2222222222222222222222222222222222222222222222222222222 +0470 2222222222222222222222222222222222222222222222222222222 +0471 2222222222222222222222222222222222222222222222222222222 +0472 2222222222222222222222222222222222222222222222222222222 +0473 2222222222222222222222222222222222222222222222222222222 +0474 2222222222222222222222222222222222222222222222222222222 +0475 2222222222222222222222222222222222222222222222222222222 +0476 2222222222222222222222222222222222222222222222222222222 +0477 2222222222222222222222222222222222222222222222222222222 +0478 2222222222222222222222222222222222222222222222222222222 +0479 2222222222222222222222222222222222222222222222222222222 +0480 2222222222222222222222222222222222222222222222222222222 +0481 2222222222222222222222222222222222222222222222222222222 +0482 2222222222222222222222222222222222222222222222222222222 +0483 2222222222222222222222222222222222222222222222222222222 +0484 2222222222222222222222222222222222222222222222222222222 +0485 2222222222222222222222222222222222222222222222222222222 +0486 2222222222222222222222222222222222222222222222222222222 +0487 2222222222222222222222222222222222222222222222222222222 +0488 2222222222222222222222222222222222222222222222222222222 +0489 2222222222222222222222222222222222222222222222222222222 +0490 2222222222222222222222222222222222222222222222222222222 +0491 2222222222222222222222222222222222222222222222222222222 +0492 2222222222222222222222222222222222222222222222222222222 +0493 2222222222222222222222222222222222222222222222222222222 +0494 2222222222222222222222222222222222222222222222222222222 +0495 2222222222222222222222222222222222222222222222222222222 +0496 2222222222222222222222222222222222222222222222222222222 +0497 2222222222222222222222222222222222222222222222222222222 +0498 2222222222222222222222222222222222222222222222222222222 +0499 2222222222222222222222222222222222222222222222222222222 +0500 2222222222222222222222222222222222222222222222222222222 +0501 2222222222222222222222222222222222222222222222222222222 +0502 2222222222222222222222222222222222222222222222222222222 +0503 2222222222222222222222222222222222222222222222222222222 +0504 2222222222222222222222222222222222222222222222222222222 +0505 2222222222222222222222222222222222222222222222222222222 +0506 2222222222222222222222222222222222222222222222222222222 +0507 2222222222222222222222222222222222222222222222222222222 +0508 2222222222222222222222222222222222222222222222222222222 +0509 2222222222222222222222222222222222222222222222222222222 +0510 2222222222222222222222222222222222222222222222222222222 +0511 2222222222222222222222222222222222222222222222222222222 +0512 2222222222222222222222222222222222222222222222222222222 +0513 2222222222222222222222222222222222222222222222222222222 +0514 2222222222222222222222222222222222222222222222222222222 +0515 2222222222222222222222222222222222222222222222222222222 +0516 2222222222222222222222222222222222222222222222222222222 +0517 2222222222222222222222222222222222222222222222222222222 +0518 2222222222222222222222222222222222222222222222222222222 +0519 2222222222222222222222222222222222222222222222222222222 +0520 2222222222222222222222222222222222222222222222222222222 +0521 2222222222222222222222222222222222222222222222222222222 +0522 2222222222222222222222222222222222222222222222222222222 +0523 2222222222222222222222222222222222222222222222222222222 +0524 2222222222222222222222222222222222222222222222222222222 +0525 2222222222222222222222222222222222222222222222222222222 +0526 2222222222222222222222222222222222222222222222222222222 +0527 2222222222222222222222222222222222222222222222222222222 +0528 2222222222222222222222222222222222222222222222222222222 +0529 2222222222222222222222222222222222222222222222222222222 +0530 2222222222222222222222222222222222222222222222222222222 +0531 2222222222222222222222222222222222222222222222222222222 +0532 2222222222222222222222222222222222222222222222222222222 +0533 2222222222222222222222222222222222222222222222222222222 +0534 2222222222222222222222222222222222222222222222222222222 +0535 2222222222222222222222222222222222222222222222222222222 +0536 2222222222222222222222222222222222222222222222222222222 +0537 2222222222222222222222222222222222222222222222222222222 +0538 2222222222222222222222222222222222222222222222222222222 +0539 2222222222222222222222222222222222222222222222222222222 +0540 2222222222222222222222222222222222222222222222222222222 +0541 2222222222222222222222222222222222222222222222222222222 +0542 2222222222222222222222222222222222222222222222222222222 +0543 2222222222222222222222222222222222222222222222222222222 +0544 2222222222222222222222222222222222222222222222222222222 +0545 2222222222222222222222222222222222222222222222222222222 +0546 2222222222222222222222222222222222222222222222222222222 +0547 2222222222222222222222222222222222222222222222222222222 +0548 2222222222222222222222222222222222222222222222222222222 +0549 2222222222222222222222222222222222222222222222222222222 +0550 2222222222222222222222222222222222222222222222222222222 +0551 2222222222222222222222222222222222222222222222222222222 +0552 2222222222222222222222222222222222222222222222222222222 +0553 2222222222222222222222222222222222222222222222222222222 +0554 2222222222222222222222222222222222222222222222222222222 +0555 2222222222222222222222222222222222222222222222222222222 +0556 2222222222222222222222222222222222222222222222222222222 +0557 2222222222222222222222222222222222222222222222222222222 +0558 2222222222222222222222222222222222222222222222222222222 +0559 2222222222222222222222222222222222222222222222222222222 +0560 2222222222222222222222222222222222222222222222222222222 +0561 2222222222222222222222222222222222222222222222222222222 +0562 2222222222222222222222222222222222222222222222222222222 +0563 2222222222222222222222222222222222222222222222222222222 +0564 2222222222222222222222222222222222222222222222222222222 +0565 2222222222222222222222222222222222222222222222222222222 +0566 2222222222222222222222222222222222222222222222222222222 +0567 2222222222222222222222222222222222222222222222222222222 +0568 2222222222222222222222222222222222222222222222222222222 +0569 2222222222222222222222222222222222222222222222222222222 +0570 2222222222222222222222222222222222222222222222222222222 +0571 2222222222222222222222222222222222222222222222222222222 +0572 2222222222222222222222222222222222222222222222222222222 +0573 2222222222222222222222222222222222222222222222222222222 +0574 2222222222222222222222222222222222222222222222222222222 +0575 2222222222222222222222222222222222222222222222222222222 +0576 2222222222222222222222222222222222222222222222222222222 +0577 2222222222222222222222222222222222222222222222222222222 +0578 2222222222222222222222222222222222222222222222222222222 +0579 2222222222222222222222222222222222222222222222222222222 +0580 2222222222222222222222222222222222222222222222222222222 +0581 2222222222222222222222222222222222222222222222222222222 +0582 2222222222222222222222222222222222222222222222222222222 +0583 2222222222222222222222222222222222222222222222222222222 +0584 2222222222222222222222222222222222222222222222222222222 +0585 2222222222222222222222222222222222222222222222222222222 +0586 2222222222222222222222222222222222222222222222222222222 +0587 2222222222222222222222222222222222222222222222222222222 +0588 2222222222222222222222222222222222222222222222222222222 +0589 2222222222222222222222222222222222222222222222222222222 +0590 2222222222222222222222222222222222222222222222222222222 +0591 2222222222222222222222222222222222222222222222222222222 +0592 2222222222222222222222222222222222222222222222222222222 +0593 2222222222222222222222222222222222222222222222222222222 +0594 2222222222222222222222222222222222222222222222222222222 +0595 2222222222222222222222222222222222222222222222222222222 +0596 2222222222222222222222222222222222222222222222222222222 +0597 2222222222222222222222222222222222222222222222222222222 +0598 2222222222222222222222222222222222222222222222222222222 +0599 2222222222222222222222222222222222222222222222222222222 +0600 2222222222222222222222222222222222222222222222222222222 +0601 2222222222222222222222222222222222222222222222222222222 +0602 2222222222222222222222222222222222222222222222222222222 +0603 2222222222222222222222222222222222222222222222222222222 +0604 2222222222222222222222222222222222222222222222222222222 +0605 2222222222222222222222222222222222222222222222222222222 +0606 2222222222222222222222222222222222222222222222222222222 +0607 2222222222222222222222222222222222222222222222222222222 +0608 2222222222222222222222222222222222222222222222222222222 +0609 2222222222222222222222222222222222222222222222222222222 +0610 2222222222222222222222222222222222222222222222222222222 +0611 2222222222222222222222222222222222222222222222222222222 +0612 2222222222222222222222222222222222222222222222222222222 +0613 2222222222222222222222222222222222222222222222222222222 +0614 2222222222222222222222222222222222222222222222222222222 +0615 2222222222222222222222222222222222222222222222222222222 +0616 2222222222222222222222222222222222222222222222222222222 +0617 2222222222222222222222222222222222222222222222222222222 +0618 2222222222222222222222222222222222222222222222222222222 +0619 2222222222222222222222222222222222222222222222222222222 +0620 2222222222222222222222222222222222222222222222222222222 +0621 2222222222222222222222222222222222222222222222222222222 +0622 2222222222222222222222222222222222222222222222222222222 +0623 2222222222222222222222222222222222222222222222222222222 +0624 2222222222222222222222222222222222222222222222222222222 +0625 2222222222222222222222222222222222222222222222222222222 +0626 2222222222222222222222222222222222222222222222222222222 +0627 2222222222222222222222222222222222222222222222222222222 +0628 2222222222222222222222222222222222222222222222222222222 +0629 2222222222222222222222222222222222222222222222222222222 +0630 2222222222222222222222222222222222222222222222222222222 +0631 2222222222222222222222222222222222222222222222222222222 +0632 2222222222222222222222222222222222222222222222222222222 +0633 2222222222222222222222222222222222222222222222222222222 +0634 2222222222222222222222222222222222222222222222222222222 +0635 2222222222222222222222222222222222222222222222222222222 +0636 2222222222222222222222222222222222222222222222222222222 +0637 2222222222222222222222222222222222222222222222222222222 +0638 2222222222222222222222222222222222222222222222222222222 +0639 2222222222222222222222222222222222222222222222222222222 +0640 2222222222222222222222222222222222222222222222222222222 +0641 2222222222222222222222222222222222222222222222222222222 +0642 2222222222222222222222222222222222222222222222222222222 +0643 2222222222222222222222222222222222222222222222222222222 +0644 2222222222222222222222222222222222222222222222222222222 +0645 2222222222222222222222222222222222222222222222222222222 +0646 2222222222222222222222222222222222222222222222222222222 +0647 2222222222222222222222222222222222222222222222222222222 +0648 2222222222222222222222222222222222222222222222222222222 +0649 2222222222222222222222222222222222222222222222222222222 +0650 2222222222222222222222222222222222222222222222222222222 +0651 2222222222222222222222222222222222222222222222222222222 +0652 2222222222222222222222222222222222222222222222222222222 +0653 2222222222222222222222222222222222222222222222222222222 +0654 2222222222222222222222222222222222222222222222222222222 +0655 2222222222222222222222222222222222222222222222222222222 +0656 2222222222222222222222222222222222222222222222222222222 +0657 2222222222222222222222222222222222222222222222222222222 +0658 2222222222222222222222222222222222222222222222222222222 +0659 2222222222222222222222222222222222222222222222222222222 +0660 2222222222222222222222222222222222222222222222222222222 +0661 2222222222222222222222222222222222222222222222222222222 +0662 2222222222222222222222222222222222222222222222222222222 +0663 2222222222222222222222222222222222222222222222222222222 +0664 2222222222222222222222222222222222222222222222222222222 +0665 2222222222222222222222222222222222222222222222222222222 +0666 2222222222222222222222222222222222222222222222222222222 +0667 2222222222222222222222222222222222222222222222222222222 +0668 2222222222222222222222222222222222222222222222222222222 +0669 2222222222222222222222222222222222222222222222222222222 +0670 2222222222222222222222222222222222222222222222222222222 +0671 2222222222222222222222222222222222222222222222222222222 +0672 2222222222222222222222222222222222222222222222222222222 +0673 2222222222222222222222222222222222222222222222222222222 +0674 2222222222222222222222222222222222222222222222222222222 +0675 2222222222222222222222222222222222222222222222222222222 +0676 2222222222222222222222222222222222222222222222222222222 +0677 2222222222222222222222222222222222222222222222222222222 +0678 2222222222222222222222222222222222222222222222222222222 +0679 2222222222222222222222222222222222222222222222222222222 +0680 2222222222222222222222222222222222222222222222222222222 +0681 2222222222222222222222222222222222222222222222222222222 +0682 2222222222222222222222222222222222222222222222222222222 +0683 2222222222222222222222222222222222222222222222222222222 +0684 2222222222222222222222222222222222222222222222222222222 +0685 2222222222222222222222222222222222222222222222222222222 +0686 2222222222222222222222222222222222222222222222222222222 +0687 2222222222222222222222222222222222222222222222222222222 +0688 2222222222222222222222222222222222222222222222222222222 +0689 2222222222222222222222222222222222222222222222222222222 +0690 2222222222222222222222222222222222222222222222222222222 +0691 2222222222222222222222222222222222222222222222222222222 +0692 2222222222222222222222222222222222222222222222222222222 +0693 2222222222222222222222222222222222222222222222222222222 +0694 2222222222222222222222222222222222222222222222222222222 +0695 2222222222222222222222222222222222222222222222222222222 +0696 2222222222222222222222222222222222222222222222222222222 +0697 2222222222222222222222222222222222222222222222222222222 +0698 2222222222222222222222222222222222222222222222222222222 +0699 2222222222222222222222222222222222222222222222222222222 +0700 2222222222222222222222222222222222222222222222222222222 +0701 2222222222222222222222222222222222222222222222222222222 +0702 2222222222222222222222222222222222222222222222222222222 +0703 2222222222222222222222222222222222222222222222222222222 +0704 2222222222222222222222222222222222222222222222222222222 +0705 2222222222222222222222222222222222222222222222222222222 +0706 2222222222222222222222222222222222222222222222222222222 +0707 2222222222222222222222222222222222222222222222222222222 +0708 2222222222222222222222222222222222222222222222222222222 +0709 2222222222222222222222222222222222222222222222222222222 +0710 2222222222222222222222222222222222222222222222222222222 +0711 2222222222222222222222222222222222222222222222222222222 +0712 2222222222222222222222222222222222222222222222222222222 +0713 2222222222222222222222222222222222222222222222222222222 +0714 2222222222222222222222222222222222222222222222222222222 +0715 2222222222222222222222222222222222222222222222222222222 +0716 2222222222222222222222222222222222222222222222222222222 +0717 2222222222222222222222222222222222222222222222222222222 +0718 2222222222222222222222222222222222222222222222222222222 +0719 2222222222222222222222222222222222222222222222222222222 +0720 2222222222222222222222222222222222222222222222222222222 +0721 2222222222222222222222222222222222222222222222222222222 +0722 2222222222222222222222222222222222222222222222222222222 +0723 2222222222222222222222222222222222222222222222222222222 +0724 2222222222222222222222222222222222222222222222222222222 +0725 2222222222222222222222222222222222222222222222222222222 +0726 2222222222222222222222222222222222222222222222222222222 +0727 2222222222222222222222222222222222222222222222222222222 +0728 2222222222222222222222222222222222222222222222222222222 +0729 2222222222222222222222222222222222222222222222222222222 +0730 2222222222222222222222222222222222222222222222222222222 +0731 2222222222222222222222222222222222222222222222222222222 +0732 2222222222222222222222222222222222222222222222222222222 +0733 2222222222222222222222222222222222222222222222222222222 +0734 2222222222222222222222222222222222222222222222222222222 +0735 2222222222222222222222222222222222222222222222222222222 +0736 2222222222222222222222222222222222222222222222222222222 +0737 2222222222222222222222222222222222222222222222222222222 +0738 2222222222222222222222222222222222222222222222222222222 +0739 2222222222222222222222222222222222222222222222222222222 +0740 2222222222222222222222222222222222222222222222222222222 +0741 2222222222222222222222222222222222222222222222222222222 +0742 2222222222222222222222222222222222222222222222222222222 +0743 2222222222222222222222222222222222222222222222222222222 +0744 2222222222222222222222222222222222222222222222222222222 +0745 2222222222222222222222222222222222222222222222222222222 +0746 2222222222222222222222222222222222222222222222222222222 +0747 2222222222222222222222222222222222222222222222222222222 +0748 2222222222222222222222222222222222222222222222222222222 +0749 2222222222222222222222222222222222222222222222222222222 +0750 2222222222222222222222222222222222222222222222222222222 +0751 2222222222222222222222222222222222222222222222222222222 +0752 2222222222222222222222222222222222222222222222222222222 +0753 2222222222222222222222222222222222222222222222222222222 +0754 2222222222222222222222222222222222222222222222222222222 +0755 2222222222222222222222222222222222222222222222222222222 +0756 2222222222222222222222222222222222222222222222222222222 +0757 2222222222222222222222222222222222222222222222222222222 +0758 2222222222222222222222222222222222222222222222222222222 +0759 2222222222222222222222222222222222222222222222222222222 +0760 2222222222222222222222222222222222222222222222222222222 +0761 2222222222222222222222222222222222222222222222222222222 +0762 2222222222222222222222222222222222222222222222222222222 +0763 2222222222222222222222222222222222222222222222222222222 +0764 2222222222222222222222222222222222222222222222222222222 +0765 2222222222222222222222222222222222222222222222222222222 +0766 2222222222222222222222222222222222222222222222222222222 +0767 2222222222222222222222222222222222222222222222222222222 +0768 2222222222222222222222222222222222222222222222222222222 +0769 2222222222222222222222222222222222222222222222222222222 +0770 2222222222222222222222222222222222222222222222222222222 +0771 2222222222222222222222222222222222222222222222222222222 +0772 2222222222222222222222222222222222222222222222222222222 +0773 2222222222222222222222222222222222222222222222222222222 +0774 2222222222222222222222222222222222222222222222222222222 +0775 2222222222222222222222222222222222222222222222222222222 +0776 2222222222222222222222222222222222222222222222222222222 +0777 2222222222222222222222222222222222222222222222222222222 +0778 2222222222222222222222222222222222222222222222222222222 +0779 2222222222222222222222222222222222222222222222222222222 +0780 2222222222222222222222222222222222222222222222222222222 +0781 2222222222222222222222222222222222222222222222222222222 +0782 2222222222222222222222222222222222222222222222222222222 +0783 2222222222222222222222222222222222222222222222222222222 +0784 2222222222222222222222222222222222222222222222222222222 +0785 2222222222222222222222222222222222222222222222222222222 +0786 2222222222222222222222222222222222222222222222222222222 +0787 2222222222222222222222222222222222222222222222222222222 +0788 2222222222222222222222222222222222222222222222222222222 +0789 2222222222222222222222222222222222222222222222222222222 +0790 2222222222222222222222222222222222222222222222222222222 +0791 2222222222222222222222222222222222222222222222222222222 +0792 2222222222222222222222222222222222222222222222222222222 +0793 2222222222222222222222222222222222222222222222222222222 +0794 2222222222222222222222222222222222222222222222222222222 +0795 2222222222222222222222222222222222222222222222222222222 +0796 2222222222222222222222222222222222222222222222222222222 +0797 2222222222222222222222222222222222222222222222222222222 +0798 2222222222222222222222222222222222222222222222222222222 +0799 2222222222222222222222222222222222222222222222222222222 +0800 2222222222222222222222222222222222222222222222222222222 +0801 2222222222222222222222222222222222222222222222222222222 +0802 2222222222222222222222222222222222222222222222222222222 +0803 2222222222222222222222222222222222222222222222222222222 +0804 2222222222222222222222222222222222222222222222222222222 +0805 2222222222222222222222222222222222222222222222222222222 +0806 2222222222222222222222222222222222222222222222222222222 +0807 2222222222222222222222222222222222222222222222222222222 +0808 2222222222222222222222222222222222222222222222222222222 +0809 2222222222222222222222222222222222222222222222222222222 +0810 2222222222222222222222222222222222222222222222222222222 +0811 2222222222222222222222222222222222222222222222222222222 +0812 2222222222222222222222222222222222222222222222222222222 +0813 2222222222222222222222222222222222222222222222222222222 +0814 2222222222222222222222222222222222222222222222222222222 +0815 2222222222222222222222222222222222222222222222222222222 +0816 2222222222222222222222222222222222222222222222222222222 +0817 2222222222222222222222222222222222222222222222222222222 +0818 2222222222222222222222222222222222222222222222222222222 +0819 2222222222222222222222222222222222222222222222222222222 +0820 2222222222222222222222222222222222222222222222222222222 +0821 2222222222222222222222222222222222222222222222222222222 +0822 2222222222222222222222222222222222222222222222222222222 +0823 2222222222222222222222222222222222222222222222222222222 +0824 2222222222222222222222222222222222222222222222222222222 +0825 2222222222222222222222222222222222222222222222222222222 +0826 2222222222222222222222222222222222222222222222222222222 +0827 2222222222222222222222222222222222222222222222222222222 +0828 2222222222222222222222222222222222222222222222222222222 +0829 2222222222222222222222222222222222222222222222222222222 +0830 2222222222222222222222222222222222222222222222222222222 +0831 2222222222222222222222222222222222222222222222222222222 +0832 2222222222222222222222222222222222222222222222222222222 +0833 2222222222222222222222222222222222222222222222222222222 +0834 2222222222222222222222222222222222222222222222222222222 +0835 2222222222222222222222222222222222222222222222222222222 +0836 2222222222222222222222222222222222222222222222222222222 +0837 2222222222222222222222222222222222222222222222222222222 +0838 2222222222222222222222222222222222222222222222222222222 +0839 2222222222222222222222222222222222222222222222222222222 +0840 2222222222222222222222222222222222222222222222222222222 +0841 2222222222222222222222222222222222222222222222222222222 +0842 2222222222222222222222222222222222222222222222222222222 +0843 2222222222222222222222222222222222222222222222222222222 +0844 2222222222222222222222222222222222222222222222222222222 +0845 2222222222222222222222222222222222222222222222222222222 +0846 2222222222222222222222222222222222222222222222222222222 +0847 2222222222222222222222222222222222222222222222222222222 +0848 2222222222222222222222222222222222222222222222222222222 +0849 2222222222222222222222222222222222222222222222222222222 +0850 2222222222222222222222222222222222222222222222222222222 +0851 2222222222222222222222222222222222222222222222222222222 +0852 2222222222222222222222222222222222222222222222222222222 +0853 2222222222222222222222222222222222222222222222222222222 +0854 2222222222222222222222222222222222222222222222222222222 +0855 2222222222222222222222222222222222222222222222222222222 +0856 2222222222222222222222222222222222222222222222222222222 +0857 2222222222222222222222222222222222222222222222222222222 +0858 2222222222222222222222222222222222222222222222222222222 +0859 2222222222222222222222222222222222222222222222222222222 +0860 2222222222222222222222222222222222222222222222222222222 +0861 2222222222222222222222222222222222222222222222222222222 +0862 2222222222222222222222222222222222222222222222222222222 +0863 2222222222222222222222222222222222222222222222222222222 +0864 2222222222222222222222222222222222222222222222222222222 +0865 2222222222222222222222222222222222222222222222222222222 +0866 2222222222222222222222222222222222222222222222222222222 +0867 2222222222222222222222222222222222222222222222222222222 +0868 2222222222222222222222222222222222222222222222222222222 +0869 2222222222222222222222222222222222222222222222222222222 +0870 2222222222222222222222222222222222222222222222222222222 +0871 2222222222222222222222222222222222222222222222222222222 +0872 2222222222222222222222222222222222222222222222222222222 +0873 2222222222222222222222222222222222222222222222222222222 +0874 2222222222222222222222222222222222222222222222222222222 +0875 2222222222222222222222222222222222222222222222222222222 +0876 2222222222222222222222222222222222222222222222222222222 +0877 2222222222222222222222222222222222222222222222222222222 +0878 2222222222222222222222222222222222222222222222222222222 +0879 2222222222222222222222222222222222222222222222222222222 +0880 2222222222222222222222222222222222222222222222222222222 +0881 2222222222222222222222222222222222222222222222222222222 +0882 2222222222222222222222222222222222222222222222222222222 +0883 2222222222222222222222222222222222222222222222222222222 +0884 2222222222222222222222222222222222222222222222222222222 +0885 2222222222222222222222222222222222222222222222222222222 +0886 2222222222222222222222222222222222222222222222222222222 +0887 2222222222222222222222222222222222222222222222222222222 +0888 2222222222222222222222222222222222222222222222222222222 +0889 2222222222222222222222222222222222222222222222222222222 +0890 2222222222222222222222222222222222222222222222222222222 +0891 2222222222222222222222222222222222222222222222222222222 +0892 2222222222222222222222222222222222222222222222222222222 +0893 2222222222222222222222222222222222222222222222222222222 +0894 2222222222222222222222222222222222222222222222222222222 +0895 2222222222222222222222222222222222222222222222222222222 +0896 2222222222222222222222222222222222222222222222222222222 +0897 2222222222222222222222222222222222222222222222222222222 +0898 2222222222222222222222222222222222222222222222222222222 +0899 2222222222222222222222222222222222222222222222222222222 +0900 2222222222222222222222222222222222222222222222222222222 +0901 2222222222222222222222222222222222222222222222222222222 +0902 2222222222222222222222222222222222222222222222222222222 +0903 2222222222222222222222222222222222222222222222222222222 +0904 2222222222222222222222222222222222222222222222222222222 +0905 2222222222222222222222222222222222222222222222222222222 +0906 2222222222222222222222222222222222222222222222222222222 +0907 2222222222222222222222222222222222222222222222222222222 +0908 2222222222222222222222222222222222222222222222222222222 +0909 2222222222222222222222222222222222222222222222222222222 +0910 2222222222222222222222222222222222222222222222222222222 +0911 2222222222222222222222222222222222222222222222222222222 +0912 2222222222222222222222222222222222222222222222222222222 +0913 2222222222222222222222222222222222222222222222222222222 +0914 2222222222222222222222222222222222222222222222222222222 +0915 2222222222222222222222222222222222222222222222222222222 +0916 2222222222222222222222222222222222222222222222222222222 +0917 2222222222222222222222222222222222222222222222222222222 +0918 2222222222222222222222222222222222222222222222222222222 +0919 2222222222222222222222222222222222222222222222222222222 +0920 2222222222222222222222222222222222222222222222222222222 +0921 2222222222222222222222222222222222222222222222222222222 +0922 2222222222222222222222222222222222222222222222222222222 +0923 2222222222222222222222222222222222222222222222222222222 +0924 2222222222222222222222222222222222222222222222222222222 +0925 2222222222222222222222222222222222222222222222222222222 +0926 2222222222222222222222222222222222222222222222222222222 +0927 2222222222222222222222222222222222222222222222222222222 +0928 2222222222222222222222222222222222222222222222222222222 +0929 2222222222222222222222222222222222222222222222222222222 +0930 2222222222222222222222222222222222222222222222222222222 +0931 2222222222222222222222222222222222222222222222222222222 +0932 2222222222222222222222222222222222222222222222222222222 +0933 2222222222222222222222222222222222222222222222222222222 +0934 2222222222222222222222222222222222222222222222222222222 +0935 2222222222222222222222222222222222222222222222222222222 +0936 2222222222222222222222222222222222222222222222222222222 +0937 2222222222222222222222222222222222222222222222222222222 +0938 2222222222222222222222222222222222222222222222222222222 +0939 2222222222222222222222222222222222222222222222222222222 +0940 2222222222222222222222222222222222222222222222222222222 +0941 2222222222222222222222222222222222222222222222222222222 +0942 2222222222222222222222222222222222222222222222222222222 +0943 2222222222222222222222222222222222222222222222222222222 +0944 2222222222222222222222222222222222222222222222222222222 +0945 2222222222222222222222222222222222222222222222222222222 +0946 2222222222222222222222222222222222222222222222222222222 +0947 2222222222222222222222222222222222222222222222222222222 +0948 2222222222222222222222222222222222222222222222222222222 +0949 2222222222222222222222222222222222222222222222222222222 +0950 2222222222222222222222222222222222222222222222222222222 +0951 2222222222222222222222222222222222222222222222222222222 +0952 2222222222222222222222222222222222222222222222222222222 +0953 2222222222222222222222222222222222222222222222222222222 +0954 2222222222222222222222222222222222222222222222222222222 +0955 2222222222222222222222222222222222222222222222222222222 +0956 2222222222222222222222222222222222222222222222222222222 +0957 2222222222222222222222222222222222222222222222222222222 +0958 2222222222222222222222222222222222222222222222222222222 +0959 2222222222222222222222222222222222222222222222222222222 +0960 2222222222222222222222222222222222222222222222222222222 +0961 2222222222222222222222222222222222222222222222222222222 +0962 2222222222222222222222222222222222222222222222222222222 +0963 2222222222222222222222222222222222222222222222222222222 +0964 2222222222222222222222222222222222222222222222222222222 +0965 2222222222222222222222222222222222222222222222222222222 +0966 2222222222222222222222222222222222222222222222222222222 +0967 2222222222222222222222222222222222222222222222222222222 +0968 2222222222222222222222222222222222222222222222222222222 +0969 2222222222222222222222222222222222222222222222222222222 +0970 2222222222222222222222222222222222222222222222222222222 +0971 2222222222222222222222222222222222222222222222222222222 +0972 2222222222222222222222222222222222222222222222222222222 +0973 2222222222222222222222222222222222222222222222222222222 +0974 2222222222222222222222222222222222222222222222222222222 +0975 2222222222222222222222222222222222222222222222222222222 +0976 2222222222222222222222222222222222222222222222222222222 +0977 2222222222222222222222222222222222222222222222222222222 +0978 2222222222222222222222222222222222222222222222222222222 +0979 2222222222222222222222222222222222222222222222222222222 +0980 2222222222222222222222222222222222222222222222222222222 +0981 2222222222222222222222222222222222222222222222222222222 +0982 2222222222222222222222222222222222222222222222222222222 +0983 2222222222222222222222222222222222222222222222222222222 +0984 2222222222222222222222222222222222222222222222222222222 +0985 2222222222222222222222222222222222222222222222222222222 +0986 2222222222222222222222222222222222222222222222222222222 +0987 2222222222222222222222222222222222222222222222222222222 +0988 2222222222222222222222222222222222222222222222222222222 +0989 2222222222222222222222222222222222222222222222222222222 +0990 2222222222222222222222222222222222222222222222222222222 +0991 2222222222222222222222222222222222222222222222222222222 +0992 2222222222222222222222222222222222222222222222222222222 +0993 2222222222222222222222222222222222222222222222222222222 +0994 2222222222222222222222222222222222222222222222222222222 +0995 2222222222222222222222222222222222222222222222222222222 +0996 2222222222222222222222222222222222222222222222222222222 +0997 2222222222222222222222222222222222222222222222222222222 +0998 2222222222222222222222222222222222222222222222222222222 +0999 2222222222222222222222222222222222222222222222222222222 +1000 2222222222222222222222222222222222222222222222222222222 +1001 2222222222222222222222222222222222222222222222222222222 +1002 2222222222222222222222222222222222222222222222222222222 +1003 2222222222222222222222222222222222222222222222222222222 +1004 2222222222222222222222222222222222222222222222222222222 +1005 2222222222222222222222222222222222222222222222222222222 +1006 2222222222222222222222222222222222222222222222222222222 +1007 2222222222222222222222222222222222222222222222222222222 +1008 2222222222222222222222222222222222222222222222222222222 +1009 2222222222222222222222222222222222222222222222222222222 +1010 2222222222222222222222222222222222222222222222222222222 +1011 2222222222222222222222222222222222222222222222222222222 +1012 2222222222222222222222222222222222222222222222222222222 +1013 2222222222222222222222222222222222222222222222222222222 +1014 2222222222222222222222222222222222222222222222222222222 +1015 2222222222222222222222222222222222222222222222222222222 +1016 2222222222222222222222222222222222222222222222222222222 +1017 2222222222222222222222222222222222222222222222222222222 +1018 2222222222222222222222222222222222222222222222222222222 +1019 2222222222222222222222222222222222222222222222222222222 +1020 2222222222222222222222222222222222222222222222222222222 +1021 2222222222222222222222222222222222222222222222222222222 +1022 2222222222222222222222222222222222222222222222222222222 +1023 2222222222222222222222222222222222222222222222222222222 +1024 2222222222222222222222222222222222222222222222222222222 +1025 2222222222222222222222222222222222222222222222222222222 +1026 2222222222222222222222222222222222222222222222222222222 +1027 2222222222222222222222222222222222222222222222222222222 +1028 2222222222222222222222222222222222222222222222222222222 +1029 2222222222222222222222222222222222222222222222222222222 +1030 2222222222222222222222222222222222222222222222222222222 +1031 2222222222222222222222222222222222222222222222222222222 +1032 2222222222222222222222222222222222222222222222222222222 +1033 2222222222222222222222222222222222222222222222222222222 +1034 2222222222222222222222222222222222222222222222222222222 +1035 2222222222222222222222222222222222222222222222222222222 +1036 2222222222222222222222222222222222222222222222222222222 +1037 2222222222222222222222222222222222222222222222222222222 +1038 2222222222222222222222222222222222222222222222222222222 +1039 2222222222222222222222222222222222222222222222222222222 +1040 2222222222222222222222222222222222222222222222222222222 +1041 2222222222222222222222222222222222222222222222222222222 +1042 2222222222222222222222222222222222222222222222222222222 +1043 2222222222222222222222222222222222222222222222222222222 +1044 2222222222222222222222222222222222222222222222222222222 +1045 2222222222222222222222222222222222222222222222222222222 +1046 2222222222222222222222222222222222222222222222222222222 +1047 2222222222222222222222222222222222222222222222222222222 +1048 2222222222222222222222222222222222222222222222222222222 +1049 2222222222222222222222222222222222222222222222222222222 +1050 2222222222222222222222222222222222222222222222222222222 +1051 2222222222222222222222222222222222222222222222222222222 +1052 2222222222222222222222222222222222222222222222222222222 +1053 2222222222222222222222222222222222222222222222222222222 +1054 2222222222222222222222222222222222222222222222222222222 +1055 2222222222222222222222222222222222222222222222222222222 +1056 2222222222222222222222222222222222222222222222222222222 +1057 2222222222222222222222222222222222222222222222222222222 +1058 2222222222222222222222222222222222222222222222222222222 +1059 2222222222222222222222222222222222222222222222222222222 +1060 2222222222222222222222222222222222222222222222222222222 +1061 2222222222222222222222222222222222222222222222222222222 +1062 2222222222222222222222222222222222222222222222222222222 +1063 2222222222222222222222222222222222222222222222222222222 +1064 2222222222222222222222222222222222222222222222222222222 +1065 2222222222222222222222222222222222222222222222222222222 +1066 2222222222222222222222222222222222222222222222222222222 +1067 2222222222222222222222222222222222222222222222222222222 +1068 2222222222222222222222222222222222222222222222222222222 +1069 2222222222222222222222222222222222222222222222222222222 +1070 2222222222222222222222222222222222222222222222222222222 +1071 2222222222222222222222222222222222222222222222222222222 +1072 2222222222222222222222222222222222222222222222222222222 +1073 2222222222222222222222222222222222222222222222222222222 +1074 2222222222222222222222222222222222222222222222222222222 +1075 2222222222222222222222222222222222222222222222222222222 +1076 2222222222222222222222222222222222222222222222222222222 +1077 2222222222222222222222222222222222222222222222222222222 +1078 2222222222222222222222222222222222222222222222222222222 +1079 2222222222222222222222222222222222222222222222222222222 +1080 2222222222222222222222222222222222222222222222222222222 +1081 2222222222222222222222222222222222222222222222222222222 +1082 2222222222222222222222222222222222222222222222222222222 +1083 2222222222222222222222222222222222222222222222222222222 +1084 2222222222222222222222222222222222222222222222222222222 +1085 2222222222222222222222222222222222222222222222222222222 +1086 2222222222222222222222222222222222222222222222222222222 +1087 2222222222222222222222222222222222222222222222222222222 +1088 2222222222222222222222222222222222222222222222222222222 +1089 2222222222222222222222222222222222222222222222222222222 +1090 2222222222222222222222222222222222222222222222222222222 +1091 2222222222222222222222222222222222222222222222222222222 +1092 2222222222222222222222222222222222222222222222222222222 +1093 2222222222222222222222222222222222222222222222222222222 +1094 2222222222222222222222222222222222222222222222222222222 +1095 2222222222222222222222222222222222222222222222222222222 +1096 2222222222222222222222222222222222222222222222222222222 +1097 2222222222222222222222222222222222222222222222222222222 +1098 2222222222222222222222222222222222222222222222222222222 +1099 2222222222222222222222222222222222222222222222222222222 +1100 2222222222222222222222222222222222222222222222222222222 +1101 2222222222222222222222222222222222222222222222222222222 +1102 2222222222222222222222222222222222222222222222222222222 +1103 2222222222222222222222222222222222222222222222222222222 +1104 2222222222222222222222222222222222222222222222222222222 +1105 2222222222222222222222222222222222222222222222222222222 +1106 2222222222222222222222222222222222222222222222222222222 +1107 2222222222222222222222222222222222222222222222222222222 +1108 2222222222222222222222222222222222222222222222222222222 +1109 2222222222222222222222222222222222222222222222222222222 +1110 2222222222222222222222222222222222222222222222222222222 +1111 2222222222222222222222222222222222222222222222222222222 +1112 2222222222222222222222222222222222222222222222222222222 +1113 2222222222222222222222222222222222222222222222222222222 +1114 2222222222222222222222222222222222222222222222222222222 +1115 2222222222222222222222222222222222222222222222222222222 +1116 2222222222222222222222222222222222222222222222222222222 +1117 2222222222222222222222222222222222222222222222222222222 +1118 2222222222222222222222222222222222222222222222222222222 +1119 2222222222222222222222222222222222222222222222222222222 +1120 2222222222222222222222222222222222222222222222222222222 +1121 2222222222222222222222222222222222222222222222222222222 +1122 2222222222222222222222222222222222222222222222222222222 +1123 2222222222222222222222222222222222222222222222222222222 +1124 2222222222222222222222222222222222222222222222222222222 +1125 2222222222222222222222222222222222222222222222222222222 +1126 2222222222222222222222222222222222222222222222222222222 +1127 2222222222222222222222222222222222222222222222222222222 +1128 2222222222222222222222222222222222222222222222222222222 +1129 2222222222222222222222222222222222222222222222222222222 +1130 2222222222222222222222222222222222222222222222222222222 +1131 2222222222222222222222222222222222222222222222222222222 +1132 2222222222222222222222222222222222222222222222222222222 +1133 2222222222222222222222222222222222222222222222222222222 +1134 2222222222222222222222222222222222222222222222222222222 +1135 2222222222222222222222222222222222222222222222222222222 +1136 2222222222222222222222222222222222222222222222222222222 +1137 2222222222222222222222222222222222222222222222222222222 +1138 2222222222222222222222222222222222222222222222222222222 +1139 2222222222222222222222222222222222222222222222222222222 +1140 2222222222222222222222222222222222222222222222222222222 +1141 2222222222222222222222222222222222222222222222222222222 +1142 2222222222222222222222222222222222222222222222222222222 +1143 2222222222222222222222222222222222222222222222222222222 +1144 2222222222222222222222222222222222222222222222222222222 +1145 2222222222222222222222222222222222222222222222222222222 +1146 2222222222222222222222222222222222222222222222222222222 +1147 2222222222222222222222222222222222222222222222222222222 +1148 2222222222222222222222222222222222222222222222222222222 +1149 2222222222222222222222222222222222222222222222222222222 +1150 2222222222222222222222222222222222222222222222222222222 +1151 2222222222222222222222222222222222222222222222222222222 +1152 2222222222222222222222222222222222222222222222222222222 +1153 2222222222222222222222222222222222222222222222222222222 +1154 2222222222222222222222222222222222222222222222222222222 +1155 2222222222222222222222222222222222222222222222222222222 +1156 2222222222222222222222222222222222222222222222222222222 +1157 2222222222222222222222222222222222222222222222222222222 +1158 2222222222222222222222222222222222222222222222222222222 +1159 2222222222222222222222222222222222222222222222222222222 +1160 2222222222222222222222222222222222222222222222222222222 +1161 2222222222222222222222222222222222222222222222222222222 +1162 2222222222222222222222222222222222222222222222222222222 +1163 2222222222222222222222222222222222222222222222222222222 +1164 2222222222222222222222222222222222222222222222222222222 +1165 2222222222222222222222222222222222222222222222222222222 +1166 2222222222222222222222222222222222222222222222222222222 +1167 2222222222222222222222222222222222222222222222222222222 +1168 2222222222222222222222222222222222222222222222222222222 +1169 2222222222222222222222222222222222222222222222222222222 +1170 2222222222222222222222222222222222222222222222222222222 +1171 2222222222222222222222222222222222222222222222222222222 +1172 2222222222222222222222222222222222222222222222222222222 +1173 2222222222222222222222222222222222222222222222222222222 +1174 2222222222222222222222222222222222222222222222222222222 +1175 2222222222222222222222222222222222222222222222222222222 +1176 2222222222222222222222222222222222222222222222222222222 +1177 2222222222222222222222222222222222222222222222222222222 +1178 2222222222222222222222222222222222222222222222222222222 +1179 2222222222222222222222222222222222222222222222222222222 +1180 2222222222222222222222222222222222222222222222222222222 +1181 2222222222222222222222222222222222222222222222222222222 +1182 2222222222222222222222222222222222222222222222222222222 +1183 2222222222222222222222222222222222222222222222222222222 +1184 2222222222222222222222222222222222222222222222222222222 +1185 2222222222222222222222222222222222222222222222222222222 +1186 2222222222222222222222222222222222222222222222222222222 +1187 2222222222222222222222222222222222222222222222222222222 +1188 2222222222222222222222222222222222222222222222222222222 +1189 2222222222222222222222222222222222222222222222222222222 +1190 2222222222222222222222222222222222222222222222222222222 +1191 2222222222222222222222222222222222222222222222222222222 +1192 2222222222222222222222222222222222222222222222222222222 +1193 2222222222222222222222222222222222222222222222222222222 +1194 2222222222222222222222222222222222222222222222222222222 +1195 2222222222222222222222222222222222222222222222222222222 +1196 2222222222222222222222222222222222222222222222222222222 +1197 2222222222222222222222222222222222222222222222222222222 +1198 2222222222222222222222222222222222222222222222222222222 +1199 2222222222222222222222222222222222222222222222222222222 +1200 2222222222222222222222222222222222222222222222222222222 +1201 2222222222222222222222222222222222222222222222222222222 +1202 2222222222222222222222222222222222222222222222222222222 +1203 2222222222222222222222222222222222222222222222222222222 +1204 2222222222222222222222222222222222222222222222222222222 +1205 2222222222222222222222222222222222222222222222222222222 +1206 2222222222222222222222222222222222222222222222222222222 +1207 2222222222222222222222222222222222222222222222222222222 +1208 2222222222222222222222222222222222222222222222222222222 +1209 2222222222222222222222222222222222222222222222222222222 +1210 2222222222222222222222222222222222222222222222222222222 +1211 2222222222222222222222222222222222222222222222222222222 +1212 2222222222222222222222222222222222222222222222222222222 +1213 2222222222222222222222222222222222222222222222222222222 +1214 2222222222222222222222222222222222222222222222222222222 +1215 2222222222222222222222222222222222222222222222222222222 +1216 2222222222222222222222222222222222222222222222222222222 +1217 2222222222222222222222222222222222222222222222222222222 +1218 2222222222222222222222222222222222222222222222222222222 +1219 2222222222222222222222222222222222222222222222222222222 +1220 2222222222222222222222222222222222222222222222222222222 +1221 2222222222222222222222222222222222222222222222222222222 +1222 2222222222222222222222222222222222222222222222222222222 +1223 2222222222222222222222222222222222222222222222222222222 +1224 2222222222222222222222222222222222222222222222222222222 +1225 2222222222222222222222222222222222222222222222222222222 +1226 2222222222222222222222222222222222222222222222222222222 +1227 2222222222222222222222222222222222222222222222222222222 +1228 2222222222222222222222222222222222222222222222222222222 +1229 2222222222222222222222222222222222222222222222222222222 +1230 2222222222222222222222222222222222222222222222222222222 +1231 2222222222222222222222222222222222222222222222222222222 +1232 2222222222222222222222222222222222222222222222222222222 +1233 2222222222222222222222222222222222222222222222222222222 +1234 2222222222222222222222222222222222222222222222222222222 +1235 2222222222222222222222222222222222222222222222222222222 +1236 2222222222222222222222222222222222222222222222222222222 +1237 2222222222222222222222222222222222222222222222222222222 +1238 2222222222222222222222222222222222222222222222222222222 +1239 2222222222222222222222222222222222222222222222222222222 +1240 2222222222222222222222222222222222222222222222222222222 +1241 2222222222222222222222222222222222222222222222222222222 +1242 2222222222222222222222222222222222222222222222222222222 +1243 2222222222222222222222222222222222222222222222222222222 +1244 2222222222222222222222222222222222222222222222222222222 +1245 2222222222222222222222222222222222222222222222222222222 +1246 2222222222222222222222222222222222222222222222222222222 +1247 2222222222222222222222222222222222222222222222222222222 +1248 2222222222222222222222222222222222222222222222222222222 +1249 2222222222222222222222222222222222222222222222222222222 +1250 2222222222222222222222222222222222222222222222222222222 +1251 2222222222222222222222222222222222222222222222222222222 +1252 2222222222222222222222222222222222222222222222222222222 +1253 2222222222222222222222222222222222222222222222222222222 +1254 2222222222222222222222222222222222222222222222222222222 +1255 2222222222222222222222222222222222222222222222222222222 +1256 2222222222222222222222222222222222222222222222222222222 +1257 2222222222222222222222222222222222222222222222222222222 +1258 2222222222222222222222222222222222222222222222222222222 +1259 2222222222222222222222222222222222222222222222222222222 +1260 2222222222222222222222222222222222222222222222222222222 +1261 2222222222222222222222222222222222222222222222222222222 +1262 2222222222222222222222222222222222222222222222222222222 +1263 2222222222222222222222222222222222222222222222222222222 +1264 2222222222222222222222222222222222222222222222222222222 +1265 2222222222222222222222222222222222222222222222222222222 +1266 2222222222222222222222222222222222222222222222222222222 +1267 2222222222222222222222222222222222222222222222222222222 +1268 2222222222222222222222222222222222222222222222222222222 +1269 2222222222222222222222222222222222222222222222222222222 +1270 2222222222222222222222222222222222222222222222222222222 +1271 2222222222222222222222222222222222222222222222222222222 +1272 2222222222222222222222222222222222222222222222222222222 +1273 2222222222222222222222222222222222222222222222222222222 +1274 2222222222222222222222222222222222222222222222222222222 +1275 2222222222222222222222222222222222222222222222222222222 +1276 2222222222222222222222222222222222222222222222222222222 +1277 2222222222222222222222222222222222222222222222222222222 +1278 2222222222222222222222222222222222222222222222222222222 +1279 2222222222222222222222222222222222222222222222222222222 +1280 2222222222222222222222222222222222222222222222222222222 +1281 2222222222222222222222222222222222222222222222222222222 +1282 2222222222222222222222222222222222222222222222222222222 +1283 2222222222222222222222222222222222222222222222222222222 +1284 2222222222222222222222222222222222222222222222222222222 +1285 2222222222222222222222222222222222222222222222222222222 +1286 2222222222222222222222222222222222222222222222222222222 +1287 2222222222222222222222222222222222222222222222222222222 +1288 2222222222222222222222222222222222222222222222222222222 +1289 2222222222222222222222222222222222222222222222222222222 +1290 2222222222222222222222222222222222222222222222222222222 +1291 2222222222222222222222222222222222222222222222222222222 +1292 2222222222222222222222222222222222222222222222222222222 +1293 2222222222222222222222222222222222222222222222222222222 +1294 2222222222222222222222222222222222222222222222222222222 +1295 2222222222222222222222222222222222222222222222222222222 +1296 2222222222222222222222222222222222222222222222222222222 +1297 2222222222222222222222222222222222222222222222222222222 +1298 2222222222222222222222222222222222222222222222222222222 +1299 2222222222222222222222222222222222222222222222222222222 +1300 2222222222222222222222222222222222222222222222222222222 +1301 2222222222222222222222222222222222222222222222222222222 +1302 2222222222222222222222222222222222222222222222222222222 +1303 2222222222222222222222222222222222222222222222222222222 +1304 2222222222222222222222222222222222222222222222222222222 +1305 2222222222222222222222222222222222222222222222222222222 +1306 2222222222222222222222222222222222222222222222222222222 +1307 2222222222222222222222222222222222222222222222222222222 +1308 2222222222222222222222222222222222222222222222222222222 +1309 2222222222222222222222222222222222222222222222222222222 +1310 2222222222222222222222222222222222222222222222222222222 +1311 2222222222222222222222222222222222222222222222222222222 +1312 2222222222222222222222222222222222222222222222222222222 +1313 2222222222222222222222222222222222222222222222222222222 +1314 2222222222222222222222222222222222222222222222222222222 +1315 2222222222222222222222222222222222222222222222222222222 +1316 2222222222222222222222222222222222222222222222222222222 +1317 2222222222222222222222222222222222222222222222222222222 +1318 2222222222222222222222222222222222222222222222222222222 +1319 2222222222222222222222222222222222222222222222222222222 +1320 2222222222222222222222222222222222222222222222222222222 +1321 2222222222222222222222222222222222222222222222222222222 +1322 2222222222222222222222222222222222222222222222222222222 +1323 2222222222222222222222222222222222222222222222222222222 +1324 2222222222222222222222222222222222222222222222222222222 +1325 2222222222222222222222222222222222222222222222222222222 +1326 2222222222222222222222222222222222222222222222222222222 +1327 2222222222222222222222222222222222222222222222222222222 +1328 2222222222222222222222222222222222222222222222222222222 +1329 2222222222222222222222222222222222222222222222222222222 +1330 2222222222222222222222222222222222222222222222222222222 +1331 2222222222222222222222222222222222222222222222222222222 +1332 2222222222222222222222222222222222222222222222222222222 +1333 2222222222222222222222222222222222222222222222222222222 +1334 2222222222222222222222222222222222222222222222222222222 +1335 2222222222222222222222222222222222222222222222222222222 +1336 2222222222222222222222222222222222222222222222222222222 +1337 2222222222222222222222222222222222222222222222222222222 +1338 2222222222222222222222222222222222222222222222222222222 +1339 2222222222222222222222222222222222222222222222222222222 +1340 2222222222222222222222222222222222222222222222222222222 +1341 2222222222222222222222222222222222222222222222222222222 +1342 2222222222222222222222222222222222222222222222222222222 +1343 2222222222222222222222222222222222222222222222222222222 +1344 2222222222222222222222222222222222222222222222222222222 +1345 2222222222222222222222222222222222222222222222222222222 +1346 2222222222222222222222222222222222222222222222222222222 +1347 2222222222222222222222222222222222222222222222222222222 +1348 2222222222222222222222222222222222222222222222222222222 +1349 2222222222222222222222222222222222222222222222222222222 +1350 2222222222222222222222222222222222222222222222222222222 +1351 2222222222222222222222222222222222222222222222222222222 +1352 2222222222222222222222222222222222222222222222222222222 +1353 2222222222222222222222222222222222222222222222222222222 +1354 2222222222222222222222222222222222222222222222222222222 +1355 2222222222222222222222222222222222222222222222222222222 +1356 2222222222222222222222222222222222222222222222222222222 +1357 2222222222222222222222222222222222222222222222222222222 +1358 2222222222222222222222222222222222222222222222222222222 +1359 2222222222222222222222222222222222222222222222222222222 +1360 2222222222222222222222222222222222222222222222222222222 +1361 2222222222222222222222222222222222222222222222222222222 +1362 2222222222222222222222222222222222222222222222222222222 +1363 2222222222222222222222222222222222222222222222222222222 +1364 2222222222222222222222222222222222222222222222222222222 +1365 2222222222222222222222222222222222222222222222222222222 +1366 2222222222222222222222222222222222222222222222222222222 +1367 2222222222222222222222222222222222222222222222222222222 +1368 2222222222222222222222222222222222222222222222222222222 +1369 2222222222222222222222222222222222222222222222222222222 +1370 2222222222222222222222222222222222222222222222222222222 +1371 2222222222222222222222222222222222222222222222222222222 +1372 2222222222222222222222222222222222222222222222222222222 +1373 2222222222222222222222222222222222222222222222222222222 +1374 2222222222222222222222222222222222222222222222222222222 +1375 2222222222222222222222222222222222222222222222222222222 +1376 2222222222222222222222222222222222222222222222222222222 +1377 2222222222222222222222222222222222222222222222222222222 +1378 2222222222222222222222222222222222222222222222222222222 +1379 2222222222222222222222222222222222222222222222222222222 +1380 2222222222222222222222222222222222222222222222222222222 +1381 2222222222222222222222222222222222222222222222222222222 +1382 2222222222222222222222222222222222222222222222222222222 +1383 2222222222222222222222222222222222222222222222222222222 +1384 2222222222222222222222222222222222222222222222222222222 +1385 2222222222222222222222222222222222222222222222222222222 +1386 2222222222222222222222222222222222222222222222222222222 +1387 2222222222222222222222222222222222222222222222222222222 +1388 2222222222222222222222222222222222222222222222222222222 +1389 2222222222222222222222222222222222222222222222222222222 +1390 2222222222222222222222222222222222222222222222222222222 +1391 2222222222222222222222222222222222222222222222222222222 +1392 2222222222222222222222222222222222222222222222222222222 +1393 2222222222222222222222222222222222222222222222222222222 +1394 2222222222222222222222222222222222222222222222222222222 +1395 2222222222222222222222222222222222222222222222222222222 +1396 2222222222222222222222222222222222222222222222222222222 +1397 2222222222222222222222222222222222222222222222222222222 +1398 2222222222222222222222222222222222222222222222222222222 +1399 2222222222222222222222222222222222222222222222222222222 +1400 2222222222222222222222222222222222222222222222222222222 +1401 2222222222222222222222222222222222222222222222222222222 +1402 2222222222222222222222222222222222222222222222222222222 +1403 2222222222222222222222222222222222222222222222222222222 +1404 2222222222222222222222222222222222222222222222222222222 +1405 2222222222222222222222222222222222222222222222222222222 +1406 2222222222222222222222222222222222222222222222222222222 +1407 2222222222222222222222222222222222222222222222222222222 +1408 2222222222222222222222222222222222222222222222222222222 +1409 2222222222222222222222222222222222222222222222222222222 +1410 2222222222222222222222222222222222222222222222222222222 +1411 2222222222222222222222222222222222222222222222222222222 +1412 2222222222222222222222222222222222222222222222222222222 +1413 2222222222222222222222222222222222222222222222222222222 +1414 2222222222222222222222222222222222222222222222222222222 +1415 2222222222222222222222222222222222222222222222222222222 +1416 2222222222222222222222222222222222222222222222222222222 +1417 2222222222222222222222222222222222222222222222222222222 +1418 2222222222222222222222222222222222222222222222222222222 +1419 2222222222222222222222222222222222222222222222222222222 +1420 2222222222222222222222222222222222222222222222222222222 +1421 2222222222222222222222222222222222222222222222222222222 +1422 2222222222222222222222222222222222222222222222222222222 +1423 2222222222222222222222222222222222222222222222222222222 +1424 2222222222222222222222222222222222222222222222222222222 +1425 2222222222222222222222222222222222222222222222222222222 +1426 2222222222222222222222222222222222222222222222222222222 +1427 2222222222222222222222222222222222222222222222222222222 +1428 2222222222222222222222222222222222222222222222222222222 +1429 2222222222222222222222222222222222222222222222222222222 +1430 2222222222222222222222222222222222222222222222222222222 +1431 2222222222222222222222222222222222222222222222222222222 +1432 2222222222222222222222222222222222222222222222222222222 +1433 2222222222222222222222222222222222222222222222222222222 +1434 2222222222222222222222222222222222222222222222222222222 +1435 2222222222222222222222222222222222222222222222222222222 +1436 2222222222222222222222222222222222222222222222222222222 +1437 2222222222222222222222222222222222222222222222222222222 +1438 2222222222222222222222222222222222222222222222222222222 +1439 2222222222222222222222222222222222222222222222222222222 +1440 2222222222222222222222222222222222222222222222222222222 +1441 2222222222222222222222222222222222222222222222222222222 +1442 2222222222222222222222222222222222222222222222222222222 +1443 2222222222222222222222222222222222222222222222222222222 +1444 2222222222222222222222222222222222222222222222222222222 +1445 2222222222222222222222222222222222222222222222222222222 +1446 2222222222222222222222222222222222222222222222222222222 +1447 2222222222222222222222222222222222222222222222222222222 +1448 2222222222222222222222222222222222222222222222222222222 +1449 2222222222222222222222222222222222222222222222222222222 +1450 2222222222222222222222222222222222222222222222222222222 +1451 2222222222222222222222222222222222222222222222222222222 +1452 2222222222222222222222222222222222222222222222222222222 +1453 2222222222222222222222222222222222222222222222222222222 +1454 2222222222222222222222222222222222222222222222222222222 +1455 2222222222222222222222222222222222222222222222222222222 +1456 2222222222222222222222222222222222222222222222222222222 +1457 2222222222222222222222222222222222222222222222222222222 +1458 2222222222222222222222222222222222222222222222222222222 +1459 2222222222222222222222222222222222222222222222222222222 +1460 2222222222222222222222222222222222222222222222222222222 +1461 2222222222222222222222222222222222222222222222222222222 +1462 2222222222222222222222222222222222222222222222222222222 +1463 2222222222222222222222222222222222222222222222222222222 +1464 2222222222222222222222222222222222222222222222222222222 +1465 2222222222222222222222222222222222222222222222222222222 +1466 2222222222222222222222222222222222222222222222222222222 +1467 2222222222222222222222222222222222222222222222222222222 +1468 2222222222222222222222222222222222222222222222222222222 +1469 2222222222222222222222222222222222222222222222222222222 +1470 2222222222222222222222222222222222222222222222222222222 +1471 2222222222222222222222222222222222222222222222222222222 +1472 2222222222222222222222222222222222222222222222222222222 +1473 2222222222222222222222222222222222222222222222222222222 +1474 2222222222222222222222222222222222222222222222222222222 +1475 2222222222222222222222222222222222222222222222222222222 +1476 2222222222222222222222222222222222222222222222222222222 +1477 2222222222222222222222222222222222222222222222222222222 +1478 2222222222222222222222222222222222222222222222222222222 +1479 2222222222222222222222222222222222222222222222222222222 +1480 2222222222222222222222222222222222222222222222222222222 +1481 2222222222222222222222222222222222222222222222222222222 +1482 2222222222222222222222222222222222222222222222222222222 +1483 2222222222222222222222222222222222222222222222222222222 +1484 2222222222222222222222222222222222222222222222222222222 +1485 2222222222222222222222222222222222222222222222222222222 +1486 2222222222222222222222222222222222222222222222222222222 +1487 2222222222222222222222222222222222222222222222222222222 +1488 2222222222222222222222222222222222222222222222222222222 +1489 2222222222222222222222222222222222222222222222222222222 +1490 2222222222222222222222222222222222222222222222222222222 +1491 2222222222222222222222222222222222222222222222222222222 +1492 2222222222222222222222222222222222222222222222222222222 +1493 2222222222222222222222222222222222222222222222222222222 +1494 2222222222222222222222222222222222222222222222222222222 +1495 2222222222222222222222222222222222222222222222222222222 +1496 2222222222222222222222222222222222222222222222222222222 +1497 2222222222222222222222222222222222222222222222222222222 +1498 2222222222222222222222222222222222222222222222222222222 +1499 2222222222222222222222222222222222222222222222222222222 +1500 2222222222222222222222222222222222222222222222222222222 +1501 2222222222222222222222222222222222222222222222222222222 +1502 2222222222222222222222222222222222222222222222222222222 +1503 2222222222222222222222222222222222222222222222222222222 +1504 2222222222222222222222222222222222222222222222222222222 +1505 2222222222222222222222222222222222222222222222222222222 +1506 2222222222222222222222222222222222222222222222222222222 +1507 2222222222222222222222222222222222222222222222222222222 +1508 2222222222222222222222222222222222222222222222222222222 +1509 2222222222222222222222222222222222222222222222222222222 +1510 2222222222222222222222222222222222222222222222222222222 +1511 2222222222222222222222222222222222222222222222222222222 +1512 2222222222222222222222222222222222222222222222222222222 +1513 2222222222222222222222222222222222222222222222222222222 +1514 2222222222222222222222222222222222222222222222222222222 +1515 2222222222222222222222222222222222222222222222222222222 +1516 2222222222222222222222222222222222222222222222222222222 +1517 2222222222222222222222222222222222222222222222222222222 +1518 2222222222222222222222222222222222222222222222222222222 +1519 2222222222222222222222222222222222222222222222222222222 +1520 2222222222222222222222222222222222222222222222222222222 +1521 2222222222222222222222222222222222222222222222222222222 +1522 2222222222222222222222222222222222222222222222222222222 +1523 2222222222222222222222222222222222222222222222222222222 +1524 2222222222222222222222222222222222222222222222222222222 +1525 2222222222222222222222222222222222222222222222222222222 +1526 2222222222222222222222222222222222222222222222222222222 +1527 2222222222222222222222222222222222222222222222222222222 +1528 2222222222222222222222222222222222222222222222222222222 +1529 2222222222222222222222222222222222222222222222222222222 +1530 2222222222222222222222222222222222222222222222222222222 +1531 2222222222222222222222222222222222222222222222222222222 +1532 2222222222222222222222222222222222222222222222222222222 +1533 2222222222222222222222222222222222222222222222222222222 +1534 2222222222222222222222222222222222222222222222222222222 +1535 2222222222222222222222222222222222222222222222222222222 +1536 2222222222222222222222222222222222222222222222222222222 +1537 2222222222222222222222222222222222222222222222222222222 +1538 2222222222222222222222222222222222222222222222222222222 +1539 2222222222222222222222222222222222222222222222222222222 +1540 2222222222222222222222222222222222222222222222222222222 +1541 2222222222222222222222222222222222222222222222222222222 +1542 2222222222222222222222222222222222222222222222222222222 +1543 2222222222222222222222222222222222222222222222222222222 +1544 2222222222222222222222222222222222222222222222222222222 +1545 2222222222222222222222222222222222222222222222222222222 +1546 2222222222222222222222222222222222222222222222222222222 +1547 2222222222222222222222222222222222222222222222222222222 +1548 2222222222222222222222222222222222222222222222222222222 +1549 2222222222222222222222222222222222222222222222222222222 +1550 2222222222222222222222222222222222222222222222222222222 +1551 2222222222222222222222222222222222222222222222222222222 +1552 2222222222222222222222222222222222222222222222222222222 +1553 2222222222222222222222222222222222222222222222222222222 +1554 2222222222222222222222222222222222222222222222222222222 +1555 2222222222222222222222222222222222222222222222222222222 +1556 2222222222222222222222222222222222222222222222222222222 +1557 2222222222222222222222222222222222222222222222222222222 +1558 2222222222222222222222222222222222222222222222222222222 +1559 2222222222222222222222222222222222222222222222222222222 +1560 2222222222222222222222222222222222222222222222222222222 +1561 2222222222222222222222222222222222222222222222222222222 +1562 2222222222222222222222222222222222222222222222222222222 +1563 2222222222222222222222222222222222222222222222222222222 +1564 2222222222222222222222222222222222222222222222222222222 +1565 2222222222222222222222222222222222222222222222222222222 +1566 2222222222222222222222222222222222222222222222222222222 +1567 2222222222222222222222222222222222222222222222222222222 +1568 2222222222222222222222222222222222222222222222222222222 +1569 2222222222222222222222222222222222222222222222222222222 +1570 2222222222222222222222222222222222222222222222222222222 +1571 2222222222222222222222222222222222222222222222222222222 +1572 2222222222222222222222222222222222222222222222222222222 +1573 2222222222222222222222222222222222222222222222222222222 +1574 2222222222222222222222222222222222222222222222222222222 +1575 2222222222222222222222222222222222222222222222222222222 +1576 2222222222222222222222222222222222222222222222222222222 +1577 2222222222222222222222222222222222222222222222222222222 +1578 2222222222222222222222222222222222222222222222222222222 +1579 2222222222222222222222222222222222222222222222222222222 +1580 2222222222222222222222222222222222222222222222222222222 +1581 2222222222222222222222222222222222222222222222222222222 +1582 2222222222222222222222222222222222222222222222222222222 +1583 2222222222222222222222222222222222222222222222222222222 +1584 2222222222222222222222222222222222222222222222222222222 +1585 2222222222222222222222222222222222222222222222222222222 +1586 2222222222222222222222222222222222222222222222222222222 +1587 2222222222222222222222222222222222222222222222222222222 +1588 2222222222222222222222222222222222222222222222222222222 +1589 2222222222222222222222222222222222222222222222222222222 +1590 2222222222222222222222222222222222222222222222222222222 +1591 2222222222222222222222222222222222222222222222222222222 +1592 2222222222222222222222222222222222222222222222222222222 +1593 2222222222222222222222222222222222222222222222222222222 +1594 2222222222222222222222222222222222222222222222222222222 +1595 2222222222222222222222222222222222222222222222222222222 +1596 2222222222222222222222222222222222222222222222222222222 +1597 2222222222222222222222222222222222222222222222222222222 +1598 2222222222222222222222222222222222222222222222222222222 +1599 2222222222222222222222222222222222222222222222222222222 +1600 2222222222222222222222222222222222222222222222222222222 +1601 2222222222222222222222222222222222222222222222222222222 +1602 2222222222222222222222222222222222222222222222222222222 +1603 2222222222222222222222222222222222222222222222222222222 +1604 2222222222222222222222222222222222222222222222222222222 +1605 2222222222222222222222222222222222222222222222222222222 +1606 2222222222222222222222222222222222222222222222222222222 +1607 2222222222222222222222222222222222222222222222222222222 +1608 2222222222222222222222222222222222222222222222222222222 +1609 2222222222222222222222222222222222222222222222222222222 +1610 2222222222222222222222222222222222222222222222222222222 +1611 2222222222222222222222222222222222222222222222222222222 +1612 2222222222222222222222222222222222222222222222222222222 +1613 2222222222222222222222222222222222222222222222222222222 +1614 2222222222222222222222222222222222222222222222222222222 +1615 2222222222222222222222222222222222222222222222222222222 +1616 2222222222222222222222222222222222222222222222222222222 +1617 2222222222222222222222222222222222222222222222222222222 +1618 2222222222222222222222222222222222222222222222222222222 +1619 2222222222222222222222222222222222222222222222222222222 +1620 2222222222222222222222222222222222222222222222222222222 +1621 2222222222222222222222222222222222222222222222222222222 +1622 2222222222222222222222222222222222222222222222222222222 +1623 2222222222222222222222222222222222222222222222222222222 +1624 2222222222222222222222222222222222222222222222222222222 +1625 2222222222222222222222222222222222222222222222222222222 +1626 2222222222222222222222222222222222222222222222222222222 +1627 2222222222222222222222222222222222222222222222222222222 +1628 2222222222222222222222222222222222222222222222222222222 +1629 2222222222222222222222222222222222222222222222222222222 +1630 2222222222222222222222222222222222222222222222222222222 +1631 2222222222222222222222222222222222222222222222222222222 +1632 2222222222222222222222222222222222222222222222222222222 +1633 2222222222222222222222222222222222222222222222222222222 +1634 2222222222222222222222222222222222222222222222222222222 +1635 2222222222222222222222222222222222222222222222222222222 +1636 2222222222222222222222222222222222222222222222222222222 +1637 2222222222222222222222222222222222222222222222222222222 +1638 2222222222222222222222222222222222222222222222222222222 +1639 2222222222222222222222222222222222222222222222222222222 +1640 2222222222222222222222222222222222222222222222222222222 +1641 2222222222222222222222222222222222222222222222222222222 +1642 2222222222222222222222222222222222222222222222222222222 +1643 2222222222222222222222222222222222222222222222222222222 +1644 2222222222222222222222222222222222222222222222222222222 +1645 2222222222222222222222222222222222222222222222222222222 +1646 2222222222222222222222222222222222222222222222222222222 +1647 2222222222222222222222222222222222222222222222222222222 +1648 2222222222222222222222222222222222222222222222222222222 +1649 2222222222222222222222222222222222222222222222222222222 +1650 2222222222222222222222222222222222222222222222222222222 +1651 2222222222222222222222222222222222222222222222222222222 +1652 2222222222222222222222222222222222222222222222222222222 +1653 2222222222222222222222222222222222222222222222222222222 +1654 2222222222222222222222222222222222222222222222222222222 +1655 2222222222222222222222222222222222222222222222222222222 +1656 2222222222222222222222222222222222222222222222222222222 +1657 2222222222222222222222222222222222222222222222222222222 +1658 2222222222222222222222222222222222222222222222222222222 +1659 2222222222222222222222222222222222222222222222222222222 +1660 2222222222222222222222222222222222222222222222222222222 +1661 2222222222222222222222222222222222222222222222222222222 +1662 2222222222222222222222222222222222222222222222222222222 +1663 2222222222222222222222222222222222222222222222222222222 +1664 2222222222222222222222222222222222222222222222222222222 +1665 2222222222222222222222222222222222222222222222222222222 +1666 2222222222222222222222222222222222222222222222222222222 +1667 2222222222222222222222222222222222222222222222222222222 +1668 2222222222222222222222222222222222222222222222222222222 +1669 2222222222222222222222222222222222222222222222222222222 +1670 2222222222222222222222222222222222222222222222222222222 +1671 2222222222222222222222222222222222222222222222222222222 +1672 2222222222222222222222222222222222222222222222222222222 +1673 2222222222222222222222222222222222222222222222222222222 +1674 2222222222222222222222222222222222222222222222222222222 +1675 2222222222222222222222222222222222222222222222222222222 +1676 2222222222222222222222222222222222222222222222222222222 +1677 2222222222222222222222222222222222222222222222222222222 +1678 2222222222222222222222222222222222222222222222222222222 +1679 2222222222222222222222222222222222222222222222222222222 +1680 2222222222222222222222222222222222222222222222222222222 +1681 2222222222222222222222222222222222222222222222222222222 +1682 2222222222222222222222222222222222222222222222222222222 +1683 2222222222222222222222222222222222222222222222222222222 +1684 2222222222222222222222222222222222222222222222222222222 +1685 2222222222222222222222222222222222222222222222222222222 +1686 2222222222222222222222222222222222222222222222222222222 +1687 2222222222222222222222222222222222222222222222222222222 +1688 2222222222222222222222222222222222222222222222222222222 +1689 2222222222222222222222222222222222222222222222222222222 +1690 2222222222222222222222222222222222222222222222222222222 +1691 2222222222222222222222222222222222222222222222222222222 +1692 2222222222222222222222222222222222222222222222222222222 +1693 2222222222222222222222222222222222222222222222222222222 +1694 2222222222222222222222222222222222222222222222222222222 +1695 2222222222222222222222222222222222222222222222222222222 +1696 2222222222222222222222222222222222222222222222222222222 +1697 2222222222222222222222222222222222222222222222222222222 +1698 2222222222222222222222222222222222222222222222222222222 +1699 2222222222222222222222222222222222222222222222222222222 +1700 2222222222222222222222222222222222222222222222222222222 +1701 2222222222222222222222222222222222222222222222222222222 +1702 2222222222222222222222222222222222222222222222222222222 +1703 2222222222222222222222222222222222222222222222222222222 +1704 2222222222222222222222222222222222222222222222222222222 +1705 2222222222222222222222222222222222222222222222222222222 +1706 2222222222222222222222222222222222222222222222222222222 +1707 2222222222222222222222222222222222222222222222222222222 +1708 2222222222222222222222222222222222222222222222222222222 +1709 2222222222222222222222222222222222222222222222222222222 +1710 2222222222222222222222222222222222222222222222222222222 +1711 2222222222222222222222222222222222222222222222222222222 +1712 2222222222222222222222222222222222222222222222222222222 +1713 2222222222222222222222222222222222222222222222222222222 +1714 2222222222222222222222222222222222222222222222222222222 +1715 2222222222222222222222222222222222222222222222222222222 +1716 2222222222222222222222222222222222222222222222222222222 +1717 2222222222222222222222222222222222222222222222222222222 +1718 2222222222222222222222222222222222222222222222222222222 +1719 2222222222222222222222222222222222222222222222222222222 +1720 2222222222222222222222222222222222222222222222222222222 +1721 2222222222222222222222222222222222222222222222222222222 +1722 2222222222222222222222222222222222222222222222222222222 +1723 2222222222222222222222222222222222222222222222222222222 +1724 2222222222222222222222222222222222222222222222222222222 +1725 2222222222222222222222222222222222222222222222222222222 +1726 2222222222222222222222222222222222222222222222222222222 +1727 2222222222222222222222222222222222222222222222222222222 +1728 2222222222222222222222222222222222222222222222222222222 +1729 2222222222222222222222222222222222222222222222222222222 +1730 2222222222222222222222222222222222222222222222222222222 +1731 2222222222222222222222222222222222222222222222222222222 +1732 2222222222222222222222222222222222222222222222222222222 +1733 2222222222222222222222222222222222222222222222222222222 +1734 2222222222222222222222222222222222222222222222222222222 +1735 2222222222222222222222222222222222222222222222222222222 +1736 2222222222222222222222222222222222222222222222222222222 +1737 2222222222222222222222222222222222222222222222222222222 +1738 2222222222222222222222222222222222222222222222222222222 +1739 2222222222222222222222222222222222222222222222222222222 +1740 2222222222222222222222222222222222222222222222222222222 +1741 2222222222222222222222222222222222222222222222222222222 +1742 2222222222222222222222222222222222222222222222222222222 +1743 2222222222222222222222222222222222222222222222222222222 +1744 2222222222222222222222222222222222222222222222222222222 +1745 2222222222222222222222222222222222222222222222222222222 +1746 2222222222222222222222222222222222222222222222222222222 +1747 2222222222222222222222222222222222222222222222222222222 +1748 2222222222222222222222222222222222222222222222222222222 +1749 2222222222222222222222222222222222222222222222222222222 +1750 2222222222222222222222222222222222222222222222222222222 +1751 2222222222222222222222222222222222222222222222222222222 +1752 2222222222222222222222222222222222222222222222222222222 +1753 2222222222222222222222222222222222222222222222222222222 +1754 2222222222222222222222222222222222222222222222222222222 +1755 2222222222222222222222222222222222222222222222222222222 +1756 2222222222222222222222222222222222222222222222222222222 +1757 2222222222222222222222222222222222222222222222222222222 +1758 2222222222222222222222222222222222222222222222222222222 +1759 2222222222222222222222222222222222222222222222222222222 +1760 2222222222222222222222222222222222222222222222222222222 +1761 2222222222222222222222222222222222222222222222222222222 +1762 2222222222222222222222222222222222222222222222222222222 +1763 2222222222222222222222222222222222222222222222222222222 +1764 2222222222222222222222222222222222222222222222222222222 +1765 2222222222222222222222222222222222222222222222222222222 +1766 2222222222222222222222222222222222222222222222222222222 +1767 2222222222222222222222222222222222222222222222222222222 +1768 2222222222222222222222222222222222222222222222222222222 +1769 2222222222222222222222222222222222222222222222222222222 +1770 2222222222222222222222222222222222222222222222222222222 +1771 2222222222222222222222222222222222222222222222222222222 +1772 2222222222222222222222222222222222222222222222222222222 +1773 2222222222222222222222222222222222222222222222222222222 +1774 2222222222222222222222222222222222222222222222222222222 +1775 2222222222222222222222222222222222222222222222222222222 +1776 2222222222222222222222222222222222222222222222222222222 +1777 2222222222222222222222222222222222222222222222222222222 +1778 2222222222222222222222222222222222222222222222222222222 +1779 2222222222222222222222222222222222222222222222222222222 +1780 2222222222222222222222222222222222222222222222222222222 +1781 2222222222222222222222222222222222222222222222222222222 +1782 2222222222222222222222222222222222222222222222222222222 +1783 2222222222222222222222222222222222222222222222222222222 +1784 2222222222222222222222222222222222222222222222222222222 +1785 2222222222222222222222222222222222222222222222222222222 +1786 2222222222222222222222222222222222222222222222222222222 +1787 2222222222222222222222222222222222222222222222222222222 +1788 2222222222222222222222222222222222222222222222222222222 +1789 2222222222222222222222222222222222222222222222222222222 +1790 2222222222222222222222222222222222222222222222222222222 +1791 2222222222222222222222222222222222222222222222222222222 +1792 2222222222222222222222222222222222222222222222222222222 +1793 2222222222222222222222222222222222222222222222222222222 +1794 2222222222222222222222222222222222222222222222222222222 +1795 2222222222222222222222222222222222222222222222222222222 +1796 2222222222222222222222222222222222222222222222222222222 +1797 2222222222222222222222222222222222222222222222222222222 +1798 2222222222222222222222222222222222222222222222222222222 +1799 2222222222222222222222222222222222222222222222222222222 +1800 2222222222222222222222222222222222222222222222222222222 +1801 2222222222222222222222222222222222222222222222222222222 +1802 2222222222222222222222222222222222222222222222222222222 +1803 2222222222222222222222222222222222222222222222222222222 +1804 2222222222222222222222222222222222222222222222222222222 +1805 2222222222222222222222222222222222222222222222222222222 +1806 2222222222222222222222222222222222222222222222222222222 +1807 2222222222222222222222222222222222222222222222222222222 +1808 2222222222222222222222222222222222222222222222222222222 +1809 2222222222222222222222222222222222222222222222222222222 +1810 2222222222222222222222222222222222222222222222222222222 +1811 2222222222222222222222222222222222222222222222222222222 +1812 2222222222222222222222222222222222222222222222222222222 +1813 2222222222222222222222222222222222222222222222222222222 +1814 2222222222222222222222222222222222222222222222222222222 +1815 2222222222222222222222222222222222222222222222222222222 +1816 2222222222222222222222222222222222222222222222222222222 +1817 2222222222222222222222222222222222222222222222222222222 +1818 2222222222222222222222222222222222222222222222222222222 +1819 2222222222222222222222222222222222222222222222222222222 +1820 2222222222222222222222222222222222222222222222222222222 +1821 2222222222222222222222222222222222222222222222222222222 +1822 2222222222222222222222222222222222222222222222222222222 +1823 2222222222222222222222222222222222222222222222222222222 +1824 2222222222222222222222222222222222222222222222222222222 +1825 2222222222222222222222222222222222222222222222222222222 +1826 2222222222222222222222222222222222222222222222222222222 +1827 2222222222222222222222222222222222222222222222222222222 +1828 2222222222222222222222222222222222222222222222222222222 +1829 2222222222222222222222222222222222222222222222222222222 +1830 2222222222222222222222222222222222222222222222222222222 +1831 2222222222222222222222222222222222222222222222222222222 +1832 2222222222222222222222222222222222222222222222222222222 +1833 2222222222222222222222222222222222222222222222222222222 +1834 2222222222222222222222222222222222222222222222222222222 +1835 2222222222222222222222222222222222222222222222222222222 +1836 2222222222222222222222222222222222222222222222222222222 +1837 2222222222222222222222222222222222222222222222222222222 +1838 2222222222222222222222222222222222222222222222222222222 +1839 2222222222222222222222222222222222222222222222222222222 +1840 2222222222222222222222222222222222222222222222222222222 +1841 2222222222222222222222222222222222222222222222222222222 +1842 2222222222222222222222222222222222222222222222222222222 +1843 2222222222222222222222222222222222222222222222222222222 +1844 2222222222222222222222222222222222222222222222222222222 +1845 2222222222222222222222222222222222222222222222222222222 +1846 2222222222222222222222222222222222222222222222222222222 +1847 2222222222222222222222222222222222222222222222222222222 +1848 2222222222222222222222222222222222222222222222222222222 +1849 2222222222222222222222222222222222222222222222222222222 +1850 2222222222222222222222222222222222222222222222222222222 +1851 2222222222222222222222222222222222222222222222222222222 +1852 2222222222222222222222222222222222222222222222222222222 +1853 2222222222222222222222222222222222222222222222222222222 +1854 2222222222222222222222222222222222222222222222222222222 +1855 2222222222222222222222222222222222222222222222222222222 +1856 2222222222222222222222222222222222222222222222222222222 +1857 2222222222222222222222222222222222222222222222222222222 +1858 2222222222222222222222222222222222222222222222222222222 +1859 2222222222222222222222222222222222222222222222222222222 +1860 2222222222222222222222222222222222222222222222222222222 +1861 2222222222222222222222222222222222222222222222222222222 +1862 2222222222222222222222222222222222222222222222222222222 +1863 2222222222222222222222222222222222222222222222222222222 +1864 2222222222222222222222222222222222222222222222222222222 +1865 2222222222222222222222222222222222222222222222222222222 +1866 2222222222222222222222222222222222222222222222222222222 +1867 2222222222222222222222222222222222222222222222222222222 +1868 2222222222222222222222222222222222222222222222222222222 +1869 2222222222222222222222222222222222222222222222222222222 +1870 2222222222222222222222222222222222222222222222222222222 +1871 2222222222222222222222222222222222222222222222222222222 +1872 2222222222222222222222222222222222222222222222222222222 +1873 2222222222222222222222222222222222222222222222222222222 +1874 2222222222222222222222222222222222222222222222222222222 +1875 2222222222222222222222222222222222222222222222222222222 +1876 2222222222222222222222222222222222222222222222222222222 +1877 2222222222222222222222222222222222222222222222222222222 +1878 2222222222222222222222222222222222222222222222222222222 +1879 2222222222222222222222222222222222222222222222222222222 +1880 2222222222222222222222222222222222222222222222222222222 +1881 2222222222222222222222222222222222222222222222222222222 +1882 2222222222222222222222222222222222222222222222222222222 +1883 2222222222222222222222222222222222222222222222222222222 +1884 2222222222222222222222222222222222222222222222222222222 +1885 2222222222222222222222222222222222222222222222222222222 +1886 2222222222222222222222222222222222222222222222222222222 +1887 2222222222222222222222222222222222222222222222222222222 +1888 2222222222222222222222222222222222222222222222222222222 +1889 2222222222222222222222222222222222222222222222222222222 +1890 2222222222222222222222222222222222222222222222222222222 +1891 2222222222222222222222222222222222222222222222222222222 +1892 2222222222222222222222222222222222222222222222222222222 +1893 2222222222222222222222222222222222222222222222222222222 +1894 2222222222222222222222222222222222222222222222222222222 +1895 2222222222222222222222222222222222222222222222222222222 +1896 2222222222222222222222222222222222222222222222222222222 +1897 2222222222222222222222222222222222222222222222222222222 +1898 2222222222222222222222222222222222222222222222222222222 +1899 2222222222222222222222222222222222222222222222222222222 +1900 2222222222222222222222222222222222222222222222222222222 +1901 2222222222222222222222222222222222222222222222222222222 +1902 2222222222222222222222222222222222222222222222222222222 +1903 2222222222222222222222222222222222222222222222222222222 +1904 2222222222222222222222222222222222222222222222222222222 +1905 2222222222222222222222222222222222222222222222222222222 +1906 2222222222222222222222222222222222222222222222222222222 +1907 2222222222222222222222222222222222222222222222222222222 +1908 2222222222222222222222222222222222222222222222222222222 +1909 2222222222222222222222222222222222222222222222222222222 +1910 2222222222222222222222222222222222222222222222222222222 +1911 2222222222222222222222222222222222222222222222222222222 +1912 2222222222222222222222222222222222222222222222222222222 +1913 2222222222222222222222222222222222222222222222222222222 +1914 2222222222222222222222222222222222222222222222222222222 +1915 2222222222222222222222222222222222222222222222222222222 +1916 2222222222222222222222222222222222222222222222222222222 +1917 2222222222222222222222222222222222222222222222222222222 +1918 2222222222222222222222222222222222222222222222222222222 +1919 2222222222222222222222222222222222222222222222222222222 +1920 2222222222222222222222222222222222222222222222222222222 +1921 2222222222222222222222222222222222222222222222222222222 +1922 2222222222222222222222222222222222222222222222222222222 +1923 2222222222222222222222222222222222222222222222222222222 +1924 2222222222222222222222222222222222222222222222222222222 +1925 2222222222222222222222222222222222222222222222222222222 +1926 2222222222222222222222222222222222222222222222222222222 +1927 2222222222222222222222222222222222222222222222222222222 +1928 2222222222222222222222222222222222222222222222222222222 +1929 2222222222222222222222222222222222222222222222222222222 +1930 2222222222222222222222222222222222222222222222222222222 +1931 2222222222222222222222222222222222222222222222222222222 +1932 2222222222222222222222222222222222222222222222222222222 +1933 2222222222222222222222222222222222222222222222222222222 +1934 2222222222222222222222222222222222222222222222222222222 +1935 2222222222222222222222222222222222222222222222222222222 +1936 2222222222222222222222222222222222222222222222222222222 +1937 2222222222222222222222222222222222222222222222222222222 +1938 2222222222222222222222222222222222222222222222222222222 +1939 2222222222222222222222222222222222222222222222222222222 +1940 2222222222222222222222222222222222222222222222222222222 +1941 2222222222222222222222222222222222222222222222222222222 +1942 2222222222222222222222222222222222222222222222222222222 +1943 2222222222222222222222222222222222222222222222222222222 +1944 2222222222222222222222222222222222222222222222222222222 +1945 2222222222222222222222222222222222222222222222222222222 +1946 2222222222222222222222222222222222222222222222222222222 +1947 2222222222222222222222222222222222222222222222222222222 +1948 2222222222222222222222222222222222222222222222222222222 +1949 2222222222222222222222222222222222222222222222222222222 +1950 2222222222222222222222222222222222222222222222222222222 +1951 2222222222222222222222222222222222222222222222222222222 +1952 2222222222222222222222222222222222222222222222222222222 +1953 2222222222222222222222222222222222222222222222222222222 +1954 2222222222222222222222222222222222222222222222222222222 +1955 2222222222222222222222222222222222222222222222222222222 +1956 2222222222222222222222222222222222222222222222222222222 +1957 2222222222222222222222222222222222222222222222222222222 +1958 2222222222222222222222222222222222222222222222222222222 +1959 2222222222222222222222222222222222222222222222222222222 +1960 2222222222222222222222222222222222222222222222222222222 +1961 2222222222222222222222222222222222222222222222222222222 +1962 2222222222222222222222222222222222222222222222222222222 +1963 2222222222222222222222222222222222222222222222222222222 +1964 2222222222222222222222222222222222222222222222222222222 +1965 2222222222222222222222222222222222222222222222222222222 +1966 2222222222222222222222222222222222222222222222222222222 +1967 2222222222222222222222222222222222222222222222222222222 +1968 2222222222222222222222222222222222222222222222222222222 +1969 2222222222222222222222222222222222222222222222222222222 +1970 2222222222222222222222222222222222222222222222222222222 +1971 2222222222222222222222222222222222222222222222222222222 +1972 2222222222222222222222222222222222222222222222222222222 +1973 2222222222222222222222222222222222222222222222222222222 +1974 2222222222222222222222222222222222222222222222222222222 +1975 2222222222222222222222222222222222222222222222222222222 +1976 2222222222222222222222222222222222222222222222222222222 +1977 2222222222222222222222222222222222222222222222222222222 +1978 2222222222222222222222222222222222222222222222222222222 +1979 2222222222222222222222222222222222222222222222222222222 +1980 2222222222222222222222222222222222222222222222222222222 +1981 2222222222222222222222222222222222222222222222222222222 +1982 2222222222222222222222222222222222222222222222222222222 +1983 2222222222222222222222222222222222222222222222222222222 +1984 2222222222222222222222222222222222222222222222222222222 +1985 2222222222222222222222222222222222222222222222222222222 +1986 2222222222222222222222222222222222222222222222222222222 +1987 2222222222222222222222222222222222222222222222222222222 +1988 2222222222222222222222222222222222222222222222222222222 +1989 2222222222222222222222222222222222222222222222222222222 +1990 2222222222222222222222222222222222222222222222222222222 +1991 2222222222222222222222222222222222222222222222222222222 +1992 2222222222222222222222222222222222222222222222222222222 +1993 2222222222222222222222222222222222222222222222222222222 +1994 2222222222222222222222222222222222222222222222222222222 +1995 2222222222222222222222222222222222222222222222222222222 +1996 2222222222222222222222222222222222222222222222222222222 +1997 2222222222222222222222222222222222222222222222222222222 +1998 2222222222222222222222222222222222222222222222222222222 +1999 2222222222222222222222222222222222222222222222222222222 +2000 2222222222222222222222222222222222222222222222222222222 +2001 2222222222222222222222222222222222222222222222222222222 +2002 2222222222222222222222222222222222222222222222222222222 +2003 2222222222222222222222222222222222222222222222222222222 +2004 2222222222222222222222222222222222222222222222222222222 +2005 2222222222222222222222222222222222222222222222222222222 +2006 2222222222222222222222222222222222222222222222222222222 +2007 2222222222222222222222222222222222222222222222222222222 +2008 2222222222222222222222222222222222222222222222222222222 +2009 2222222222222222222222222222222222222222222222222222222 +2010 2222222222222222222222222222222222222222222222222222222 +2011 2222222222222222222222222222222222222222222222222222222 +2012 2222222222222222222222222222222222222222222222222222222 +2013 2222222222222222222222222222222222222222222222222222222 +2014 2222222222222222222222222222222222222222222222222222222 +2015 2222222222222222222222222222222222222222222222222222222 +2016 2222222222222222222222222222222222222222222222222222222 +2017 2222222222222222222222222222222222222222222222222222222 +2018 2222222222222222222222222222222222222222222222222222222 +2019 2222222222222222222222222222222222222222222222222222222 +2020 2222222222222222222222222222222222222222222222222222222 +2021 2222222222222222222222222222222222222222222222222222222 +2022 2222222222222222222222222222222222222222222222222222222 +2023 2222222222222222222222222222222222222222222222222222222 +2024 2222222222222222222222222222222222222222222222222222222 +2025 2222222222222222222222222222222222222222222222222222222 +2026 2222222222222222222222222222222222222222222222222222222 +2027 2222222222222222222222222222222222222222222222222222222 +2028 2222222222222222222222222222222222222222222222222222222 +2029 2222222222222222222222222222222222222222222222222222222 +2030 2222222222222222222222222222222222222222222222222222222 +2031 2222222222222222222222222222222222222222222222222222222 +2032 2222222222222222222222222222222222222222222222222222222 +2033 2222222222222222222222222222222222222222222222222222222 +2034 2222222222222222222222222222222222222222222222222222222 +2035 2222222222222222222222222222222222222222222222222222222 +2036 2222222222222222222222222222222222222222222222222222222 +2037 2222222222222222222222222222222222222222222222222222222 +2038 2222222222222222222222222222222222222222222222222222222 +2039 2222222222222222222222222222222222222222222222222222222 +2040 2222222222222222222222222222222222222222222222222222222 +2041 2222222222222222222222222222222222222222222222222222222 +2042 2222222222222222222222222222222222222222222222222222222 +2043 2222222222222222222222222222222222222222222222222222222 +2044 2222222222222222222222222222222222222222222222222222222 +2045 2222222222222222222222222222222222222222222222222222222 +2046 2222222222222222222222222222222222222222222222222222222 +2047 2222222222222222222222222222222222222222222222222222222 +2048 2222222222222222222222222222222222222222222222222222222 +2049 2222222222222222222222222222222222222222222222222222222 +2050 2222222222222222222222222222222222222222222222222222222 +2051 2222222222222222222222222222222222222222222222222222222 +2052 2222222222222222222222222222222222222222222222222222222 +2053 2222222222222222222222222222222222222222222222222222222 +2054 2222222222222222222222222222222222222222222222222222222 +2055 2222222222222222222222222222222222222222222222222222222 +2056 2222222222222222222222222222222222222222222222222222222 +2057 2222222222222222222222222222222222222222222222222222222 +2058 2222222222222222222222222222222222222222222222222222222 +2059 2222222222222222222222222222222222222222222222222222222 +2060 2222222222222222222222222222222222222222222222222222222 +2061 2222222222222222222222222222222222222222222222222222222 +2062 2222222222222222222222222222222222222222222222222222222 +2063 2222222222222222222222222222222222222222222222222222222 +2064 2222222222222222222222222222222222222222222222222222222 +2065 2222222222222222222222222222222222222222222222222222222 +2066 2222222222222222222222222222222222222222222222222222222 +2067 2222222222222222222222222222222222222222222222222222222 +2068 2222222222222222222222222222222222222222222222222222222 +2069 2222222222222222222222222222222222222222222222222222222 +2070 2222222222222222222222222222222222222222222222222222222 +2071 2222222222222222222222222222222222222222222222222222222 +2072 2222222222222222222222222222222222222222222222222222222 +2073 2222222222222222222222222222222222222222222222222222222 +2074 2222222222222222222222222222222222222222222222222222222 +2075 2222222222222222222222222222222222222222222222222222222 +2076 2222222222222222222222222222222222222222222222222222222 +2077 2222222222222222222222222222222222222222222222222222222 +2078 2222222222222222222222222222222222222222222222222222222 +2079 2222222222222222222222222222222222222222222222222222222 +2080 2222222222222222222222222222222222222222222222222222222 +2081 2222222222222222222222222222222222222222222222222222222 +2082 2222222222222222222222222222222222222222222222222222222 +2083 2222222222222222222222222222222222222222222222222222222 +2084 2222222222222222222222222222222222222222222222222222222 +2085 2222222222222222222222222222222222222222222222222222222 +2086 2222222222222222222222222222222222222222222222222222222 +2087 2222222222222222222222222222222222222222222222222222222 +2088 2222222222222222222222222222222222222222222222222222222 +2089 2222222222222222222222222222222222222222222222222222222 +2090 2222222222222222222222222222222222222222222222222222222 +2091 2222222222222222222222222222222222222222222222222222222 +2092 2222222222222222222222222222222222222222222222222222222 +2093 2222222222222222222222222222222222222222222222222222222 +2094 2222222222222222222222222222222222222222222222222222222 +2095 2222222222222222222222222222222222222222222222222222222 +2096 2222222222222222222222222222222222222222222222222222222 +2097 2222222222222222222222222222222222222222222222222222222 +2098 2222222222222222222222222222222222222222222222222222222 +2099 2222222222222222222222222222222222222222222222222222222 +2100 2222222222222222222222222222222222222222222222222222222 +2101 2222222222222222222222222222222222222222222222222222222 +2102 2222222222222222222222222222222222222222222222222222222 +2103 2222222222222222222222222222222222222222222222222222222 +2104 2222222222222222222222222222222222222222222222222222222 +2105 2222222222222222222222222222222222222222222222222222222 +2106 2222222222222222222222222222222222222222222222222222222 +2107 2222222222222222222222222222222222222222222222222222222 +2108 2222222222222222222222222222222222222222222222222222222 +2109 2222222222222222222222222222222222222222222222222222222 +2110 2222222222222222222222222222222222222222222222222222222 +2111 2222222222222222222222222222222222222222222222222222222 +2112 2222222222222222222222222222222222222222222222222222222 +2113 2222222222222222222222222222222222222222222222222222222 +2114 2222222222222222222222222222222222222222222222222222222 +2115 2222222222222222222222222222222222222222222222222222222 +2116 2222222222222222222222222222222222222222222222222222222 +2117 2222222222222222222222222222222222222222222222222222222 +2118 2222222222222222222222222222222222222222222222222222222 +2119 2222222222222222222222222222222222222222222222222222222 +2120 2222222222222222222222222222222222222222222222222222222 +2121 2222222222222222222222222222222222222222222222222222222 +2122 2222222222222222222222222222222222222222222222222222222 +2123 2222222222222222222222222222222222222222222222222222222 +2124 2222222222222222222222222222222222222222222222222222222 +2125 2222222222222222222222222222222222222222222222222222222 +2126 2222222222222222222222222222222222222222222222222222222 +2127 2222222222222222222222222222222222222222222222222222222 +2128 2222222222222222222222222222222222222222222222222222222 +2129 2222222222222222222222222222222222222222222222222222222 +2130 2222222222222222222222222222222222222222222222222222222 +2131 2222222222222222222222222222222222222222222222222222222 +2132 2222222222222222222222222222222222222222222222222222222 +2133 2222222222222222222222222222222222222222222222222222222 +2134 2222222222222222222222222222222222222222222222222222222 +2135 2222222222222222222222222222222222222222222222222222222 +2136 2222222222222222222222222222222222222222222222222222222 +2137 2222222222222222222222222222222222222222222222222222222 +2138 2222222222222222222222222222222222222222222222222222222 +2139 2222222222222222222222222222222222222222222222222222222 +2140 2222222222222222222222222222222222222222222222222222222 +2141 2222222222222222222222222222222222222222222222222222222 +2142 2222222222222222222222222222222222222222222222222222222 +2143 2222222222222222222222222222222222222222222222222222222 +2144 2222222222222222222222222222222222222222222222222222222 +2145 2222222222222222222222222222222222222222222222222222222 +2146 2222222222222222222222222222222222222222222222222222222 +2147 2222222222222222222222222222222222222222222222222222222 +2148 2222222222222222222222222222222222222222222222222222222 +2149 2222222222222222222222222222222222222222222222222222222 +2150 2222222222222222222222222222222222222222222222222222222 +2151 2222222222222222222222222222222222222222222222222222222 +2152 2222222222222222222222222222222222222222222222222222222 +2153 2222222222222222222222222222222222222222222222222222222 +2154 2222222222222222222222222222222222222222222222222222222 +2155 2222222222222222222222222222222222222222222222222222222 +2156 2222222222222222222222222222222222222222222222222222222 +2157 2222222222222222222222222222222222222222222222222222222 +2158 2222222222222222222222222222222222222222222222222222222 +2159 2222222222222222222222222222222222222222222222222222222 +2160 2222222222222222222222222222222222222222222222222222222 +2161 2222222222222222222222222222222222222222222222222222222 +2162 2222222222222222222222222222222222222222222222222222222 +2163 2222222222222222222222222222222222222222222222222222222 +2164 2222222222222222222222222222222222222222222222222222222 +2165 2222222222222222222222222222222222222222222222222222222 +2166 2222222222222222222222222222222222222222222222222222222 +2167 2222222222222222222222222222222222222222222222222222222 +2168 2222222222222222222222222222222222222222222222222222222 +2169 2222222222222222222222222222222222222222222222222222222 +2170 2222222222222222222222222222222222222222222222222222222 +2171 2222222222222222222222222222222222222222222222222222222 +2172 2222222222222222222222222222222222222222222222222222222 +2173 2222222222222222222222222222222222222222222222222222222 +2174 2222222222222222222222222222222222222222222222222222222 +2175 2222222222222222222222222222222222222222222222222222222 +2176 2222222222222222222222222222222222222222222222222222222 +2177 2222222222222222222222222222222222222222222222222222222 +2178 2222222222222222222222222222222222222222222222222222222 +2179 2222222222222222222222222222222222222222222222222222222 +2180 2222222222222222222222222222222222222222222222222222222 +2181 2222222222222222222222222222222222222222222222222222222 +2182 2222222222222222222222222222222222222222222222222222222 +2183 2222222222222222222222222222222222222222222222222222222 +2184 2222222222222222222222222222222222222222222222222222222 +2185 2222222222222222222222222222222222222222222222222222222 +2186 2222222222222222222222222222222222222222222222222222222 +2187 2222222222222222222222222222222222222222222222222222222 +2188 2222222222222222222222222222222222222222222222222222222 +2189 2222222222222222222222222222222222222222222222222222222 +2190 2222222222222222222222222222222222222222222222222222222 +2191 2222222222222222222222222222222222222222222222222222222 +2192 2222222222222222222222222222222222222222222222222222222 +2193 2222222222222222222222222222222222222222222222222222222 +2194 2222222222222222222222222222222222222222222222222222222 +2195 2222222222222222222222222222222222222222222222222222222 +2196 2222222222222222222222222222222222222222222222222222222 +2197 2222222222222222222222222222222222222222222222222222222 +2198 2222222222222222222222222222222222222222222222222222222 +2199 2222222222222222222222222222222222222222222222222222222 +2200 2222222222222222222222222222222222222222222222222222222 +2201 2222222222222222222222222222222222222222222222222222222 +2202 2222222222222222222222222222222222222222222222222222222 +2203 2222222222222222222222222222222222222222222222222222222 +2204 2222222222222222222222222222222222222222222222222222222 +2205 2222222222222222222222222222222222222222222222222222222 +2206 2222222222222222222222222222222222222222222222222222222 +2207 2222222222222222222222222222222222222222222222222222222 +2208 2222222222222222222222222222222222222222222222222222222 +2209 2222222222222222222222222222222222222222222222222222222 +2210 2222222222222222222222222222222222222222222222222222222 +2211 2222222222222222222222222222222222222222222222222222222 +2212 2222222222222222222222222222222222222222222222222222222 +2213 2222222222222222222222222222222222222222222222222222222 +2214 2222222222222222222222222222222222222222222222222222222 +2215 2222222222222222222222222222222222222222222222222222222 +2216 2222222222222222222222222222222222222222222222222222222 +2217 2222222222222222222222222222222222222222222222222222222 +2218 2222222222222222222222222222222222222222222222222222222 +2219 2222222222222222222222222222222222222222222222222222222 +2220 2222222222222222222222222222222222222222222222222222222 +2221 2222222222222222222222222222222222222222222222222222222 +2222 2222222222222222222222222222222222222222222222222222222 +2223 2222222222222222222222222222222222222222222222222222222 +2224 2222222222222222222222222222222222222222222222222222222 +2225 2222222222222222222222222222222222222222222222222222222 +2226 2222222222222222222222222222222222222222222222222222222 +2227 2222222222222222222222222222222222222222222222222222222 +2228 2222222222222222222222222222222222222222222222222222222 +2229 2222222222222222222222222222222222222222222222222222222 +2230 2222222222222222222222222222222222222222222222222222222 +2231 2222222222222222222222222222222222222222222222222222222 +2232 2222222222222222222222222222222222222222222222222222222 +2233 2222222222222222222222222222222222222222222222222222222 +2234 2222222222222222222222222222222222222222222222222222222 +2235 2222222222222222222222222222222222222222222222222222222 +2236 2222222222222222222222222222222222222222222222222222222 +2237 2222222222222222222222222222222222222222222222222222222 +2238 2222222222222222222222222222222222222222222222222222222 +2239 2222222222222222222222222222222222222222222222222222222 +2240 2222222222222222222222222222222222222222222222222222222 +2241 2222222222222222222222222222222222222222222222222222222 +2242 2222222222222222222222222222222222222222222222222222222 +2243 2222222222222222222222222222222222222222222222222222222 +2244 2222222222222222222222222222222222222222222222222222222 +2245 2222222222222222222222222222222222222222222222222222222 +2246 2222222222222222222222222222222222222222222222222222222 +2247 2222222222222222222222222222222222222222222222222222222 +2248 2222222222222222222222222222222222222222222222222222222 +2249 2222222222222222222222222222222222222222222222222222222 +2250 2222222222222222222222222222222222222222222222222222222 +2251 2222222222222222222222222222222222222222222222222222222 +2252 2222222222222222222222222222222222222222222222222222222 +2253 2222222222222222222222222222222222222222222222222222222 +2254 2222222222222222222222222222222222222222222222222222222 +2255 2222222222222222222222222222222222222222222222222222222 +2256 2222222222222222222222222222222222222222222222222222222 +2257 2222222222222222222222222222222222222222222222222222222 +2258 2222222222222222222222222222222222222222222222222222222 +2259 2222222222222222222222222222222222222222222222222222222 +2260 2222222222222222222222222222222222222222222222222222222 +2261 2222222222222222222222222222222222222222222222222222222 +2262 2222222222222222222222222222222222222222222222222222222 +2263 2222222222222222222222222222222222222222222222222222222 +2264 2222222222222222222222222222222222222222222222222222222 +2265 2222222222222222222222222222222222222222222222222222222 +2266 2222222222222222222222222222222222222222222222222222222 +2267 2222222222222222222222222222222222222222222222222222222 +2268 2222222222222222222222222222222222222222222222222222222 +2269 2222222222222222222222222222222222222222222222222222222 +2270 2222222222222222222222222222222222222222222222222222222 +2271 2222222222222222222222222222222222222222222222222222222 +2272 2222222222222222222222222222222222222222222222222222222 +2273 2222222222222222222222222222222222222222222222222222222 +2274 2222222222222222222222222222222222222222222222222222222 +2275 2222222222222222222222222222222222222222222222222222222 +2276 2222222222222222222222222222222222222222222222222222222 +2277 2222222222222222222222222222222222222222222222222222222 +2278 2222222222222222222222222222222222222222222222222222222 +2279 2222222222222222222222222222222222222222222222222222222 +2280 2222222222222222222222222222222222222222222222222222222 +2281 2222222222222222222222222222222222222222222222222222222 +2282 2222222222222222222222222222222222222222222222222222222 +2283 2222222222222222222222222222222222222222222222222222222 +2284 2222222222222222222222222222222222222222222222222222222 +2285 2222222222222222222222222222222222222222222222222222222 +2286 2222222222222222222222222222222222222222222222222222222 +2287 2222222222222222222222222222222222222222222222222222222 +2288 2222222222222222222222222222222222222222222222222222222 +2289 2222222222222222222222222222222222222222222222222222222 +2290 2222222222222222222222222222222222222222222222222222222 +2291 2222222222222222222222222222222222222222222222222222222 +2292 2222222222222222222222222222222222222222222222222222222 +2293 2222222222222222222222222222222222222222222222222222222 +2294 2222222222222222222222222222222222222222222222222222222 +2295 2222222222222222222222222222222222222222222222222222222 +2296 2222222222222222222222222222222222222222222222222222222 +2297 2222222222222222222222222222222222222222222222222222222 +2298 2222222222222222222222222222222222222222222222222222222 +2299 2222222222222222222222222222222222222222222222222222222 +2300 2222222222222222222222222222222222222222222222222222222 +2301 2222222222222222222222222222222222222222222222222222222 +2302 2222222222222222222222222222222222222222222222222222222 +2303 2222222222222222222222222222222222222222222222222222222 +2304 2222222222222222222222222222222222222222222222222222222 +2305 2222222222222222222222222222222222222222222222222222222 +2306 2222222222222222222222222222222222222222222222222222222 +2307 2222222222222222222222222222222222222222222222222222222 +2308 2222222222222222222222222222222222222222222222222222222 +2309 2222222222222222222222222222222222222222222222222222222 +2310 2222222222222222222222222222222222222222222222222222222 +2311 2222222222222222222222222222222222222222222222222222222 +2312 2222222222222222222222222222222222222222222222222222222 +2313 2222222222222222222222222222222222222222222222222222222 +2314 2222222222222222222222222222222222222222222222222222222 +2315 2222222222222222222222222222222222222222222222222222222 +2316 2222222222222222222222222222222222222222222222222222222 +2317 2222222222222222222222222222222222222222222222222222222 +2318 2222222222222222222222222222222222222222222222222222222 +2319 2222222222222222222222222222222222222222222222222222222 +2320 2222222222222222222222222222222222222222222222222222222 +2321 2222222222222222222222222222222222222222222222222222222 +2322 2222222222222222222222222222222222222222222222222222222 +2323 2222222222222222222222222222222222222222222222222222222 +2324 2222222222222222222222222222222222222222222222222222222 +2325 2222222222222222222222222222222222222222222222222222222 +2326 2222222222222222222222222222222222222222222222222222222 +2327 2222222222222222222222222222222222222222222222222222222 +2328 2222222222222222222222222222222222222222222222222222222 +2329 2222222222222222222222222222222222222222222222222222222 +2330 2222222222222222222222222222222222222222222222222222222 +2331 2222222222222222222222222222222222222222222222222222222 +2332 2222222222222222222222222222222222222222222222222222222 +2333 2222222222222222222222222222222222222222222222222222222 +2334 2222222222222222222222222222222222222222222222222222222 +2335 2222222222222222222222222222222222222222222222222222222 +2336 2222222222222222222222222222222222222222222222222222222 +2337 2222222222222222222222222222222222222222222222222222222 +2338 2222222222222222222222222222222222222222222222222222222 +2339 2222222222222222222222222222222222222222222222222222222 +2340 2222222222222222222222222222222222222222222222222222222 +2341 2222222222222222222222222222222222222222222222222222222 +2342 2222222222222222222222222222222222222222222222222222222 +2343 2222222222222222222222222222222222222222222222222222222 +2344 2222222222222222222222222222222222222222222222222222222 +2345 2222222222222222222222222222222222222222222222222222222 +2346 2222222222222222222222222222222222222222222222222222222 +2347 2222222222222222222222222222222222222222222222222222222 +2348 2222222222222222222222222222222222222222222222222222222 +2349 2222222222222222222222222222222222222222222222222222222 +2350 2222222222222222222222222222222222222222222222222222222 +2351 2222222222222222222222222222222222222222222222222222222 +2352 2222222222222222222222222222222222222222222222222222222 +2353 2222222222222222222222222222222222222222222222222222222 +2354 2222222222222222222222222222222222222222222222222222222 +2355 2222222222222222222222222222222222222222222222222222222 +2356 2222222222222222222222222222222222222222222222222222222 +2357 2222222222222222222222222222222222222222222222222222222 +2358 2222222222222222222222222222222222222222222222222222222 +2359 2222222222222222222222222222222222222222222222222222222 +2360 2222222222222222222222222222222222222222222222222222222 +2361 2222222222222222222222222222222222222222222222222222222 +2362 2222222222222222222222222222222222222222222222222222222 +2363 2222222222222222222222222222222222222222222222222222222 +2364 2222222222222222222222222222222222222222222222222222222 +2365 2222222222222222222222222222222222222222222222222222222 +2366 2222222222222222222222222222222222222222222222222222222 +2367 2222222222222222222222222222222222222222222222222222222 +2368 2222222222222222222222222222222222222222222222222222222 +2369 2222222222222222222222222222222222222222222222222222222 +2370 2222222222222222222222222222222222222222222222222222222 +2371 2222222222222222222222222222222222222222222222222222222 +2372 2222222222222222222222222222222222222222222222222222222 +2373 2222222222222222222222222222222222222222222222222222222 +2374 2222222222222222222222222222222222222222222222222222222 +2375 2222222222222222222222222222222222222222222222222222222 +2376 2222222222222222222222222222222222222222222222222222222 +2377 2222222222222222222222222222222222222222222222222222222 +2378 2222222222222222222222222222222222222222222222222222222 +2379 2222222222222222222222222222222222222222222222222222222 +2380 2222222222222222222222222222222222222222222222222222222 +2381 2222222222222222222222222222222222222222222222222222222 +2382 2222222222222222222222222222222222222222222222222222222 +2383 2222222222222222222222222222222222222222222222222222222 +2384 2222222222222222222222222222222222222222222222222222222 +2385 2222222222222222222222222222222222222222222222222222222 +2386 2222222222222222222222222222222222222222222222222222222 +2387 2222222222222222222222222222222222222222222222222222222 +2388 2222222222222222222222222222222222222222222222222222222 +2389 2222222222222222222222222222222222222222222222222222222 +2390 2222222222222222222222222222222222222222222222222222222 +2391 2222222222222222222222222222222222222222222222222222222 +2392 2222222222222222222222222222222222222222222222222222222 +2393 2222222222222222222222222222222222222222222222222222222 +2394 2222222222222222222222222222222222222222222222222222222 +2395 2222222222222222222222222222222222222222222222222222222 +2396 2222222222222222222222222222222222222222222222222222222 +2397 2222222222222222222222222222222222222222222222222222222 +2398 2222222222222222222222222222222222222222222222222222222 +2399 2222222222222222222222222222222222222222222222222222222 +2400 2222222222222222222222222222222222222222222222222222222 +2401 2222222222222222222222222222222222222222222222222222222 +2402 2222222222222222222222222222222222222222222222222222222 +2403 2222222222222222222222222222222222222222222222222222222 +2404 2222222222222222222222222222222222222222222222222222222 +2405 2222222222222222222222222222222222222222222222222222222 +2406 2222222222222222222222222222222222222222222222222222222 +2407 2222222222222222222222222222222222222222222222222222222 +2408 2222222222222222222222222222222222222222222222222222222 +2409 2222222222222222222222222222222222222222222222222222222 +2410 2222222222222222222222222222222222222222222222222222222 +2411 2222222222222222222222222222222222222222222222222222222 +2412 2222222222222222222222222222222222222222222222222222222 +2413 2222222222222222222222222222222222222222222222222222222 +2414 2222222222222222222222222222222222222222222222222222222 +2415 2222222222222222222222222222222222222222222222222222222 +2416 2222222222222222222222222222222222222222222222222222222 +2417 2222222222222222222222222222222222222222222222222222222 +2418 2222222222222222222222222222222222222222222222222222222 +2419 2222222222222222222222222222222222222222222222222222222 +2420 2222222222222222222222222222222222222222222222222222222 +2421 2222222222222222222222222222222222222222222222222222222 +2422 2222222222222222222222222222222222222222222222222222222 +2423 2222222222222222222222222222222222222222222222222222222 +2424 2222222222222222222222222222222222222222222222222222222 +2425 2222222222222222222222222222222222222222222222222222222 +2426 2222222222222222222222222222222222222222222222222222222 +2427 2222222222222222222222222222222222222222222222222222222 +2428 2222222222222222222222222222222222222222222222222222222 +2429 2222222222222222222222222222222222222222222222222222222 +2430 2222222222222222222222222222222222222222222222222222222 +2431 2222222222222222222222222222222222222222222222222222222 +2432 2222222222222222222222222222222222222222222222222222222 +2433 2222222222222222222222222222222222222222222222222222222 +2434 2222222222222222222222222222222222222222222222222222222 +2435 2222222222222222222222222222222222222222222222222222222 +2436 2222222222222222222222222222222222222222222222222222222 +2437 2222222222222222222222222222222222222222222222222222222 +2438 2222222222222222222222222222222222222222222222222222222 +2439 2222222222222222222222222222222222222222222222222222222 +2440 2222222222222222222222222222222222222222222222222222222 +2441 2222222222222222222222222222222222222222222222222222222 +2442 2222222222222222222222222222222222222222222222222222222 +2443 2222222222222222222222222222222222222222222222222222222 +2444 2222222222222222222222222222222222222222222222222222222 +2445 2222222222222222222222222222222222222222222222222222222 +2446 2222222222222222222222222222222222222222222222222222222 +2447 2222222222222222222222222222222222222222222222222222222 +2448 2222222222222222222222222222222222222222222222222222222 +2449 2222222222222222222222222222222222222222222222222222222 +2450 2222222222222222222222222222222222222222222222222222222 +2451 2222222222222222222222222222222222222222222222222222222 +2452 2222222222222222222222222222222222222222222222222222222 +2453 2222222222222222222222222222222222222222222222222222222 +2454 2222222222222222222222222222222222222222222222222222222 +2455 2222222222222222222222222222222222222222222222222222222 +2456 2222222222222222222222222222222222222222222222222222222 +2457 2222222222222222222222222222222222222222222222222222222 +2458 2222222222222222222222222222222222222222222222222222222 +2459 2222222222222222222222222222222222222222222222222222222 +2460 2222222222222222222222222222222222222222222222222222222 +2461 2222222222222222222222222222222222222222222222222222222 +2462 2222222222222222222222222222222222222222222222222222222 +2463 2222222222222222222222222222222222222222222222222222222 +2464 2222222222222222222222222222222222222222222222222222222 +2465 2222222222222222222222222222222222222222222222222222222 +2466 2222222222222222222222222222222222222222222222222222222 +2467 2222222222222222222222222222222222222222222222222222222 +2468 2222222222222222222222222222222222222222222222222222222 +2469 2222222222222222222222222222222222222222222222222222222 +2470 2222222222222222222222222222222222222222222222222222222 +2471 2222222222222222222222222222222222222222222222222222222 +2472 2222222222222222222222222222222222222222222222222222222 +2473 2222222222222222222222222222222222222222222222222222222 +2474 2222222222222222222222222222222222222222222222222222222 +2475 2222222222222222222222222222222222222222222222222222222 +2476 2222222222222222222222222222222222222222222222222222222 +2477 2222222222222222222222222222222222222222222222222222222 +2478 2222222222222222222222222222222222222222222222222222222 +2479 2222222222222222222222222222222222222222222222222222222 +2480 2222222222222222222222222222222222222222222222222222222 +2481 2222222222222222222222222222222222222222222222222222222 +2482 2222222222222222222222222222222222222222222222222222222 +2483 2222222222222222222222222222222222222222222222222222222 +2484 2222222222222222222222222222222222222222222222222222222 +2485 2222222222222222222222222222222222222222222222222222222 +2486 2222222222222222222222222222222222222222222222222222222 +2487 2222222222222222222222222222222222222222222222222222222 +2488 2222222222222222222222222222222222222222222222222222222 +2489 2222222222222222222222222222222222222222222222222222222 +2490 2222222222222222222222222222222222222222222222222222222 +2491 2222222222222222222222222222222222222222222222222222222 +2492 2222222222222222222222222222222222222222222222222222222 +2493 2222222222222222222222222222222222222222222222222222222 +2494 2222222222222222222222222222222222222222222222222222222 +2495 2222222222222222222222222222222222222222222222222222222 +2496 2222222222222222222222222222222222222222222222222222222 +2497 2222222222222222222222222222222222222222222222222222222 +2498 2222222222222222222222222222222222222222222222222222222 +2499 2222222222222222222222222222222222222222222222222222222 +2500 2222222222222222222222222222222222222222222222222222222 +2501 2222222222222222222222222222222222222222222222222222222 +2502 2222222222222222222222222222222222222222222222222222222 +2503 2222222222222222222222222222222222222222222222222222222 +2504 2222222222222222222222222222222222222222222222222222222 +2505 2222222222222222222222222222222222222222222222222222222 +2506 2222222222222222222222222222222222222222222222222222222 +2507 2222222222222222222222222222222222222222222222222222222 +2508 2222222222222222222222222222222222222222222222222222222 +2509 2222222222222222222222222222222222222222222222222222222 +2510 2222222222222222222222222222222222222222222222222222222 +2511 2222222222222222222222222222222222222222222222222222222 +2512 2222222222222222222222222222222222222222222222222222222 +2513 2222222222222222222222222222222222222222222222222222222 +2514 2222222222222222222222222222222222222222222222222222222 +2515 2222222222222222222222222222222222222222222222222222222 +2516 2222222222222222222222222222222222222222222222222222222 +2517 2222222222222222222222222222222222222222222222222222222 +2518 2222222222222222222222222222222222222222222222222222222 +2519 2222222222222222222222222222222222222222222222222222222 +2520 2222222222222222222222222222222222222222222222222222222 +2521 2222222222222222222222222222222222222222222222222222222 +2522 2222222222222222222222222222222222222222222222222222222 +2523 2222222222222222222222222222222222222222222222222222222 +2524 2222222222222222222222222222222222222222222222222222222 +2525 2222222222222222222222222222222222222222222222222222222 +2526 2222222222222222222222222222222222222222222222222222222 +2527 2222222222222222222222222222222222222222222222222222222 +2528 2222222222222222222222222222222222222222222222222222222 +2529 2222222222222222222222222222222222222222222222222222222 +2530 2222222222222222222222222222222222222222222222222222222 +2531 2222222222222222222222222222222222222222222222222222222 +2532 2222222222222222222222222222222222222222222222222222222 +2533 2222222222222222222222222222222222222222222222222222222 +2534 2222222222222222222222222222222222222222222222222222222 +2535 2222222222222222222222222222222222222222222222222222222 +2536 2222222222222222222222222222222222222222222222222222222 +2537 2222222222222222222222222222222222222222222222222222222 +2538 2222222222222222222222222222222222222222222222222222222 +2539 2222222222222222222222222222222222222222222222222222222 +2540 2222222222222222222222222222222222222222222222222222222 +2541 2222222222222222222222222222222222222222222222222222222 +2542 2222222222222222222222222222222222222222222222222222222 +2543 2222222222222222222222222222222222222222222222222222222 +2544 2222222222222222222222222222222222222222222222222222222 +2545 2222222222222222222222222222222222222222222222222222222 +2546 2222222222222222222222222222222222222222222222222222222 +2547 2222222222222222222222222222222222222222222222222222222 +2548 2222222222222222222222222222222222222222222222222222222 +2549 2222222222222222222222222222222222222222222222222222222 +2550 2222222222222222222222222222222222222222222222222222222 +2551 2222222222222222222222222222222222222222222222222222222 +2552 2222222222222222222222222222222222222222222222222222222 +2553 2222222222222222222222222222222222222222222222222222222 +2554 2222222222222222222222222222222222222222222222222222222 +2555 2222222222222222222222222222222222222222222222222222222 +2556 2222222222222222222222222222222222222222222222222222222 +2557 2222222222222222222222222222222222222222222222222222222 +2558 2222222222222222222222222222222222222222222222222222222 +2559 2222222222222222222222222222222222222222222222222222222 +2560 2222222222222222222222222222222222222222222222222222222 +2561 2222222222222222222222222222222222222222222222222222222 +2562 2222222222222222222222222222222222222222222222222222222 +2563 2222222222222222222222222222222222222222222222222222222 +2564 2222222222222222222222222222222222222222222222222222222 +2565 2222222222222222222222222222222222222222222222222222222 +2566 2222222222222222222222222222222222222222222222222222222 +2567 2222222222222222222222222222222222222222222222222222222 +2568 2222222222222222222222222222222222222222222222222222222 +2569 2222222222222222222222222222222222222222222222222222222 +2570 2222222222222222222222222222222222222222222222222222222 +2571 2222222222222222222222222222222222222222222222222222222 +2572 2222222222222222222222222222222222222222222222222222222 +2573 2222222222222222222222222222222222222222222222222222222 +2574 2222222222222222222222222222222222222222222222222222222 +2575 2222222222222222222222222222222222222222222222222222222 +2576 2222222222222222222222222222222222222222222222222222222 +2577 2222222222222222222222222222222222222222222222222222222 +2578 2222222222222222222222222222222222222222222222222222222 +2579 2222222222222222222222222222222222222222222222222222222 +2580 2222222222222222222222222222222222222222222222222222222 +2581 2222222222222222222222222222222222222222222222222222222 +2582 2222222222222222222222222222222222222222222222222222222 +2583 2222222222222222222222222222222222222222222222222222222 +2584 2222222222222222222222222222222222222222222222222222222 +2585 2222222222222222222222222222222222222222222222222222222 +2586 2222222222222222222222222222222222222222222222222222222 +2587 2222222222222222222222222222222222222222222222222222222 +2588 2222222222222222222222222222222222222222222222222222222 +2589 2222222222222222222222222222222222222222222222222222222 +2590 2222222222222222222222222222222222222222222222222222222 +2591 2222222222222222222222222222222222222222222222222222222 +2592 2222222222222222222222222222222222222222222222222222222 +2593 2222222222222222222222222222222222222222222222222222222 +2594 2222222222222222222222222222222222222222222222222222222 +2595 2222222222222222222222222222222222222222222222222222222 +2596 2222222222222222222222222222222222222222222222222222222 +2597 2222222222222222222222222222222222222222222222222222222 +2598 2222222222222222222222222222222222222222222222222222222 +2599 2222222222222222222222222222222222222222222222222222222 +2600 2222222222222222222222222222222222222222222222222222222 +2601 2222222222222222222222222222222222222222222222222222222 +2602 2222222222222222222222222222222222222222222222222222222 +2603 2222222222222222222222222222222222222222222222222222222 +2604 2222222222222222222222222222222222222222222222222222222 +2605 2222222222222222222222222222222222222222222222222222222 +2606 2222222222222222222222222222222222222222222222222222222 +2607 2222222222222222222222222222222222222222222222222222222 +2608 2222222222222222222222222222222222222222222222222222222 +2609 2222222222222222222222222222222222222222222222222222222 +2610 2222222222222222222222222222222222222222222222222222222 +2611 2222222222222222222222222222222222222222222222222222222 +2612 2222222222222222222222222222222222222222222222222222222 +2613 2222222222222222222222222222222222222222222222222222222 +2614 2222222222222222222222222222222222222222222222222222222 +2615 2222222222222222222222222222222222222222222222222222222 +2616 2222222222222222222222222222222222222222222222222222222 +2617 2222222222222222222222222222222222222222222222222222222 +2618 2222222222222222222222222222222222222222222222222222222 +2619 2222222222222222222222222222222222222222222222222222222 +2620 2222222222222222222222222222222222222222222222222222222 +2621 2222222222222222222222222222222222222222222222222222222 +2622 2222222222222222222222222222222222222222222222222222222 +2623 2222222222222222222222222222222222222222222222222222222 +2624 2222222222222222222222222222222222222222222222222222222 +2625 2222222222222222222222222222222222222222222222222222222 +2626 2222222222222222222222222222222222222222222222222222222 +2627 2222222222222222222222222222222222222222222222222222222 +2628 2222222222222222222222222222222222222222222222222222222 +2629 2222222222222222222222222222222222222222222222222222222 +2630 2222222222222222222222222222222222222222222222222222222 +2631 2222222222222222222222222222222222222222222222222222222 +2632 2222222222222222222222222222222222222222222222222222222 +2633 2222222222222222222222222222222222222222222222222222222 +2634 2222222222222222222222222222222222222222222222222222222 +2635 2222222222222222222222222222222222222222222222222222222 +2636 2222222222222222222222222222222222222222222222222222222 +2637 2222222222222222222222222222222222222222222222222222222 +2638 2222222222222222222222222222222222222222222222222222222 +2639 2222222222222222222222222222222222222222222222222222222 +2640 2222222222222222222222222222222222222222222222222222222 +2641 2222222222222222222222222222222222222222222222222222222 +2642 2222222222222222222222222222222222222222222222222222222 +2643 2222222222222222222222222222222222222222222222222222222 +2644 2222222222222222222222222222222222222222222222222222222 +2645 2222222222222222222222222222222222222222222222222222222 +2646 2222222222222222222222222222222222222222222222222222222 +2647 2222222222222222222222222222222222222222222222222222222 +2648 2222222222222222222222222222222222222222222222222222222 +2649 2222222222222222222222222222222222222222222222222222222 +2650 2222222222222222222222222222222222222222222222222222222 +2651 2222222222222222222222222222222222222222222222222222222 +2652 2222222222222222222222222222222222222222222222222222222 +2653 2222222222222222222222222222222222222222222222222222222 +2654 2222222222222222222222222222222222222222222222222222222 +2655 2222222222222222222222222222222222222222222222222222222 +2656 2222222222222222222222222222222222222222222222222222222 +2657 2222222222222222222222222222222222222222222222222222222 +2658 2222222222222222222222222222222222222222222222222222222 +2659 2222222222222222222222222222222222222222222222222222222 +2660 2222222222222222222222222222222222222222222222222222222 +2661 2222222222222222222222222222222222222222222222222222222 +2662 2222222222222222222222222222222222222222222222222222222 +2663 2222222222222222222222222222222222222222222222222222222 +2664 2222222222222222222222222222222222222222222222222222222 +2665 2222222222222222222222222222222222222222222222222222222 +2666 2222222222222222222222222222222222222222222222222222222 +2667 2222222222222222222222222222222222222222222222222222222 +2668 2222222222222222222222222222222222222222222222222222222 +2669 2222222222222222222222222222222222222222222222222222222 +2670 2222222222222222222222222222222222222222222222222222222 +2671 2222222222222222222222222222222222222222222222222222222 +2672 2222222222222222222222222222222222222222222222222222222 +2673 2222222222222222222222222222222222222222222222222222222 +2674 2222222222222222222222222222222222222222222222222222222 +2675 2222222222222222222222222222222222222222222222222222222 +2676 2222222222222222222222222222222222222222222222222222222 +2677 2222222222222222222222222222222222222222222222222222222 +2678 2222222222222222222222222222222222222222222222222222222 +2679 2222222222222222222222222222222222222222222222222222222 +2680 2222222222222222222222222222222222222222222222222222222 +2681 2222222222222222222222222222222222222222222222222222222 +2682 2222222222222222222222222222222222222222222222222222222 +2683 2222222222222222222222222222222222222222222222222222222 +2684 2222222222222222222222222222222222222222222222222222222 +2685 2222222222222222222222222222222222222222222222222222222 +2686 2222222222222222222222222222222222222222222222222222222 +2687 2222222222222222222222222222222222222222222222222222222 +2688 2222222222222222222222222222222222222222222222222222222 +2689 2222222222222222222222222222222222222222222222222222222 +2690 2222222222222222222222222222222222222222222222222222222 +2691 2222222222222222222222222222222222222222222222222222222 +2692 2222222222222222222222222222222222222222222222222222222 +2693 2222222222222222222222222222222222222222222222222222222 +2694 2222222222222222222222222222222222222222222222222222222 +2695 2222222222222222222222222222222222222222222222222222222 +2696 2222222222222222222222222222222222222222222222222222222 +2697 2222222222222222222222222222222222222222222222222222222 +2698 2222222222222222222222222222222222222222222222222222222 +2699 2222222222222222222222222222222222222222222222222222222 +2700 2222222222222222222222222222222222222222222222222222222 +2701 2222222222222222222222222222222222222222222222222222222 +2702 2222222222222222222222222222222222222222222222222222222 +2703 2222222222222222222222222222222222222222222222222222222 +2704 2222222222222222222222222222222222222222222222222222222 +2705 2222222222222222222222222222222222222222222222222222222 +2706 2222222222222222222222222222222222222222222222222222222 +2707 2222222222222222222222222222222222222222222222222222222 +2708 2222222222222222222222222222222222222222222222222222222 +2709 2222222222222222222222222222222222222222222222222222222 +2710 2222222222222222222222222222222222222222222222222222222 +2711 2222222222222222222222222222222222222222222222222222222 +2712 2222222222222222222222222222222222222222222222222222222 +2713 2222222222222222222222222222222222222222222222222222222 +2714 2222222222222222222222222222222222222222222222222222222 +2715 2222222222222222222222222222222222222222222222222222222 +2716 2222222222222222222222222222222222222222222222222222222 +2717 2222222222222222222222222222222222222222222222222222222 +2718 2222222222222222222222222222222222222222222222222222222 +2719 2222222222222222222222222222222222222222222222222222222 +2720 2222222222222222222222222222222222222222222222222222222 +2721 2222222222222222222222222222222222222222222222222222222 +2722 2222222222222222222222222222222222222222222222222222222 +2723 2222222222222222222222222222222222222222222222222222222 +2724 2222222222222222222222222222222222222222222222222222222 +2725 2222222222222222222222222222222222222222222222222222222 +2726 2222222222222222222222222222222222222222222222222222222 +2727 2222222222222222222222222222222222222222222222222222222 +2728 2222222222222222222222222222222222222222222222222222222 +2729 2222222222222222222222222222222222222222222222222222222 +2730 2222222222222222222222222222222222222222222222222222222 +2731 2222222222222222222222222222222222222222222222222222222 +2732 2222222222222222222222222222222222222222222222222222222 +2733 2222222222222222222222222222222222222222222222222222222 +2734 2222222222222222222222222222222222222222222222222222222 +2735 2222222222222222222222222222222222222222222222222222222 +2736 2222222222222222222222222222222222222222222222222222222 +2737 2222222222222222222222222222222222222222222222222222222 +2738 2222222222222222222222222222222222222222222222222222222 +2739 2222222222222222222222222222222222222222222222222222222 +2740 2222222222222222222222222222222222222222222222222222222 +2741 2222222222222222222222222222222222222222222222222222222 +2742 2222222222222222222222222222222222222222222222222222222 +2743 2222222222222222222222222222222222222222222222222222222 +2744 2222222222222222222222222222222222222222222222222222222 +2745 2222222222222222222222222222222222222222222222222222222 +2746 2222222222222222222222222222222222222222222222222222222 +2747 2222222222222222222222222222222222222222222222222222222 +2748 2222222222222222222222222222222222222222222222222222222 +2749 2222222222222222222222222222222222222222222222222222222 +2750 2222222222222222222222222222222222222222222222222222222 +2751 2222222222222222222222222222222222222222222222222222222 +2752 2222222222222222222222222222222222222222222222222222222 +2753 2222222222222222222222222222222222222222222222222222222 +2754 2222222222222222222222222222222222222222222222222222222 +2755 2222222222222222222222222222222222222222222222222222222 +2756 2222222222222222222222222222222222222222222222222222222 +2757 2222222222222222222222222222222222222222222222222222222 +2758 2222222222222222222222222222222222222222222222222222222 +2759 2222222222222222222222222222222222222222222222222222222 +2760 2222222222222222222222222222222222222222222222222222222 +2761 2222222222222222222222222222222222222222222222222222222 +2762 2222222222222222222222222222222222222222222222222222222 +2763 2222222222222222222222222222222222222222222222222222222 +2764 2222222222222222222222222222222222222222222222222222222 +2765 2222222222222222222222222222222222222222222222222222222 +2766 2222222222222222222222222222222222222222222222222222222 +2767 2222222222222222222222222222222222222222222222222222222 +2768 2222222222222222222222222222222222222222222222222222222 +2769 2222222222222222222222222222222222222222222222222222222 +2770 2222222222222222222222222222222222222222222222222222222 +2771 2222222222222222222222222222222222222222222222222222222 +2772 2222222222222222222222222222222222222222222222222222222 +2773 2222222222222222222222222222222222222222222222222222222 +2774 2222222222222222222222222222222222222222222222222222222 +2775 2222222222222222222222222222222222222222222222222222222 +2776 2222222222222222222222222222222222222222222222222222222 +2777 2222222222222222222222222222222222222222222222222222222 +2778 2222222222222222222222222222222222222222222222222222222 +2779 2222222222222222222222222222222222222222222222222222222 +2780 2222222222222222222222222222222222222222222222222222222 +2781 2222222222222222222222222222222222222222222222222222222 +2782 2222222222222222222222222222222222222222222222222222222 +2783 2222222222222222222222222222222222222222222222222222222 +2784 2222222222222222222222222222222222222222222222222222222 +2785 2222222222222222222222222222222222222222222222222222222 +2786 2222222222222222222222222222222222222222222222222222222 +2787 2222222222222222222222222222222222222222222222222222222 +2788 2222222222222222222222222222222222222222222222222222222 +2789 2222222222222222222222222222222222222222222222222222222 +2790 2222222222222222222222222222222222222222222222222222222 +2791 2222222222222222222222222222222222222222222222222222222 +2792 2222222222222222222222222222222222222222222222222222222 +2793 2222222222222222222222222222222222222222222222222222222 +2794 2222222222222222222222222222222222222222222222222222222 +2795 2222222222222222222222222222222222222222222222222222222 +2796 2222222222222222222222222222222222222222222222222222222 +2797 2222222222222222222222222222222222222222222222222222222 +2798 2222222222222222222222222222222222222222222222222222222 +2799 2222222222222222222222222222222222222222222222222222222 +2800 2222222222222222222222222222222222222222222222222222222 +2801 2222222222222222222222222222222222222222222222222222222 +2802 2222222222222222222222222222222222222222222222222222222 +2803 2222222222222222222222222222222222222222222222222222222 +2804 2222222222222222222222222222222222222222222222222222222 +2805 2222222222222222222222222222222222222222222222222222222 +2806 2222222222222222222222222222222222222222222222222222222 +2807 2222222222222222222222222222222222222222222222222222222 +2808 2222222222222222222222222222222222222222222222222222222 +2809 2222222222222222222222222222222222222222222222222222222 +2810 2222222222222222222222222222222222222222222222222222222 +2811 2222222222222222222222222222222222222222222222222222222 +2812 2222222222222222222222222222222222222222222222222222222 +2813 2222222222222222222222222222222222222222222222222222222 +2814 2222222222222222222222222222222222222222222222222222222 +2815 2222222222222222222222222222222222222222222222222222222 +2816 2222222222222222222222222222222222222222222222222222222 +2817 2222222222222222222222222222222222222222222222222222222 +2818 2222222222222222222222222222222222222222222222222222222 +2819 2222222222222222222222222222222222222222222222222222222 +2820 2222222222222222222222222222222222222222222222222222222 +2821 2222222222222222222222222222222222222222222222222222222 +2822 2222222222222222222222222222222222222222222222222222222 +2823 2222222222222222222222222222222222222222222222222222222 +2824 2222222222222222222222222222222222222222222222222222222 +2825 2222222222222222222222222222222222222222222222222222222 +2826 2222222222222222222222222222222222222222222222222222222 +2827 2222222222222222222222222222222222222222222222222222222 +2828 2222222222222222222222222222222222222222222222222222222 +2829 2222222222222222222222222222222222222222222222222222222 +2830 2222222222222222222222222222222222222222222222222222222 +2831 2222222222222222222222222222222222222222222222222222222 +2832 2222222222222222222222222222222222222222222222222222222 +2833 2222222222222222222222222222222222222222222222222222222 +2834 2222222222222222222222222222222222222222222222222222222 +2835 2222222222222222222222222222222222222222222222222222222 +2836 2222222222222222222222222222222222222222222222222222222 +2837 2222222222222222222222222222222222222222222222222222222 +2838 2222222222222222222222222222222222222222222222222222222 +2839 2222222222222222222222222222222222222222222222222222222 +2840 2222222222222222222222222222222222222222222222222222222 +2841 2222222222222222222222222222222222222222222222222222222 +2842 2222222222222222222222222222222222222222222222222222222 +2843 2222222222222222222222222222222222222222222222222222222 +2844 2222222222222222222222222222222222222222222222222222222 +2845 2222222222222222222222222222222222222222222222222222222 +2846 2222222222222222222222222222222222222222222222222222222 +2847 2222222222222222222222222222222222222222222222222222222 +2848 2222222222222222222222222222222222222222222222222222222 +2849 2222222222222222222222222222222222222222222222222222222 +2850 2222222222222222222222222222222222222222222222222222222 +2851 2222222222222222222222222222222222222222222222222222222 +2852 2222222222222222222222222222222222222222222222222222222 +2853 2222222222222222222222222222222222222222222222222222222 +2854 2222222222222222222222222222222222222222222222222222222 +2855 2222222222222222222222222222222222222222222222222222222 +2856 2222222222222222222222222222222222222222222222222222222 +2857 2222222222222222222222222222222222222222222222222222222 +2858 2222222222222222222222222222222222222222222222222222222 +2859 2222222222222222222222222222222222222222222222222222222 +2860 2222222222222222222222222222222222222222222222222222222 +2861 2222222222222222222222222222222222222222222222222222222 +2862 2222222222222222222222222222222222222222222222222222222 +2863 2222222222222222222222222222222222222222222222222222222 +2864 2222222222222222222222222222222222222222222222222222222 +2865 2222222222222222222222222222222222222222222222222222222 +2866 2222222222222222222222222222222222222222222222222222222 +2867 2222222222222222222222222222222222222222222222222222222 +2868 2222222222222222222222222222222222222222222222222222222 +2869 2222222222222222222222222222222222222222222222222222222 +2870 2222222222222222222222222222222222222222222222222222222 +2871 2222222222222222222222222222222222222222222222222222222 +2872 2222222222222222222222222222222222222222222222222222222 +2873 2222222222222222222222222222222222222222222222222222222 +2874 2222222222222222222222222222222222222222222222222222222 +2875 2222222222222222222222222222222222222222222222222222222 +2876 2222222222222222222222222222222222222222222222222222222 +2877 2222222222222222222222222222222222222222222222222222222 +2878 2222222222222222222222222222222222222222222222222222222 +2879 2222222222222222222222222222222222222222222222222222222 +2880 2222222222222222222222222222222222222222222222222222222 +2881 2222222222222222222222222222222222222222222222222222222 +2882 2222222222222222222222222222222222222222222222222222222 +2883 2222222222222222222222222222222222222222222222222222222 +2884 2222222222222222222222222222222222222222222222222222222 +2885 2222222222222222222222222222222222222222222222222222222 +2886 2222222222222222222222222222222222222222222222222222222 +2887 2222222222222222222222222222222222222222222222222222222 +2888 2222222222222222222222222222222222222222222222222222222 +2889 2222222222222222222222222222222222222222222222222222222 +2890 2222222222222222222222222222222222222222222222222222222 +2891 2222222222222222222222222222222222222222222222222222222 +2892 2222222222222222222222222222222222222222222222222222222 +2893 2222222222222222222222222222222222222222222222222222222 +2894 2222222222222222222222222222222222222222222222222222222 +2895 2222222222222222222222222222222222222222222222222222222 +2896 2222222222222222222222222222222222222222222222222222222 +2897 2222222222222222222222222222222222222222222222222222222 +2898 2222222222222222222222222222222222222222222222222222222 +2899 2222222222222222222222222222222222222222222222222222222 +2900 2222222222222222222222222222222222222222222222222222222 +2901 2222222222222222222222222222222222222222222222222222222 +2902 2222222222222222222222222222222222222222222222222222222 +2903 2222222222222222222222222222222222222222222222222222222 +2904 2222222222222222222222222222222222222222222222222222222 +2905 2222222222222222222222222222222222222222222222222222222 +2906 2222222222222222222222222222222222222222222222222222222 +2907 2222222222222222222222222222222222222222222222222222222 +2908 2222222222222222222222222222222222222222222222222222222 +2909 2222222222222222222222222222222222222222222222222222222 +2910 2222222222222222222222222222222222222222222222222222222 +2911 2222222222222222222222222222222222222222222222222222222 +2912 2222222222222222222222222222222222222222222222222222222 +2913 2222222222222222222222222222222222222222222222222222222 +2914 2222222222222222222222222222222222222222222222222222222 +2915 2222222222222222222222222222222222222222222222222222222 +2916 2222222222222222222222222222222222222222222222222222222 +2917 2222222222222222222222222222222222222222222222222222222 +2918 2222222222222222222222222222222222222222222222222222222 +2919 2222222222222222222222222222222222222222222222222222222 +2920 2222222222222222222222222222222222222222222222222222222 +2921 2222222222222222222222222222222222222222222222222222222 +2922 2222222222222222222222222222222222222222222222222222222 +2923 2222222222222222222222222222222222222222222222222222222 +2924 2222222222222222222222222222222222222222222222222222222 +2925 2222222222222222222222222222222222222222222222222222222 +2926 2222222222222222222222222222222222222222222222222222222 +2927 2222222222222222222222222222222222222222222222222222222 +2928 2222222222222222222222222222222222222222222222222222222 +2929 2222222222222222222222222222222222222222222222222222222 +2930 2222222222222222222222222222222222222222222222222222222 +2931 2222222222222222222222222222222222222222222222222222222 +2932 2222222222222222222222222222222222222222222222222222222 +2933 2222222222222222222222222222222222222222222222222222222 +2934 2222222222222222222222222222222222222222222222222222222 +2935 2222222222222222222222222222222222222222222222222222222 +2936 2222222222222222222222222222222222222222222222222222222 +2937 2222222222222222222222222222222222222222222222222222222 +2938 2222222222222222222222222222222222222222222222222222222 +2939 2222222222222222222222222222222222222222222222222222222 +2940 2222222222222222222222222222222222222222222222222222222 +2941 2222222222222222222222222222222222222222222222222222222 +2942 2222222222222222222222222222222222222222222222222222222 +2943 2222222222222222222222222222222222222222222222222222222 +2944 2222222222222222222222222222222222222222222222222222222 +2945 2222222222222222222222222222222222222222222222222222222 +2946 2222222222222222222222222222222222222222222222222222222 +2947 2222222222222222222222222222222222222222222222222222222 +2948 2222222222222222222222222222222222222222222222222222222 +2949 2222222222222222222222222222222222222222222222222222222 +2950 2222222222222222222222222222222222222222222222222222222 +2951 2222222222222222222222222222222222222222222222222222222 +2952 2222222222222222222222222222222222222222222222222222222 +2953 2222222222222222222222222222222222222222222222222222222 +2954 2222222222222222222222222222222222222222222222222222222 +2955 2222222222222222222222222222222222222222222222222222222 +2956 2222222222222222222222222222222222222222222222222222222 +2957 2222222222222222222222222222222222222222222222222222222 +2958 2222222222222222222222222222222222222222222222222222222 +2959 2222222222222222222222222222222222222222222222222222222 +2960 2222222222222222222222222222222222222222222222222222222 +2961 2222222222222222222222222222222222222222222222222222222 +2962 2222222222222222222222222222222222222222222222222222222 +2963 2222222222222222222222222222222222222222222222222222222 +2964 2222222222222222222222222222222222222222222222222222222 +2965 2222222222222222222222222222222222222222222222222222222 +2966 2222222222222222222222222222222222222222222222222222222 +2967 2222222222222222222222222222222222222222222222222222222 +2968 2222222222222222222222222222222222222222222222222222222 +2969 2222222222222222222222222222222222222222222222222222222 +2970 2222222222222222222222222222222222222222222222222222222 +2971 2222222222222222222222222222222222222222222222222222222 +2972 2222222222222222222222222222222222222222222222222222222 +2973 2222222222222222222222222222222222222222222222222222222 +2974 2222222222222222222222222222222222222222222222222222222 +2975 2222222222222222222222222222222222222222222222222222222 +2976 2222222222222222222222222222222222222222222222222222222 +2977 2222222222222222222222222222222222222222222222222222222 +2978 2222222222222222222222222222222222222222222222222222222 +2979 2222222222222222222222222222222222222222222222222222222 +2980 2222222222222222222222222222222222222222222222222222222 +2981 2222222222222222222222222222222222222222222222222222222 +2982 2222222222222222222222222222222222222222222222222222222 +2983 2222222222222222222222222222222222222222222222222222222 +2984 2222222222222222222222222222222222222222222222222222222 +2985 2222222222222222222222222222222222222222222222222222222 +2986 2222222222222222222222222222222222222222222222222222222 +2987 2222222222222222222222222222222222222222222222222222222 +2988 2222222222222222222222222222222222222222222222222222222 +2989 2222222222222222222222222222222222222222222222222222222 +2990 2222222222222222222222222222222222222222222222222222222 +2991 2222222222222222222222222222222222222222222222222222222 +2992 2222222222222222222222222222222222222222222222222222222 +2993 2222222222222222222222222222222222222222222222222222222 +2994 2222222222222222222222222222222222222222222222222222222 +2995 2222222222222222222222222222222222222222222222222222222 +2996 2222222222222222222222222222222222222222222222222222222 +2997 2222222222222222222222222222222222222222222222222222222 +2998 2222222222222222222222222222222222222222222222222222222 +2999 2222222222222222222222222222222222222222222222222222222 +3000 2222222222222222222222222222222222222222222222222222222 +3001 2222222222222222222222222222222222222222222222222222222 +3002 2222222222222222222222222222222222222222222222222222222 +3003 2222222222222222222222222222222222222222222222222222222 +3004 2222222222222222222222222222222222222222222222222222222 +3005 2222222222222222222222222222222222222222222222222222222 +3006 2222222222222222222222222222222222222222222222222222222 +3007 2222222222222222222222222222222222222222222222222222222 +3008 2222222222222222222222222222222222222222222222222222222 +3009 2222222222222222222222222222222222222222222222222222222 +3010 2222222222222222222222222222222222222222222222222222222 +3011 2222222222222222222222222222222222222222222222222222222 +3012 2222222222222222222222222222222222222222222222222222222 +3013 2222222222222222222222222222222222222222222222222222222 +3014 2222222222222222222222222222222222222222222222222222222 +3015 2222222222222222222222222222222222222222222222222222222 +3016 2222222222222222222222222222222222222222222222222222222 +3017 2222222222222222222222222222222222222222222222222222222 +3018 2222222222222222222222222222222222222222222222222222222 +3019 2222222222222222222222222222222222222222222222222222222 +3020 2222222222222222222222222222222222222222222222222222222 +3021 2222222222222222222222222222222222222222222222222222222 +3022 2222222222222222222222222222222222222222222222222222222 +3023 2222222222222222222222222222222222222222222222222222222 +3024 2222222222222222222222222222222222222222222222222222222 +3025 2222222222222222222222222222222222222222222222222222222 +3026 2222222222222222222222222222222222222222222222222222222 +3027 2222222222222222222222222222222222222222222222222222222 +3028 2222222222222222222222222222222222222222222222222222222 +3029 2222222222222222222222222222222222222222222222222222222 +3030 2222222222222222222222222222222222222222222222222222222 +3031 2222222222222222222222222222222222222222222222222222222 +3032 2222222222222222222222222222222222222222222222222222222 +3033 2222222222222222222222222222222222222222222222222222222 +3034 2222222222222222222222222222222222222222222222222222222 +3035 2222222222222222222222222222222222222222222222222222222 +3036 2222222222222222222222222222222222222222222222222222222 +3037 2222222222222222222222222222222222222222222222222222222 +3038 2222222222222222222222222222222222222222222222222222222 +3039 2222222222222222222222222222222222222222222222222222222 +3040 2222222222222222222222222222222222222222222222222222222 +3041 2222222222222222222222222222222222222222222222222222222 +3042 2222222222222222222222222222222222222222222222222222222 +3043 2222222222222222222222222222222222222222222222222222222 +3044 2222222222222222222222222222222222222222222222222222222 +3045 2222222222222222222222222222222222222222222222222222222 +3046 2222222222222222222222222222222222222222222222222222222 +3047 2222222222222222222222222222222222222222222222222222222 +3048 2222222222222222222222222222222222222222222222222222222 +3049 2222222222222222222222222222222222222222222222222222222 +3050 2222222222222222222222222222222222222222222222222222222 +3051 2222222222222222222222222222222222222222222222222222222 +3052 2222222222222222222222222222222222222222222222222222222 +3053 2222222222222222222222222222222222222222222222222222222 +3054 2222222222222222222222222222222222222222222222222222222 +3055 2222222222222222222222222222222222222222222222222222222 +3056 2222222222222222222222222222222222222222222222222222222 +3057 2222222222222222222222222222222222222222222222222222222 +3058 2222222222222222222222222222222222222222222222222222222 +3059 2222222222222222222222222222222222222222222222222222222 +3060 2222222222222222222222222222222222222222222222222222222 +3061 2222222222222222222222222222222222222222222222222222222 +3062 2222222222222222222222222222222222222222222222222222222 +3063 2222222222222222222222222222222222222222222222222222222 +3064 2222222222222222222222222222222222222222222222222222222 +3065 2222222222222222222222222222222222222222222222222222222 +3066 2222222222222222222222222222222222222222222222222222222 +3067 2222222222222222222222222222222222222222222222222222222 +3068 2222222222222222222222222222222222222222222222222222222 +3069 2222222222222222222222222222222222222222222222222222222 +3070 2222222222222222222222222222222222222222222222222222222 +3071 2222222222222222222222222222222222222222222222222222222 +3072 2222222222222222222222222222222222222222222222222222222 +3073 2222222222222222222222222222222222222222222222222222222 +3074 2222222222222222222222222222222222222222222222222222222 +3075 2222222222222222222222222222222222222222222222222222222 +3076 2222222222222222222222222222222222222222222222222222222 +3077 2222222222222222222222222222222222222222222222222222222 +3078 2222222222222222222222222222222222222222222222222222222 +3079 2222222222222222222222222222222222222222222222222222222 +3080 2222222222222222222222222222222222222222222222222222222 +3081 2222222222222222222222222222222222222222222222222222222 +3082 2222222222222222222222222222222222222222222222222222222 +3083 2222222222222222222222222222222222222222222222222222222 +3084 2222222222222222222222222222222222222222222222222222222 +3085 2222222222222222222222222222222222222222222222222222222 +3086 2222222222222222222222222222222222222222222222222222222 +3087 2222222222222222222222222222222222222222222222222222222 +3088 2222222222222222222222222222222222222222222222222222222 +3089 2222222222222222222222222222222222222222222222222222222 +3090 2222222222222222222222222222222222222222222222222222222 +3091 2222222222222222222222222222222222222222222222222222222 +3092 2222222222222222222222222222222222222222222222222222222 +3093 2222222222222222222222222222222222222222222222222222222 +3094 2222222222222222222222222222222222222222222222222222222 +3095 2222222222222222222222222222222222222222222222222222222 +3096 2222222222222222222222222222222222222222222222222222222 +3097 2222222222222222222222222222222222222222222222222222222 +3098 2222222222222222222222222222222222222222222222222222222 +3099 2222222222222222222222222222222222222222222222222222222 +3100 2222222222222222222222222222222222222222222222222222222 +3101 2222222222222222222222222222222222222222222222222222222 +3102 2222222222222222222222222222222222222222222222222222222 +3103 2222222222222222222222222222222222222222222222222222222 +3104 2222222222222222222222222222222222222222222222222222222 +3105 2222222222222222222222222222222222222222222222222222222 +3106 2222222222222222222222222222222222222222222222222222222 +3107 2222222222222222222222222222222222222222222222222222222 +3108 2222222222222222222222222222222222222222222222222222222 +3109 2222222222222222222222222222222222222222222222222222222 +3110 2222222222222222222222222222222222222222222222222222222 +3111 2222222222222222222222222222222222222222222222222222222 +3112 2222222222222222222222222222222222222222222222222222222 +3113 2222222222222222222222222222222222222222222222222222222 +3114 2222222222222222222222222222222222222222222222222222222 +3115 2222222222222222222222222222222222222222222222222222222 +3116 2222222222222222222222222222222222222222222222222222222 +3117 2222222222222222222222222222222222222222222222222222222 +3118 2222222222222222222222222222222222222222222222222222222 +3119 2222222222222222222222222222222222222222222222222222222 +3120 2222222222222222222222222222222222222222222222222222222 +3121 2222222222222222222222222222222222222222222222222222222 +3122 2222222222222222222222222222222222222222222222222222222 +3123 2222222222222222222222222222222222222222222222222222222 +3124 2222222222222222222222222222222222222222222222222222222 +3125 2222222222222222222222222222222222222222222222222222222 +3126 2222222222222222222222222222222222222222222222222222222 +3127 2222222222222222222222222222222222222222222222222222222 +3128 2222222222222222222222222222222222222222222222222222222 +3129 2222222222222222222222222222222222222222222222222222222 +3130 2222222222222222222222222222222222222222222222222222222 +3131 2222222222222222222222222222222222222222222222222222222 +3132 2222222222222222222222222222222222222222222222222222222 +3133 2222222222222222222222222222222222222222222222222222222 +3134 2222222222222222222222222222222222222222222222222222222 +3135 2222222222222222222222222222222222222222222222222222222 +3136 2222222222222222222222222222222222222222222222222222222 +3137 2222222222222222222222222222222222222222222222222222222 +3138 2222222222222222222222222222222222222222222222222222222 +3139 2222222222222222222222222222222222222222222222222222222 +3140 2222222222222222222222222222222222222222222222222222222 +3141 2222222222222222222222222222222222222222222222222222222 +3142 2222222222222222222222222222222222222222222222222222222 +3143 2222222222222222222222222222222222222222222222222222222 +3144 2222222222222222222222222222222222222222222222222222222 +3145 2222222222222222222222222222222222222222222222222222222 +3146 2222222222222222222222222222222222222222222222222222222 +3147 2222222222222222222222222222222222222222222222222222222 +3148 2222222222222222222222222222222222222222222222222222222 +3149 2222222222222222222222222222222222222222222222222222222 +3150 2222222222222222222222222222222222222222222222222222222 +3151 2222222222222222222222222222222222222222222222222222222 +3152 2222222222222222222222222222222222222222222222222222222 +3153 2222222222222222222222222222222222222222222222222222222 +3154 2222222222222222222222222222222222222222222222222222222 +3155 2222222222222222222222222222222222222222222222222222222 +3156 2222222222222222222222222222222222222222222222222222222 +3157 2222222222222222222222222222222222222222222222222222222 +3158 2222222222222222222222222222222222222222222222222222222 +3159 2222222222222222222222222222222222222222222222222222222 +3160 2222222222222222222222222222222222222222222222222222222 +3161 2222222222222222222222222222222222222222222222222222222 +3162 2222222222222222222222222222222222222222222222222222222 +3163 2222222222222222222222222222222222222222222222222222222 +3164 2222222222222222222222222222222222222222222222222222222 +3165 2222222222222222222222222222222222222222222222222222222 +3166 2222222222222222222222222222222222222222222222222222222 +3167 2222222222222222222222222222222222222222222222222222222 +3168 2222222222222222222222222222222222222222222222222222222 +3169 2222222222222222222222222222222222222222222222222222222 +3170 2222222222222222222222222222222222222222222222222222222 +3171 2222222222222222222222222222222222222222222222222222222 +3172 2222222222222222222222222222222222222222222222222222222 +3173 2222222222222222222222222222222222222222222222222222222 +3174 2222222222222222222222222222222222222222222222222222222 +3175 2222222222222222222222222222222222222222222222222222222 +3176 2222222222222222222222222222222222222222222222222222222 +3177 2222222222222222222222222222222222222222222222222222222 +3178 2222222222222222222222222222222222222222222222222222222 +3179 2222222222222222222222222222222222222222222222222222222 +3180 2222222222222222222222222222222222222222222222222222222 +3181 2222222222222222222222222222222222222222222222222222222 +3182 2222222222222222222222222222222222222222222222222222222 +3183 2222222222222222222222222222222222222222222222222222222 +3184 2222222222222222222222222222222222222222222222222222222 +3185 2222222222222222222222222222222222222222222222222222222 +3186 2222222222222222222222222222222222222222222222222222222 +3187 2222222222222222222222222222222222222222222222222222222 +3188 2222222222222222222222222222222222222222222222222222222 +3189 2222222222222222222222222222222222222222222222222222222 +3190 2222222222222222222222222222222222222222222222222222222 +3191 2222222222222222222222222222222222222222222222222222222 +3192 2222222222222222222222222222222222222222222222222222222 +3193 2222222222222222222222222222222222222222222222222222222 +3194 2222222222222222222222222222222222222222222222222222222 +3195 2222222222222222222222222222222222222222222222222222222 +3196 2222222222222222222222222222222222222222222222222222222 +3197 2222222222222222222222222222222222222222222222222222222 +3198 2222222222222222222222222222222222222222222222222222222 +3199 2222222222222222222222222222222222222222222222222222222 +3200 2222222222222222222222222222222222222222222222222222222 +3201 2222222222222222222222222222222222222222222222222222222 +3202 2222222222222222222222222222222222222222222222222222222 +3203 2222222222222222222222222222222222222222222222222222222 +3204 2222222222222222222222222222222222222222222222222222222 +3205 2222222222222222222222222222222222222222222222222222222 +3206 2222222222222222222222222222222222222222222222222222222 +3207 2222222222222222222222222222222222222222222222222222222 +3208 2222222222222222222222222222222222222222222222222222222 +3209 2222222222222222222222222222222222222222222222222222222 +3210 2222222222222222222222222222222222222222222222222222222 +3211 2222222222222222222222222222222222222222222222222222222 +3212 2222222222222222222222222222222222222222222222222222222 +3213 2222222222222222222222222222222222222222222222222222222 +3214 2222222222222222222222222222222222222222222222222222222 +3215 2222222222222222222222222222222222222222222222222222222 +3216 2222222222222222222222222222222222222222222222222222222 +3217 2222222222222222222222222222222222222222222222222222222 +3218 2222222222222222222222222222222222222222222222222222222 +3219 2222222222222222222222222222222222222222222222222222222 +3220 2222222222222222222222222222222222222222222222222222222 +3221 2222222222222222222222222222222222222222222222222222222 +3222 2222222222222222222222222222222222222222222222222222222 +3223 2222222222222222222222222222222222222222222222222222222 +3224 2222222222222222222222222222222222222222222222222222222 +3225 2222222222222222222222222222222222222222222222222222222 +3226 2222222222222222222222222222222222222222222222222222222 +3227 2222222222222222222222222222222222222222222222222222222 +3228 2222222222222222222222222222222222222222222222222222222 +3229 2222222222222222222222222222222222222222222222222222222 +3230 2222222222222222222222222222222222222222222222222222222 +3231 2222222222222222222222222222222222222222222222222222222 +3232 2222222222222222222222222222222222222222222222222222222 +3233 2222222222222222222222222222222222222222222222222222222 +3234 2222222222222222222222222222222222222222222222222222222 +3235 2222222222222222222222222222222222222222222222222222222 +3236 2222222222222222222222222222222222222222222222222222222 +3237 2222222222222222222222222222222222222222222222222222222 +3238 2222222222222222222222222222222222222222222222222222222 +3239 2222222222222222222222222222222222222222222222222222222 +3240 2222222222222222222222222222222222222222222222222222222 +3241 2222222222222222222222222222222222222222222222222222222 +3242 2222222222222222222222222222222222222222222222222222222 +3243 2222222222222222222222222222222222222222222222222222222 +3244 2222222222222222222222222222222222222222222222222222222 +3245 2222222222222222222222222222222222222222222222222222222 +3246 2222222222222222222222222222222222222222222222222222222 +3247 2222222222222222222222222222222222222222222222222222222 +3248 2222222222222222222222222222222222222222222222222222222 +3249 2222222222222222222222222222222222222222222222222222222 +3250 2222222222222222222222222222222222222222222222222222222 +3251 2222222222222222222222222222222222222222222222222222222 +3252 2222222222222222222222222222222222222222222222222222222 +3253 2222222222222222222222222222222222222222222222222222222 +3254 2222222222222222222222222222222222222222222222222222222 +3255 2222222222222222222222222222222222222222222222222222222 +3256 2222222222222222222222222222222222222222222222222222222 +3257 2222222222222222222222222222222222222222222222222222222 +3258 2222222222222222222222222222222222222222222222222222222 +3259 2222222222222222222222222222222222222222222222222222222 +3260 2222222222222222222222222222222222222222222222222222222 +3261 2222222222222222222222222222222222222222222222222222222 +3262 2222222222222222222222222222222222222222222222222222222 +3263 2222222222222222222222222222222222222222222222222222222 +3264 2222222222222222222222222222222222222222222222222222222 +3265 2222222222222222222222222222222222222222222222222222222 +3266 2222222222222222222222222222222222222222222222222222222 +3267 2222222222222222222222222222222222222222222222222222222 +3268 2222222222222222222222222222222222222222222222222222222 +3269 2222222222222222222222222222222222222222222222222222222 +3270 2222222222222222222222222222222222222222222222222222222 +3271 2222222222222222222222222222222222222222222222222222222 +3272 2222222222222222222222222222222222222222222222222222222 +3273 2222222222222222222222222222222222222222222222222222222 +3274 2222222222222222222222222222222222222222222222222222222 +3275 2222222222222222222222222222222222222222222222222222222 +3276 2222222222222222222222222222222222222222222222222222222 +3277 2222222222222222222222222222222222222222222222222222222 +3278 2222222222222222222222222222222222222222222222222222222 +3279 2222222222222222222222222222222222222222222222222222222 +3280 2222222222222222222222222222222222222222222222222222222 +3281 2222222222222222222222222222222222222222222222222222222 +3282 2222222222222222222222222222222222222222222222222222222 +3283 2222222222222222222222222222222222222222222222222222222 +3284 2222222222222222222222222222222222222222222222222222222 +3285 2222222222222222222222222222222222222222222222222222222 +3286 2222222222222222222222222222222222222222222222222222222 +3287 2222222222222222222222222222222222222222222222222222222 +3288 2222222222222222222222222222222222222222222222222222222 +3289 2222222222222222222222222222222222222222222222222222222 +3290 2222222222222222222222222222222222222222222222222222222 +3291 2222222222222222222222222222222222222222222222222222222 +3292 2222222222222222222222222222222222222222222222222222222 +3293 2222222222222222222222222222222222222222222222222222222 +3294 2222222222222222222222222222222222222222222222222222222 +3295 2222222222222222222222222222222222222222222222222222222 +3296 2222222222222222222222222222222222222222222222222222222 +3297 2222222222222222222222222222222222222222222222222222222 +3298 2222222222222222222222222222222222222222222222222222222 +3299 2222222222222222222222222222222222222222222222222222222 +3300 2222222222222222222222222222222222222222222222222222222 +3301 2222222222222222222222222222222222222222222222222222222 +3302 2222222222222222222222222222222222222222222222222222222 +3303 2222222222222222222222222222222222222222222222222222222 +3304 2222222222222222222222222222222222222222222222222222222 +3305 2222222222222222222222222222222222222222222222222222222 +3306 2222222222222222222222222222222222222222222222222222222 +3307 2222222222222222222222222222222222222222222222222222222 +3308 2222222222222222222222222222222222222222222222222222222 +3309 2222222222222222222222222222222222222222222222222222222 +3310 2222222222222222222222222222222222222222222222222222222 +3311 2222222222222222222222222222222222222222222222222222222 +3312 2222222222222222222222222222222222222222222222222222222 +3313 2222222222222222222222222222222222222222222222222222222 +3314 2222222222222222222222222222222222222222222222222222222 +3315 2222222222222222222222222222222222222222222222222222222 +3316 2222222222222222222222222222222222222222222222222222222 +3317 2222222222222222222222222222222222222222222222222222222 +3318 2222222222222222222222222222222222222222222222222222222 +3319 2222222222222222222222222222222222222222222222222222222 +3320 2222222222222222222222222222222222222222222222222222222 +3321 2222222222222222222222222222222222222222222222222222222 +3322 2222222222222222222222222222222222222222222222222222222 +3323 2222222222222222222222222222222222222222222222222222222 +3324 2222222222222222222222222222222222222222222222222222222 +3325 2222222222222222222222222222222222222222222222222222222 +3326 2222222222222222222222222222222222222222222222222222222 +3327 2222222222222222222222222222222222222222222222222222222 +3328 2222222222222222222222222222222222222222222222222222222 +3329 2222222222222222222222222222222222222222222222222222222 +3330 2222222222222222222222222222222222222222222222222222222 +3331 2222222222222222222222222222222222222222222222222222222 +3332 2222222222222222222222222222222222222222222222222222222 +3333 2222222222222222222222222222222222222222222222222222222 +3334 2222222222222222222222222222222222222222222222222222222 +3335 2222222222222222222222222222222222222222222222222222222 +3336 2222222222222222222222222222222222222222222222222222222 +3337 2222222222222222222222222222222222222222222222222222222 +3338 2222222222222222222222222222222222222222222222222222222 +3339 2222222222222222222222222222222222222222222222222222222 +3340 2222222222222222222222222222222222222222222222222222222 +3341 2222222222222222222222222222222222222222222222222222222 +3342 2222222222222222222222222222222222222222222222222222222 +3343 2222222222222222222222222222222222222222222222222222222 +3344 2222222222222222222222222222222222222222222222222222222 +3345 2222222222222222222222222222222222222222222222222222222 +3346 2222222222222222222222222222222222222222222222222222222 +3347 2222222222222222222222222222222222222222222222222222222 +3348 2222222222222222222222222222222222222222222222222222222 +3349 2222222222222222222222222222222222222222222222222222222 +3350 2222222222222222222222222222222222222222222222222222222 +3351 2222222222222222222222222222222222222222222222222222222 +3352 2222222222222222222222222222222222222222222222222222222 +3353 2222222222222222222222222222222222222222222222222222222 +3354 2222222222222222222222222222222222222222222222222222222 +3355 2222222222222222222222222222222222222222222222222222222 +3356 2222222222222222222222222222222222222222222222222222222 +3357 2222222222222222222222222222222222222222222222222222222 +3358 2222222222222222222222222222222222222222222222222222222 +3359 2222222222222222222222222222222222222222222222222222222 +3360 2222222222222222222222222222222222222222222222222222222 +3361 2222222222222222222222222222222222222222222222222222222 +3362 2222222222222222222222222222222222222222222222222222222 +3363 2222222222222222222222222222222222222222222222222222222 +3364 2222222222222222222222222222222222222222222222222222222 +3365 2222222222222222222222222222222222222222222222222222222 +3366 2222222222222222222222222222222222222222222222222222222 +3367 2222222222222222222222222222222222222222222222222222222 +3368 2222222222222222222222222222222222222222222222222222222 +3369 2222222222222222222222222222222222222222222222222222222 +3370 2222222222222222222222222222222222222222222222222222222 +3371 2222222222222222222222222222222222222222222222222222222 +3372 2222222222222222222222222222222222222222222222222222222 +3373 2222222222222222222222222222222222222222222222222222222 +3374 2222222222222222222222222222222222222222222222222222222 +3375 2222222222222222222222222222222222222222222222222222222 +3376 2222222222222222222222222222222222222222222222222222222 +3377 2222222222222222222222222222222222222222222222222222222 +3378 2222222222222222222222222222222222222222222222222222222 +3379 2222222222222222222222222222222222222222222222222222222 +3380 2222222222222222222222222222222222222222222222222222222 +3381 2222222222222222222222222222222222222222222222222222222 +3382 2222222222222222222222222222222222222222222222222222222 +3383 2222222222222222222222222222222222222222222222222222222 +3384 2222222222222222222222222222222222222222222222222222222 +3385 2222222222222222222222222222222222222222222222222222222 +3386 2222222222222222222222222222222222222222222222222222222 +3387 2222222222222222222222222222222222222222222222222222222 +3388 2222222222222222222222222222222222222222222222222222222 +3389 2222222222222222222222222222222222222222222222222222222 +3390 2222222222222222222222222222222222222222222222222222222 +3391 2222222222222222222222222222222222222222222222222222222 +3392 2222222222222222222222222222222222222222222222222222222 +3393 2222222222222222222222222222222222222222222222222222222 +3394 2222222222222222222222222222222222222222222222222222222 +3395 2222222222222222222222222222222222222222222222222222222 +3396 2222222222222222222222222222222222222222222222222222222 +3397 2222222222222222222222222222222222222222222222222222222 +3398 2222222222222222222222222222222222222222222222222222222 +3399 2222222222222222222222222222222222222222222222222222222 +3400 2222222222222222222222222222222222222222222222222222222 +3401 2222222222222222222222222222222222222222222222222222222 +3402 2222222222222222222222222222222222222222222222222222222 +3403 2222222222222222222222222222222222222222222222222222222 +3404 2222222222222222222222222222222222222222222222222222222 +3405 2222222222222222222222222222222222222222222222222222222 +3406 2222222222222222222222222222222222222222222222222222222 +3407 2222222222222222222222222222222222222222222222222222222 +3408 2222222222222222222222222222222222222222222222222222222 +3409 2222222222222222222222222222222222222222222222222222222 +3410 2222222222222222222222222222222222222222222222222222222 +3411 2222222222222222222222222222222222222222222222222222222 +3412 2222222222222222222222222222222222222222222222222222222 +3413 2222222222222222222222222222222222222222222222222222222 +3414 2222222222222222222222222222222222222222222222222222222 +3415 2222222222222222222222222222222222222222222222222222222 +3416 2222222222222222222222222222222222222222222222222222222 +3417 2222222222222222222222222222222222222222222222222222222 +3418 2222222222222222222222222222222222222222222222222222222 +3419 2222222222222222222222222222222222222222222222222222222 +3420 2222222222222222222222222222222222222222222222222222222 +3421 2222222222222222222222222222222222222222222222222222222 +3422 2222222222222222222222222222222222222222222222222222222 +3423 2222222222222222222222222222222222222222222222222222222 +3424 2222222222222222222222222222222222222222222222222222222 +3425 2222222222222222222222222222222222222222222222222222222 +3426 2222222222222222222222222222222222222222222222222222222 +3427 2222222222222222222222222222222222222222222222222222222 +3428 2222222222222222222222222222222222222222222222222222222 +3429 2222222222222222222222222222222222222222222222222222222 +3430 2222222222222222222222222222222222222222222222222222222 +3431 2222222222222222222222222222222222222222222222222222222 +3432 2222222222222222222222222222222222222222222222222222222 +3433 2222222222222222222222222222222222222222222222222222222 +3434 2222222222222222222222222222222222222222222222222222222 +3435 2222222222222222222222222222222222222222222222222222222 +3436 2222222222222222222222222222222222222222222222222222222 +3437 2222222222222222222222222222222222222222222222222222222 +3438 2222222222222222222222222222222222222222222222222222222 +3439 2222222222222222222222222222222222222222222222222222222 +3440 2222222222222222222222222222222222222222222222222222222 +3441 2222222222222222222222222222222222222222222222222222222 +3442 2222222222222222222222222222222222222222222222222222222 +3443 2222222222222222222222222222222222222222222222222222222 +3444 2222222222222222222222222222222222222222222222222222222 +3445 2222222222222222222222222222222222222222222222222222222 +3446 2222222222222222222222222222222222222222222222222222222 +3447 2222222222222222222222222222222222222222222222222222222 +3448 2222222222222222222222222222222222222222222222222222222 +3449 2222222222222222222222222222222222222222222222222222222 +3450 2222222222222222222222222222222222222222222222222222222 +3451 2222222222222222222222222222222222222222222222222222222 +3452 2222222222222222222222222222222222222222222222222222222 +3453 2222222222222222222222222222222222222222222222222222222 +3454 2222222222222222222222222222222222222222222222222222222 +3455 2222222222222222222222222222222222222222222222222222222 +3456 2222222222222222222222222222222222222222222222222222222 +3457 2222222222222222222222222222222222222222222222222222222 +3458 2222222222222222222222222222222222222222222222222222222 +3459 2222222222222222222222222222222222222222222222222222222 +3460 2222222222222222222222222222222222222222222222222222222 +3461 2222222222222222222222222222222222222222222222222222222 +3462 2222222222222222222222222222222222222222222222222222222 +3463 2222222222222222222222222222222222222222222222222222222 +3464 2222222222222222222222222222222222222222222222222222222 +3465 2222222222222222222222222222222222222222222222222222222 +3466 2222222222222222222222222222222222222222222222222222222 +3467 2222222222222222222222222222222222222222222222222222222 +3468 2222222222222222222222222222222222222222222222222222222 +3469 2222222222222222222222222222222222222222222222222222222 +3470 2222222222222222222222222222222222222222222222222222222 +3471 2222222222222222222222222222222222222222222222222222222 +3472 2222222222222222222222222222222222222222222222222222222 +3473 2222222222222222222222222222222222222222222222222222222 +3474 2222222222222222222222222222222222222222222222222222222 +3475 2222222222222222222222222222222222222222222222222222222 +3476 2222222222222222222222222222222222222222222222222222222 +3477 2222222222222222222222222222222222222222222222222222222 +3478 2222222222222222222222222222222222222222222222222222222 +3479 2222222222222222222222222222222222222222222222222222222 +3480 2222222222222222222222222222222222222222222222222222222 +3481 2222222222222222222222222222222222222222222222222222222 +3482 2222222222222222222222222222222222222222222222222222222 +3483 2222222222222222222222222222222222222222222222222222222 +3484 2222222222222222222222222222222222222222222222222222222 +3485 2222222222222222222222222222222222222222222222222222222 +3486 2222222222222222222222222222222222222222222222222222222 +3487 2222222222222222222222222222222222222222222222222222222 +3488 2222222222222222222222222222222222222222222222222222222 +3489 2222222222222222222222222222222222222222222222222222222 +3490 2222222222222222222222222222222222222222222222222222222 +3491 2222222222222222222222222222222222222222222222222222222 +3492 2222222222222222222222222222222222222222222222222222222 +3493 2222222222222222222222222222222222222222222222222222222 +3494 2222222222222222222222222222222222222222222222222222222 +3495 2222222222222222222222222222222222222222222222222222222 +3496 2222222222222222222222222222222222222222222222222222222 +3497 2222222222222222222222222222222222222222222222222222222 +3498 2222222222222222222222222222222222222222222222222222222 +3499 2222222222222222222222222222222222222222222222222222222 +3500 2222222222222222222222222222222222222222222222222222222 +3501 2222222222222222222222222222222222222222222222222222222 +3502 2222222222222222222222222222222222222222222222222222222 +3503 2222222222222222222222222222222222222222222222222222222 +3504 2222222222222222222222222222222222222222222222222222222 +3505 2222222222222222222222222222222222222222222222222222222 +3506 2222222222222222222222222222222222222222222222222222222 +3507 2222222222222222222222222222222222222222222222222222222 +3508 2222222222222222222222222222222222222222222222222222222 +3509 2222222222222222222222222222222222222222222222222222222 +3510 2222222222222222222222222222222222222222222222222222222 +3511 2222222222222222222222222222222222222222222222222222222 +3512 2222222222222222222222222222222222222222222222222222222 +3513 2222222222222222222222222222222222222222222222222222222 +3514 2222222222222222222222222222222222222222222222222222222 +3515 2222222222222222222222222222222222222222222222222222222 +3516 2222222222222222222222222222222222222222222222222222222 +3517 2222222222222222222222222222222222222222222222222222222 +3518 2222222222222222222222222222222222222222222222222222222 +3519 2222222222222222222222222222222222222222222222222222222 +3520 2222222222222222222222222222222222222222222222222222222 +3521 2222222222222222222222222222222222222222222222222222222 +3522 2222222222222222222222222222222222222222222222222222222 +3523 2222222222222222222222222222222222222222222222222222222 +3524 2222222222222222222222222222222222222222222222222222222 +3525 2222222222222222222222222222222222222222222222222222222 +3526 2222222222222222222222222222222222222222222222222222222 +3527 2222222222222222222222222222222222222222222222222222222 +3528 2222222222222222222222222222222222222222222222222222222 +3529 2222222222222222222222222222222222222222222222222222222 +3530 2222222222222222222222222222222222222222222222222222222 +3531 2222222222222222222222222222222222222222222222222222222 +3532 2222222222222222222222222222222222222222222222222222222 +3533 2222222222222222222222222222222222222222222222222222222 +3534 2222222222222222222222222222222222222222222222222222222 +3535 2222222222222222222222222222222222222222222222222222222 +3536 2222222222222222222222222222222222222222222222222222222 +3537 2222222222222222222222222222222222222222222222222222222 +3538 2222222222222222222222222222222222222222222222222222222 +3539 2222222222222222222222222222222222222222222222222222222 +3540 2222222222222222222222222222222222222222222222222222222 +3541 2222222222222222222222222222222222222222222222222222222 +3542 2222222222222222222222222222222222222222222222222222222 +3543 2222222222222222222222222222222222222222222222222222222 +3544 2222222222222222222222222222222222222222222222222222222 +3545 2222222222222222222222222222222222222222222222222222222 +3546 2222222222222222222222222222222222222222222222222222222 +3547 2222222222222222222222222222222222222222222222222222222 +3548 2222222222222222222222222222222222222222222222222222222 +3549 2222222222222222222222222222222222222222222222222222222 +3550 2222222222222222222222222222222222222222222222222222222 +3551 2222222222222222222222222222222222222222222222222222222 +3552 2222222222222222222222222222222222222222222222222222222 +3553 2222222222222222222222222222222222222222222222222222222 +3554 2222222222222222222222222222222222222222222222222222222 +3555 2222222222222222222222222222222222222222222222222222222 +3556 2222222222222222222222222222222222222222222222222222222 +3557 2222222222222222222222222222222222222222222222222222222 +3558 2222222222222222222222222222222222222222222222222222222 +3559 2222222222222222222222222222222222222222222222222222222 +3560 2222222222222222222222222222222222222222222222222222222 +3561 2222222222222222222222222222222222222222222222222222222 +3562 2222222222222222222222222222222222222222222222222222222 +3563 2222222222222222222222222222222222222222222222222222222 +3564 2222222222222222222222222222222222222222222222222222222 +3565 2222222222222222222222222222222222222222222222222222222 +3566 2222222222222222222222222222222222222222222222222222222 +3567 2222222222222222222222222222222222222222222222222222222 +3568 2222222222222222222222222222222222222222222222222222222 +3569 2222222222222222222222222222222222222222222222222222222 +3570 2222222222222222222222222222222222222222222222222222222 +3571 2222222222222222222222222222222222222222222222222222222 +3572 2222222222222222222222222222222222222222222222222222222 +3573 2222222222222222222222222222222222222222222222222222222 +3574 2222222222222222222222222222222222222222222222222222222 +3575 2222222222222222222222222222222222222222222222222222222 +3576 2222222222222222222222222222222222222222222222222222222 +3577 2222222222222222222222222222222222222222222222222222222 +3578 2222222222222222222222222222222222222222222222222222222 +3579 2222222222222222222222222222222222222222222222222222222 +3580 2222222222222222222222222222222222222222222222222222222 +3581 2222222222222222222222222222222222222222222222222222222 +3582 2222222222222222222222222222222222222222222222222222222 +3583 2222222222222222222222222222222222222222222222222222222 +3584 2222222222222222222222222222222222222222222222222222222 +3585 2222222222222222222222222222222222222222222222222222222 +3586 2222222222222222222222222222222222222222222222222222222 +3587 2222222222222222222222222222222222222222222222222222222 +3588 2222222222222222222222222222222222222222222222222222222 +3589 2222222222222222222222222222222222222222222222222222222 +3590 2222222222222222222222222222222222222222222222222222222 +3591 2222222222222222222222222222222222222222222222222222222 +3592 2222222222222222222222222222222222222222222222222222222 +3593 2222222222222222222222222222222222222222222222222222222 +3594 2222222222222222222222222222222222222222222222222222222 +3595 2222222222222222222222222222222222222222222222222222222 +3596 2222222222222222222222222222222222222222222222222222222 +3597 2222222222222222222222222222222222222222222222222222222 +3598 2222222222222222222222222222222222222222222222222222222 +3599 2222222222222222222222222222222222222222222222222222222 +3600 2222222222222222222222222222222222222222222222222222222 +3601 2222222222222222222222222222222222222222222222222222222 +3602 2222222222222222222222222222222222222222222222222222222 +3603 2222222222222222222222222222222222222222222222222222222 +3604 2222222222222222222222222222222222222222222222222222222 +3605 2222222222222222222222222222222222222222222222222222222 +3606 2222222222222222222222222222222222222222222222222222222 +3607 2222222222222222222222222222222222222222222222222222222 +3608 2222222222222222222222222222222222222222222222222222222 +3609 2222222222222222222222222222222222222222222222222222222 +3610 2222222222222222222222222222222222222222222222222222222 +3611 2222222222222222222222222222222222222222222222222222222 +3612 2222222222222222222222222222222222222222222222222222222 +3613 2222222222222222222222222222222222222222222222222222222 +3614 2222222222222222222222222222222222222222222222222222222 +3615 2222222222222222222222222222222222222222222222222222222 +3616 2222222222222222222222222222222222222222222222222222222 +3617 2222222222222222222222222222222222222222222222222222222 +3618 2222222222222222222222222222222222222222222222222222222 +3619 2222222222222222222222222222222222222222222222222222222 +3620 2222222222222222222222222222222222222222222222222222222 +3621 2222222222222222222222222222222222222222222222222222222 +3622 2222222222222222222222222222222222222222222222222222222 +3623 2222222222222222222222222222222222222222222222222222222 +3624 2222222222222222222222222222222222222222222222222222222 +3625 2222222222222222222222222222222222222222222222222222222 +3626 2222222222222222222222222222222222222222222222222222222 +3627 2222222222222222222222222222222222222222222222222222222 +3628 2222222222222222222222222222222222222222222222222222222 +3629 2222222222222222222222222222222222222222222222222222222 +3630 2222222222222222222222222222222222222222222222222222222 +3631 2222222222222222222222222222222222222222222222222222222 +3632 2222222222222222222222222222222222222222222222222222222 +3633 2222222222222222222222222222222222222222222222222222222 +3634 2222222222222222222222222222222222222222222222222222222 +3635 2222222222222222222222222222222222222222222222222222222 +3636 2222222222222222222222222222222222222222222222222222222 +3637 2222222222222222222222222222222222222222222222222222222 +3638 2222222222222222222222222222222222222222222222222222222 +3639 2222222222222222222222222222222222222222222222222222222 +3640 2222222222222222222222222222222222222222222222222222222 +3641 2222222222222222222222222222222222222222222222222222222 +3642 2222222222222222222222222222222222222222222222222222222 +3643 2222222222222222222222222222222222222222222222222222222 +3644 2222222222222222222222222222222222222222222222222222222 +3645 2222222222222222222222222222222222222222222222222222222 +3646 2222222222222222222222222222222222222222222222222222222 +3647 2222222222222222222222222222222222222222222222222222222 +3648 2222222222222222222222222222222222222222222222222222222 +3649 2222222222222222222222222222222222222222222222222222222 +3650 2222222222222222222222222222222222222222222222222222222 +3651 2222222222222222222222222222222222222222222222222222222 +3652 2222222222222222222222222222222222222222222222222222222 +3653 2222222222222222222222222222222222222222222222222222222 +3654 2222222222222222222222222222222222222222222222222222222 +3655 2222222222222222222222222222222222222222222222222222222 +3656 2222222222222222222222222222222222222222222222222222222 +3657 2222222222222222222222222222222222222222222222222222222 +3658 2222222222222222222222222222222222222222222222222222222 +3659 2222222222222222222222222222222222222222222222222222222 +3660 2222222222222222222222222222222222222222222222222222222 +3661 2222222222222222222222222222222222222222222222222222222 +3662 2222222222222222222222222222222222222222222222222222222 +3663 2222222222222222222222222222222222222222222222222222222 +3664 2222222222222222222222222222222222222222222222222222222 +3665 2222222222222222222222222222222222222222222222222222222 +3666 2222222222222222222222222222222222222222222222222222222 +3667 2222222222222222222222222222222222222222222222222222222 +3668 2222222222222222222222222222222222222222222222222222222 +3669 2222222222222222222222222222222222222222222222222222222 +3670 2222222222222222222222222222222222222222222222222222222 +3671 2222222222222222222222222222222222222222222222222222222 +3672 2222222222222222222222222222222222222222222222222222222 +3673 2222222222222222222222222222222222222222222222222222222 +3674 2222222222222222222222222222222222222222222222222222222 +3675 2222222222222222222222222222222222222222222222222222222 +3676 2222222222222222222222222222222222222222222222222222222 +3677 2222222222222222222222222222222222222222222222222222222 +3678 2222222222222222222222222222222222222222222222222222222 +3679 2222222222222222222222222222222222222222222222222222222 +3680 2222222222222222222222222222222222222222222222222222222 +3681 2222222222222222222222222222222222222222222222222222222 +3682 2222222222222222222222222222222222222222222222222222222 +3683 2222222222222222222222222222222222222222222222222222222 +3684 2222222222222222222222222222222222222222222222222222222 +3685 2222222222222222222222222222222222222222222222222222222 +3686 2222222222222222222222222222222222222222222222222222222 +3687 2222222222222222222222222222222222222222222222222222222 +3688 2222222222222222222222222222222222222222222222222222222 +3689 2222222222222222222222222222222222222222222222222222222 +3690 2222222222222222222222222222222222222222222222222222222 +3691 2222222222222222222222222222222222222222222222222222222 +3692 2222222222222222222222222222222222222222222222222222222 +3693 2222222222222222222222222222222222222222222222222222222 +3694 2222222222222222222222222222222222222222222222222222222 +3695 2222222222222222222222222222222222222222222222222222222 +3696 2222222222222222222222222222222222222222222222222222222 +3697 2222222222222222222222222222222222222222222222222222222 +3698 2222222222222222222222222222222222222222222222222222222 +3699 2222222222222222222222222222222222222222222222222222222 +3700 2222222222222222222222222222222222222222222222222222222 +3701 2222222222222222222222222222222222222222222222222222222 +3702 2222222222222222222222222222222222222222222222222222222 +3703 2222222222222222222222222222222222222222222222222222222 +3704 2222222222222222222222222222222222222222222222222222222 +3705 2222222222222222222222222222222222222222222222222222222 +3706 2222222222222222222222222222222222222222222222222222222 +3707 2222222222222222222222222222222222222222222222222222222 +3708 2222222222222222222222222222222222222222222222222222222 +3709 2222222222222222222222222222222222222222222222222222222 +3710 2222222222222222222222222222222222222222222222222222222 +3711 2222222222222222222222222222222222222222222222222222222 +3712 2222222222222222222222222222222222222222222222222222222 +3713 2222222222222222222222222222222222222222222222222222222 +3714 2222222222222222222222222222222222222222222222222222222 +3715 2222222222222222222222222222222222222222222222222222222 +3716 2222222222222222222222222222222222222222222222222222222 +3717 2222222222222222222222222222222222222222222222222222222 +3718 2222222222222222222222222222222222222222222222222222222 +3719 2222222222222222222222222222222222222222222222222222222 +3720 2222222222222222222222222222222222222222222222222222222 +3721 2222222222222222222222222222222222222222222222222222222 +3722 2222222222222222222222222222222222222222222222222222222 +3723 2222222222222222222222222222222222222222222222222222222 +3724 2222222222222222222222222222222222222222222222222222222 +3725 2222222222222222222222222222222222222222222222222222222 +3726 2222222222222222222222222222222222222222222222222222222 +3727 2222222222222222222222222222222222222222222222222222222 +3728 2222222222222222222222222222222222222222222222222222222 +3729 2222222222222222222222222222222222222222222222222222222 +3730 2222222222222222222222222222222222222222222222222222222 +3731 2222222222222222222222222222222222222222222222222222222 +3732 2222222222222222222222222222222222222222222222222222222 +3733 2222222222222222222222222222222222222222222222222222222 +3734 2222222222222222222222222222222222222222222222222222222 +3735 2222222222222222222222222222222222222222222222222222222 +3736 2222222222222222222222222222222222222222222222222222222 +3737 2222222222222222222222222222222222222222222222222222222 +3738 2222222222222222222222222222222222222222222222222222222 +3739 2222222222222222222222222222222222222222222222222222222 +3740 2222222222222222222222222222222222222222222222222222222 +3741 2222222222222222222222222222222222222222222222222222222 +3742 2222222222222222222222222222222222222222222222222222222 +3743 2222222222222222222222222222222222222222222222222222222 +3744 2222222222222222222222222222222222222222222222222222222 +3745 2222222222222222222222222222222222222222222222222222222 +3746 2222222222222222222222222222222222222222222222222222222 +3747 2222222222222222222222222222222222222222222222222222222 +3748 2222222222222222222222222222222222222222222222222222222 +3749 2222222222222222222222222222222222222222222222222222222 +3750 2222222222222222222222222222222222222222222222222222222 +3751 2222222222222222222222222222222222222222222222222222222 +3752 2222222222222222222222222222222222222222222222222222222 +3753 2222222222222222222222222222222222222222222222222222222 +3754 2222222222222222222222222222222222222222222222222222222 +3755 2222222222222222222222222222222222222222222222222222222 +3756 2222222222222222222222222222222222222222222222222222222 +3757 2222222222222222222222222222222222222222222222222222222 +3758 2222222222222222222222222222222222222222222222222222222 +3759 2222222222222222222222222222222222222222222222222222222 +3760 2222222222222222222222222222222222222222222222222222222 +3761 2222222222222222222222222222222222222222222222222222222 +3762 2222222222222222222222222222222222222222222222222222222 +3763 2222222222222222222222222222222222222222222222222222222 +3764 2222222222222222222222222222222222222222222222222222222 +3765 2222222222222222222222222222222222222222222222222222222 +3766 2222222222222222222222222222222222222222222222222222222 +3767 2222222222222222222222222222222222222222222222222222222 +3768 2222222222222222222222222222222222222222222222222222222 +3769 2222222222222222222222222222222222222222222222222222222 +3770 2222222222222222222222222222222222222222222222222222222 +3771 2222222222222222222222222222222222222222222222222222222 +3772 2222222222222222222222222222222222222222222222222222222 +3773 2222222222222222222222222222222222222222222222222222222 +3774 2222222222222222222222222222222222222222222222222222222 +3775 2222222222222222222222222222222222222222222222222222222 +3776 2222222222222222222222222222222222222222222222222222222 +3777 2222222222222222222222222222222222222222222222222222222 +3778 2222222222222222222222222222222222222222222222222222222 +3779 2222222222222222222222222222222222222222222222222222222 +3780 2222222222222222222222222222222222222222222222222222222 +3781 2222222222222222222222222222222222222222222222222222222 +3782 2222222222222222222222222222222222222222222222222222222 +3783 2222222222222222222222222222222222222222222222222222222 +3784 2222222222222222222222222222222222222222222222222222222 +3785 2222222222222222222222222222222222222222222222222222222 +3786 2222222222222222222222222222222222222222222222222222222 +3787 2222222222222222222222222222222222222222222222222222222 +3788 2222222222222222222222222222222222222222222222222222222 +3789 2222222222222222222222222222222222222222222222222222222 +3790 2222222222222222222222222222222222222222222222222222222 +3791 2222222222222222222222222222222222222222222222222222222 +3792 2222222222222222222222222222222222222222222222222222222 +3793 2222222222222222222222222222222222222222222222222222222 +3794 2222222222222222222222222222222222222222222222222222222 +3795 2222222222222222222222222222222222222222222222222222222 +3796 2222222222222222222222222222222222222222222222222222222 +3797 2222222222222222222222222222222222222222222222222222222 +3798 2222222222222222222222222222222222222222222222222222222 +3799 2222222222222222222222222222222222222222222222222222222 +3800 2222222222222222222222222222222222222222222222222222222 +3801 2222222222222222222222222222222222222222222222222222222 +3802 2222222222222222222222222222222222222222222222222222222 +3803 2222222222222222222222222222222222222222222222222222222 +3804 2222222222222222222222222222222222222222222222222222222 +3805 2222222222222222222222222222222222222222222222222222222 +3806 2222222222222222222222222222222222222222222222222222222 +3807 2222222222222222222222222222222222222222222222222222222 +3808 2222222222222222222222222222222222222222222222222222222 +3809 2222222222222222222222222222222222222222222222222222222 +3810 2222222222222222222222222222222222222222222222222222222 +3811 2222222222222222222222222222222222222222222222222222222 +3812 2222222222222222222222222222222222222222222222222222222 +3813 2222222222222222222222222222222222222222222222222222222 +3814 2222222222222222222222222222222222222222222222222222222 +3815 2222222222222222222222222222222222222222222222222222222 +3816 2222222222222222222222222222222222222222222222222222222 +3817 2222222222222222222222222222222222222222222222222222222 +3818 2222222222222222222222222222222222222222222222222222222 +3819 2222222222222222222222222222222222222222222222222222222 +3820 2222222222222222222222222222222222222222222222222222222 +3821 2222222222222222222222222222222222222222222222222222222 +3822 2222222222222222222222222222222222222222222222222222222 +3823 2222222222222222222222222222222222222222222222222222222 +3824 2222222222222222222222222222222222222222222222222222222 +3825 2222222222222222222222222222222222222222222222222222222 +3826 2222222222222222222222222222222222222222222222222222222 +3827 2222222222222222222222222222222222222222222222222222222 +3828 2222222222222222222222222222222222222222222222222222222 +3829 2222222222222222222222222222222222222222222222222222222 +3830 2222222222222222222222222222222222222222222222222222222 +3831 2222222222222222222222222222222222222222222222222222222 +3832 2222222222222222222222222222222222222222222222222222222 +3833 2222222222222222222222222222222222222222222222222222222 +3834 2222222222222222222222222222222222222222222222222222222 +3835 2222222222222222222222222222222222222222222222222222222 +3836 2222222222222222222222222222222222222222222222222222222 +3837 2222222222222222222222222222222222222222222222222222222 +3838 2222222222222222222222222222222222222222222222222222222 +3839 2222222222222222222222222222222222222222222222222222222 +3840 2222222222222222222222222222222222222222222222222222222 +3841 2222222222222222222222222222222222222222222222222222222 +3842 2222222222222222222222222222222222222222222222222222222 +3843 2222222222222222222222222222222222222222222222222222222 +3844 2222222222222222222222222222222222222222222222222222222 +3845 2222222222222222222222222222222222222222222222222222222 +3846 2222222222222222222222222222222222222222222222222222222 +3847 2222222222222222222222222222222222222222222222222222222 +3848 2222222222222222222222222222222222222222222222222222222 +3849 2222222222222222222222222222222222222222222222222222222 +3850 2222222222222222222222222222222222222222222222222222222 +3851 2222222222222222222222222222222222222222222222222222222 +3852 2222222222222222222222222222222222222222222222222222222 +3853 2222222222222222222222222222222222222222222222222222222 +3854 2222222222222222222222222222222222222222222222222222222 +3855 2222222222222222222222222222222222222222222222222222222 +3856 2222222222222222222222222222222222222222222222222222222 +3857 2222222222222222222222222222222222222222222222222222222 +3858 2222222222222222222222222222222222222222222222222222222 +3859 2222222222222222222222222222222222222222222222222222222 +3860 2222222222222222222222222222222222222222222222222222222 +3861 2222222222222222222222222222222222222222222222222222222 +3862 2222222222222222222222222222222222222222222222222222222 +3863 2222222222222222222222222222222222222222222222222222222 +3864 2222222222222222222222222222222222222222222222222222222 +3865 2222222222222222222222222222222222222222222222222222222 +3866 2222222222222222222222222222222222222222222222222222222 +3867 2222222222222222222222222222222222222222222222222222222 +3868 2222222222222222222222222222222222222222222222222222222 +3869 2222222222222222222222222222222222222222222222222222222 +3870 2222222222222222222222222222222222222222222222222222222 +3871 2222222222222222222222222222222222222222222222222222222 +3872 2222222222222222222222222222222222222222222222222222222 +3873 2222222222222222222222222222222222222222222222222222222 +3874 2222222222222222222222222222222222222222222222222222222 +3875 2222222222222222222222222222222222222222222222222222222 +3876 2222222222222222222222222222222222222222222222222222222 +3877 2222222222222222222222222222222222222222222222222222222 +3878 2222222222222222222222222222222222222222222222222222222 +3879 2222222222222222222222222222222222222222222222222222222 +3880 2222222222222222222222222222222222222222222222222222222 +3881 2222222222222222222222222222222222222222222222222222222 +3882 2222222222222222222222222222222222222222222222222222222 +3883 2222222222222222222222222222222222222222222222222222222 +3884 2222222222222222222222222222222222222222222222222222222 +3885 2222222222222222222222222222222222222222222222222222222 +3886 2222222222222222222222222222222222222222222222222222222 +3887 2222222222222222222222222222222222222222222222222222222 +3888 2222222222222222222222222222222222222222222222222222222 +3889 2222222222222222222222222222222222222222222222222222222 +3890 2222222222222222222222222222222222222222222222222222222 +3891 2222222222222222222222222222222222222222222222222222222 +3892 2222222222222222222222222222222222222222222222222222222 +3893 2222222222222222222222222222222222222222222222222222222 +3894 2222222222222222222222222222222222222222222222222222222 +3895 2222222222222222222222222222222222222222222222222222222 +3896 2222222222222222222222222222222222222222222222222222222 +3897 2222222222222222222222222222222222222222222222222222222 +3898 2222222222222222222222222222222222222222222222222222222 +3899 2222222222222222222222222222222222222222222222222222222 +3900 2222222222222222222222222222222222222222222222222222222 +3901 2222222222222222222222222222222222222222222222222222222 +3902 2222222222222222222222222222222222222222222222222222222 +3903 2222222222222222222222222222222222222222222222222222222 +3904 2222222222222222222222222222222222222222222222222222222 +3905 2222222222222222222222222222222222222222222222222222222 +3906 2222222222222222222222222222222222222222222222222222222 +3907 2222222222222222222222222222222222222222222222222222222 +3908 2222222222222222222222222222222222222222222222222222222 +3909 2222222222222222222222222222222222222222222222222222222 +3910 2222222222222222222222222222222222222222222222222222222 +3911 2222222222222222222222222222222222222222222222222222222 +3912 2222222222222222222222222222222222222222222222222222222 +3913 2222222222222222222222222222222222222222222222222222222 +3914 2222222222222222222222222222222222222222222222222222222 +3915 2222222222222222222222222222222222222222222222222222222 +3916 2222222222222222222222222222222222222222222222222222222 +3917 2222222222222222222222222222222222222222222222222222222 +3918 2222222222222222222222222222222222222222222222222222222 +3919 2222222222222222222222222222222222222222222222222222222 +3920 2222222222222222222222222222222222222222222222222222222 +3921 2222222222222222222222222222222222222222222222222222222 +3922 2222222222222222222222222222222222222222222222222222222 +3923 2222222222222222222222222222222222222222222222222222222 +3924 2222222222222222222222222222222222222222222222222222222 +3925 2222222222222222222222222222222222222222222222222222222 +3926 2222222222222222222222222222222222222222222222222222222 +3927 2222222222222222222222222222222222222222222222222222222 +3928 2222222222222222222222222222222222222222222222222222222 +3929 2222222222222222222222222222222222222222222222222222222 +3930 2222222222222222222222222222222222222222222222222222222 +3931 2222222222222222222222222222222222222222222222222222222 +3932 2222222222222222222222222222222222222222222222222222222 +3933 2222222222222222222222222222222222222222222222222222222 +3934 2222222222222222222222222222222222222222222222222222222 +3935 2222222222222222222222222222222222222222222222222222222 +3936 2222222222222222222222222222222222222222222222222222222 +3937 2222222222222222222222222222222222222222222222222222222 +3938 2222222222222222222222222222222222222222222222222222222 +3939 2222222222222222222222222222222222222222222222222222222 +3940 2222222222222222222222222222222222222222222222222222222 +3941 2222222222222222222222222222222222222222222222222222222 +3942 2222222222222222222222222222222222222222222222222222222 +3943 2222222222222222222222222222222222222222222222222222222 +3944 2222222222222222222222222222222222222222222222222222222 +3945 2222222222222222222222222222222222222222222222222222222 +3946 2222222222222222222222222222222222222222222222222222222 +3947 2222222222222222222222222222222222222222222222222222222 +3948 2222222222222222222222222222222222222222222222222222222 +3949 2222222222222222222222222222222222222222222222222222222 +3950 2222222222222222222222222222222222222222222222222222222 +3951 2222222222222222222222222222222222222222222222222222222 +3952 2222222222222222222222222222222222222222222222222222222 +3953 2222222222222222222222222222222222222222222222222222222 +3954 2222222222222222222222222222222222222222222222222222222 +3955 2222222222222222222222222222222222222222222222222222222 +3956 2222222222222222222222222222222222222222222222222222222 +3957 2222222222222222222222222222222222222222222222222222222 +3958 2222222222222222222222222222222222222222222222222222222 +3959 2222222222222222222222222222222222222222222222222222222 +3960 2222222222222222222222222222222222222222222222222222222 +3961 2222222222222222222222222222222222222222222222222222222 +3962 2222222222222222222222222222222222222222222222222222222 +3963 2222222222222222222222222222222222222222222222222222222 +3964 2222222222222222222222222222222222222222222222222222222 +3965 2222222222222222222222222222222222222222222222222222222 +3966 2222222222222222222222222222222222222222222222222222222 +3967 2222222222222222222222222222222222222222222222222222222 +3968 2222222222222222222222222222222222222222222222222222222 +3969 2222222222222222222222222222222222222222222222222222222 +3970 2222222222222222222222222222222222222222222222222222222 +3971 2222222222222222222222222222222222222222222222222222222 +3972 2222222222222222222222222222222222222222222222222222222 +3973 2222222222222222222222222222222222222222222222222222222 +3974 2222222222222222222222222222222222222222222222222222222 +3975 2222222222222222222222222222222222222222222222222222222 +3976 2222222222222222222222222222222222222222222222222222222 +3977 2222222222222222222222222222222222222222222222222222222 +3978 2222222222222222222222222222222222222222222222222222222 +3979 2222222222222222222222222222222222222222222222222222222 +3980 2222222222222222222222222222222222222222222222222222222 +3981 2222222222222222222222222222222222222222222222222222222 +3982 2222222222222222222222222222222222222222222222222222222 +3983 2222222222222222222222222222222222222222222222222222222 +3984 2222222222222222222222222222222222222222222222222222222 +3985 2222222222222222222222222222222222222222222222222222222 +3986 2222222222222222222222222222222222222222222222222222222 +3987 2222222222222222222222222222222222222222222222222222222 +3988 2222222222222222222222222222222222222222222222222222222 +3989 2222222222222222222222222222222222222222222222222222222 +3990 2222222222222222222222222222222222222222222222222222222 +3991 2222222222222222222222222222222222222222222222222222222 +3992 2222222222222222222222222222222222222222222222222222222 +3993 2222222222222222222222222222222222222222222222222222222 +3994 2222222222222222222222222222222222222222222222222222222 +3995 2222222222222222222222222222222222222222222222222222222 +3996 2222222222222222222222222222222222222222222222222222222 +3997 2222222222222222222222222222222222222222222222222222222 +3998 2222222222222222222222222222222222222222222222222222222 +3999 2222222222222222222222222222222222222222222222222222222 diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/data.txt b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/data.txt new file mode 100644 index 00000000000..f28670f2968 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/data.txt @@ -0,0 +1,10000 @@ +0000 3333333333333333333333333333333333333333333333333333333 +0001 3333333333333333333333333333333333333333333333333333333 +0002 3333333333333333333333333333333333333333333333333333333 +0003 3333333333333333333333333333333333333333333333333333333 +0004 3333333333333333333333333333333333333333333333333333333 +0005 3333333333333333333333333333333333333333333333333333333 +0006 3333333333333333333333333333333333333333333333333333333 +0007 3333333333333333333333333333333333333333333333333333333 +0008 3333333333333333333333333333333333333333333333333333333 +0009 3333333333333333333333333333333333333333333333333333333 +0010 3333333333333333333333333333333333333333333333333333333 +0011 3333333333333333333333333333333333333333333333333333333 +0012 3333333333333333333333333333333333333333333333333333333 +0013 3333333333333333333333333333333333333333333333333333333 +0014 3333333333333333333333333333333333333333333333333333333 +0015 3333333333333333333333333333333333333333333333333333333 +0016 3333333333333333333333333333333333333333333333333333333 +0017 3333333333333333333333333333333333333333333333333333333 +0018 3333333333333333333333333333333333333333333333333333333 +0019 3333333333333333333333333333333333333333333333333333333 +0020 3333333333333333333333333333333333333333333333333333333 +0021 3333333333333333333333333333333333333333333333333333333 +0022 3333333333333333333333333333333333333333333333333333333 +0023 3333333333333333333333333333333333333333333333333333333 +0024 3333333333333333333333333333333333333333333333333333333 +0025 3333333333333333333333333333333333333333333333333333333 +0026 3333333333333333333333333333333333333333333333333333333 +0027 3333333333333333333333333333333333333333333333333333333 +0028 3333333333333333333333333333333333333333333333333333333 +0029 3333333333333333333333333333333333333333333333333333333 +0030 3333333333333333333333333333333333333333333333333333333 +0031 3333333333333333333333333333333333333333333333333333333 +0032 3333333333333333333333333333333333333333333333333333333 +0033 3333333333333333333333333333333333333333333333333333333 +0034 3333333333333333333333333333333333333333333333333333333 +0035 3333333333333333333333333333333333333333333333333333333 +0036 3333333333333333333333333333333333333333333333333333333 +0037 3333333333333333333333333333333333333333333333333333333 +0038 3333333333333333333333333333333333333333333333333333333 +0039 3333333333333333333333333333333333333333333333333333333 +0040 3333333333333333333333333333333333333333333333333333333 +0041 3333333333333333333333333333333333333333333333333333333 +0042 3333333333333333333333333333333333333333333333333333333 +0043 3333333333333333333333333333333333333333333333333333333 +0044 3333333333333333333333333333333333333333333333333333333 +0045 3333333333333333333333333333333333333333333333333333333 +0046 3333333333333333333333333333333333333333333333333333333 +0047 3333333333333333333333333333333333333333333333333333333 +0048 3333333333333333333333333333333333333333333333333333333 +0049 3333333333333333333333333333333333333333333333333333333 +0050 3333333333333333333333333333333333333333333333333333333 +0051 3333333333333333333333333333333333333333333333333333333 +0052 3333333333333333333333333333333333333333333333333333333 +0053 3333333333333333333333333333333333333333333333333333333 +0054 3333333333333333333333333333333333333333333333333333333 +0055 3333333333333333333333333333333333333333333333333333333 +0056 3333333333333333333333333333333333333333333333333333333 +0057 3333333333333333333333333333333333333333333333333333333 +0058 3333333333333333333333333333333333333333333333333333333 +0059 3333333333333333333333333333333333333333333333333333333 +0060 3333333333333333333333333333333333333333333333333333333 +0061 3333333333333333333333333333333333333333333333333333333 +0062 3333333333333333333333333333333333333333333333333333333 +0063 3333333333333333333333333333333333333333333333333333333 +0064 3333333333333333333333333333333333333333333333333333333 +0065 3333333333333333333333333333333333333333333333333333333 +0066 3333333333333333333333333333333333333333333333333333333 +0067 3333333333333333333333333333333333333333333333333333333 +0068 3333333333333333333333333333333333333333333333333333333 +0069 3333333333333333333333333333333333333333333333333333333 +0070 3333333333333333333333333333333333333333333333333333333 +0071 3333333333333333333333333333333333333333333333333333333 +0072 3333333333333333333333333333333333333333333333333333333 +0073 3333333333333333333333333333333333333333333333333333333 +0074 3333333333333333333333333333333333333333333333333333333 +0075 3333333333333333333333333333333333333333333333333333333 +0076 3333333333333333333333333333333333333333333333333333333 +0077 3333333333333333333333333333333333333333333333333333333 +0078 3333333333333333333333333333333333333333333333333333333 +0079 3333333333333333333333333333333333333333333333333333333 +0080 3333333333333333333333333333333333333333333333333333333 +0081 3333333333333333333333333333333333333333333333333333333 +0082 3333333333333333333333333333333333333333333333333333333 +0083 3333333333333333333333333333333333333333333333333333333 +0084 3333333333333333333333333333333333333333333333333333333 +0085 3333333333333333333333333333333333333333333333333333333 +0086 3333333333333333333333333333333333333333333333333333333 +0087 3333333333333333333333333333333333333333333333333333333 +0088 3333333333333333333333333333333333333333333333333333333 +0089 3333333333333333333333333333333333333333333333333333333 +0090 3333333333333333333333333333333333333333333333333333333 +0091 3333333333333333333333333333333333333333333333333333333 +0092 3333333333333333333333333333333333333333333333333333333 +0093 3333333333333333333333333333333333333333333333333333333 +0094 3333333333333333333333333333333333333333333333333333333 +0095 3333333333333333333333333333333333333333333333333333333 +0096 3333333333333333333333333333333333333333333333333333333 +0097 3333333333333333333333333333333333333333333333333333333 +0098 3333333333333333333333333333333333333333333333333333333 +0099 3333333333333333333333333333333333333333333333333333333 +0100 3333333333333333333333333333333333333333333333333333333 +0101 3333333333333333333333333333333333333333333333333333333 +0102 3333333333333333333333333333333333333333333333333333333 +0103 3333333333333333333333333333333333333333333333333333333 +0104 3333333333333333333333333333333333333333333333333333333 +0105 3333333333333333333333333333333333333333333333333333333 +0106 3333333333333333333333333333333333333333333333333333333 +0107 3333333333333333333333333333333333333333333333333333333 +0108 3333333333333333333333333333333333333333333333333333333 +0109 3333333333333333333333333333333333333333333333333333333 +0110 3333333333333333333333333333333333333333333333333333333 +0111 3333333333333333333333333333333333333333333333333333333 +0112 3333333333333333333333333333333333333333333333333333333 +0113 3333333333333333333333333333333333333333333333333333333 +0114 3333333333333333333333333333333333333333333333333333333 +0115 3333333333333333333333333333333333333333333333333333333 +0116 3333333333333333333333333333333333333333333333333333333 +0117 3333333333333333333333333333333333333333333333333333333 +0118 3333333333333333333333333333333333333333333333333333333 +0119 3333333333333333333333333333333333333333333333333333333 +0120 3333333333333333333333333333333333333333333333333333333 +0121 3333333333333333333333333333333333333333333333333333333 +0122 3333333333333333333333333333333333333333333333333333333 +0123 3333333333333333333333333333333333333333333333333333333 +0124 3333333333333333333333333333333333333333333333333333333 +0125 3333333333333333333333333333333333333333333333333333333 +0126 3333333333333333333333333333333333333333333333333333333 +0127 3333333333333333333333333333333333333333333333333333333 +0128 3333333333333333333333333333333333333333333333333333333 +0129 3333333333333333333333333333333333333333333333333333333 +0130 3333333333333333333333333333333333333333333333333333333 +0131 3333333333333333333333333333333333333333333333333333333 +0132 3333333333333333333333333333333333333333333333333333333 +0133 3333333333333333333333333333333333333333333333333333333 +0134 3333333333333333333333333333333333333333333333333333333 +0135 3333333333333333333333333333333333333333333333333333333 +0136 3333333333333333333333333333333333333333333333333333333 +0137 3333333333333333333333333333333333333333333333333333333 +0138 3333333333333333333333333333333333333333333333333333333 +0139 3333333333333333333333333333333333333333333333333333333 +0140 3333333333333333333333333333333333333333333333333333333 +0141 3333333333333333333333333333333333333333333333333333333 +0142 3333333333333333333333333333333333333333333333333333333 +0143 3333333333333333333333333333333333333333333333333333333 +0144 3333333333333333333333333333333333333333333333333333333 +0145 3333333333333333333333333333333333333333333333333333333 +0146 3333333333333333333333333333333333333333333333333333333 +0147 3333333333333333333333333333333333333333333333333333333 +0148 3333333333333333333333333333333333333333333333333333333 +0149 3333333333333333333333333333333333333333333333333333333 +0150 3333333333333333333333333333333333333333333333333333333 +0151 3333333333333333333333333333333333333333333333333333333 +0152 3333333333333333333333333333333333333333333333333333333 +0153 3333333333333333333333333333333333333333333333333333333 +0154 3333333333333333333333333333333333333333333333333333333 +0155 3333333333333333333333333333333333333333333333333333333 +0156 3333333333333333333333333333333333333333333333333333333 +0157 3333333333333333333333333333333333333333333333333333333 +0158 3333333333333333333333333333333333333333333333333333333 +0159 3333333333333333333333333333333333333333333333333333333 +0160 3333333333333333333333333333333333333333333333333333333 +0161 3333333333333333333333333333333333333333333333333333333 +0162 3333333333333333333333333333333333333333333333333333333 +0163 3333333333333333333333333333333333333333333333333333333 +0164 3333333333333333333333333333333333333333333333333333333 +0165 3333333333333333333333333333333333333333333333333333333 +0166 3333333333333333333333333333333333333333333333333333333 +0167 3333333333333333333333333333333333333333333333333333333 +0168 3333333333333333333333333333333333333333333333333333333 +0169 3333333333333333333333333333333333333333333333333333333 +0170 3333333333333333333333333333333333333333333333333333333 +0171 3333333333333333333333333333333333333333333333333333333 +0172 3333333333333333333333333333333333333333333333333333333 +0173 3333333333333333333333333333333333333333333333333333333 +0174 3333333333333333333333333333333333333333333333333333333 +0175 3333333333333333333333333333333333333333333333333333333 +0176 3333333333333333333333333333333333333333333333333333333 +0177 3333333333333333333333333333333333333333333333333333333 +0178 3333333333333333333333333333333333333333333333333333333 +0179 3333333333333333333333333333333333333333333333333333333 +0180 3333333333333333333333333333333333333333333333333333333 +0181 3333333333333333333333333333333333333333333333333333333 +0182 3333333333333333333333333333333333333333333333333333333 +0183 3333333333333333333333333333333333333333333333333333333 +0184 3333333333333333333333333333333333333333333333333333333 +0185 3333333333333333333333333333333333333333333333333333333 +0186 3333333333333333333333333333333333333333333333333333333 +0187 3333333333333333333333333333333333333333333333333333333 +0188 3333333333333333333333333333333333333333333333333333333 +0189 3333333333333333333333333333333333333333333333333333333 +0190 3333333333333333333333333333333333333333333333333333333 +0191 3333333333333333333333333333333333333333333333333333333 +0192 3333333333333333333333333333333333333333333333333333333 +0193 3333333333333333333333333333333333333333333333333333333 +0194 3333333333333333333333333333333333333333333333333333333 +0195 3333333333333333333333333333333333333333333333333333333 +0196 3333333333333333333333333333333333333333333333333333333 +0197 3333333333333333333333333333333333333333333333333333333 +0198 3333333333333333333333333333333333333333333333333333333 +0199 3333333333333333333333333333333333333333333333333333333 +0200 3333333333333333333333333333333333333333333333333333333 +0201 3333333333333333333333333333333333333333333333333333333 +0202 3333333333333333333333333333333333333333333333333333333 +0203 3333333333333333333333333333333333333333333333333333333 +0204 3333333333333333333333333333333333333333333333333333333 +0205 3333333333333333333333333333333333333333333333333333333 +0206 3333333333333333333333333333333333333333333333333333333 +0207 3333333333333333333333333333333333333333333333333333333 +0208 3333333333333333333333333333333333333333333333333333333 +0209 3333333333333333333333333333333333333333333333333333333 +0210 3333333333333333333333333333333333333333333333333333333 +0211 3333333333333333333333333333333333333333333333333333333 +0212 3333333333333333333333333333333333333333333333333333333 +0213 3333333333333333333333333333333333333333333333333333333 +0214 3333333333333333333333333333333333333333333333333333333 +0215 3333333333333333333333333333333333333333333333333333333 +0216 3333333333333333333333333333333333333333333333333333333 +0217 3333333333333333333333333333333333333333333333333333333 +0218 3333333333333333333333333333333333333333333333333333333 +0219 3333333333333333333333333333333333333333333333333333333 +0220 3333333333333333333333333333333333333333333333333333333 +0221 3333333333333333333333333333333333333333333333333333333 +0222 3333333333333333333333333333333333333333333333333333333 +0223 3333333333333333333333333333333333333333333333333333333 +0224 3333333333333333333333333333333333333333333333333333333 +0225 3333333333333333333333333333333333333333333333333333333 +0226 3333333333333333333333333333333333333333333333333333333 +0227 3333333333333333333333333333333333333333333333333333333 +0228 3333333333333333333333333333333333333333333333333333333 +0229 3333333333333333333333333333333333333333333333333333333 +0230 3333333333333333333333333333333333333333333333333333333 +0231 3333333333333333333333333333333333333333333333333333333 +0232 3333333333333333333333333333333333333333333333333333333 +0233 3333333333333333333333333333333333333333333333333333333 +0234 3333333333333333333333333333333333333333333333333333333 +0235 3333333333333333333333333333333333333333333333333333333 +0236 3333333333333333333333333333333333333333333333333333333 +0237 3333333333333333333333333333333333333333333333333333333 +0238 3333333333333333333333333333333333333333333333333333333 +0239 3333333333333333333333333333333333333333333333333333333 +0240 3333333333333333333333333333333333333333333333333333333 +0241 3333333333333333333333333333333333333333333333333333333 +0242 3333333333333333333333333333333333333333333333333333333 +0243 3333333333333333333333333333333333333333333333333333333 +0244 3333333333333333333333333333333333333333333333333333333 +0245 3333333333333333333333333333333333333333333333333333333 +0246 3333333333333333333333333333333333333333333333333333333 +0247 3333333333333333333333333333333333333333333333333333333 +0248 3333333333333333333333333333333333333333333333333333333 +0249 3333333333333333333333333333333333333333333333333333333 +0250 3333333333333333333333333333333333333333333333333333333 +0251 3333333333333333333333333333333333333333333333333333333 +0252 3333333333333333333333333333333333333333333333333333333 +0253 3333333333333333333333333333333333333333333333333333333 +0254 3333333333333333333333333333333333333333333333333333333 +0255 3333333333333333333333333333333333333333333333333333333 +0256 3333333333333333333333333333333333333333333333333333333 +0257 3333333333333333333333333333333333333333333333333333333 +0258 3333333333333333333333333333333333333333333333333333333 +0259 3333333333333333333333333333333333333333333333333333333 +0260 3333333333333333333333333333333333333333333333333333333 +0261 3333333333333333333333333333333333333333333333333333333 +0262 3333333333333333333333333333333333333333333333333333333 +0263 3333333333333333333333333333333333333333333333333333333 +0264 3333333333333333333333333333333333333333333333333333333 +0265 3333333333333333333333333333333333333333333333333333333 +0266 3333333333333333333333333333333333333333333333333333333 +0267 3333333333333333333333333333333333333333333333333333333 +0268 3333333333333333333333333333333333333333333333333333333 +0269 3333333333333333333333333333333333333333333333333333333 +0270 3333333333333333333333333333333333333333333333333333333 +0271 3333333333333333333333333333333333333333333333333333333 +0272 3333333333333333333333333333333333333333333333333333333 +0273 3333333333333333333333333333333333333333333333333333333 +0274 3333333333333333333333333333333333333333333333333333333 +0275 3333333333333333333333333333333333333333333333333333333 +0276 3333333333333333333333333333333333333333333333333333333 +0277 3333333333333333333333333333333333333333333333333333333 +0278 3333333333333333333333333333333333333333333333333333333 +0279 3333333333333333333333333333333333333333333333333333333 +0280 3333333333333333333333333333333333333333333333333333333 +0281 3333333333333333333333333333333333333333333333333333333 +0282 3333333333333333333333333333333333333333333333333333333 +0283 3333333333333333333333333333333333333333333333333333333 +0284 3333333333333333333333333333333333333333333333333333333 +0285 3333333333333333333333333333333333333333333333333333333 +0286 3333333333333333333333333333333333333333333333333333333 +0287 3333333333333333333333333333333333333333333333333333333 +0288 3333333333333333333333333333333333333333333333333333333 +0289 3333333333333333333333333333333333333333333333333333333 +0290 3333333333333333333333333333333333333333333333333333333 +0291 3333333333333333333333333333333333333333333333333333333 +0292 3333333333333333333333333333333333333333333333333333333 +0293 3333333333333333333333333333333333333333333333333333333 +0294 3333333333333333333333333333333333333333333333333333333 +0295 3333333333333333333333333333333333333333333333333333333 +0296 3333333333333333333333333333333333333333333333333333333 +0297 3333333333333333333333333333333333333333333333333333333 +0298 3333333333333333333333333333333333333333333333333333333 +0299 3333333333333333333333333333333333333333333333333333333 +0300 3333333333333333333333333333333333333333333333333333333 +0301 3333333333333333333333333333333333333333333333333333333 +0302 3333333333333333333333333333333333333333333333333333333 +0303 3333333333333333333333333333333333333333333333333333333 +0304 3333333333333333333333333333333333333333333333333333333 +0305 3333333333333333333333333333333333333333333333333333333 +0306 3333333333333333333333333333333333333333333333333333333 +0307 3333333333333333333333333333333333333333333333333333333 +0308 3333333333333333333333333333333333333333333333333333333 +0309 3333333333333333333333333333333333333333333333333333333 +0310 3333333333333333333333333333333333333333333333333333333 +0311 3333333333333333333333333333333333333333333333333333333 +0312 3333333333333333333333333333333333333333333333333333333 +0313 3333333333333333333333333333333333333333333333333333333 +0314 3333333333333333333333333333333333333333333333333333333 +0315 3333333333333333333333333333333333333333333333333333333 +0316 3333333333333333333333333333333333333333333333333333333 +0317 3333333333333333333333333333333333333333333333333333333 +0318 3333333333333333333333333333333333333333333333333333333 +0319 3333333333333333333333333333333333333333333333333333333 +0320 3333333333333333333333333333333333333333333333333333333 +0321 3333333333333333333333333333333333333333333333333333333 +0322 3333333333333333333333333333333333333333333333333333333 +0323 3333333333333333333333333333333333333333333333333333333 +0324 3333333333333333333333333333333333333333333333333333333 +0325 3333333333333333333333333333333333333333333333333333333 +0326 3333333333333333333333333333333333333333333333333333333 +0327 3333333333333333333333333333333333333333333333333333333 +0328 3333333333333333333333333333333333333333333333333333333 +0329 3333333333333333333333333333333333333333333333333333333 +0330 3333333333333333333333333333333333333333333333333333333 +0331 3333333333333333333333333333333333333333333333333333333 +0332 3333333333333333333333333333333333333333333333333333333 +0333 3333333333333333333333333333333333333333333333333333333 +0334 3333333333333333333333333333333333333333333333333333333 +0335 3333333333333333333333333333333333333333333333333333333 +0336 3333333333333333333333333333333333333333333333333333333 +0337 3333333333333333333333333333333333333333333333333333333 +0338 3333333333333333333333333333333333333333333333333333333 +0339 3333333333333333333333333333333333333333333333333333333 +0340 3333333333333333333333333333333333333333333333333333333 +0341 3333333333333333333333333333333333333333333333333333333 +0342 3333333333333333333333333333333333333333333333333333333 +0343 3333333333333333333333333333333333333333333333333333333 +0344 3333333333333333333333333333333333333333333333333333333 +0345 3333333333333333333333333333333333333333333333333333333 +0346 3333333333333333333333333333333333333333333333333333333 +0347 3333333333333333333333333333333333333333333333333333333 +0348 3333333333333333333333333333333333333333333333333333333 +0349 3333333333333333333333333333333333333333333333333333333 +0350 3333333333333333333333333333333333333333333333333333333 +0351 3333333333333333333333333333333333333333333333333333333 +0352 3333333333333333333333333333333333333333333333333333333 +0353 3333333333333333333333333333333333333333333333333333333 +0354 3333333333333333333333333333333333333333333333333333333 +0355 3333333333333333333333333333333333333333333333333333333 +0356 3333333333333333333333333333333333333333333333333333333 +0357 3333333333333333333333333333333333333333333333333333333 +0358 3333333333333333333333333333333333333333333333333333333 +0359 3333333333333333333333333333333333333333333333333333333 +0360 3333333333333333333333333333333333333333333333333333333 +0361 3333333333333333333333333333333333333333333333333333333 +0362 3333333333333333333333333333333333333333333333333333333 +0363 3333333333333333333333333333333333333333333333333333333 +0364 3333333333333333333333333333333333333333333333333333333 +0365 3333333333333333333333333333333333333333333333333333333 +0366 3333333333333333333333333333333333333333333333333333333 +0367 3333333333333333333333333333333333333333333333333333333 +0368 3333333333333333333333333333333333333333333333333333333 +0369 3333333333333333333333333333333333333333333333333333333 +0370 3333333333333333333333333333333333333333333333333333333 +0371 3333333333333333333333333333333333333333333333333333333 +0372 3333333333333333333333333333333333333333333333333333333 +0373 3333333333333333333333333333333333333333333333333333333 +0374 3333333333333333333333333333333333333333333333333333333 +0375 3333333333333333333333333333333333333333333333333333333 +0376 3333333333333333333333333333333333333333333333333333333 +0377 3333333333333333333333333333333333333333333333333333333 +0378 3333333333333333333333333333333333333333333333333333333 +0379 3333333333333333333333333333333333333333333333333333333 +0380 3333333333333333333333333333333333333333333333333333333 +0381 3333333333333333333333333333333333333333333333333333333 +0382 3333333333333333333333333333333333333333333333333333333 +0383 3333333333333333333333333333333333333333333333333333333 +0384 3333333333333333333333333333333333333333333333333333333 +0385 3333333333333333333333333333333333333333333333333333333 +0386 3333333333333333333333333333333333333333333333333333333 +0387 3333333333333333333333333333333333333333333333333333333 +0388 3333333333333333333333333333333333333333333333333333333 +0389 3333333333333333333333333333333333333333333333333333333 +0390 3333333333333333333333333333333333333333333333333333333 +0391 3333333333333333333333333333333333333333333333333333333 +0392 3333333333333333333333333333333333333333333333333333333 +0393 3333333333333333333333333333333333333333333333333333333 +0394 3333333333333333333333333333333333333333333333333333333 +0395 3333333333333333333333333333333333333333333333333333333 +0396 3333333333333333333333333333333333333333333333333333333 +0397 3333333333333333333333333333333333333333333333333333333 +0398 3333333333333333333333333333333333333333333333333333333 +0399 3333333333333333333333333333333333333333333333333333333 +0400 3333333333333333333333333333333333333333333333333333333 +0401 3333333333333333333333333333333333333333333333333333333 +0402 3333333333333333333333333333333333333333333333333333333 +0403 3333333333333333333333333333333333333333333333333333333 +0404 3333333333333333333333333333333333333333333333333333333 +0405 3333333333333333333333333333333333333333333333333333333 +0406 3333333333333333333333333333333333333333333333333333333 +0407 3333333333333333333333333333333333333333333333333333333 +0408 3333333333333333333333333333333333333333333333333333333 +0409 3333333333333333333333333333333333333333333333333333333 +0410 3333333333333333333333333333333333333333333333333333333 +0411 3333333333333333333333333333333333333333333333333333333 +0412 3333333333333333333333333333333333333333333333333333333 +0413 3333333333333333333333333333333333333333333333333333333 +0414 3333333333333333333333333333333333333333333333333333333 +0415 3333333333333333333333333333333333333333333333333333333 +0416 3333333333333333333333333333333333333333333333333333333 +0417 3333333333333333333333333333333333333333333333333333333 +0418 3333333333333333333333333333333333333333333333333333333 +0419 3333333333333333333333333333333333333333333333333333333 +0420 3333333333333333333333333333333333333333333333333333333 +0421 3333333333333333333333333333333333333333333333333333333 +0422 3333333333333333333333333333333333333333333333333333333 +0423 3333333333333333333333333333333333333333333333333333333 +0424 3333333333333333333333333333333333333333333333333333333 +0425 3333333333333333333333333333333333333333333333333333333 +0426 3333333333333333333333333333333333333333333333333333333 +0427 3333333333333333333333333333333333333333333333333333333 +0428 3333333333333333333333333333333333333333333333333333333 +0429 3333333333333333333333333333333333333333333333333333333 +0430 3333333333333333333333333333333333333333333333333333333 +0431 3333333333333333333333333333333333333333333333333333333 +0432 3333333333333333333333333333333333333333333333333333333 +0433 3333333333333333333333333333333333333333333333333333333 +0434 3333333333333333333333333333333333333333333333333333333 +0435 3333333333333333333333333333333333333333333333333333333 +0436 3333333333333333333333333333333333333333333333333333333 +0437 3333333333333333333333333333333333333333333333333333333 +0438 3333333333333333333333333333333333333333333333333333333 +0439 3333333333333333333333333333333333333333333333333333333 +0440 3333333333333333333333333333333333333333333333333333333 +0441 3333333333333333333333333333333333333333333333333333333 +0442 3333333333333333333333333333333333333333333333333333333 +0443 3333333333333333333333333333333333333333333333333333333 +0444 3333333333333333333333333333333333333333333333333333333 +0445 3333333333333333333333333333333333333333333333333333333 +0446 3333333333333333333333333333333333333333333333333333333 +0447 3333333333333333333333333333333333333333333333333333333 +0448 3333333333333333333333333333333333333333333333333333333 +0449 3333333333333333333333333333333333333333333333333333333 +0450 3333333333333333333333333333333333333333333333333333333 +0451 3333333333333333333333333333333333333333333333333333333 +0452 3333333333333333333333333333333333333333333333333333333 +0453 3333333333333333333333333333333333333333333333333333333 +0454 3333333333333333333333333333333333333333333333333333333 +0455 3333333333333333333333333333333333333333333333333333333 +0456 3333333333333333333333333333333333333333333333333333333 +0457 3333333333333333333333333333333333333333333333333333333 +0458 3333333333333333333333333333333333333333333333333333333 +0459 3333333333333333333333333333333333333333333333333333333 +0460 3333333333333333333333333333333333333333333333333333333 +0461 3333333333333333333333333333333333333333333333333333333 +0462 3333333333333333333333333333333333333333333333333333333 +0463 3333333333333333333333333333333333333333333333333333333 +0464 3333333333333333333333333333333333333333333333333333333 +0465 3333333333333333333333333333333333333333333333333333333 +0466 3333333333333333333333333333333333333333333333333333333 +0467 3333333333333333333333333333333333333333333333333333333 +0468 3333333333333333333333333333333333333333333333333333333 +0469 3333333333333333333333333333333333333333333333333333333 +0470 3333333333333333333333333333333333333333333333333333333 +0471 3333333333333333333333333333333333333333333333333333333 +0472 3333333333333333333333333333333333333333333333333333333 +0473 3333333333333333333333333333333333333333333333333333333 +0474 3333333333333333333333333333333333333333333333333333333 +0475 3333333333333333333333333333333333333333333333333333333 +0476 3333333333333333333333333333333333333333333333333333333 +0477 3333333333333333333333333333333333333333333333333333333 +0478 3333333333333333333333333333333333333333333333333333333 +0479 3333333333333333333333333333333333333333333333333333333 +0480 3333333333333333333333333333333333333333333333333333333 +0481 3333333333333333333333333333333333333333333333333333333 +0482 3333333333333333333333333333333333333333333333333333333 +0483 3333333333333333333333333333333333333333333333333333333 +0484 3333333333333333333333333333333333333333333333333333333 +0485 3333333333333333333333333333333333333333333333333333333 +0486 3333333333333333333333333333333333333333333333333333333 +0487 3333333333333333333333333333333333333333333333333333333 +0488 3333333333333333333333333333333333333333333333333333333 +0489 3333333333333333333333333333333333333333333333333333333 +0490 3333333333333333333333333333333333333333333333333333333 +0491 3333333333333333333333333333333333333333333333333333333 +0492 3333333333333333333333333333333333333333333333333333333 +0493 3333333333333333333333333333333333333333333333333333333 +0494 3333333333333333333333333333333333333333333333333333333 +0495 3333333333333333333333333333333333333333333333333333333 +0496 3333333333333333333333333333333333333333333333333333333 +0497 3333333333333333333333333333333333333333333333333333333 +0498 3333333333333333333333333333333333333333333333333333333 +0499 3333333333333333333333333333333333333333333333333333333 +0500 3333333333333333333333333333333333333333333333333333333 +0501 3333333333333333333333333333333333333333333333333333333 +0502 3333333333333333333333333333333333333333333333333333333 +0503 3333333333333333333333333333333333333333333333333333333 +0504 3333333333333333333333333333333333333333333333333333333 +0505 3333333333333333333333333333333333333333333333333333333 +0506 3333333333333333333333333333333333333333333333333333333 +0507 3333333333333333333333333333333333333333333333333333333 +0508 3333333333333333333333333333333333333333333333333333333 +0509 3333333333333333333333333333333333333333333333333333333 +0510 3333333333333333333333333333333333333333333333333333333 +0511 3333333333333333333333333333333333333333333333333333333 +0512 3333333333333333333333333333333333333333333333333333333 +0513 3333333333333333333333333333333333333333333333333333333 +0514 3333333333333333333333333333333333333333333333333333333 +0515 3333333333333333333333333333333333333333333333333333333 +0516 3333333333333333333333333333333333333333333333333333333 +0517 3333333333333333333333333333333333333333333333333333333 +0518 3333333333333333333333333333333333333333333333333333333 +0519 3333333333333333333333333333333333333333333333333333333 +0520 3333333333333333333333333333333333333333333333333333333 +0521 3333333333333333333333333333333333333333333333333333333 +0522 3333333333333333333333333333333333333333333333333333333 +0523 3333333333333333333333333333333333333333333333333333333 +0524 3333333333333333333333333333333333333333333333333333333 +0525 3333333333333333333333333333333333333333333333333333333 +0526 3333333333333333333333333333333333333333333333333333333 +0527 3333333333333333333333333333333333333333333333333333333 +0528 3333333333333333333333333333333333333333333333333333333 +0529 3333333333333333333333333333333333333333333333333333333 +0530 3333333333333333333333333333333333333333333333333333333 +0531 3333333333333333333333333333333333333333333333333333333 +0532 3333333333333333333333333333333333333333333333333333333 +0533 3333333333333333333333333333333333333333333333333333333 +0534 3333333333333333333333333333333333333333333333333333333 +0535 3333333333333333333333333333333333333333333333333333333 +0536 3333333333333333333333333333333333333333333333333333333 +0537 3333333333333333333333333333333333333333333333333333333 +0538 3333333333333333333333333333333333333333333333333333333 +0539 3333333333333333333333333333333333333333333333333333333 +0540 3333333333333333333333333333333333333333333333333333333 +0541 3333333333333333333333333333333333333333333333333333333 +0542 3333333333333333333333333333333333333333333333333333333 +0543 3333333333333333333333333333333333333333333333333333333 +0544 3333333333333333333333333333333333333333333333333333333 +0545 3333333333333333333333333333333333333333333333333333333 +0546 3333333333333333333333333333333333333333333333333333333 +0547 3333333333333333333333333333333333333333333333333333333 +0548 3333333333333333333333333333333333333333333333333333333 +0549 3333333333333333333333333333333333333333333333333333333 +0550 3333333333333333333333333333333333333333333333333333333 +0551 3333333333333333333333333333333333333333333333333333333 +0552 3333333333333333333333333333333333333333333333333333333 +0553 3333333333333333333333333333333333333333333333333333333 +0554 3333333333333333333333333333333333333333333333333333333 +0555 3333333333333333333333333333333333333333333333333333333 +0556 3333333333333333333333333333333333333333333333333333333 +0557 3333333333333333333333333333333333333333333333333333333 +0558 3333333333333333333333333333333333333333333333333333333 +0559 3333333333333333333333333333333333333333333333333333333 +0560 3333333333333333333333333333333333333333333333333333333 +0561 3333333333333333333333333333333333333333333333333333333 +0562 3333333333333333333333333333333333333333333333333333333 +0563 3333333333333333333333333333333333333333333333333333333 +0564 3333333333333333333333333333333333333333333333333333333 +0565 3333333333333333333333333333333333333333333333333333333 +0566 3333333333333333333333333333333333333333333333333333333 +0567 3333333333333333333333333333333333333333333333333333333 +0568 3333333333333333333333333333333333333333333333333333333 +0569 3333333333333333333333333333333333333333333333333333333 +0570 3333333333333333333333333333333333333333333333333333333 +0571 3333333333333333333333333333333333333333333333333333333 +0572 3333333333333333333333333333333333333333333333333333333 +0573 3333333333333333333333333333333333333333333333333333333 +0574 3333333333333333333333333333333333333333333333333333333 +0575 3333333333333333333333333333333333333333333333333333333 +0576 3333333333333333333333333333333333333333333333333333333 +0577 3333333333333333333333333333333333333333333333333333333 +0578 3333333333333333333333333333333333333333333333333333333 +0579 3333333333333333333333333333333333333333333333333333333 +0580 3333333333333333333333333333333333333333333333333333333 +0581 3333333333333333333333333333333333333333333333333333333 +0582 3333333333333333333333333333333333333333333333333333333 +0583 3333333333333333333333333333333333333333333333333333333 +0584 3333333333333333333333333333333333333333333333333333333 +0585 3333333333333333333333333333333333333333333333333333333 +0586 3333333333333333333333333333333333333333333333333333333 +0587 3333333333333333333333333333333333333333333333333333333 +0588 3333333333333333333333333333333333333333333333333333333 +0589 3333333333333333333333333333333333333333333333333333333 +0590 3333333333333333333333333333333333333333333333333333333 +0591 3333333333333333333333333333333333333333333333333333333 +0592 3333333333333333333333333333333333333333333333333333333 +0593 3333333333333333333333333333333333333333333333333333333 +0594 3333333333333333333333333333333333333333333333333333333 +0595 3333333333333333333333333333333333333333333333333333333 +0596 3333333333333333333333333333333333333333333333333333333 +0597 3333333333333333333333333333333333333333333333333333333 +0598 3333333333333333333333333333333333333333333333333333333 +0599 3333333333333333333333333333333333333333333333333333333 +0600 3333333333333333333333333333333333333333333333333333333 +0601 3333333333333333333333333333333333333333333333333333333 +0602 3333333333333333333333333333333333333333333333333333333 +0603 3333333333333333333333333333333333333333333333333333333 +0604 3333333333333333333333333333333333333333333333333333333 +0605 3333333333333333333333333333333333333333333333333333333 +0606 3333333333333333333333333333333333333333333333333333333 +0607 3333333333333333333333333333333333333333333333333333333 +0608 3333333333333333333333333333333333333333333333333333333 +0609 3333333333333333333333333333333333333333333333333333333 +0610 3333333333333333333333333333333333333333333333333333333 +0611 3333333333333333333333333333333333333333333333333333333 +0612 3333333333333333333333333333333333333333333333333333333 +0613 3333333333333333333333333333333333333333333333333333333 +0614 3333333333333333333333333333333333333333333333333333333 +0615 3333333333333333333333333333333333333333333333333333333 +0616 3333333333333333333333333333333333333333333333333333333 +0617 3333333333333333333333333333333333333333333333333333333 +0618 3333333333333333333333333333333333333333333333333333333 +0619 3333333333333333333333333333333333333333333333333333333 +0620 3333333333333333333333333333333333333333333333333333333 +0621 3333333333333333333333333333333333333333333333333333333 +0622 3333333333333333333333333333333333333333333333333333333 +0623 3333333333333333333333333333333333333333333333333333333 +0624 3333333333333333333333333333333333333333333333333333333 +0625 3333333333333333333333333333333333333333333333333333333 +0626 3333333333333333333333333333333333333333333333333333333 +0627 3333333333333333333333333333333333333333333333333333333 +0628 3333333333333333333333333333333333333333333333333333333 +0629 3333333333333333333333333333333333333333333333333333333 +0630 3333333333333333333333333333333333333333333333333333333 +0631 3333333333333333333333333333333333333333333333333333333 +0632 3333333333333333333333333333333333333333333333333333333 +0633 3333333333333333333333333333333333333333333333333333333 +0634 3333333333333333333333333333333333333333333333333333333 +0635 3333333333333333333333333333333333333333333333333333333 +0636 3333333333333333333333333333333333333333333333333333333 +0637 3333333333333333333333333333333333333333333333333333333 +0638 3333333333333333333333333333333333333333333333333333333 +0639 3333333333333333333333333333333333333333333333333333333 +0640 3333333333333333333333333333333333333333333333333333333 +0641 3333333333333333333333333333333333333333333333333333333 +0642 3333333333333333333333333333333333333333333333333333333 +0643 3333333333333333333333333333333333333333333333333333333 +0644 3333333333333333333333333333333333333333333333333333333 +0645 3333333333333333333333333333333333333333333333333333333 +0646 3333333333333333333333333333333333333333333333333333333 +0647 3333333333333333333333333333333333333333333333333333333 +0648 3333333333333333333333333333333333333333333333333333333 +0649 3333333333333333333333333333333333333333333333333333333 +0650 3333333333333333333333333333333333333333333333333333333 +0651 3333333333333333333333333333333333333333333333333333333 +0652 3333333333333333333333333333333333333333333333333333333 +0653 3333333333333333333333333333333333333333333333333333333 +0654 3333333333333333333333333333333333333333333333333333333 +0655 3333333333333333333333333333333333333333333333333333333 +0656 3333333333333333333333333333333333333333333333333333333 +0657 3333333333333333333333333333333333333333333333333333333 +0658 3333333333333333333333333333333333333333333333333333333 +0659 3333333333333333333333333333333333333333333333333333333 +0660 3333333333333333333333333333333333333333333333333333333 +0661 3333333333333333333333333333333333333333333333333333333 +0662 3333333333333333333333333333333333333333333333333333333 +0663 3333333333333333333333333333333333333333333333333333333 +0664 3333333333333333333333333333333333333333333333333333333 +0665 3333333333333333333333333333333333333333333333333333333 +0666 3333333333333333333333333333333333333333333333333333333 +0667 3333333333333333333333333333333333333333333333333333333 +0668 3333333333333333333333333333333333333333333333333333333 +0669 3333333333333333333333333333333333333333333333333333333 +0670 3333333333333333333333333333333333333333333333333333333 +0671 3333333333333333333333333333333333333333333333333333333 +0672 3333333333333333333333333333333333333333333333333333333 +0673 3333333333333333333333333333333333333333333333333333333 +0674 3333333333333333333333333333333333333333333333333333333 +0675 3333333333333333333333333333333333333333333333333333333 +0676 3333333333333333333333333333333333333333333333333333333 +0677 3333333333333333333333333333333333333333333333333333333 +0678 3333333333333333333333333333333333333333333333333333333 +0679 3333333333333333333333333333333333333333333333333333333 +0680 3333333333333333333333333333333333333333333333333333333 +0681 3333333333333333333333333333333333333333333333333333333 +0682 3333333333333333333333333333333333333333333333333333333 +0683 3333333333333333333333333333333333333333333333333333333 +0684 3333333333333333333333333333333333333333333333333333333 +0685 3333333333333333333333333333333333333333333333333333333 +0686 3333333333333333333333333333333333333333333333333333333 +0687 3333333333333333333333333333333333333333333333333333333 +0688 3333333333333333333333333333333333333333333333333333333 +0689 3333333333333333333333333333333333333333333333333333333 +0690 3333333333333333333333333333333333333333333333333333333 +0691 3333333333333333333333333333333333333333333333333333333 +0692 3333333333333333333333333333333333333333333333333333333 +0693 3333333333333333333333333333333333333333333333333333333 +0694 3333333333333333333333333333333333333333333333333333333 +0695 3333333333333333333333333333333333333333333333333333333 +0696 3333333333333333333333333333333333333333333333333333333 +0697 3333333333333333333333333333333333333333333333333333333 +0698 3333333333333333333333333333333333333333333333333333333 +0699 3333333333333333333333333333333333333333333333333333333 +0700 3333333333333333333333333333333333333333333333333333333 +0701 3333333333333333333333333333333333333333333333333333333 +0702 3333333333333333333333333333333333333333333333333333333 +0703 3333333333333333333333333333333333333333333333333333333 +0704 3333333333333333333333333333333333333333333333333333333 +0705 3333333333333333333333333333333333333333333333333333333 +0706 3333333333333333333333333333333333333333333333333333333 +0707 3333333333333333333333333333333333333333333333333333333 +0708 3333333333333333333333333333333333333333333333333333333 +0709 3333333333333333333333333333333333333333333333333333333 +0710 3333333333333333333333333333333333333333333333333333333 +0711 3333333333333333333333333333333333333333333333333333333 +0712 3333333333333333333333333333333333333333333333333333333 +0713 3333333333333333333333333333333333333333333333333333333 +0714 3333333333333333333333333333333333333333333333333333333 +0715 3333333333333333333333333333333333333333333333333333333 +0716 3333333333333333333333333333333333333333333333333333333 +0717 3333333333333333333333333333333333333333333333333333333 +0718 3333333333333333333333333333333333333333333333333333333 +0719 3333333333333333333333333333333333333333333333333333333 +0720 3333333333333333333333333333333333333333333333333333333 +0721 3333333333333333333333333333333333333333333333333333333 +0722 3333333333333333333333333333333333333333333333333333333 +0723 3333333333333333333333333333333333333333333333333333333 +0724 3333333333333333333333333333333333333333333333333333333 +0725 3333333333333333333333333333333333333333333333333333333 +0726 3333333333333333333333333333333333333333333333333333333 +0727 3333333333333333333333333333333333333333333333333333333 +0728 3333333333333333333333333333333333333333333333333333333 +0729 3333333333333333333333333333333333333333333333333333333 +0730 3333333333333333333333333333333333333333333333333333333 +0731 3333333333333333333333333333333333333333333333333333333 +0732 3333333333333333333333333333333333333333333333333333333 +0733 3333333333333333333333333333333333333333333333333333333 +0734 3333333333333333333333333333333333333333333333333333333 +0735 3333333333333333333333333333333333333333333333333333333 +0736 3333333333333333333333333333333333333333333333333333333 +0737 3333333333333333333333333333333333333333333333333333333 +0738 3333333333333333333333333333333333333333333333333333333 +0739 3333333333333333333333333333333333333333333333333333333 +0740 3333333333333333333333333333333333333333333333333333333 +0741 3333333333333333333333333333333333333333333333333333333 +0742 3333333333333333333333333333333333333333333333333333333 +0743 3333333333333333333333333333333333333333333333333333333 +0744 3333333333333333333333333333333333333333333333333333333 +0745 3333333333333333333333333333333333333333333333333333333 +0746 3333333333333333333333333333333333333333333333333333333 +0747 3333333333333333333333333333333333333333333333333333333 +0748 3333333333333333333333333333333333333333333333333333333 +0749 3333333333333333333333333333333333333333333333333333333 +0750 3333333333333333333333333333333333333333333333333333333 +0751 3333333333333333333333333333333333333333333333333333333 +0752 3333333333333333333333333333333333333333333333333333333 +0753 3333333333333333333333333333333333333333333333333333333 +0754 3333333333333333333333333333333333333333333333333333333 +0755 3333333333333333333333333333333333333333333333333333333 +0756 3333333333333333333333333333333333333333333333333333333 +0757 3333333333333333333333333333333333333333333333333333333 +0758 3333333333333333333333333333333333333333333333333333333 +0759 3333333333333333333333333333333333333333333333333333333 +0760 3333333333333333333333333333333333333333333333333333333 +0761 3333333333333333333333333333333333333333333333333333333 +0762 3333333333333333333333333333333333333333333333333333333 +0763 3333333333333333333333333333333333333333333333333333333 +0764 3333333333333333333333333333333333333333333333333333333 +0765 3333333333333333333333333333333333333333333333333333333 +0766 3333333333333333333333333333333333333333333333333333333 +0767 3333333333333333333333333333333333333333333333333333333 +0768 3333333333333333333333333333333333333333333333333333333 +0769 3333333333333333333333333333333333333333333333333333333 +0770 3333333333333333333333333333333333333333333333333333333 +0771 3333333333333333333333333333333333333333333333333333333 +0772 3333333333333333333333333333333333333333333333333333333 +0773 3333333333333333333333333333333333333333333333333333333 +0774 3333333333333333333333333333333333333333333333333333333 +0775 3333333333333333333333333333333333333333333333333333333 +0776 3333333333333333333333333333333333333333333333333333333 +0777 3333333333333333333333333333333333333333333333333333333 +0778 3333333333333333333333333333333333333333333333333333333 +0779 3333333333333333333333333333333333333333333333333333333 +0780 3333333333333333333333333333333333333333333333333333333 +0781 3333333333333333333333333333333333333333333333333333333 +0782 3333333333333333333333333333333333333333333333333333333 +0783 3333333333333333333333333333333333333333333333333333333 +0784 3333333333333333333333333333333333333333333333333333333 +0785 3333333333333333333333333333333333333333333333333333333 +0786 3333333333333333333333333333333333333333333333333333333 +0787 3333333333333333333333333333333333333333333333333333333 +0788 3333333333333333333333333333333333333333333333333333333 +0789 3333333333333333333333333333333333333333333333333333333 +0790 3333333333333333333333333333333333333333333333333333333 +0791 3333333333333333333333333333333333333333333333333333333 +0792 3333333333333333333333333333333333333333333333333333333 +0793 3333333333333333333333333333333333333333333333333333333 +0794 3333333333333333333333333333333333333333333333333333333 +0795 3333333333333333333333333333333333333333333333333333333 +0796 3333333333333333333333333333333333333333333333333333333 +0797 3333333333333333333333333333333333333333333333333333333 +0798 3333333333333333333333333333333333333333333333333333333 +0799 3333333333333333333333333333333333333333333333333333333 +0800 3333333333333333333333333333333333333333333333333333333 +0801 3333333333333333333333333333333333333333333333333333333 +0802 3333333333333333333333333333333333333333333333333333333 +0803 3333333333333333333333333333333333333333333333333333333 +0804 3333333333333333333333333333333333333333333333333333333 +0805 3333333333333333333333333333333333333333333333333333333 +0806 3333333333333333333333333333333333333333333333333333333 +0807 3333333333333333333333333333333333333333333333333333333 +0808 3333333333333333333333333333333333333333333333333333333 +0809 3333333333333333333333333333333333333333333333333333333 +0810 3333333333333333333333333333333333333333333333333333333 +0811 3333333333333333333333333333333333333333333333333333333 +0812 3333333333333333333333333333333333333333333333333333333 +0813 3333333333333333333333333333333333333333333333333333333 +0814 3333333333333333333333333333333333333333333333333333333 +0815 3333333333333333333333333333333333333333333333333333333 +0816 3333333333333333333333333333333333333333333333333333333 +0817 3333333333333333333333333333333333333333333333333333333 +0818 3333333333333333333333333333333333333333333333333333333 +0819 3333333333333333333333333333333333333333333333333333333 +0820 3333333333333333333333333333333333333333333333333333333 +0821 3333333333333333333333333333333333333333333333333333333 +0822 3333333333333333333333333333333333333333333333333333333 +0823 3333333333333333333333333333333333333333333333333333333 +0824 3333333333333333333333333333333333333333333333333333333 +0825 3333333333333333333333333333333333333333333333333333333 +0826 3333333333333333333333333333333333333333333333333333333 +0827 3333333333333333333333333333333333333333333333333333333 +0828 3333333333333333333333333333333333333333333333333333333 +0829 3333333333333333333333333333333333333333333333333333333 +0830 3333333333333333333333333333333333333333333333333333333 +0831 3333333333333333333333333333333333333333333333333333333 +0832 3333333333333333333333333333333333333333333333333333333 +0833 3333333333333333333333333333333333333333333333333333333 +0834 3333333333333333333333333333333333333333333333333333333 +0835 3333333333333333333333333333333333333333333333333333333 +0836 3333333333333333333333333333333333333333333333333333333 +0837 3333333333333333333333333333333333333333333333333333333 +0838 3333333333333333333333333333333333333333333333333333333 +0839 3333333333333333333333333333333333333333333333333333333 +0840 3333333333333333333333333333333333333333333333333333333 +0841 3333333333333333333333333333333333333333333333333333333 +0842 3333333333333333333333333333333333333333333333333333333 +0843 3333333333333333333333333333333333333333333333333333333 +0844 3333333333333333333333333333333333333333333333333333333 +0845 3333333333333333333333333333333333333333333333333333333 +0846 3333333333333333333333333333333333333333333333333333333 +0847 3333333333333333333333333333333333333333333333333333333 +0848 3333333333333333333333333333333333333333333333333333333 +0849 3333333333333333333333333333333333333333333333333333333 +0850 3333333333333333333333333333333333333333333333333333333 +0851 3333333333333333333333333333333333333333333333333333333 +0852 3333333333333333333333333333333333333333333333333333333 +0853 3333333333333333333333333333333333333333333333333333333 +0854 3333333333333333333333333333333333333333333333333333333 +0855 3333333333333333333333333333333333333333333333333333333 +0856 3333333333333333333333333333333333333333333333333333333 +0857 3333333333333333333333333333333333333333333333333333333 +0858 3333333333333333333333333333333333333333333333333333333 +0859 3333333333333333333333333333333333333333333333333333333 +0860 3333333333333333333333333333333333333333333333333333333 +0861 3333333333333333333333333333333333333333333333333333333 +0862 3333333333333333333333333333333333333333333333333333333 +0863 3333333333333333333333333333333333333333333333333333333 +0864 3333333333333333333333333333333333333333333333333333333 +0865 3333333333333333333333333333333333333333333333333333333 +0866 3333333333333333333333333333333333333333333333333333333 +0867 3333333333333333333333333333333333333333333333333333333 +0868 3333333333333333333333333333333333333333333333333333333 +0869 3333333333333333333333333333333333333333333333333333333 +0870 3333333333333333333333333333333333333333333333333333333 +0871 3333333333333333333333333333333333333333333333333333333 +0872 3333333333333333333333333333333333333333333333333333333 +0873 3333333333333333333333333333333333333333333333333333333 +0874 3333333333333333333333333333333333333333333333333333333 +0875 3333333333333333333333333333333333333333333333333333333 +0876 3333333333333333333333333333333333333333333333333333333 +0877 3333333333333333333333333333333333333333333333333333333 +0878 3333333333333333333333333333333333333333333333333333333 +0879 3333333333333333333333333333333333333333333333333333333 +0880 3333333333333333333333333333333333333333333333333333333 +0881 3333333333333333333333333333333333333333333333333333333 +0882 3333333333333333333333333333333333333333333333333333333 +0883 3333333333333333333333333333333333333333333333333333333 +0884 3333333333333333333333333333333333333333333333333333333 +0885 3333333333333333333333333333333333333333333333333333333 +0886 3333333333333333333333333333333333333333333333333333333 +0887 3333333333333333333333333333333333333333333333333333333 +0888 3333333333333333333333333333333333333333333333333333333 +0889 3333333333333333333333333333333333333333333333333333333 +0890 3333333333333333333333333333333333333333333333333333333 +0891 3333333333333333333333333333333333333333333333333333333 +0892 3333333333333333333333333333333333333333333333333333333 +0893 3333333333333333333333333333333333333333333333333333333 +0894 3333333333333333333333333333333333333333333333333333333 +0895 3333333333333333333333333333333333333333333333333333333 +0896 3333333333333333333333333333333333333333333333333333333 +0897 3333333333333333333333333333333333333333333333333333333 +0898 3333333333333333333333333333333333333333333333333333333 +0899 3333333333333333333333333333333333333333333333333333333 +0900 3333333333333333333333333333333333333333333333333333333 +0901 3333333333333333333333333333333333333333333333333333333 +0902 3333333333333333333333333333333333333333333333333333333 +0903 3333333333333333333333333333333333333333333333333333333 +0904 3333333333333333333333333333333333333333333333333333333 +0905 3333333333333333333333333333333333333333333333333333333 +0906 3333333333333333333333333333333333333333333333333333333 +0907 3333333333333333333333333333333333333333333333333333333 +0908 3333333333333333333333333333333333333333333333333333333 +0909 3333333333333333333333333333333333333333333333333333333 +0910 3333333333333333333333333333333333333333333333333333333 +0911 3333333333333333333333333333333333333333333333333333333 +0912 3333333333333333333333333333333333333333333333333333333 +0913 3333333333333333333333333333333333333333333333333333333 +0914 3333333333333333333333333333333333333333333333333333333 +0915 3333333333333333333333333333333333333333333333333333333 +0916 3333333333333333333333333333333333333333333333333333333 +0917 3333333333333333333333333333333333333333333333333333333 +0918 3333333333333333333333333333333333333333333333333333333 +0919 3333333333333333333333333333333333333333333333333333333 +0920 3333333333333333333333333333333333333333333333333333333 +0921 3333333333333333333333333333333333333333333333333333333 +0922 3333333333333333333333333333333333333333333333333333333 +0923 3333333333333333333333333333333333333333333333333333333 +0924 3333333333333333333333333333333333333333333333333333333 +0925 3333333333333333333333333333333333333333333333333333333 +0926 3333333333333333333333333333333333333333333333333333333 +0927 3333333333333333333333333333333333333333333333333333333 +0928 3333333333333333333333333333333333333333333333333333333 +0929 3333333333333333333333333333333333333333333333333333333 +0930 3333333333333333333333333333333333333333333333333333333 +0931 3333333333333333333333333333333333333333333333333333333 +0932 3333333333333333333333333333333333333333333333333333333 +0933 3333333333333333333333333333333333333333333333333333333 +0934 3333333333333333333333333333333333333333333333333333333 +0935 3333333333333333333333333333333333333333333333333333333 +0936 3333333333333333333333333333333333333333333333333333333 +0937 3333333333333333333333333333333333333333333333333333333 +0938 3333333333333333333333333333333333333333333333333333333 +0939 3333333333333333333333333333333333333333333333333333333 +0940 3333333333333333333333333333333333333333333333333333333 +0941 3333333333333333333333333333333333333333333333333333333 +0942 3333333333333333333333333333333333333333333333333333333 +0943 3333333333333333333333333333333333333333333333333333333 +0944 3333333333333333333333333333333333333333333333333333333 +0945 3333333333333333333333333333333333333333333333333333333 +0946 3333333333333333333333333333333333333333333333333333333 +0947 3333333333333333333333333333333333333333333333333333333 +0948 3333333333333333333333333333333333333333333333333333333 +0949 3333333333333333333333333333333333333333333333333333333 +0950 3333333333333333333333333333333333333333333333333333333 +0951 3333333333333333333333333333333333333333333333333333333 +0952 3333333333333333333333333333333333333333333333333333333 +0953 3333333333333333333333333333333333333333333333333333333 +0954 3333333333333333333333333333333333333333333333333333333 +0955 3333333333333333333333333333333333333333333333333333333 +0956 3333333333333333333333333333333333333333333333333333333 +0957 3333333333333333333333333333333333333333333333333333333 +0958 3333333333333333333333333333333333333333333333333333333 +0959 3333333333333333333333333333333333333333333333333333333 +0960 3333333333333333333333333333333333333333333333333333333 +0961 3333333333333333333333333333333333333333333333333333333 +0962 3333333333333333333333333333333333333333333333333333333 +0963 3333333333333333333333333333333333333333333333333333333 +0964 3333333333333333333333333333333333333333333333333333333 +0965 3333333333333333333333333333333333333333333333333333333 +0966 3333333333333333333333333333333333333333333333333333333 +0967 3333333333333333333333333333333333333333333333333333333 +0968 3333333333333333333333333333333333333333333333333333333 +0969 3333333333333333333333333333333333333333333333333333333 +0970 3333333333333333333333333333333333333333333333333333333 +0971 3333333333333333333333333333333333333333333333333333333 +0972 3333333333333333333333333333333333333333333333333333333 +0973 3333333333333333333333333333333333333333333333333333333 +0974 3333333333333333333333333333333333333333333333333333333 +0975 3333333333333333333333333333333333333333333333333333333 +0976 3333333333333333333333333333333333333333333333333333333 +0977 3333333333333333333333333333333333333333333333333333333 +0978 3333333333333333333333333333333333333333333333333333333 +0979 3333333333333333333333333333333333333333333333333333333 +0980 3333333333333333333333333333333333333333333333333333333 +0981 3333333333333333333333333333333333333333333333333333333 +0982 3333333333333333333333333333333333333333333333333333333 +0983 3333333333333333333333333333333333333333333333333333333 +0984 3333333333333333333333333333333333333333333333333333333 +0985 3333333333333333333333333333333333333333333333333333333 +0986 3333333333333333333333333333333333333333333333333333333 +0987 3333333333333333333333333333333333333333333333333333333 +0988 3333333333333333333333333333333333333333333333333333333 +0989 3333333333333333333333333333333333333333333333333333333 +0990 3333333333333333333333333333333333333333333333333333333 +0991 3333333333333333333333333333333333333333333333333333333 +0992 3333333333333333333333333333333333333333333333333333333 +0993 3333333333333333333333333333333333333333333333333333333 +0994 3333333333333333333333333333333333333333333333333333333 +0995 3333333333333333333333333333333333333333333333333333333 +0996 3333333333333333333333333333333333333333333333333333333 +0997 3333333333333333333333333333333333333333333333333333333 +0998 3333333333333333333333333333333333333333333333333333333 +0999 3333333333333333333333333333333333333333333333333333333 +1000 3333333333333333333333333333333333333333333333333333333 +1001 3333333333333333333333333333333333333333333333333333333 +1002 3333333333333333333333333333333333333333333333333333333 +1003 3333333333333333333333333333333333333333333333333333333 +1004 3333333333333333333333333333333333333333333333333333333 +1005 3333333333333333333333333333333333333333333333333333333 +1006 3333333333333333333333333333333333333333333333333333333 +1007 3333333333333333333333333333333333333333333333333333333 +1008 3333333333333333333333333333333333333333333333333333333 +1009 3333333333333333333333333333333333333333333333333333333 +1010 3333333333333333333333333333333333333333333333333333333 +1011 3333333333333333333333333333333333333333333333333333333 +1012 3333333333333333333333333333333333333333333333333333333 +1013 3333333333333333333333333333333333333333333333333333333 +1014 3333333333333333333333333333333333333333333333333333333 +1015 3333333333333333333333333333333333333333333333333333333 +1016 3333333333333333333333333333333333333333333333333333333 +1017 3333333333333333333333333333333333333333333333333333333 +1018 3333333333333333333333333333333333333333333333333333333 +1019 3333333333333333333333333333333333333333333333333333333 +1020 3333333333333333333333333333333333333333333333333333333 +1021 3333333333333333333333333333333333333333333333333333333 +1022 3333333333333333333333333333333333333333333333333333333 +1023 3333333333333333333333333333333333333333333333333333333 +1024 3333333333333333333333333333333333333333333333333333333 +1025 3333333333333333333333333333333333333333333333333333333 +1026 3333333333333333333333333333333333333333333333333333333 +1027 3333333333333333333333333333333333333333333333333333333 +1028 3333333333333333333333333333333333333333333333333333333 +1029 3333333333333333333333333333333333333333333333333333333 +1030 3333333333333333333333333333333333333333333333333333333 +1031 3333333333333333333333333333333333333333333333333333333 +1032 3333333333333333333333333333333333333333333333333333333 +1033 3333333333333333333333333333333333333333333333333333333 +1034 3333333333333333333333333333333333333333333333333333333 +1035 3333333333333333333333333333333333333333333333333333333 +1036 3333333333333333333333333333333333333333333333333333333 +1037 3333333333333333333333333333333333333333333333333333333 +1038 3333333333333333333333333333333333333333333333333333333 +1039 3333333333333333333333333333333333333333333333333333333 +1040 3333333333333333333333333333333333333333333333333333333 +1041 3333333333333333333333333333333333333333333333333333333 +1042 3333333333333333333333333333333333333333333333333333333 +1043 3333333333333333333333333333333333333333333333333333333 +1044 3333333333333333333333333333333333333333333333333333333 +1045 3333333333333333333333333333333333333333333333333333333 +1046 3333333333333333333333333333333333333333333333333333333 +1047 3333333333333333333333333333333333333333333333333333333 +1048 3333333333333333333333333333333333333333333333333333333 +1049 3333333333333333333333333333333333333333333333333333333 +1050 3333333333333333333333333333333333333333333333333333333 +1051 3333333333333333333333333333333333333333333333333333333 +1052 3333333333333333333333333333333333333333333333333333333 +1053 3333333333333333333333333333333333333333333333333333333 +1054 3333333333333333333333333333333333333333333333333333333 +1055 3333333333333333333333333333333333333333333333333333333 +1056 3333333333333333333333333333333333333333333333333333333 +1057 3333333333333333333333333333333333333333333333333333333 +1058 3333333333333333333333333333333333333333333333333333333 +1059 3333333333333333333333333333333333333333333333333333333 +1060 3333333333333333333333333333333333333333333333333333333 +1061 3333333333333333333333333333333333333333333333333333333 +1062 3333333333333333333333333333333333333333333333333333333 +1063 3333333333333333333333333333333333333333333333333333333 +1064 3333333333333333333333333333333333333333333333333333333 +1065 3333333333333333333333333333333333333333333333333333333 +1066 3333333333333333333333333333333333333333333333333333333 +1067 3333333333333333333333333333333333333333333333333333333 +1068 3333333333333333333333333333333333333333333333333333333 +1069 3333333333333333333333333333333333333333333333333333333 +1070 3333333333333333333333333333333333333333333333333333333 +1071 3333333333333333333333333333333333333333333333333333333 +1072 3333333333333333333333333333333333333333333333333333333 +1073 3333333333333333333333333333333333333333333333333333333 +1074 3333333333333333333333333333333333333333333333333333333 +1075 3333333333333333333333333333333333333333333333333333333 +1076 3333333333333333333333333333333333333333333333333333333 +1077 3333333333333333333333333333333333333333333333333333333 +1078 3333333333333333333333333333333333333333333333333333333 +1079 3333333333333333333333333333333333333333333333333333333 +1080 3333333333333333333333333333333333333333333333333333333 +1081 3333333333333333333333333333333333333333333333333333333 +1082 3333333333333333333333333333333333333333333333333333333 +1083 3333333333333333333333333333333333333333333333333333333 +1084 3333333333333333333333333333333333333333333333333333333 +1085 3333333333333333333333333333333333333333333333333333333 +1086 3333333333333333333333333333333333333333333333333333333 +1087 3333333333333333333333333333333333333333333333333333333 +1088 3333333333333333333333333333333333333333333333333333333 +1089 3333333333333333333333333333333333333333333333333333333 +1090 3333333333333333333333333333333333333333333333333333333 +1091 3333333333333333333333333333333333333333333333333333333 +1092 3333333333333333333333333333333333333333333333333333333 +1093 3333333333333333333333333333333333333333333333333333333 +1094 3333333333333333333333333333333333333333333333333333333 +1095 3333333333333333333333333333333333333333333333333333333 +1096 3333333333333333333333333333333333333333333333333333333 +1097 3333333333333333333333333333333333333333333333333333333 +1098 3333333333333333333333333333333333333333333333333333333 +1099 3333333333333333333333333333333333333333333333333333333 +1100 3333333333333333333333333333333333333333333333333333333 +1101 3333333333333333333333333333333333333333333333333333333 +1102 3333333333333333333333333333333333333333333333333333333 +1103 3333333333333333333333333333333333333333333333333333333 +1104 3333333333333333333333333333333333333333333333333333333 +1105 3333333333333333333333333333333333333333333333333333333 +1106 3333333333333333333333333333333333333333333333333333333 +1107 3333333333333333333333333333333333333333333333333333333 +1108 3333333333333333333333333333333333333333333333333333333 +1109 3333333333333333333333333333333333333333333333333333333 +1110 3333333333333333333333333333333333333333333333333333333 +1111 3333333333333333333333333333333333333333333333333333333 +1112 3333333333333333333333333333333333333333333333333333333 +1113 3333333333333333333333333333333333333333333333333333333 +1114 3333333333333333333333333333333333333333333333333333333 +1115 3333333333333333333333333333333333333333333333333333333 +1116 3333333333333333333333333333333333333333333333333333333 +1117 3333333333333333333333333333333333333333333333333333333 +1118 3333333333333333333333333333333333333333333333333333333 +1119 3333333333333333333333333333333333333333333333333333333 +1120 3333333333333333333333333333333333333333333333333333333 +1121 3333333333333333333333333333333333333333333333333333333 +1122 3333333333333333333333333333333333333333333333333333333 +1123 3333333333333333333333333333333333333333333333333333333 +1124 3333333333333333333333333333333333333333333333333333333 +1125 3333333333333333333333333333333333333333333333333333333 +1126 3333333333333333333333333333333333333333333333333333333 +1127 3333333333333333333333333333333333333333333333333333333 +1128 3333333333333333333333333333333333333333333333333333333 +1129 3333333333333333333333333333333333333333333333333333333 +1130 3333333333333333333333333333333333333333333333333333333 +1131 3333333333333333333333333333333333333333333333333333333 +1132 3333333333333333333333333333333333333333333333333333333 +1133 3333333333333333333333333333333333333333333333333333333 +1134 3333333333333333333333333333333333333333333333333333333 +1135 3333333333333333333333333333333333333333333333333333333 +1136 3333333333333333333333333333333333333333333333333333333 +1137 3333333333333333333333333333333333333333333333333333333 +1138 3333333333333333333333333333333333333333333333333333333 +1139 3333333333333333333333333333333333333333333333333333333 +1140 3333333333333333333333333333333333333333333333333333333 +1141 3333333333333333333333333333333333333333333333333333333 +1142 3333333333333333333333333333333333333333333333333333333 +1143 3333333333333333333333333333333333333333333333333333333 +1144 3333333333333333333333333333333333333333333333333333333 +1145 3333333333333333333333333333333333333333333333333333333 +1146 3333333333333333333333333333333333333333333333333333333 +1147 3333333333333333333333333333333333333333333333333333333 +1148 3333333333333333333333333333333333333333333333333333333 +1149 3333333333333333333333333333333333333333333333333333333 +1150 3333333333333333333333333333333333333333333333333333333 +1151 3333333333333333333333333333333333333333333333333333333 +1152 3333333333333333333333333333333333333333333333333333333 +1153 3333333333333333333333333333333333333333333333333333333 +1154 3333333333333333333333333333333333333333333333333333333 +1155 3333333333333333333333333333333333333333333333333333333 +1156 3333333333333333333333333333333333333333333333333333333 +1157 3333333333333333333333333333333333333333333333333333333 +1158 3333333333333333333333333333333333333333333333333333333 +1159 3333333333333333333333333333333333333333333333333333333 +1160 3333333333333333333333333333333333333333333333333333333 +1161 3333333333333333333333333333333333333333333333333333333 +1162 3333333333333333333333333333333333333333333333333333333 +1163 3333333333333333333333333333333333333333333333333333333 +1164 3333333333333333333333333333333333333333333333333333333 +1165 3333333333333333333333333333333333333333333333333333333 +1166 3333333333333333333333333333333333333333333333333333333 +1167 3333333333333333333333333333333333333333333333333333333 +1168 3333333333333333333333333333333333333333333333333333333 +1169 3333333333333333333333333333333333333333333333333333333 +1170 3333333333333333333333333333333333333333333333333333333 +1171 3333333333333333333333333333333333333333333333333333333 +1172 3333333333333333333333333333333333333333333333333333333 +1173 3333333333333333333333333333333333333333333333333333333 +1174 3333333333333333333333333333333333333333333333333333333 +1175 3333333333333333333333333333333333333333333333333333333 +1176 3333333333333333333333333333333333333333333333333333333 +1177 3333333333333333333333333333333333333333333333333333333 +1178 3333333333333333333333333333333333333333333333333333333 +1179 3333333333333333333333333333333333333333333333333333333 +1180 3333333333333333333333333333333333333333333333333333333 +1181 3333333333333333333333333333333333333333333333333333333 +1182 3333333333333333333333333333333333333333333333333333333 +1183 3333333333333333333333333333333333333333333333333333333 +1184 3333333333333333333333333333333333333333333333333333333 +1185 3333333333333333333333333333333333333333333333333333333 +1186 3333333333333333333333333333333333333333333333333333333 +1187 3333333333333333333333333333333333333333333333333333333 +1188 3333333333333333333333333333333333333333333333333333333 +1189 3333333333333333333333333333333333333333333333333333333 +1190 3333333333333333333333333333333333333333333333333333333 +1191 3333333333333333333333333333333333333333333333333333333 +1192 3333333333333333333333333333333333333333333333333333333 +1193 3333333333333333333333333333333333333333333333333333333 +1194 3333333333333333333333333333333333333333333333333333333 +1195 3333333333333333333333333333333333333333333333333333333 +1196 3333333333333333333333333333333333333333333333333333333 +1197 3333333333333333333333333333333333333333333333333333333 +1198 3333333333333333333333333333333333333333333333333333333 +1199 3333333333333333333333333333333333333333333333333333333 +1200 3333333333333333333333333333333333333333333333333333333 +1201 3333333333333333333333333333333333333333333333333333333 +1202 3333333333333333333333333333333333333333333333333333333 +1203 3333333333333333333333333333333333333333333333333333333 +1204 3333333333333333333333333333333333333333333333333333333 +1205 3333333333333333333333333333333333333333333333333333333 +1206 3333333333333333333333333333333333333333333333333333333 +1207 3333333333333333333333333333333333333333333333333333333 +1208 3333333333333333333333333333333333333333333333333333333 +1209 3333333333333333333333333333333333333333333333333333333 +1210 3333333333333333333333333333333333333333333333333333333 +1211 3333333333333333333333333333333333333333333333333333333 +1212 3333333333333333333333333333333333333333333333333333333 +1213 3333333333333333333333333333333333333333333333333333333 +1214 3333333333333333333333333333333333333333333333333333333 +1215 3333333333333333333333333333333333333333333333333333333 +1216 3333333333333333333333333333333333333333333333333333333 +1217 3333333333333333333333333333333333333333333333333333333 +1218 3333333333333333333333333333333333333333333333333333333 +1219 3333333333333333333333333333333333333333333333333333333 +1220 3333333333333333333333333333333333333333333333333333333 +1221 3333333333333333333333333333333333333333333333333333333 +1222 3333333333333333333333333333333333333333333333333333333 +1223 3333333333333333333333333333333333333333333333333333333 +1224 3333333333333333333333333333333333333333333333333333333 +1225 3333333333333333333333333333333333333333333333333333333 +1226 3333333333333333333333333333333333333333333333333333333 +1227 3333333333333333333333333333333333333333333333333333333 +1228 3333333333333333333333333333333333333333333333333333333 +1229 3333333333333333333333333333333333333333333333333333333 +1230 3333333333333333333333333333333333333333333333333333333 +1231 3333333333333333333333333333333333333333333333333333333 +1232 3333333333333333333333333333333333333333333333333333333 +1233 3333333333333333333333333333333333333333333333333333333 +1234 3333333333333333333333333333333333333333333333333333333 +1235 3333333333333333333333333333333333333333333333333333333 +1236 3333333333333333333333333333333333333333333333333333333 +1237 3333333333333333333333333333333333333333333333333333333 +1238 3333333333333333333333333333333333333333333333333333333 +1239 3333333333333333333333333333333333333333333333333333333 +1240 3333333333333333333333333333333333333333333333333333333 +1241 3333333333333333333333333333333333333333333333333333333 +1242 3333333333333333333333333333333333333333333333333333333 +1243 3333333333333333333333333333333333333333333333333333333 +1244 3333333333333333333333333333333333333333333333333333333 +1245 3333333333333333333333333333333333333333333333333333333 +1246 3333333333333333333333333333333333333333333333333333333 +1247 3333333333333333333333333333333333333333333333333333333 +1248 3333333333333333333333333333333333333333333333333333333 +1249 3333333333333333333333333333333333333333333333333333333 +1250 3333333333333333333333333333333333333333333333333333333 +1251 3333333333333333333333333333333333333333333333333333333 +1252 3333333333333333333333333333333333333333333333333333333 +1253 3333333333333333333333333333333333333333333333333333333 +1254 3333333333333333333333333333333333333333333333333333333 +1255 3333333333333333333333333333333333333333333333333333333 +1256 3333333333333333333333333333333333333333333333333333333 +1257 3333333333333333333333333333333333333333333333333333333 +1258 3333333333333333333333333333333333333333333333333333333 +1259 3333333333333333333333333333333333333333333333333333333 +1260 3333333333333333333333333333333333333333333333333333333 +1261 3333333333333333333333333333333333333333333333333333333 +1262 3333333333333333333333333333333333333333333333333333333 +1263 3333333333333333333333333333333333333333333333333333333 +1264 3333333333333333333333333333333333333333333333333333333 +1265 3333333333333333333333333333333333333333333333333333333 +1266 3333333333333333333333333333333333333333333333333333333 +1267 3333333333333333333333333333333333333333333333333333333 +1268 3333333333333333333333333333333333333333333333333333333 +1269 3333333333333333333333333333333333333333333333333333333 +1270 3333333333333333333333333333333333333333333333333333333 +1271 3333333333333333333333333333333333333333333333333333333 +1272 3333333333333333333333333333333333333333333333333333333 +1273 3333333333333333333333333333333333333333333333333333333 +1274 3333333333333333333333333333333333333333333333333333333 +1275 3333333333333333333333333333333333333333333333333333333 +1276 3333333333333333333333333333333333333333333333333333333 +1277 3333333333333333333333333333333333333333333333333333333 +1278 3333333333333333333333333333333333333333333333333333333 +1279 3333333333333333333333333333333333333333333333333333333 +1280 3333333333333333333333333333333333333333333333333333333 +1281 3333333333333333333333333333333333333333333333333333333 +1282 3333333333333333333333333333333333333333333333333333333 +1283 3333333333333333333333333333333333333333333333333333333 +1284 3333333333333333333333333333333333333333333333333333333 +1285 3333333333333333333333333333333333333333333333333333333 +1286 3333333333333333333333333333333333333333333333333333333 +1287 3333333333333333333333333333333333333333333333333333333 +1288 3333333333333333333333333333333333333333333333333333333 +1289 3333333333333333333333333333333333333333333333333333333 +1290 3333333333333333333333333333333333333333333333333333333 +1291 3333333333333333333333333333333333333333333333333333333 +1292 3333333333333333333333333333333333333333333333333333333 +1293 3333333333333333333333333333333333333333333333333333333 +1294 3333333333333333333333333333333333333333333333333333333 +1295 3333333333333333333333333333333333333333333333333333333 +1296 3333333333333333333333333333333333333333333333333333333 +1297 3333333333333333333333333333333333333333333333333333333 +1298 3333333333333333333333333333333333333333333333333333333 +1299 3333333333333333333333333333333333333333333333333333333 +1300 3333333333333333333333333333333333333333333333333333333 +1301 3333333333333333333333333333333333333333333333333333333 +1302 3333333333333333333333333333333333333333333333333333333 +1303 3333333333333333333333333333333333333333333333333333333 +1304 3333333333333333333333333333333333333333333333333333333 +1305 3333333333333333333333333333333333333333333333333333333 +1306 3333333333333333333333333333333333333333333333333333333 +1307 3333333333333333333333333333333333333333333333333333333 +1308 3333333333333333333333333333333333333333333333333333333 +1309 3333333333333333333333333333333333333333333333333333333 +1310 3333333333333333333333333333333333333333333333333333333 +1311 3333333333333333333333333333333333333333333333333333333 +1312 3333333333333333333333333333333333333333333333333333333 +1313 3333333333333333333333333333333333333333333333333333333 +1314 3333333333333333333333333333333333333333333333333333333 +1315 3333333333333333333333333333333333333333333333333333333 +1316 3333333333333333333333333333333333333333333333333333333 +1317 3333333333333333333333333333333333333333333333333333333 +1318 3333333333333333333333333333333333333333333333333333333 +1319 3333333333333333333333333333333333333333333333333333333 +1320 3333333333333333333333333333333333333333333333333333333 +1321 3333333333333333333333333333333333333333333333333333333 +1322 3333333333333333333333333333333333333333333333333333333 +1323 3333333333333333333333333333333333333333333333333333333 +1324 3333333333333333333333333333333333333333333333333333333 +1325 3333333333333333333333333333333333333333333333333333333 +1326 3333333333333333333333333333333333333333333333333333333 +1327 3333333333333333333333333333333333333333333333333333333 +1328 3333333333333333333333333333333333333333333333333333333 +1329 3333333333333333333333333333333333333333333333333333333 +1330 3333333333333333333333333333333333333333333333333333333 +1331 3333333333333333333333333333333333333333333333333333333 +1332 3333333333333333333333333333333333333333333333333333333 +1333 3333333333333333333333333333333333333333333333333333333 +1334 3333333333333333333333333333333333333333333333333333333 +1335 3333333333333333333333333333333333333333333333333333333 +1336 3333333333333333333333333333333333333333333333333333333 +1337 3333333333333333333333333333333333333333333333333333333 +1338 3333333333333333333333333333333333333333333333333333333 +1339 3333333333333333333333333333333333333333333333333333333 +1340 3333333333333333333333333333333333333333333333333333333 +1341 3333333333333333333333333333333333333333333333333333333 +1342 3333333333333333333333333333333333333333333333333333333 +1343 3333333333333333333333333333333333333333333333333333333 +1344 3333333333333333333333333333333333333333333333333333333 +1345 3333333333333333333333333333333333333333333333333333333 +1346 3333333333333333333333333333333333333333333333333333333 +1347 3333333333333333333333333333333333333333333333333333333 +1348 3333333333333333333333333333333333333333333333333333333 +1349 3333333333333333333333333333333333333333333333333333333 +1350 3333333333333333333333333333333333333333333333333333333 +1351 3333333333333333333333333333333333333333333333333333333 +1352 3333333333333333333333333333333333333333333333333333333 +1353 3333333333333333333333333333333333333333333333333333333 +1354 3333333333333333333333333333333333333333333333333333333 +1355 3333333333333333333333333333333333333333333333333333333 +1356 3333333333333333333333333333333333333333333333333333333 +1357 3333333333333333333333333333333333333333333333333333333 +1358 3333333333333333333333333333333333333333333333333333333 +1359 3333333333333333333333333333333333333333333333333333333 +1360 3333333333333333333333333333333333333333333333333333333 +1361 3333333333333333333333333333333333333333333333333333333 +1362 3333333333333333333333333333333333333333333333333333333 +1363 3333333333333333333333333333333333333333333333333333333 +1364 3333333333333333333333333333333333333333333333333333333 +1365 3333333333333333333333333333333333333333333333333333333 +1366 3333333333333333333333333333333333333333333333333333333 +1367 3333333333333333333333333333333333333333333333333333333 +1368 3333333333333333333333333333333333333333333333333333333 +1369 3333333333333333333333333333333333333333333333333333333 +1370 3333333333333333333333333333333333333333333333333333333 +1371 3333333333333333333333333333333333333333333333333333333 +1372 3333333333333333333333333333333333333333333333333333333 +1373 3333333333333333333333333333333333333333333333333333333 +1374 3333333333333333333333333333333333333333333333333333333 +1375 3333333333333333333333333333333333333333333333333333333 +1376 3333333333333333333333333333333333333333333333333333333 +1377 3333333333333333333333333333333333333333333333333333333 +1378 3333333333333333333333333333333333333333333333333333333 +1379 3333333333333333333333333333333333333333333333333333333 +1380 3333333333333333333333333333333333333333333333333333333 +1381 3333333333333333333333333333333333333333333333333333333 +1382 3333333333333333333333333333333333333333333333333333333 +1383 3333333333333333333333333333333333333333333333333333333 +1384 3333333333333333333333333333333333333333333333333333333 +1385 3333333333333333333333333333333333333333333333333333333 +1386 3333333333333333333333333333333333333333333333333333333 +1387 3333333333333333333333333333333333333333333333333333333 +1388 3333333333333333333333333333333333333333333333333333333 +1389 3333333333333333333333333333333333333333333333333333333 +1390 3333333333333333333333333333333333333333333333333333333 +1391 3333333333333333333333333333333333333333333333333333333 +1392 3333333333333333333333333333333333333333333333333333333 +1393 3333333333333333333333333333333333333333333333333333333 +1394 3333333333333333333333333333333333333333333333333333333 +1395 3333333333333333333333333333333333333333333333333333333 +1396 3333333333333333333333333333333333333333333333333333333 +1397 3333333333333333333333333333333333333333333333333333333 +1398 3333333333333333333333333333333333333333333333333333333 +1399 3333333333333333333333333333333333333333333333333333333 +1400 3333333333333333333333333333333333333333333333333333333 +1401 3333333333333333333333333333333333333333333333333333333 +1402 3333333333333333333333333333333333333333333333333333333 +1403 3333333333333333333333333333333333333333333333333333333 +1404 3333333333333333333333333333333333333333333333333333333 +1405 3333333333333333333333333333333333333333333333333333333 +1406 3333333333333333333333333333333333333333333333333333333 +1407 3333333333333333333333333333333333333333333333333333333 +1408 3333333333333333333333333333333333333333333333333333333 +1409 3333333333333333333333333333333333333333333333333333333 +1410 3333333333333333333333333333333333333333333333333333333 +1411 3333333333333333333333333333333333333333333333333333333 +1412 3333333333333333333333333333333333333333333333333333333 +1413 3333333333333333333333333333333333333333333333333333333 +1414 3333333333333333333333333333333333333333333333333333333 +1415 3333333333333333333333333333333333333333333333333333333 +1416 3333333333333333333333333333333333333333333333333333333 +1417 3333333333333333333333333333333333333333333333333333333 +1418 3333333333333333333333333333333333333333333333333333333 +1419 3333333333333333333333333333333333333333333333333333333 +1420 3333333333333333333333333333333333333333333333333333333 +1421 3333333333333333333333333333333333333333333333333333333 +1422 3333333333333333333333333333333333333333333333333333333 +1423 3333333333333333333333333333333333333333333333333333333 +1424 3333333333333333333333333333333333333333333333333333333 +1425 3333333333333333333333333333333333333333333333333333333 +1426 3333333333333333333333333333333333333333333333333333333 +1427 3333333333333333333333333333333333333333333333333333333 +1428 3333333333333333333333333333333333333333333333333333333 +1429 3333333333333333333333333333333333333333333333333333333 +1430 3333333333333333333333333333333333333333333333333333333 +1431 3333333333333333333333333333333333333333333333333333333 +1432 3333333333333333333333333333333333333333333333333333333 +1433 3333333333333333333333333333333333333333333333333333333 +1434 3333333333333333333333333333333333333333333333333333333 +1435 3333333333333333333333333333333333333333333333333333333 +1436 3333333333333333333333333333333333333333333333333333333 +1437 3333333333333333333333333333333333333333333333333333333 +1438 3333333333333333333333333333333333333333333333333333333 +1439 3333333333333333333333333333333333333333333333333333333 +1440 3333333333333333333333333333333333333333333333333333333 +1441 3333333333333333333333333333333333333333333333333333333 +1442 3333333333333333333333333333333333333333333333333333333 +1443 3333333333333333333333333333333333333333333333333333333 +1444 3333333333333333333333333333333333333333333333333333333 +1445 3333333333333333333333333333333333333333333333333333333 +1446 3333333333333333333333333333333333333333333333333333333 +1447 3333333333333333333333333333333333333333333333333333333 +1448 3333333333333333333333333333333333333333333333333333333 +1449 3333333333333333333333333333333333333333333333333333333 +1450 3333333333333333333333333333333333333333333333333333333 +1451 3333333333333333333333333333333333333333333333333333333 +1452 3333333333333333333333333333333333333333333333333333333 +1453 3333333333333333333333333333333333333333333333333333333 +1454 3333333333333333333333333333333333333333333333333333333 +1455 3333333333333333333333333333333333333333333333333333333 +1456 3333333333333333333333333333333333333333333333333333333 +1457 3333333333333333333333333333333333333333333333333333333 +1458 3333333333333333333333333333333333333333333333333333333 +1459 3333333333333333333333333333333333333333333333333333333 +1460 3333333333333333333333333333333333333333333333333333333 +1461 3333333333333333333333333333333333333333333333333333333 +1462 3333333333333333333333333333333333333333333333333333333 +1463 3333333333333333333333333333333333333333333333333333333 +1464 3333333333333333333333333333333333333333333333333333333 +1465 3333333333333333333333333333333333333333333333333333333 +1466 3333333333333333333333333333333333333333333333333333333 +1467 3333333333333333333333333333333333333333333333333333333 +1468 3333333333333333333333333333333333333333333333333333333 +1469 3333333333333333333333333333333333333333333333333333333 +1470 3333333333333333333333333333333333333333333333333333333 +1471 3333333333333333333333333333333333333333333333333333333 +1472 3333333333333333333333333333333333333333333333333333333 +1473 3333333333333333333333333333333333333333333333333333333 +1474 3333333333333333333333333333333333333333333333333333333 +1475 3333333333333333333333333333333333333333333333333333333 +1476 3333333333333333333333333333333333333333333333333333333 +1477 3333333333333333333333333333333333333333333333333333333 +1478 3333333333333333333333333333333333333333333333333333333 +1479 3333333333333333333333333333333333333333333333333333333 +1480 3333333333333333333333333333333333333333333333333333333 +1481 3333333333333333333333333333333333333333333333333333333 +1482 3333333333333333333333333333333333333333333333333333333 +1483 3333333333333333333333333333333333333333333333333333333 +1484 3333333333333333333333333333333333333333333333333333333 +1485 3333333333333333333333333333333333333333333333333333333 +1486 3333333333333333333333333333333333333333333333333333333 +1487 3333333333333333333333333333333333333333333333333333333 +1488 3333333333333333333333333333333333333333333333333333333 +1489 3333333333333333333333333333333333333333333333333333333 +1490 3333333333333333333333333333333333333333333333333333333 +1491 3333333333333333333333333333333333333333333333333333333 +1492 3333333333333333333333333333333333333333333333333333333 +1493 3333333333333333333333333333333333333333333333333333333 +1494 3333333333333333333333333333333333333333333333333333333 +1495 3333333333333333333333333333333333333333333333333333333 +1496 3333333333333333333333333333333333333333333333333333333 +1497 3333333333333333333333333333333333333333333333333333333 +1498 3333333333333333333333333333333333333333333333333333333 +1499 3333333333333333333333333333333333333333333333333333333 +1500 3333333333333333333333333333333333333333333333333333333 +1501 3333333333333333333333333333333333333333333333333333333 +1502 3333333333333333333333333333333333333333333333333333333 +1503 3333333333333333333333333333333333333333333333333333333 +1504 3333333333333333333333333333333333333333333333333333333 +1505 3333333333333333333333333333333333333333333333333333333 +1506 3333333333333333333333333333333333333333333333333333333 +1507 3333333333333333333333333333333333333333333333333333333 +1508 3333333333333333333333333333333333333333333333333333333 +1509 3333333333333333333333333333333333333333333333333333333 +1510 3333333333333333333333333333333333333333333333333333333 +1511 3333333333333333333333333333333333333333333333333333333 +1512 3333333333333333333333333333333333333333333333333333333 +1513 3333333333333333333333333333333333333333333333333333333 +1514 3333333333333333333333333333333333333333333333333333333 +1515 3333333333333333333333333333333333333333333333333333333 +1516 3333333333333333333333333333333333333333333333333333333 +1517 3333333333333333333333333333333333333333333333333333333 +1518 3333333333333333333333333333333333333333333333333333333 +1519 3333333333333333333333333333333333333333333333333333333 +1520 3333333333333333333333333333333333333333333333333333333 +1521 3333333333333333333333333333333333333333333333333333333 +1522 3333333333333333333333333333333333333333333333333333333 +1523 3333333333333333333333333333333333333333333333333333333 +1524 3333333333333333333333333333333333333333333333333333333 +1525 3333333333333333333333333333333333333333333333333333333 +1526 3333333333333333333333333333333333333333333333333333333 +1527 3333333333333333333333333333333333333333333333333333333 +1528 3333333333333333333333333333333333333333333333333333333 +1529 3333333333333333333333333333333333333333333333333333333 +1530 3333333333333333333333333333333333333333333333333333333 +1531 3333333333333333333333333333333333333333333333333333333 +1532 3333333333333333333333333333333333333333333333333333333 +1533 3333333333333333333333333333333333333333333333333333333 +1534 3333333333333333333333333333333333333333333333333333333 +1535 3333333333333333333333333333333333333333333333333333333 +1536 3333333333333333333333333333333333333333333333333333333 +1537 3333333333333333333333333333333333333333333333333333333 +1538 3333333333333333333333333333333333333333333333333333333 +1539 3333333333333333333333333333333333333333333333333333333 +1540 3333333333333333333333333333333333333333333333333333333 +1541 3333333333333333333333333333333333333333333333333333333 +1542 3333333333333333333333333333333333333333333333333333333 +1543 3333333333333333333333333333333333333333333333333333333 +1544 3333333333333333333333333333333333333333333333333333333 +1545 3333333333333333333333333333333333333333333333333333333 +1546 3333333333333333333333333333333333333333333333333333333 +1547 3333333333333333333333333333333333333333333333333333333 +1548 3333333333333333333333333333333333333333333333333333333 +1549 3333333333333333333333333333333333333333333333333333333 +1550 3333333333333333333333333333333333333333333333333333333 +1551 3333333333333333333333333333333333333333333333333333333 +1552 3333333333333333333333333333333333333333333333333333333 +1553 3333333333333333333333333333333333333333333333333333333 +1554 3333333333333333333333333333333333333333333333333333333 +1555 3333333333333333333333333333333333333333333333333333333 +1556 3333333333333333333333333333333333333333333333333333333 +1557 3333333333333333333333333333333333333333333333333333333 +1558 3333333333333333333333333333333333333333333333333333333 +1559 3333333333333333333333333333333333333333333333333333333 +1560 3333333333333333333333333333333333333333333333333333333 +1561 3333333333333333333333333333333333333333333333333333333 +1562 3333333333333333333333333333333333333333333333333333333 +1563 3333333333333333333333333333333333333333333333333333333 +1564 3333333333333333333333333333333333333333333333333333333 +1565 3333333333333333333333333333333333333333333333333333333 +1566 3333333333333333333333333333333333333333333333333333333 +1567 3333333333333333333333333333333333333333333333333333333 +1568 3333333333333333333333333333333333333333333333333333333 +1569 3333333333333333333333333333333333333333333333333333333 +1570 3333333333333333333333333333333333333333333333333333333 +1571 3333333333333333333333333333333333333333333333333333333 +1572 3333333333333333333333333333333333333333333333333333333 +1573 3333333333333333333333333333333333333333333333333333333 +1574 3333333333333333333333333333333333333333333333333333333 +1575 3333333333333333333333333333333333333333333333333333333 +1576 3333333333333333333333333333333333333333333333333333333 +1577 3333333333333333333333333333333333333333333333333333333 +1578 3333333333333333333333333333333333333333333333333333333 +1579 3333333333333333333333333333333333333333333333333333333 +1580 3333333333333333333333333333333333333333333333333333333 +1581 3333333333333333333333333333333333333333333333333333333 +1582 3333333333333333333333333333333333333333333333333333333 +1583 3333333333333333333333333333333333333333333333333333333 +1584 3333333333333333333333333333333333333333333333333333333 +1585 3333333333333333333333333333333333333333333333333333333 +1586 3333333333333333333333333333333333333333333333333333333 +1587 3333333333333333333333333333333333333333333333333333333 +1588 3333333333333333333333333333333333333333333333333333333 +1589 3333333333333333333333333333333333333333333333333333333 +1590 3333333333333333333333333333333333333333333333333333333 +1591 3333333333333333333333333333333333333333333333333333333 +1592 3333333333333333333333333333333333333333333333333333333 +1593 3333333333333333333333333333333333333333333333333333333 +1594 3333333333333333333333333333333333333333333333333333333 +1595 3333333333333333333333333333333333333333333333333333333 +1596 3333333333333333333333333333333333333333333333333333333 +1597 3333333333333333333333333333333333333333333333333333333 +1598 3333333333333333333333333333333333333333333333333333333 +1599 3333333333333333333333333333333333333333333333333333333 +1600 3333333333333333333333333333333333333333333333333333333 +1601 3333333333333333333333333333333333333333333333333333333 +1602 3333333333333333333333333333333333333333333333333333333 +1603 3333333333333333333333333333333333333333333333333333333 +1604 3333333333333333333333333333333333333333333333333333333 +1605 3333333333333333333333333333333333333333333333333333333 +1606 3333333333333333333333333333333333333333333333333333333 +1607 3333333333333333333333333333333333333333333333333333333 +1608 3333333333333333333333333333333333333333333333333333333 +1609 3333333333333333333333333333333333333333333333333333333 +1610 3333333333333333333333333333333333333333333333333333333 +1611 3333333333333333333333333333333333333333333333333333333 +1612 3333333333333333333333333333333333333333333333333333333 +1613 3333333333333333333333333333333333333333333333333333333 +1614 3333333333333333333333333333333333333333333333333333333 +1615 3333333333333333333333333333333333333333333333333333333 +1616 3333333333333333333333333333333333333333333333333333333 +1617 3333333333333333333333333333333333333333333333333333333 +1618 3333333333333333333333333333333333333333333333333333333 +1619 3333333333333333333333333333333333333333333333333333333 +1620 3333333333333333333333333333333333333333333333333333333 +1621 3333333333333333333333333333333333333333333333333333333 +1622 3333333333333333333333333333333333333333333333333333333 +1623 3333333333333333333333333333333333333333333333333333333 +1624 3333333333333333333333333333333333333333333333333333333 +1625 3333333333333333333333333333333333333333333333333333333 +1626 3333333333333333333333333333333333333333333333333333333 +1627 3333333333333333333333333333333333333333333333333333333 +1628 3333333333333333333333333333333333333333333333333333333 +1629 3333333333333333333333333333333333333333333333333333333 +1630 3333333333333333333333333333333333333333333333333333333 +1631 3333333333333333333333333333333333333333333333333333333 +1632 3333333333333333333333333333333333333333333333333333333 +1633 3333333333333333333333333333333333333333333333333333333 +1634 3333333333333333333333333333333333333333333333333333333 +1635 3333333333333333333333333333333333333333333333333333333 +1636 3333333333333333333333333333333333333333333333333333333 +1637 3333333333333333333333333333333333333333333333333333333 +1638 3333333333333333333333333333333333333333333333333333333 +1639 3333333333333333333333333333333333333333333333333333333 +1640 3333333333333333333333333333333333333333333333333333333 +1641 3333333333333333333333333333333333333333333333333333333 +1642 3333333333333333333333333333333333333333333333333333333 +1643 3333333333333333333333333333333333333333333333333333333 +1644 3333333333333333333333333333333333333333333333333333333 +1645 3333333333333333333333333333333333333333333333333333333 +1646 3333333333333333333333333333333333333333333333333333333 +1647 3333333333333333333333333333333333333333333333333333333 +1648 3333333333333333333333333333333333333333333333333333333 +1649 3333333333333333333333333333333333333333333333333333333 +1650 3333333333333333333333333333333333333333333333333333333 +1651 3333333333333333333333333333333333333333333333333333333 +1652 3333333333333333333333333333333333333333333333333333333 +1653 3333333333333333333333333333333333333333333333333333333 +1654 3333333333333333333333333333333333333333333333333333333 +1655 3333333333333333333333333333333333333333333333333333333 +1656 3333333333333333333333333333333333333333333333333333333 +1657 3333333333333333333333333333333333333333333333333333333 +1658 3333333333333333333333333333333333333333333333333333333 +1659 3333333333333333333333333333333333333333333333333333333 +1660 3333333333333333333333333333333333333333333333333333333 +1661 3333333333333333333333333333333333333333333333333333333 +1662 3333333333333333333333333333333333333333333333333333333 +1663 3333333333333333333333333333333333333333333333333333333 +1664 3333333333333333333333333333333333333333333333333333333 +1665 3333333333333333333333333333333333333333333333333333333 +1666 3333333333333333333333333333333333333333333333333333333 +1667 3333333333333333333333333333333333333333333333333333333 +1668 3333333333333333333333333333333333333333333333333333333 +1669 3333333333333333333333333333333333333333333333333333333 +1670 3333333333333333333333333333333333333333333333333333333 +1671 3333333333333333333333333333333333333333333333333333333 +1672 3333333333333333333333333333333333333333333333333333333 +1673 3333333333333333333333333333333333333333333333333333333 +1674 3333333333333333333333333333333333333333333333333333333 +1675 3333333333333333333333333333333333333333333333333333333 +1676 3333333333333333333333333333333333333333333333333333333 +1677 3333333333333333333333333333333333333333333333333333333 +1678 3333333333333333333333333333333333333333333333333333333 +1679 3333333333333333333333333333333333333333333333333333333 +1680 3333333333333333333333333333333333333333333333333333333 +1681 3333333333333333333333333333333333333333333333333333333 +1682 3333333333333333333333333333333333333333333333333333333 +1683 3333333333333333333333333333333333333333333333333333333 +1684 3333333333333333333333333333333333333333333333333333333 +1685 3333333333333333333333333333333333333333333333333333333 +1686 3333333333333333333333333333333333333333333333333333333 +1687 3333333333333333333333333333333333333333333333333333333 +1688 3333333333333333333333333333333333333333333333333333333 +1689 3333333333333333333333333333333333333333333333333333333 +1690 3333333333333333333333333333333333333333333333333333333 +1691 3333333333333333333333333333333333333333333333333333333 +1692 3333333333333333333333333333333333333333333333333333333 +1693 3333333333333333333333333333333333333333333333333333333 +1694 3333333333333333333333333333333333333333333333333333333 +1695 3333333333333333333333333333333333333333333333333333333 +1696 3333333333333333333333333333333333333333333333333333333 +1697 3333333333333333333333333333333333333333333333333333333 +1698 3333333333333333333333333333333333333333333333333333333 +1699 3333333333333333333333333333333333333333333333333333333 +1700 3333333333333333333333333333333333333333333333333333333 +1701 3333333333333333333333333333333333333333333333333333333 +1702 3333333333333333333333333333333333333333333333333333333 +1703 3333333333333333333333333333333333333333333333333333333 +1704 3333333333333333333333333333333333333333333333333333333 +1705 3333333333333333333333333333333333333333333333333333333 +1706 3333333333333333333333333333333333333333333333333333333 +1707 3333333333333333333333333333333333333333333333333333333 +1708 3333333333333333333333333333333333333333333333333333333 +1709 3333333333333333333333333333333333333333333333333333333 +1710 3333333333333333333333333333333333333333333333333333333 +1711 3333333333333333333333333333333333333333333333333333333 +1712 3333333333333333333333333333333333333333333333333333333 +1713 3333333333333333333333333333333333333333333333333333333 +1714 3333333333333333333333333333333333333333333333333333333 +1715 3333333333333333333333333333333333333333333333333333333 +1716 3333333333333333333333333333333333333333333333333333333 +1717 3333333333333333333333333333333333333333333333333333333 +1718 3333333333333333333333333333333333333333333333333333333 +1719 3333333333333333333333333333333333333333333333333333333 +1720 3333333333333333333333333333333333333333333333333333333 +1721 3333333333333333333333333333333333333333333333333333333 +1722 3333333333333333333333333333333333333333333333333333333 +1723 3333333333333333333333333333333333333333333333333333333 +1724 3333333333333333333333333333333333333333333333333333333 +1725 3333333333333333333333333333333333333333333333333333333 +1726 3333333333333333333333333333333333333333333333333333333 +1727 3333333333333333333333333333333333333333333333333333333 +1728 3333333333333333333333333333333333333333333333333333333 +1729 3333333333333333333333333333333333333333333333333333333 +1730 3333333333333333333333333333333333333333333333333333333 +1731 3333333333333333333333333333333333333333333333333333333 +1732 3333333333333333333333333333333333333333333333333333333 +1733 3333333333333333333333333333333333333333333333333333333 +1734 3333333333333333333333333333333333333333333333333333333 +1735 3333333333333333333333333333333333333333333333333333333 +1736 3333333333333333333333333333333333333333333333333333333 +1737 3333333333333333333333333333333333333333333333333333333 +1738 3333333333333333333333333333333333333333333333333333333 +1739 3333333333333333333333333333333333333333333333333333333 +1740 3333333333333333333333333333333333333333333333333333333 +1741 3333333333333333333333333333333333333333333333333333333 +1742 3333333333333333333333333333333333333333333333333333333 +1743 3333333333333333333333333333333333333333333333333333333 +1744 3333333333333333333333333333333333333333333333333333333 +1745 3333333333333333333333333333333333333333333333333333333 +1746 3333333333333333333333333333333333333333333333333333333 +1747 3333333333333333333333333333333333333333333333333333333 +1748 3333333333333333333333333333333333333333333333333333333 +1749 3333333333333333333333333333333333333333333333333333333 +1750 3333333333333333333333333333333333333333333333333333333 +1751 3333333333333333333333333333333333333333333333333333333 +1752 3333333333333333333333333333333333333333333333333333333 +1753 3333333333333333333333333333333333333333333333333333333 +1754 3333333333333333333333333333333333333333333333333333333 +1755 3333333333333333333333333333333333333333333333333333333 +1756 3333333333333333333333333333333333333333333333333333333 +1757 3333333333333333333333333333333333333333333333333333333 +1758 3333333333333333333333333333333333333333333333333333333 +1759 3333333333333333333333333333333333333333333333333333333 +1760 3333333333333333333333333333333333333333333333333333333 +1761 3333333333333333333333333333333333333333333333333333333 +1762 3333333333333333333333333333333333333333333333333333333 +1763 3333333333333333333333333333333333333333333333333333333 +1764 3333333333333333333333333333333333333333333333333333333 +1765 3333333333333333333333333333333333333333333333333333333 +1766 3333333333333333333333333333333333333333333333333333333 +1767 3333333333333333333333333333333333333333333333333333333 +1768 3333333333333333333333333333333333333333333333333333333 +1769 3333333333333333333333333333333333333333333333333333333 +1770 3333333333333333333333333333333333333333333333333333333 +1771 3333333333333333333333333333333333333333333333333333333 +1772 3333333333333333333333333333333333333333333333333333333 +1773 3333333333333333333333333333333333333333333333333333333 +1774 3333333333333333333333333333333333333333333333333333333 +1775 3333333333333333333333333333333333333333333333333333333 +1776 3333333333333333333333333333333333333333333333333333333 +1777 3333333333333333333333333333333333333333333333333333333 +1778 3333333333333333333333333333333333333333333333333333333 +1779 3333333333333333333333333333333333333333333333333333333 +1780 3333333333333333333333333333333333333333333333333333333 +1781 3333333333333333333333333333333333333333333333333333333 +1782 3333333333333333333333333333333333333333333333333333333 +1783 3333333333333333333333333333333333333333333333333333333 +1784 3333333333333333333333333333333333333333333333333333333 +1785 3333333333333333333333333333333333333333333333333333333 +1786 3333333333333333333333333333333333333333333333333333333 +1787 3333333333333333333333333333333333333333333333333333333 +1788 3333333333333333333333333333333333333333333333333333333 +1789 3333333333333333333333333333333333333333333333333333333 +1790 3333333333333333333333333333333333333333333333333333333 +1791 3333333333333333333333333333333333333333333333333333333 +1792 3333333333333333333333333333333333333333333333333333333 +1793 3333333333333333333333333333333333333333333333333333333 +1794 3333333333333333333333333333333333333333333333333333333 +1795 3333333333333333333333333333333333333333333333333333333 +1796 3333333333333333333333333333333333333333333333333333333 +1797 3333333333333333333333333333333333333333333333333333333 +1798 3333333333333333333333333333333333333333333333333333333 +1799 3333333333333333333333333333333333333333333333333333333 +1800 3333333333333333333333333333333333333333333333333333333 +1801 3333333333333333333333333333333333333333333333333333333 +1802 3333333333333333333333333333333333333333333333333333333 +1803 3333333333333333333333333333333333333333333333333333333 +1804 3333333333333333333333333333333333333333333333333333333 +1805 3333333333333333333333333333333333333333333333333333333 +1806 3333333333333333333333333333333333333333333333333333333 +1807 3333333333333333333333333333333333333333333333333333333 +1808 3333333333333333333333333333333333333333333333333333333 +1809 3333333333333333333333333333333333333333333333333333333 +1810 3333333333333333333333333333333333333333333333333333333 +1811 3333333333333333333333333333333333333333333333333333333 +1812 3333333333333333333333333333333333333333333333333333333 +1813 3333333333333333333333333333333333333333333333333333333 +1814 3333333333333333333333333333333333333333333333333333333 +1815 3333333333333333333333333333333333333333333333333333333 +1816 3333333333333333333333333333333333333333333333333333333 +1817 3333333333333333333333333333333333333333333333333333333 +1818 3333333333333333333333333333333333333333333333333333333 +1819 3333333333333333333333333333333333333333333333333333333 +1820 3333333333333333333333333333333333333333333333333333333 +1821 3333333333333333333333333333333333333333333333333333333 +1822 3333333333333333333333333333333333333333333333333333333 +1823 3333333333333333333333333333333333333333333333333333333 +1824 3333333333333333333333333333333333333333333333333333333 +1825 3333333333333333333333333333333333333333333333333333333 +1826 3333333333333333333333333333333333333333333333333333333 +1827 3333333333333333333333333333333333333333333333333333333 +1828 3333333333333333333333333333333333333333333333333333333 +1829 3333333333333333333333333333333333333333333333333333333 +1830 3333333333333333333333333333333333333333333333333333333 +1831 3333333333333333333333333333333333333333333333333333333 +1832 3333333333333333333333333333333333333333333333333333333 +1833 3333333333333333333333333333333333333333333333333333333 +1834 3333333333333333333333333333333333333333333333333333333 +1835 3333333333333333333333333333333333333333333333333333333 +1836 3333333333333333333333333333333333333333333333333333333 +1837 3333333333333333333333333333333333333333333333333333333 +1838 3333333333333333333333333333333333333333333333333333333 +1839 3333333333333333333333333333333333333333333333333333333 +1840 3333333333333333333333333333333333333333333333333333333 +1841 3333333333333333333333333333333333333333333333333333333 +1842 3333333333333333333333333333333333333333333333333333333 +1843 3333333333333333333333333333333333333333333333333333333 +1844 3333333333333333333333333333333333333333333333333333333 +1845 3333333333333333333333333333333333333333333333333333333 +1846 3333333333333333333333333333333333333333333333333333333 +1847 3333333333333333333333333333333333333333333333333333333 +1848 3333333333333333333333333333333333333333333333333333333 +1849 3333333333333333333333333333333333333333333333333333333 +1850 3333333333333333333333333333333333333333333333333333333 +1851 3333333333333333333333333333333333333333333333333333333 +1852 3333333333333333333333333333333333333333333333333333333 +1853 3333333333333333333333333333333333333333333333333333333 +1854 3333333333333333333333333333333333333333333333333333333 +1855 3333333333333333333333333333333333333333333333333333333 +1856 3333333333333333333333333333333333333333333333333333333 +1857 3333333333333333333333333333333333333333333333333333333 +1858 3333333333333333333333333333333333333333333333333333333 +1859 3333333333333333333333333333333333333333333333333333333 +1860 3333333333333333333333333333333333333333333333333333333 +1861 3333333333333333333333333333333333333333333333333333333 +1862 3333333333333333333333333333333333333333333333333333333 +1863 3333333333333333333333333333333333333333333333333333333 +1864 3333333333333333333333333333333333333333333333333333333 +1865 3333333333333333333333333333333333333333333333333333333 +1866 3333333333333333333333333333333333333333333333333333333 +1867 3333333333333333333333333333333333333333333333333333333 +1868 3333333333333333333333333333333333333333333333333333333 +1869 3333333333333333333333333333333333333333333333333333333 +1870 3333333333333333333333333333333333333333333333333333333 +1871 3333333333333333333333333333333333333333333333333333333 +1872 3333333333333333333333333333333333333333333333333333333 +1873 3333333333333333333333333333333333333333333333333333333 +1874 3333333333333333333333333333333333333333333333333333333 +1875 3333333333333333333333333333333333333333333333333333333 +1876 3333333333333333333333333333333333333333333333333333333 +1877 3333333333333333333333333333333333333333333333333333333 +1878 3333333333333333333333333333333333333333333333333333333 +1879 3333333333333333333333333333333333333333333333333333333 +1880 3333333333333333333333333333333333333333333333333333333 +1881 3333333333333333333333333333333333333333333333333333333 +1882 3333333333333333333333333333333333333333333333333333333 +1883 3333333333333333333333333333333333333333333333333333333 +1884 3333333333333333333333333333333333333333333333333333333 +1885 3333333333333333333333333333333333333333333333333333333 +1886 3333333333333333333333333333333333333333333333333333333 +1887 3333333333333333333333333333333333333333333333333333333 +1888 3333333333333333333333333333333333333333333333333333333 +1889 3333333333333333333333333333333333333333333333333333333 +1890 3333333333333333333333333333333333333333333333333333333 +1891 3333333333333333333333333333333333333333333333333333333 +1892 3333333333333333333333333333333333333333333333333333333 +1893 3333333333333333333333333333333333333333333333333333333 +1894 3333333333333333333333333333333333333333333333333333333 +1895 3333333333333333333333333333333333333333333333333333333 +1896 3333333333333333333333333333333333333333333333333333333 +1897 3333333333333333333333333333333333333333333333333333333 +1898 3333333333333333333333333333333333333333333333333333333 +1899 3333333333333333333333333333333333333333333333333333333 +1900 3333333333333333333333333333333333333333333333333333333 +1901 3333333333333333333333333333333333333333333333333333333 +1902 3333333333333333333333333333333333333333333333333333333 +1903 3333333333333333333333333333333333333333333333333333333 +1904 3333333333333333333333333333333333333333333333333333333 +1905 3333333333333333333333333333333333333333333333333333333 +1906 3333333333333333333333333333333333333333333333333333333 +1907 3333333333333333333333333333333333333333333333333333333 +1908 3333333333333333333333333333333333333333333333333333333 +1909 3333333333333333333333333333333333333333333333333333333 +1910 3333333333333333333333333333333333333333333333333333333 +1911 3333333333333333333333333333333333333333333333333333333 +1912 3333333333333333333333333333333333333333333333333333333 +1913 3333333333333333333333333333333333333333333333333333333 +1914 3333333333333333333333333333333333333333333333333333333 +1915 3333333333333333333333333333333333333333333333333333333 +1916 3333333333333333333333333333333333333333333333333333333 +1917 3333333333333333333333333333333333333333333333333333333 +1918 3333333333333333333333333333333333333333333333333333333 +1919 3333333333333333333333333333333333333333333333333333333 +1920 3333333333333333333333333333333333333333333333333333333 +1921 3333333333333333333333333333333333333333333333333333333 +1922 3333333333333333333333333333333333333333333333333333333 +1923 3333333333333333333333333333333333333333333333333333333 +1924 3333333333333333333333333333333333333333333333333333333 +1925 3333333333333333333333333333333333333333333333333333333 +1926 3333333333333333333333333333333333333333333333333333333 +1927 3333333333333333333333333333333333333333333333333333333 +1928 3333333333333333333333333333333333333333333333333333333 +1929 3333333333333333333333333333333333333333333333333333333 +1930 3333333333333333333333333333333333333333333333333333333 +1931 3333333333333333333333333333333333333333333333333333333 +1932 3333333333333333333333333333333333333333333333333333333 +1933 3333333333333333333333333333333333333333333333333333333 +1934 3333333333333333333333333333333333333333333333333333333 +1935 3333333333333333333333333333333333333333333333333333333 +1936 3333333333333333333333333333333333333333333333333333333 +1937 3333333333333333333333333333333333333333333333333333333 +1938 3333333333333333333333333333333333333333333333333333333 +1939 3333333333333333333333333333333333333333333333333333333 +1940 3333333333333333333333333333333333333333333333333333333 +1941 3333333333333333333333333333333333333333333333333333333 +1942 3333333333333333333333333333333333333333333333333333333 +1943 3333333333333333333333333333333333333333333333333333333 +1944 3333333333333333333333333333333333333333333333333333333 +1945 3333333333333333333333333333333333333333333333333333333 +1946 3333333333333333333333333333333333333333333333333333333 +1947 3333333333333333333333333333333333333333333333333333333 +1948 3333333333333333333333333333333333333333333333333333333 +1949 3333333333333333333333333333333333333333333333333333333 +1950 3333333333333333333333333333333333333333333333333333333 +1951 3333333333333333333333333333333333333333333333333333333 +1952 3333333333333333333333333333333333333333333333333333333 +1953 3333333333333333333333333333333333333333333333333333333 +1954 3333333333333333333333333333333333333333333333333333333 +1955 3333333333333333333333333333333333333333333333333333333 +1956 3333333333333333333333333333333333333333333333333333333 +1957 3333333333333333333333333333333333333333333333333333333 +1958 3333333333333333333333333333333333333333333333333333333 +1959 3333333333333333333333333333333333333333333333333333333 +1960 3333333333333333333333333333333333333333333333333333333 +1961 3333333333333333333333333333333333333333333333333333333 +1962 3333333333333333333333333333333333333333333333333333333 +1963 3333333333333333333333333333333333333333333333333333333 +1964 3333333333333333333333333333333333333333333333333333333 +1965 3333333333333333333333333333333333333333333333333333333 +1966 3333333333333333333333333333333333333333333333333333333 +1967 3333333333333333333333333333333333333333333333333333333 +1968 3333333333333333333333333333333333333333333333333333333 +1969 3333333333333333333333333333333333333333333333333333333 +1970 3333333333333333333333333333333333333333333333333333333 +1971 3333333333333333333333333333333333333333333333333333333 +1972 3333333333333333333333333333333333333333333333333333333 +1973 3333333333333333333333333333333333333333333333333333333 +1974 3333333333333333333333333333333333333333333333333333333 +1975 3333333333333333333333333333333333333333333333333333333 +1976 3333333333333333333333333333333333333333333333333333333 +1977 3333333333333333333333333333333333333333333333333333333 +1978 3333333333333333333333333333333333333333333333333333333 +1979 3333333333333333333333333333333333333333333333333333333 +1980 3333333333333333333333333333333333333333333333333333333 +1981 3333333333333333333333333333333333333333333333333333333 +1982 3333333333333333333333333333333333333333333333333333333 +1983 3333333333333333333333333333333333333333333333333333333 +1984 3333333333333333333333333333333333333333333333333333333 +1985 3333333333333333333333333333333333333333333333333333333 +1986 3333333333333333333333333333333333333333333333333333333 +1987 3333333333333333333333333333333333333333333333333333333 +1988 3333333333333333333333333333333333333333333333333333333 +1989 3333333333333333333333333333333333333333333333333333333 +1990 3333333333333333333333333333333333333333333333333333333 +1991 3333333333333333333333333333333333333333333333333333333 +1992 3333333333333333333333333333333333333333333333333333333 +1993 3333333333333333333333333333333333333333333333333333333 +1994 3333333333333333333333333333333333333333333333333333333 +1995 3333333333333333333333333333333333333333333333333333333 +1996 3333333333333333333333333333333333333333333333333333333 +1997 3333333333333333333333333333333333333333333333333333333 +1998 3333333333333333333333333333333333333333333333333333333 +1999 3333333333333333333333333333333333333333333333333333333 +2000 3333333333333333333333333333333333333333333333333333333 +2001 3333333333333333333333333333333333333333333333333333333 +2002 3333333333333333333333333333333333333333333333333333333 +2003 3333333333333333333333333333333333333333333333333333333 +2004 3333333333333333333333333333333333333333333333333333333 +2005 3333333333333333333333333333333333333333333333333333333 +2006 3333333333333333333333333333333333333333333333333333333 +2007 3333333333333333333333333333333333333333333333333333333 +2008 3333333333333333333333333333333333333333333333333333333 +2009 3333333333333333333333333333333333333333333333333333333 +2010 3333333333333333333333333333333333333333333333333333333 +2011 3333333333333333333333333333333333333333333333333333333 +2012 3333333333333333333333333333333333333333333333333333333 +2013 3333333333333333333333333333333333333333333333333333333 +2014 3333333333333333333333333333333333333333333333333333333 +2015 3333333333333333333333333333333333333333333333333333333 +2016 3333333333333333333333333333333333333333333333333333333 +2017 3333333333333333333333333333333333333333333333333333333 +2018 3333333333333333333333333333333333333333333333333333333 +2019 3333333333333333333333333333333333333333333333333333333 +2020 3333333333333333333333333333333333333333333333333333333 +2021 3333333333333333333333333333333333333333333333333333333 +2022 3333333333333333333333333333333333333333333333333333333 +2023 3333333333333333333333333333333333333333333333333333333 +2024 3333333333333333333333333333333333333333333333333333333 +2025 3333333333333333333333333333333333333333333333333333333 +2026 3333333333333333333333333333333333333333333333333333333 +2027 3333333333333333333333333333333333333333333333333333333 +2028 3333333333333333333333333333333333333333333333333333333 +2029 3333333333333333333333333333333333333333333333333333333 +2030 3333333333333333333333333333333333333333333333333333333 +2031 3333333333333333333333333333333333333333333333333333333 +2032 3333333333333333333333333333333333333333333333333333333 +2033 3333333333333333333333333333333333333333333333333333333 +2034 3333333333333333333333333333333333333333333333333333333 +2035 3333333333333333333333333333333333333333333333333333333 +2036 3333333333333333333333333333333333333333333333333333333 +2037 3333333333333333333333333333333333333333333333333333333 +2038 3333333333333333333333333333333333333333333333333333333 +2039 3333333333333333333333333333333333333333333333333333333 +2040 3333333333333333333333333333333333333333333333333333333 +2041 3333333333333333333333333333333333333333333333333333333 +2042 3333333333333333333333333333333333333333333333333333333 +2043 3333333333333333333333333333333333333333333333333333333 +2044 3333333333333333333333333333333333333333333333333333333 +2045 3333333333333333333333333333333333333333333333333333333 +2046 3333333333333333333333333333333333333333333333333333333 +2047 3333333333333333333333333333333333333333333333333333333 +2048 3333333333333333333333333333333333333333333333333333333 +2049 3333333333333333333333333333333333333333333333333333333 +2050 3333333333333333333333333333333333333333333333333333333 +2051 3333333333333333333333333333333333333333333333333333333 +2052 3333333333333333333333333333333333333333333333333333333 +2053 3333333333333333333333333333333333333333333333333333333 +2054 3333333333333333333333333333333333333333333333333333333 +2055 3333333333333333333333333333333333333333333333333333333 +2056 3333333333333333333333333333333333333333333333333333333 +2057 3333333333333333333333333333333333333333333333333333333 +2058 3333333333333333333333333333333333333333333333333333333 +2059 3333333333333333333333333333333333333333333333333333333 +2060 3333333333333333333333333333333333333333333333333333333 +2061 3333333333333333333333333333333333333333333333333333333 +2062 3333333333333333333333333333333333333333333333333333333 +2063 3333333333333333333333333333333333333333333333333333333 +2064 3333333333333333333333333333333333333333333333333333333 +2065 3333333333333333333333333333333333333333333333333333333 +2066 3333333333333333333333333333333333333333333333333333333 +2067 3333333333333333333333333333333333333333333333333333333 +2068 3333333333333333333333333333333333333333333333333333333 +2069 3333333333333333333333333333333333333333333333333333333 +2070 3333333333333333333333333333333333333333333333333333333 +2071 3333333333333333333333333333333333333333333333333333333 +2072 3333333333333333333333333333333333333333333333333333333 +2073 3333333333333333333333333333333333333333333333333333333 +2074 3333333333333333333333333333333333333333333333333333333 +2075 3333333333333333333333333333333333333333333333333333333 +2076 3333333333333333333333333333333333333333333333333333333 +2077 3333333333333333333333333333333333333333333333333333333 +2078 3333333333333333333333333333333333333333333333333333333 +2079 3333333333333333333333333333333333333333333333333333333 +2080 3333333333333333333333333333333333333333333333333333333 +2081 3333333333333333333333333333333333333333333333333333333 +2082 3333333333333333333333333333333333333333333333333333333 +2083 3333333333333333333333333333333333333333333333333333333 +2084 3333333333333333333333333333333333333333333333333333333 +2085 3333333333333333333333333333333333333333333333333333333 +2086 3333333333333333333333333333333333333333333333333333333 +2087 3333333333333333333333333333333333333333333333333333333 +2088 3333333333333333333333333333333333333333333333333333333 +2089 3333333333333333333333333333333333333333333333333333333 +2090 3333333333333333333333333333333333333333333333333333333 +2091 3333333333333333333333333333333333333333333333333333333 +2092 3333333333333333333333333333333333333333333333333333333 +2093 3333333333333333333333333333333333333333333333333333333 +2094 3333333333333333333333333333333333333333333333333333333 +2095 3333333333333333333333333333333333333333333333333333333 +2096 3333333333333333333333333333333333333333333333333333333 +2097 3333333333333333333333333333333333333333333333333333333 +2098 3333333333333333333333333333333333333333333333333333333 +2099 3333333333333333333333333333333333333333333333333333333 +2100 3333333333333333333333333333333333333333333333333333333 +2101 3333333333333333333333333333333333333333333333333333333 +2102 3333333333333333333333333333333333333333333333333333333 +2103 3333333333333333333333333333333333333333333333333333333 +2104 3333333333333333333333333333333333333333333333333333333 +2105 3333333333333333333333333333333333333333333333333333333 +2106 3333333333333333333333333333333333333333333333333333333 +2107 3333333333333333333333333333333333333333333333333333333 +2108 3333333333333333333333333333333333333333333333333333333 +2109 3333333333333333333333333333333333333333333333333333333 +2110 3333333333333333333333333333333333333333333333333333333 +2111 3333333333333333333333333333333333333333333333333333333 +2112 3333333333333333333333333333333333333333333333333333333 +2113 3333333333333333333333333333333333333333333333333333333 +2114 3333333333333333333333333333333333333333333333333333333 +2115 3333333333333333333333333333333333333333333333333333333 +2116 3333333333333333333333333333333333333333333333333333333 +2117 3333333333333333333333333333333333333333333333333333333 +2118 3333333333333333333333333333333333333333333333333333333 +2119 3333333333333333333333333333333333333333333333333333333 +2120 3333333333333333333333333333333333333333333333333333333 +2121 3333333333333333333333333333333333333333333333333333333 +2122 3333333333333333333333333333333333333333333333333333333 +2123 3333333333333333333333333333333333333333333333333333333 +2124 3333333333333333333333333333333333333333333333333333333 +2125 3333333333333333333333333333333333333333333333333333333 +2126 3333333333333333333333333333333333333333333333333333333 +2127 3333333333333333333333333333333333333333333333333333333 +2128 3333333333333333333333333333333333333333333333333333333 +2129 3333333333333333333333333333333333333333333333333333333 +2130 3333333333333333333333333333333333333333333333333333333 +2131 3333333333333333333333333333333333333333333333333333333 +2132 3333333333333333333333333333333333333333333333333333333 +2133 3333333333333333333333333333333333333333333333333333333 +2134 3333333333333333333333333333333333333333333333333333333 +2135 3333333333333333333333333333333333333333333333333333333 +2136 3333333333333333333333333333333333333333333333333333333 +2137 3333333333333333333333333333333333333333333333333333333 +2138 3333333333333333333333333333333333333333333333333333333 +2139 3333333333333333333333333333333333333333333333333333333 +2140 3333333333333333333333333333333333333333333333333333333 +2141 3333333333333333333333333333333333333333333333333333333 +2142 3333333333333333333333333333333333333333333333333333333 +2143 3333333333333333333333333333333333333333333333333333333 +2144 3333333333333333333333333333333333333333333333333333333 +2145 3333333333333333333333333333333333333333333333333333333 +2146 3333333333333333333333333333333333333333333333333333333 +2147 3333333333333333333333333333333333333333333333333333333 +2148 3333333333333333333333333333333333333333333333333333333 +2149 3333333333333333333333333333333333333333333333333333333 +2150 3333333333333333333333333333333333333333333333333333333 +2151 3333333333333333333333333333333333333333333333333333333 +2152 3333333333333333333333333333333333333333333333333333333 +2153 3333333333333333333333333333333333333333333333333333333 +2154 3333333333333333333333333333333333333333333333333333333 +2155 3333333333333333333333333333333333333333333333333333333 +2156 3333333333333333333333333333333333333333333333333333333 +2157 3333333333333333333333333333333333333333333333333333333 +2158 3333333333333333333333333333333333333333333333333333333 +2159 3333333333333333333333333333333333333333333333333333333 +2160 3333333333333333333333333333333333333333333333333333333 +2161 3333333333333333333333333333333333333333333333333333333 +2162 3333333333333333333333333333333333333333333333333333333 +2163 3333333333333333333333333333333333333333333333333333333 +2164 3333333333333333333333333333333333333333333333333333333 +2165 3333333333333333333333333333333333333333333333333333333 +2166 3333333333333333333333333333333333333333333333333333333 +2167 3333333333333333333333333333333333333333333333333333333 +2168 3333333333333333333333333333333333333333333333333333333 +2169 3333333333333333333333333333333333333333333333333333333 +2170 3333333333333333333333333333333333333333333333333333333 +2171 3333333333333333333333333333333333333333333333333333333 +2172 3333333333333333333333333333333333333333333333333333333 +2173 3333333333333333333333333333333333333333333333333333333 +2174 3333333333333333333333333333333333333333333333333333333 +2175 3333333333333333333333333333333333333333333333333333333 +2176 3333333333333333333333333333333333333333333333333333333 +2177 3333333333333333333333333333333333333333333333333333333 +2178 3333333333333333333333333333333333333333333333333333333 +2179 3333333333333333333333333333333333333333333333333333333 +2180 3333333333333333333333333333333333333333333333333333333 +2181 3333333333333333333333333333333333333333333333333333333 +2182 3333333333333333333333333333333333333333333333333333333 +2183 3333333333333333333333333333333333333333333333333333333 +2184 3333333333333333333333333333333333333333333333333333333 +2185 3333333333333333333333333333333333333333333333333333333 +2186 3333333333333333333333333333333333333333333333333333333 +2187 3333333333333333333333333333333333333333333333333333333 +2188 3333333333333333333333333333333333333333333333333333333 +2189 3333333333333333333333333333333333333333333333333333333 +2190 3333333333333333333333333333333333333333333333333333333 +2191 3333333333333333333333333333333333333333333333333333333 +2192 3333333333333333333333333333333333333333333333333333333 +2193 3333333333333333333333333333333333333333333333333333333 +2194 3333333333333333333333333333333333333333333333333333333 +2195 3333333333333333333333333333333333333333333333333333333 +2196 3333333333333333333333333333333333333333333333333333333 +2197 3333333333333333333333333333333333333333333333333333333 +2198 3333333333333333333333333333333333333333333333333333333 +2199 3333333333333333333333333333333333333333333333333333333 +2200 3333333333333333333333333333333333333333333333333333333 +2201 3333333333333333333333333333333333333333333333333333333 +2202 3333333333333333333333333333333333333333333333333333333 +2203 3333333333333333333333333333333333333333333333333333333 +2204 3333333333333333333333333333333333333333333333333333333 +2205 3333333333333333333333333333333333333333333333333333333 +2206 3333333333333333333333333333333333333333333333333333333 +2207 3333333333333333333333333333333333333333333333333333333 +2208 3333333333333333333333333333333333333333333333333333333 +2209 3333333333333333333333333333333333333333333333333333333 +2210 3333333333333333333333333333333333333333333333333333333 +2211 3333333333333333333333333333333333333333333333333333333 +2212 3333333333333333333333333333333333333333333333333333333 +2213 3333333333333333333333333333333333333333333333333333333 +2214 3333333333333333333333333333333333333333333333333333333 +2215 3333333333333333333333333333333333333333333333333333333 +2216 3333333333333333333333333333333333333333333333333333333 +2217 3333333333333333333333333333333333333333333333333333333 +2218 3333333333333333333333333333333333333333333333333333333 +2219 3333333333333333333333333333333333333333333333333333333 +2220 3333333333333333333333333333333333333333333333333333333 +2221 3333333333333333333333333333333333333333333333333333333 +2222 3333333333333333333333333333333333333333333333333333333 +2223 3333333333333333333333333333333333333333333333333333333 +2224 3333333333333333333333333333333333333333333333333333333 +2225 3333333333333333333333333333333333333333333333333333333 +2226 3333333333333333333333333333333333333333333333333333333 +2227 3333333333333333333333333333333333333333333333333333333 +2228 3333333333333333333333333333333333333333333333333333333 +2229 3333333333333333333333333333333333333333333333333333333 +2230 3333333333333333333333333333333333333333333333333333333 +2231 3333333333333333333333333333333333333333333333333333333 +2232 3333333333333333333333333333333333333333333333333333333 +2233 3333333333333333333333333333333333333333333333333333333 +2234 3333333333333333333333333333333333333333333333333333333 +2235 3333333333333333333333333333333333333333333333333333333 +2236 3333333333333333333333333333333333333333333333333333333 +2237 3333333333333333333333333333333333333333333333333333333 +2238 3333333333333333333333333333333333333333333333333333333 +2239 3333333333333333333333333333333333333333333333333333333 +2240 3333333333333333333333333333333333333333333333333333333 +2241 3333333333333333333333333333333333333333333333333333333 +2242 3333333333333333333333333333333333333333333333333333333 +2243 3333333333333333333333333333333333333333333333333333333 +2244 3333333333333333333333333333333333333333333333333333333 +2245 3333333333333333333333333333333333333333333333333333333 +2246 3333333333333333333333333333333333333333333333333333333 +2247 3333333333333333333333333333333333333333333333333333333 +2248 3333333333333333333333333333333333333333333333333333333 +2249 3333333333333333333333333333333333333333333333333333333 +2250 3333333333333333333333333333333333333333333333333333333 +2251 3333333333333333333333333333333333333333333333333333333 +2252 3333333333333333333333333333333333333333333333333333333 +2253 3333333333333333333333333333333333333333333333333333333 +2254 3333333333333333333333333333333333333333333333333333333 +2255 3333333333333333333333333333333333333333333333333333333 +2256 3333333333333333333333333333333333333333333333333333333 +2257 3333333333333333333333333333333333333333333333333333333 +2258 3333333333333333333333333333333333333333333333333333333 +2259 3333333333333333333333333333333333333333333333333333333 +2260 3333333333333333333333333333333333333333333333333333333 +2261 3333333333333333333333333333333333333333333333333333333 +2262 3333333333333333333333333333333333333333333333333333333 +2263 3333333333333333333333333333333333333333333333333333333 +2264 3333333333333333333333333333333333333333333333333333333 +2265 3333333333333333333333333333333333333333333333333333333 +2266 3333333333333333333333333333333333333333333333333333333 +2267 3333333333333333333333333333333333333333333333333333333 +2268 3333333333333333333333333333333333333333333333333333333 +2269 3333333333333333333333333333333333333333333333333333333 +2270 3333333333333333333333333333333333333333333333333333333 +2271 3333333333333333333333333333333333333333333333333333333 +2272 3333333333333333333333333333333333333333333333333333333 +2273 3333333333333333333333333333333333333333333333333333333 +2274 3333333333333333333333333333333333333333333333333333333 +2275 3333333333333333333333333333333333333333333333333333333 +2276 3333333333333333333333333333333333333333333333333333333 +2277 3333333333333333333333333333333333333333333333333333333 +2278 3333333333333333333333333333333333333333333333333333333 +2279 3333333333333333333333333333333333333333333333333333333 +2280 3333333333333333333333333333333333333333333333333333333 +2281 3333333333333333333333333333333333333333333333333333333 +2282 3333333333333333333333333333333333333333333333333333333 +2283 3333333333333333333333333333333333333333333333333333333 +2284 3333333333333333333333333333333333333333333333333333333 +2285 3333333333333333333333333333333333333333333333333333333 +2286 3333333333333333333333333333333333333333333333333333333 +2287 3333333333333333333333333333333333333333333333333333333 +2288 3333333333333333333333333333333333333333333333333333333 +2289 3333333333333333333333333333333333333333333333333333333 +2290 3333333333333333333333333333333333333333333333333333333 +2291 3333333333333333333333333333333333333333333333333333333 +2292 3333333333333333333333333333333333333333333333333333333 +2293 3333333333333333333333333333333333333333333333333333333 +2294 3333333333333333333333333333333333333333333333333333333 +2295 3333333333333333333333333333333333333333333333333333333 +2296 3333333333333333333333333333333333333333333333333333333 +2297 3333333333333333333333333333333333333333333333333333333 +2298 3333333333333333333333333333333333333333333333333333333 +2299 3333333333333333333333333333333333333333333333333333333 +2300 3333333333333333333333333333333333333333333333333333333 +2301 3333333333333333333333333333333333333333333333333333333 +2302 3333333333333333333333333333333333333333333333333333333 +2303 3333333333333333333333333333333333333333333333333333333 +2304 3333333333333333333333333333333333333333333333333333333 +2305 3333333333333333333333333333333333333333333333333333333 +2306 3333333333333333333333333333333333333333333333333333333 +2307 3333333333333333333333333333333333333333333333333333333 +2308 3333333333333333333333333333333333333333333333333333333 +2309 3333333333333333333333333333333333333333333333333333333 +2310 3333333333333333333333333333333333333333333333333333333 +2311 3333333333333333333333333333333333333333333333333333333 +2312 3333333333333333333333333333333333333333333333333333333 +2313 3333333333333333333333333333333333333333333333333333333 +2314 3333333333333333333333333333333333333333333333333333333 +2315 3333333333333333333333333333333333333333333333333333333 +2316 3333333333333333333333333333333333333333333333333333333 +2317 3333333333333333333333333333333333333333333333333333333 +2318 3333333333333333333333333333333333333333333333333333333 +2319 3333333333333333333333333333333333333333333333333333333 +2320 3333333333333333333333333333333333333333333333333333333 +2321 3333333333333333333333333333333333333333333333333333333 +2322 3333333333333333333333333333333333333333333333333333333 +2323 3333333333333333333333333333333333333333333333333333333 +2324 3333333333333333333333333333333333333333333333333333333 +2325 3333333333333333333333333333333333333333333333333333333 +2326 3333333333333333333333333333333333333333333333333333333 +2327 3333333333333333333333333333333333333333333333333333333 +2328 3333333333333333333333333333333333333333333333333333333 +2329 3333333333333333333333333333333333333333333333333333333 +2330 3333333333333333333333333333333333333333333333333333333 +2331 3333333333333333333333333333333333333333333333333333333 +2332 3333333333333333333333333333333333333333333333333333333 +2333 3333333333333333333333333333333333333333333333333333333 +2334 3333333333333333333333333333333333333333333333333333333 +2335 3333333333333333333333333333333333333333333333333333333 +2336 3333333333333333333333333333333333333333333333333333333 +2337 3333333333333333333333333333333333333333333333333333333 +2338 3333333333333333333333333333333333333333333333333333333 +2339 3333333333333333333333333333333333333333333333333333333 +2340 3333333333333333333333333333333333333333333333333333333 +2341 3333333333333333333333333333333333333333333333333333333 +2342 3333333333333333333333333333333333333333333333333333333 +2343 3333333333333333333333333333333333333333333333333333333 +2344 3333333333333333333333333333333333333333333333333333333 +2345 3333333333333333333333333333333333333333333333333333333 +2346 3333333333333333333333333333333333333333333333333333333 +2347 3333333333333333333333333333333333333333333333333333333 +2348 3333333333333333333333333333333333333333333333333333333 +2349 3333333333333333333333333333333333333333333333333333333 +2350 3333333333333333333333333333333333333333333333333333333 +2351 3333333333333333333333333333333333333333333333333333333 +2352 3333333333333333333333333333333333333333333333333333333 +2353 3333333333333333333333333333333333333333333333333333333 +2354 3333333333333333333333333333333333333333333333333333333 +2355 3333333333333333333333333333333333333333333333333333333 +2356 3333333333333333333333333333333333333333333333333333333 +2357 3333333333333333333333333333333333333333333333333333333 +2358 3333333333333333333333333333333333333333333333333333333 +2359 3333333333333333333333333333333333333333333333333333333 +2360 3333333333333333333333333333333333333333333333333333333 +2361 3333333333333333333333333333333333333333333333333333333 +2362 3333333333333333333333333333333333333333333333333333333 +2363 3333333333333333333333333333333333333333333333333333333 +2364 3333333333333333333333333333333333333333333333333333333 +2365 3333333333333333333333333333333333333333333333333333333 +2366 3333333333333333333333333333333333333333333333333333333 +2367 3333333333333333333333333333333333333333333333333333333 +2368 3333333333333333333333333333333333333333333333333333333 +2369 3333333333333333333333333333333333333333333333333333333 +2370 3333333333333333333333333333333333333333333333333333333 +2371 3333333333333333333333333333333333333333333333333333333 +2372 3333333333333333333333333333333333333333333333333333333 +2373 3333333333333333333333333333333333333333333333333333333 +2374 3333333333333333333333333333333333333333333333333333333 +2375 3333333333333333333333333333333333333333333333333333333 +2376 3333333333333333333333333333333333333333333333333333333 +2377 3333333333333333333333333333333333333333333333333333333 +2378 3333333333333333333333333333333333333333333333333333333 +2379 3333333333333333333333333333333333333333333333333333333 +2380 3333333333333333333333333333333333333333333333333333333 +2381 3333333333333333333333333333333333333333333333333333333 +2382 3333333333333333333333333333333333333333333333333333333 +2383 3333333333333333333333333333333333333333333333333333333 +2384 3333333333333333333333333333333333333333333333333333333 +2385 3333333333333333333333333333333333333333333333333333333 +2386 3333333333333333333333333333333333333333333333333333333 +2387 3333333333333333333333333333333333333333333333333333333 +2388 3333333333333333333333333333333333333333333333333333333 +2389 3333333333333333333333333333333333333333333333333333333 +2390 3333333333333333333333333333333333333333333333333333333 +2391 3333333333333333333333333333333333333333333333333333333 +2392 3333333333333333333333333333333333333333333333333333333 +2393 3333333333333333333333333333333333333333333333333333333 +2394 3333333333333333333333333333333333333333333333333333333 +2395 3333333333333333333333333333333333333333333333333333333 +2396 3333333333333333333333333333333333333333333333333333333 +2397 3333333333333333333333333333333333333333333333333333333 +2398 3333333333333333333333333333333333333333333333333333333 +2399 3333333333333333333333333333333333333333333333333333333 +2400 3333333333333333333333333333333333333333333333333333333 +2401 3333333333333333333333333333333333333333333333333333333 +2402 3333333333333333333333333333333333333333333333333333333 +2403 3333333333333333333333333333333333333333333333333333333 +2404 3333333333333333333333333333333333333333333333333333333 +2405 3333333333333333333333333333333333333333333333333333333 +2406 3333333333333333333333333333333333333333333333333333333 +2407 3333333333333333333333333333333333333333333333333333333 +2408 3333333333333333333333333333333333333333333333333333333 +2409 3333333333333333333333333333333333333333333333333333333 +2410 3333333333333333333333333333333333333333333333333333333 +2411 3333333333333333333333333333333333333333333333333333333 +2412 3333333333333333333333333333333333333333333333333333333 +2413 3333333333333333333333333333333333333333333333333333333 +2414 3333333333333333333333333333333333333333333333333333333 +2415 3333333333333333333333333333333333333333333333333333333 +2416 3333333333333333333333333333333333333333333333333333333 +2417 3333333333333333333333333333333333333333333333333333333 +2418 3333333333333333333333333333333333333333333333333333333 +2419 3333333333333333333333333333333333333333333333333333333 +2420 3333333333333333333333333333333333333333333333333333333 +2421 3333333333333333333333333333333333333333333333333333333 +2422 3333333333333333333333333333333333333333333333333333333 +2423 3333333333333333333333333333333333333333333333333333333 +2424 3333333333333333333333333333333333333333333333333333333 +2425 3333333333333333333333333333333333333333333333333333333 +2426 3333333333333333333333333333333333333333333333333333333 +2427 3333333333333333333333333333333333333333333333333333333 +2428 3333333333333333333333333333333333333333333333333333333 +2429 3333333333333333333333333333333333333333333333333333333 +2430 3333333333333333333333333333333333333333333333333333333 +2431 3333333333333333333333333333333333333333333333333333333 +2432 3333333333333333333333333333333333333333333333333333333 +2433 3333333333333333333333333333333333333333333333333333333 +2434 3333333333333333333333333333333333333333333333333333333 +2435 3333333333333333333333333333333333333333333333333333333 +2436 3333333333333333333333333333333333333333333333333333333 +2437 3333333333333333333333333333333333333333333333333333333 +2438 3333333333333333333333333333333333333333333333333333333 +2439 3333333333333333333333333333333333333333333333333333333 +2440 3333333333333333333333333333333333333333333333333333333 +2441 3333333333333333333333333333333333333333333333333333333 +2442 3333333333333333333333333333333333333333333333333333333 +2443 3333333333333333333333333333333333333333333333333333333 +2444 3333333333333333333333333333333333333333333333333333333 +2445 3333333333333333333333333333333333333333333333333333333 +2446 3333333333333333333333333333333333333333333333333333333 +2447 3333333333333333333333333333333333333333333333333333333 +2448 3333333333333333333333333333333333333333333333333333333 +2449 3333333333333333333333333333333333333333333333333333333 +2450 3333333333333333333333333333333333333333333333333333333 +2451 3333333333333333333333333333333333333333333333333333333 +2452 3333333333333333333333333333333333333333333333333333333 +2453 3333333333333333333333333333333333333333333333333333333 +2454 3333333333333333333333333333333333333333333333333333333 +2455 3333333333333333333333333333333333333333333333333333333 +2456 3333333333333333333333333333333333333333333333333333333 +2457 3333333333333333333333333333333333333333333333333333333 +2458 3333333333333333333333333333333333333333333333333333333 +2459 3333333333333333333333333333333333333333333333333333333 +2460 3333333333333333333333333333333333333333333333333333333 +2461 3333333333333333333333333333333333333333333333333333333 +2462 3333333333333333333333333333333333333333333333333333333 +2463 3333333333333333333333333333333333333333333333333333333 +2464 3333333333333333333333333333333333333333333333333333333 +2465 3333333333333333333333333333333333333333333333333333333 +2466 3333333333333333333333333333333333333333333333333333333 +2467 3333333333333333333333333333333333333333333333333333333 +2468 3333333333333333333333333333333333333333333333333333333 +2469 3333333333333333333333333333333333333333333333333333333 +2470 3333333333333333333333333333333333333333333333333333333 +2471 3333333333333333333333333333333333333333333333333333333 +2472 3333333333333333333333333333333333333333333333333333333 +2473 3333333333333333333333333333333333333333333333333333333 +2474 3333333333333333333333333333333333333333333333333333333 +2475 3333333333333333333333333333333333333333333333333333333 +2476 3333333333333333333333333333333333333333333333333333333 +2477 3333333333333333333333333333333333333333333333333333333 +2478 3333333333333333333333333333333333333333333333333333333 +2479 3333333333333333333333333333333333333333333333333333333 +2480 3333333333333333333333333333333333333333333333333333333 +2481 3333333333333333333333333333333333333333333333333333333 +2482 3333333333333333333333333333333333333333333333333333333 +2483 3333333333333333333333333333333333333333333333333333333 +2484 3333333333333333333333333333333333333333333333333333333 +2485 3333333333333333333333333333333333333333333333333333333 +2486 3333333333333333333333333333333333333333333333333333333 +2487 3333333333333333333333333333333333333333333333333333333 +2488 3333333333333333333333333333333333333333333333333333333 +2489 3333333333333333333333333333333333333333333333333333333 +2490 3333333333333333333333333333333333333333333333333333333 +2491 3333333333333333333333333333333333333333333333333333333 +2492 3333333333333333333333333333333333333333333333333333333 +2493 3333333333333333333333333333333333333333333333333333333 +2494 3333333333333333333333333333333333333333333333333333333 +2495 3333333333333333333333333333333333333333333333333333333 +2496 3333333333333333333333333333333333333333333333333333333 +2497 3333333333333333333333333333333333333333333333333333333 +2498 3333333333333333333333333333333333333333333333333333333 +2499 3333333333333333333333333333333333333333333333333333333 +2500 3333333333333333333333333333333333333333333333333333333 +2501 3333333333333333333333333333333333333333333333333333333 +2502 3333333333333333333333333333333333333333333333333333333 +2503 3333333333333333333333333333333333333333333333333333333 +2504 3333333333333333333333333333333333333333333333333333333 +2505 3333333333333333333333333333333333333333333333333333333 +2506 3333333333333333333333333333333333333333333333333333333 +2507 3333333333333333333333333333333333333333333333333333333 +2508 3333333333333333333333333333333333333333333333333333333 +2509 3333333333333333333333333333333333333333333333333333333 +2510 3333333333333333333333333333333333333333333333333333333 +2511 3333333333333333333333333333333333333333333333333333333 +2512 3333333333333333333333333333333333333333333333333333333 +2513 3333333333333333333333333333333333333333333333333333333 +2514 3333333333333333333333333333333333333333333333333333333 +2515 3333333333333333333333333333333333333333333333333333333 +2516 3333333333333333333333333333333333333333333333333333333 +2517 3333333333333333333333333333333333333333333333333333333 +2518 3333333333333333333333333333333333333333333333333333333 +2519 3333333333333333333333333333333333333333333333333333333 +2520 3333333333333333333333333333333333333333333333333333333 +2521 3333333333333333333333333333333333333333333333333333333 +2522 3333333333333333333333333333333333333333333333333333333 +2523 3333333333333333333333333333333333333333333333333333333 +2524 3333333333333333333333333333333333333333333333333333333 +2525 3333333333333333333333333333333333333333333333333333333 +2526 3333333333333333333333333333333333333333333333333333333 +2527 3333333333333333333333333333333333333333333333333333333 +2528 3333333333333333333333333333333333333333333333333333333 +2529 3333333333333333333333333333333333333333333333333333333 +2530 3333333333333333333333333333333333333333333333333333333 +2531 3333333333333333333333333333333333333333333333333333333 +2532 3333333333333333333333333333333333333333333333333333333 +2533 3333333333333333333333333333333333333333333333333333333 +2534 3333333333333333333333333333333333333333333333333333333 +2535 3333333333333333333333333333333333333333333333333333333 +2536 3333333333333333333333333333333333333333333333333333333 +2537 3333333333333333333333333333333333333333333333333333333 +2538 3333333333333333333333333333333333333333333333333333333 +2539 3333333333333333333333333333333333333333333333333333333 +2540 3333333333333333333333333333333333333333333333333333333 +2541 3333333333333333333333333333333333333333333333333333333 +2542 3333333333333333333333333333333333333333333333333333333 +2543 3333333333333333333333333333333333333333333333333333333 +2544 3333333333333333333333333333333333333333333333333333333 +2545 3333333333333333333333333333333333333333333333333333333 +2546 3333333333333333333333333333333333333333333333333333333 +2547 3333333333333333333333333333333333333333333333333333333 +2548 3333333333333333333333333333333333333333333333333333333 +2549 3333333333333333333333333333333333333333333333333333333 +2550 3333333333333333333333333333333333333333333333333333333 +2551 3333333333333333333333333333333333333333333333333333333 +2552 3333333333333333333333333333333333333333333333333333333 +2553 3333333333333333333333333333333333333333333333333333333 +2554 3333333333333333333333333333333333333333333333333333333 +2555 3333333333333333333333333333333333333333333333333333333 +2556 3333333333333333333333333333333333333333333333333333333 +2557 3333333333333333333333333333333333333333333333333333333 +2558 3333333333333333333333333333333333333333333333333333333 +2559 3333333333333333333333333333333333333333333333333333333 +2560 3333333333333333333333333333333333333333333333333333333 +2561 3333333333333333333333333333333333333333333333333333333 +2562 3333333333333333333333333333333333333333333333333333333 +2563 3333333333333333333333333333333333333333333333333333333 +2564 3333333333333333333333333333333333333333333333333333333 +2565 3333333333333333333333333333333333333333333333333333333 +2566 3333333333333333333333333333333333333333333333333333333 +2567 3333333333333333333333333333333333333333333333333333333 +2568 3333333333333333333333333333333333333333333333333333333 +2569 3333333333333333333333333333333333333333333333333333333 +2570 3333333333333333333333333333333333333333333333333333333 +2571 3333333333333333333333333333333333333333333333333333333 +2572 3333333333333333333333333333333333333333333333333333333 +2573 3333333333333333333333333333333333333333333333333333333 +2574 3333333333333333333333333333333333333333333333333333333 +2575 3333333333333333333333333333333333333333333333333333333 +2576 3333333333333333333333333333333333333333333333333333333 +2577 3333333333333333333333333333333333333333333333333333333 +2578 3333333333333333333333333333333333333333333333333333333 +2579 3333333333333333333333333333333333333333333333333333333 +2580 3333333333333333333333333333333333333333333333333333333 +2581 3333333333333333333333333333333333333333333333333333333 +2582 3333333333333333333333333333333333333333333333333333333 +2583 3333333333333333333333333333333333333333333333333333333 +2584 3333333333333333333333333333333333333333333333333333333 +2585 3333333333333333333333333333333333333333333333333333333 +2586 3333333333333333333333333333333333333333333333333333333 +2587 3333333333333333333333333333333333333333333333333333333 +2588 3333333333333333333333333333333333333333333333333333333 +2589 3333333333333333333333333333333333333333333333333333333 +2590 3333333333333333333333333333333333333333333333333333333 +2591 3333333333333333333333333333333333333333333333333333333 +2592 3333333333333333333333333333333333333333333333333333333 +2593 3333333333333333333333333333333333333333333333333333333 +2594 3333333333333333333333333333333333333333333333333333333 +2595 3333333333333333333333333333333333333333333333333333333 +2596 3333333333333333333333333333333333333333333333333333333 +2597 3333333333333333333333333333333333333333333333333333333 +2598 3333333333333333333333333333333333333333333333333333333 +2599 3333333333333333333333333333333333333333333333333333333 +2600 3333333333333333333333333333333333333333333333333333333 +2601 3333333333333333333333333333333333333333333333333333333 +2602 3333333333333333333333333333333333333333333333333333333 +2603 3333333333333333333333333333333333333333333333333333333 +2604 3333333333333333333333333333333333333333333333333333333 +2605 3333333333333333333333333333333333333333333333333333333 +2606 3333333333333333333333333333333333333333333333333333333 +2607 3333333333333333333333333333333333333333333333333333333 +2608 3333333333333333333333333333333333333333333333333333333 +2609 3333333333333333333333333333333333333333333333333333333 +2610 3333333333333333333333333333333333333333333333333333333 +2611 3333333333333333333333333333333333333333333333333333333 +2612 3333333333333333333333333333333333333333333333333333333 +2613 3333333333333333333333333333333333333333333333333333333 +2614 3333333333333333333333333333333333333333333333333333333 +2615 3333333333333333333333333333333333333333333333333333333 +2616 3333333333333333333333333333333333333333333333333333333 +2617 3333333333333333333333333333333333333333333333333333333 +2618 3333333333333333333333333333333333333333333333333333333 +2619 3333333333333333333333333333333333333333333333333333333 +2620 3333333333333333333333333333333333333333333333333333333 +2621 3333333333333333333333333333333333333333333333333333333 +2622 3333333333333333333333333333333333333333333333333333333 +2623 3333333333333333333333333333333333333333333333333333333 +2624 3333333333333333333333333333333333333333333333333333333 +2625 3333333333333333333333333333333333333333333333333333333 +2626 3333333333333333333333333333333333333333333333333333333 +2627 3333333333333333333333333333333333333333333333333333333 +2628 3333333333333333333333333333333333333333333333333333333 +2629 3333333333333333333333333333333333333333333333333333333 +2630 3333333333333333333333333333333333333333333333333333333 +2631 3333333333333333333333333333333333333333333333333333333 +2632 3333333333333333333333333333333333333333333333333333333 +2633 3333333333333333333333333333333333333333333333333333333 +2634 3333333333333333333333333333333333333333333333333333333 +2635 3333333333333333333333333333333333333333333333333333333 +2636 3333333333333333333333333333333333333333333333333333333 +2637 3333333333333333333333333333333333333333333333333333333 +2638 3333333333333333333333333333333333333333333333333333333 +2639 3333333333333333333333333333333333333333333333333333333 +2640 3333333333333333333333333333333333333333333333333333333 +2641 3333333333333333333333333333333333333333333333333333333 +2642 3333333333333333333333333333333333333333333333333333333 +2643 3333333333333333333333333333333333333333333333333333333 +2644 3333333333333333333333333333333333333333333333333333333 +2645 3333333333333333333333333333333333333333333333333333333 +2646 3333333333333333333333333333333333333333333333333333333 +2647 3333333333333333333333333333333333333333333333333333333 +2648 3333333333333333333333333333333333333333333333333333333 +2649 3333333333333333333333333333333333333333333333333333333 +2650 3333333333333333333333333333333333333333333333333333333 +2651 3333333333333333333333333333333333333333333333333333333 +2652 3333333333333333333333333333333333333333333333333333333 +2653 3333333333333333333333333333333333333333333333333333333 +2654 3333333333333333333333333333333333333333333333333333333 +2655 3333333333333333333333333333333333333333333333333333333 +2656 3333333333333333333333333333333333333333333333333333333 +2657 3333333333333333333333333333333333333333333333333333333 +2658 3333333333333333333333333333333333333333333333333333333 +2659 3333333333333333333333333333333333333333333333333333333 +2660 3333333333333333333333333333333333333333333333333333333 +2661 3333333333333333333333333333333333333333333333333333333 +2662 3333333333333333333333333333333333333333333333333333333 +2663 3333333333333333333333333333333333333333333333333333333 +2664 3333333333333333333333333333333333333333333333333333333 +2665 3333333333333333333333333333333333333333333333333333333 +2666 3333333333333333333333333333333333333333333333333333333 +2667 3333333333333333333333333333333333333333333333333333333 +2668 3333333333333333333333333333333333333333333333333333333 +2669 3333333333333333333333333333333333333333333333333333333 +2670 3333333333333333333333333333333333333333333333333333333 +2671 3333333333333333333333333333333333333333333333333333333 +2672 3333333333333333333333333333333333333333333333333333333 +2673 3333333333333333333333333333333333333333333333333333333 +2674 3333333333333333333333333333333333333333333333333333333 +2675 3333333333333333333333333333333333333333333333333333333 +2676 3333333333333333333333333333333333333333333333333333333 +2677 3333333333333333333333333333333333333333333333333333333 +2678 3333333333333333333333333333333333333333333333333333333 +2679 3333333333333333333333333333333333333333333333333333333 +2680 3333333333333333333333333333333333333333333333333333333 +2681 3333333333333333333333333333333333333333333333333333333 +2682 3333333333333333333333333333333333333333333333333333333 +2683 3333333333333333333333333333333333333333333333333333333 +2684 3333333333333333333333333333333333333333333333333333333 +2685 3333333333333333333333333333333333333333333333333333333 +2686 3333333333333333333333333333333333333333333333333333333 +2687 3333333333333333333333333333333333333333333333333333333 +2688 3333333333333333333333333333333333333333333333333333333 +2689 3333333333333333333333333333333333333333333333333333333 +2690 3333333333333333333333333333333333333333333333333333333 +2691 3333333333333333333333333333333333333333333333333333333 +2692 3333333333333333333333333333333333333333333333333333333 +2693 3333333333333333333333333333333333333333333333333333333 +2694 3333333333333333333333333333333333333333333333333333333 +2695 3333333333333333333333333333333333333333333333333333333 +2696 3333333333333333333333333333333333333333333333333333333 +2697 3333333333333333333333333333333333333333333333333333333 +2698 3333333333333333333333333333333333333333333333333333333 +2699 3333333333333333333333333333333333333333333333333333333 +2700 3333333333333333333333333333333333333333333333333333333 +2701 3333333333333333333333333333333333333333333333333333333 +2702 3333333333333333333333333333333333333333333333333333333 +2703 3333333333333333333333333333333333333333333333333333333 +2704 3333333333333333333333333333333333333333333333333333333 +2705 3333333333333333333333333333333333333333333333333333333 +2706 3333333333333333333333333333333333333333333333333333333 +2707 3333333333333333333333333333333333333333333333333333333 +2708 3333333333333333333333333333333333333333333333333333333 +2709 3333333333333333333333333333333333333333333333333333333 +2710 3333333333333333333333333333333333333333333333333333333 +2711 3333333333333333333333333333333333333333333333333333333 +2712 3333333333333333333333333333333333333333333333333333333 +2713 3333333333333333333333333333333333333333333333333333333 +2714 3333333333333333333333333333333333333333333333333333333 +2715 3333333333333333333333333333333333333333333333333333333 +2716 3333333333333333333333333333333333333333333333333333333 +2717 3333333333333333333333333333333333333333333333333333333 +2718 3333333333333333333333333333333333333333333333333333333 +2719 3333333333333333333333333333333333333333333333333333333 +2720 3333333333333333333333333333333333333333333333333333333 +2721 3333333333333333333333333333333333333333333333333333333 +2722 3333333333333333333333333333333333333333333333333333333 +2723 3333333333333333333333333333333333333333333333333333333 +2724 3333333333333333333333333333333333333333333333333333333 +2725 3333333333333333333333333333333333333333333333333333333 +2726 3333333333333333333333333333333333333333333333333333333 +2727 3333333333333333333333333333333333333333333333333333333 +2728 3333333333333333333333333333333333333333333333333333333 +2729 3333333333333333333333333333333333333333333333333333333 +2730 3333333333333333333333333333333333333333333333333333333 +2731 3333333333333333333333333333333333333333333333333333333 +2732 3333333333333333333333333333333333333333333333333333333 +2733 3333333333333333333333333333333333333333333333333333333 +2734 3333333333333333333333333333333333333333333333333333333 +2735 3333333333333333333333333333333333333333333333333333333 +2736 3333333333333333333333333333333333333333333333333333333 +2737 3333333333333333333333333333333333333333333333333333333 +2738 3333333333333333333333333333333333333333333333333333333 +2739 3333333333333333333333333333333333333333333333333333333 +2740 3333333333333333333333333333333333333333333333333333333 +2741 3333333333333333333333333333333333333333333333333333333 +2742 3333333333333333333333333333333333333333333333333333333 +2743 3333333333333333333333333333333333333333333333333333333 +2744 3333333333333333333333333333333333333333333333333333333 +2745 3333333333333333333333333333333333333333333333333333333 +2746 3333333333333333333333333333333333333333333333333333333 +2747 3333333333333333333333333333333333333333333333333333333 +2748 3333333333333333333333333333333333333333333333333333333 +2749 3333333333333333333333333333333333333333333333333333333 +2750 3333333333333333333333333333333333333333333333333333333 +2751 3333333333333333333333333333333333333333333333333333333 +2752 3333333333333333333333333333333333333333333333333333333 +2753 3333333333333333333333333333333333333333333333333333333 +2754 3333333333333333333333333333333333333333333333333333333 +2755 3333333333333333333333333333333333333333333333333333333 +2756 3333333333333333333333333333333333333333333333333333333 +2757 3333333333333333333333333333333333333333333333333333333 +2758 3333333333333333333333333333333333333333333333333333333 +2759 3333333333333333333333333333333333333333333333333333333 +2760 3333333333333333333333333333333333333333333333333333333 +2761 3333333333333333333333333333333333333333333333333333333 +2762 3333333333333333333333333333333333333333333333333333333 +2763 3333333333333333333333333333333333333333333333333333333 +2764 3333333333333333333333333333333333333333333333333333333 +2765 3333333333333333333333333333333333333333333333333333333 +2766 3333333333333333333333333333333333333333333333333333333 +2767 3333333333333333333333333333333333333333333333333333333 +2768 3333333333333333333333333333333333333333333333333333333 +2769 3333333333333333333333333333333333333333333333333333333 +2770 3333333333333333333333333333333333333333333333333333333 +2771 3333333333333333333333333333333333333333333333333333333 +2772 3333333333333333333333333333333333333333333333333333333 +2773 3333333333333333333333333333333333333333333333333333333 +2774 3333333333333333333333333333333333333333333333333333333 +2775 3333333333333333333333333333333333333333333333333333333 +2776 3333333333333333333333333333333333333333333333333333333 +2777 3333333333333333333333333333333333333333333333333333333 +2778 3333333333333333333333333333333333333333333333333333333 +2779 3333333333333333333333333333333333333333333333333333333 +2780 3333333333333333333333333333333333333333333333333333333 +2781 3333333333333333333333333333333333333333333333333333333 +2782 3333333333333333333333333333333333333333333333333333333 +2783 3333333333333333333333333333333333333333333333333333333 +2784 3333333333333333333333333333333333333333333333333333333 +2785 3333333333333333333333333333333333333333333333333333333 +2786 3333333333333333333333333333333333333333333333333333333 +2787 3333333333333333333333333333333333333333333333333333333 +2788 3333333333333333333333333333333333333333333333333333333 +2789 3333333333333333333333333333333333333333333333333333333 +2790 3333333333333333333333333333333333333333333333333333333 +2791 3333333333333333333333333333333333333333333333333333333 +2792 3333333333333333333333333333333333333333333333333333333 +2793 3333333333333333333333333333333333333333333333333333333 +2794 3333333333333333333333333333333333333333333333333333333 +2795 3333333333333333333333333333333333333333333333333333333 +2796 3333333333333333333333333333333333333333333333333333333 +2797 3333333333333333333333333333333333333333333333333333333 +2798 3333333333333333333333333333333333333333333333333333333 +2799 3333333333333333333333333333333333333333333333333333333 +2800 3333333333333333333333333333333333333333333333333333333 +2801 3333333333333333333333333333333333333333333333333333333 +2802 3333333333333333333333333333333333333333333333333333333 +2803 3333333333333333333333333333333333333333333333333333333 +2804 3333333333333333333333333333333333333333333333333333333 +2805 3333333333333333333333333333333333333333333333333333333 +2806 3333333333333333333333333333333333333333333333333333333 +2807 3333333333333333333333333333333333333333333333333333333 +2808 3333333333333333333333333333333333333333333333333333333 +2809 3333333333333333333333333333333333333333333333333333333 +2810 3333333333333333333333333333333333333333333333333333333 +2811 3333333333333333333333333333333333333333333333333333333 +2812 3333333333333333333333333333333333333333333333333333333 +2813 3333333333333333333333333333333333333333333333333333333 +2814 3333333333333333333333333333333333333333333333333333333 +2815 3333333333333333333333333333333333333333333333333333333 +2816 3333333333333333333333333333333333333333333333333333333 +2817 3333333333333333333333333333333333333333333333333333333 +2818 3333333333333333333333333333333333333333333333333333333 +2819 3333333333333333333333333333333333333333333333333333333 +2820 3333333333333333333333333333333333333333333333333333333 +2821 3333333333333333333333333333333333333333333333333333333 +2822 3333333333333333333333333333333333333333333333333333333 +2823 3333333333333333333333333333333333333333333333333333333 +2824 3333333333333333333333333333333333333333333333333333333 +2825 3333333333333333333333333333333333333333333333333333333 +2826 3333333333333333333333333333333333333333333333333333333 +2827 3333333333333333333333333333333333333333333333333333333 +2828 3333333333333333333333333333333333333333333333333333333 +2829 3333333333333333333333333333333333333333333333333333333 +2830 3333333333333333333333333333333333333333333333333333333 +2831 3333333333333333333333333333333333333333333333333333333 +2832 3333333333333333333333333333333333333333333333333333333 +2833 3333333333333333333333333333333333333333333333333333333 +2834 3333333333333333333333333333333333333333333333333333333 +2835 3333333333333333333333333333333333333333333333333333333 +2836 3333333333333333333333333333333333333333333333333333333 +2837 3333333333333333333333333333333333333333333333333333333 +2838 3333333333333333333333333333333333333333333333333333333 +2839 3333333333333333333333333333333333333333333333333333333 +2840 3333333333333333333333333333333333333333333333333333333 +2841 3333333333333333333333333333333333333333333333333333333 +2842 3333333333333333333333333333333333333333333333333333333 +2843 3333333333333333333333333333333333333333333333333333333 +2844 3333333333333333333333333333333333333333333333333333333 +2845 3333333333333333333333333333333333333333333333333333333 +2846 3333333333333333333333333333333333333333333333333333333 +2847 3333333333333333333333333333333333333333333333333333333 +2848 3333333333333333333333333333333333333333333333333333333 +2849 3333333333333333333333333333333333333333333333333333333 +2850 3333333333333333333333333333333333333333333333333333333 +2851 3333333333333333333333333333333333333333333333333333333 +2852 3333333333333333333333333333333333333333333333333333333 +2853 3333333333333333333333333333333333333333333333333333333 +2854 3333333333333333333333333333333333333333333333333333333 +2855 3333333333333333333333333333333333333333333333333333333 +2856 3333333333333333333333333333333333333333333333333333333 +2857 3333333333333333333333333333333333333333333333333333333 +2858 3333333333333333333333333333333333333333333333333333333 +2859 3333333333333333333333333333333333333333333333333333333 +2860 3333333333333333333333333333333333333333333333333333333 +2861 3333333333333333333333333333333333333333333333333333333 +2862 3333333333333333333333333333333333333333333333333333333 +2863 3333333333333333333333333333333333333333333333333333333 +2864 3333333333333333333333333333333333333333333333333333333 +2865 3333333333333333333333333333333333333333333333333333333 +2866 3333333333333333333333333333333333333333333333333333333 +2867 3333333333333333333333333333333333333333333333333333333 +2868 3333333333333333333333333333333333333333333333333333333 +2869 3333333333333333333333333333333333333333333333333333333 +2870 3333333333333333333333333333333333333333333333333333333 +2871 3333333333333333333333333333333333333333333333333333333 +2872 3333333333333333333333333333333333333333333333333333333 +2873 3333333333333333333333333333333333333333333333333333333 +2874 3333333333333333333333333333333333333333333333333333333 +2875 3333333333333333333333333333333333333333333333333333333 +2876 3333333333333333333333333333333333333333333333333333333 +2877 3333333333333333333333333333333333333333333333333333333 +2878 3333333333333333333333333333333333333333333333333333333 +2879 3333333333333333333333333333333333333333333333333333333 +2880 3333333333333333333333333333333333333333333333333333333 +2881 3333333333333333333333333333333333333333333333333333333 +2882 3333333333333333333333333333333333333333333333333333333 +2883 3333333333333333333333333333333333333333333333333333333 +2884 3333333333333333333333333333333333333333333333333333333 +2885 3333333333333333333333333333333333333333333333333333333 +2886 3333333333333333333333333333333333333333333333333333333 +2887 3333333333333333333333333333333333333333333333333333333 +2888 3333333333333333333333333333333333333333333333333333333 +2889 3333333333333333333333333333333333333333333333333333333 +2890 3333333333333333333333333333333333333333333333333333333 +2891 3333333333333333333333333333333333333333333333333333333 +2892 3333333333333333333333333333333333333333333333333333333 +2893 3333333333333333333333333333333333333333333333333333333 +2894 3333333333333333333333333333333333333333333333333333333 +2895 3333333333333333333333333333333333333333333333333333333 +2896 3333333333333333333333333333333333333333333333333333333 +2897 3333333333333333333333333333333333333333333333333333333 +2898 3333333333333333333333333333333333333333333333333333333 +2899 3333333333333333333333333333333333333333333333333333333 +2900 3333333333333333333333333333333333333333333333333333333 +2901 3333333333333333333333333333333333333333333333333333333 +2902 3333333333333333333333333333333333333333333333333333333 +2903 3333333333333333333333333333333333333333333333333333333 +2904 3333333333333333333333333333333333333333333333333333333 +2905 3333333333333333333333333333333333333333333333333333333 +2906 3333333333333333333333333333333333333333333333333333333 +2907 3333333333333333333333333333333333333333333333333333333 +2908 3333333333333333333333333333333333333333333333333333333 +2909 3333333333333333333333333333333333333333333333333333333 +2910 3333333333333333333333333333333333333333333333333333333 +2911 3333333333333333333333333333333333333333333333333333333 +2912 3333333333333333333333333333333333333333333333333333333 +2913 3333333333333333333333333333333333333333333333333333333 +2914 3333333333333333333333333333333333333333333333333333333 +2915 3333333333333333333333333333333333333333333333333333333 +2916 3333333333333333333333333333333333333333333333333333333 +2917 3333333333333333333333333333333333333333333333333333333 +2918 3333333333333333333333333333333333333333333333333333333 +2919 3333333333333333333333333333333333333333333333333333333 +2920 3333333333333333333333333333333333333333333333333333333 +2921 3333333333333333333333333333333333333333333333333333333 +2922 3333333333333333333333333333333333333333333333333333333 +2923 3333333333333333333333333333333333333333333333333333333 +2924 3333333333333333333333333333333333333333333333333333333 +2925 3333333333333333333333333333333333333333333333333333333 +2926 3333333333333333333333333333333333333333333333333333333 +2927 3333333333333333333333333333333333333333333333333333333 +2928 3333333333333333333333333333333333333333333333333333333 +2929 3333333333333333333333333333333333333333333333333333333 +2930 3333333333333333333333333333333333333333333333333333333 +2931 3333333333333333333333333333333333333333333333333333333 +2932 3333333333333333333333333333333333333333333333333333333 +2933 3333333333333333333333333333333333333333333333333333333 +2934 3333333333333333333333333333333333333333333333333333333 +2935 3333333333333333333333333333333333333333333333333333333 +2936 3333333333333333333333333333333333333333333333333333333 +2937 3333333333333333333333333333333333333333333333333333333 +2938 3333333333333333333333333333333333333333333333333333333 +2939 3333333333333333333333333333333333333333333333333333333 +2940 3333333333333333333333333333333333333333333333333333333 +2941 3333333333333333333333333333333333333333333333333333333 +2942 3333333333333333333333333333333333333333333333333333333 +2943 3333333333333333333333333333333333333333333333333333333 +2944 3333333333333333333333333333333333333333333333333333333 +2945 3333333333333333333333333333333333333333333333333333333 +2946 3333333333333333333333333333333333333333333333333333333 +2947 3333333333333333333333333333333333333333333333333333333 +2948 3333333333333333333333333333333333333333333333333333333 +2949 3333333333333333333333333333333333333333333333333333333 +2950 3333333333333333333333333333333333333333333333333333333 +2951 3333333333333333333333333333333333333333333333333333333 +2952 3333333333333333333333333333333333333333333333333333333 +2953 3333333333333333333333333333333333333333333333333333333 +2954 3333333333333333333333333333333333333333333333333333333 +2955 3333333333333333333333333333333333333333333333333333333 +2956 3333333333333333333333333333333333333333333333333333333 +2957 3333333333333333333333333333333333333333333333333333333 +2958 3333333333333333333333333333333333333333333333333333333 +2959 3333333333333333333333333333333333333333333333333333333 +2960 3333333333333333333333333333333333333333333333333333333 +2961 3333333333333333333333333333333333333333333333333333333 +2962 3333333333333333333333333333333333333333333333333333333 +2963 3333333333333333333333333333333333333333333333333333333 +2964 3333333333333333333333333333333333333333333333333333333 +2965 3333333333333333333333333333333333333333333333333333333 +2966 3333333333333333333333333333333333333333333333333333333 +2967 3333333333333333333333333333333333333333333333333333333 +2968 3333333333333333333333333333333333333333333333333333333 +2969 3333333333333333333333333333333333333333333333333333333 +2970 3333333333333333333333333333333333333333333333333333333 +2971 3333333333333333333333333333333333333333333333333333333 +2972 3333333333333333333333333333333333333333333333333333333 +2973 3333333333333333333333333333333333333333333333333333333 +2974 3333333333333333333333333333333333333333333333333333333 +2975 3333333333333333333333333333333333333333333333333333333 +2976 3333333333333333333333333333333333333333333333333333333 +2977 3333333333333333333333333333333333333333333333333333333 +2978 3333333333333333333333333333333333333333333333333333333 +2979 3333333333333333333333333333333333333333333333333333333 +2980 3333333333333333333333333333333333333333333333333333333 +2981 3333333333333333333333333333333333333333333333333333333 +2982 3333333333333333333333333333333333333333333333333333333 +2983 3333333333333333333333333333333333333333333333333333333 +2984 3333333333333333333333333333333333333333333333333333333 +2985 3333333333333333333333333333333333333333333333333333333 +2986 3333333333333333333333333333333333333333333333333333333 +2987 3333333333333333333333333333333333333333333333333333333 +2988 3333333333333333333333333333333333333333333333333333333 +2989 3333333333333333333333333333333333333333333333333333333 +2990 3333333333333333333333333333333333333333333333333333333 +2991 3333333333333333333333333333333333333333333333333333333 +2992 3333333333333333333333333333333333333333333333333333333 +2993 3333333333333333333333333333333333333333333333333333333 +2994 3333333333333333333333333333333333333333333333333333333 +2995 3333333333333333333333333333333333333333333333333333333 +2996 3333333333333333333333333333333333333333333333333333333 +2997 3333333333333333333333333333333333333333333333333333333 +2998 3333333333333333333333333333333333333333333333333333333 +2999 3333333333333333333333333333333333333333333333333333333 +3000 3333333333333333333333333333333333333333333333333333333 +3001 3333333333333333333333333333333333333333333333333333333 +3002 3333333333333333333333333333333333333333333333333333333 +3003 3333333333333333333333333333333333333333333333333333333 +3004 3333333333333333333333333333333333333333333333333333333 +3005 3333333333333333333333333333333333333333333333333333333 +3006 3333333333333333333333333333333333333333333333333333333 +3007 3333333333333333333333333333333333333333333333333333333 +3008 3333333333333333333333333333333333333333333333333333333 +3009 3333333333333333333333333333333333333333333333333333333 +3010 3333333333333333333333333333333333333333333333333333333 +3011 3333333333333333333333333333333333333333333333333333333 +3012 3333333333333333333333333333333333333333333333333333333 +3013 3333333333333333333333333333333333333333333333333333333 +3014 3333333333333333333333333333333333333333333333333333333 +3015 3333333333333333333333333333333333333333333333333333333 +3016 3333333333333333333333333333333333333333333333333333333 +3017 3333333333333333333333333333333333333333333333333333333 +3018 3333333333333333333333333333333333333333333333333333333 +3019 3333333333333333333333333333333333333333333333333333333 +3020 3333333333333333333333333333333333333333333333333333333 +3021 3333333333333333333333333333333333333333333333333333333 +3022 3333333333333333333333333333333333333333333333333333333 +3023 3333333333333333333333333333333333333333333333333333333 +3024 3333333333333333333333333333333333333333333333333333333 +3025 3333333333333333333333333333333333333333333333333333333 +3026 3333333333333333333333333333333333333333333333333333333 +3027 3333333333333333333333333333333333333333333333333333333 +3028 3333333333333333333333333333333333333333333333333333333 +3029 3333333333333333333333333333333333333333333333333333333 +3030 3333333333333333333333333333333333333333333333333333333 +3031 3333333333333333333333333333333333333333333333333333333 +3032 3333333333333333333333333333333333333333333333333333333 +3033 3333333333333333333333333333333333333333333333333333333 +3034 3333333333333333333333333333333333333333333333333333333 +3035 3333333333333333333333333333333333333333333333333333333 +3036 3333333333333333333333333333333333333333333333333333333 +3037 3333333333333333333333333333333333333333333333333333333 +3038 3333333333333333333333333333333333333333333333333333333 +3039 3333333333333333333333333333333333333333333333333333333 +3040 3333333333333333333333333333333333333333333333333333333 +3041 3333333333333333333333333333333333333333333333333333333 +3042 3333333333333333333333333333333333333333333333333333333 +3043 3333333333333333333333333333333333333333333333333333333 +3044 3333333333333333333333333333333333333333333333333333333 +3045 3333333333333333333333333333333333333333333333333333333 +3046 3333333333333333333333333333333333333333333333333333333 +3047 3333333333333333333333333333333333333333333333333333333 +3048 3333333333333333333333333333333333333333333333333333333 +3049 3333333333333333333333333333333333333333333333333333333 +3050 3333333333333333333333333333333333333333333333333333333 +3051 3333333333333333333333333333333333333333333333333333333 +3052 3333333333333333333333333333333333333333333333333333333 +3053 3333333333333333333333333333333333333333333333333333333 +3054 3333333333333333333333333333333333333333333333333333333 +3055 3333333333333333333333333333333333333333333333333333333 +3056 3333333333333333333333333333333333333333333333333333333 +3057 3333333333333333333333333333333333333333333333333333333 +3058 3333333333333333333333333333333333333333333333333333333 +3059 3333333333333333333333333333333333333333333333333333333 +3060 3333333333333333333333333333333333333333333333333333333 +3061 3333333333333333333333333333333333333333333333333333333 +3062 3333333333333333333333333333333333333333333333333333333 +3063 3333333333333333333333333333333333333333333333333333333 +3064 3333333333333333333333333333333333333333333333333333333 +3065 3333333333333333333333333333333333333333333333333333333 +3066 3333333333333333333333333333333333333333333333333333333 +3067 3333333333333333333333333333333333333333333333333333333 +3068 3333333333333333333333333333333333333333333333333333333 +3069 3333333333333333333333333333333333333333333333333333333 +3070 3333333333333333333333333333333333333333333333333333333 +3071 3333333333333333333333333333333333333333333333333333333 +3072 3333333333333333333333333333333333333333333333333333333 +3073 3333333333333333333333333333333333333333333333333333333 +3074 3333333333333333333333333333333333333333333333333333333 +3075 3333333333333333333333333333333333333333333333333333333 +3076 3333333333333333333333333333333333333333333333333333333 +3077 3333333333333333333333333333333333333333333333333333333 +3078 3333333333333333333333333333333333333333333333333333333 +3079 3333333333333333333333333333333333333333333333333333333 +3080 3333333333333333333333333333333333333333333333333333333 +3081 3333333333333333333333333333333333333333333333333333333 +3082 3333333333333333333333333333333333333333333333333333333 +3083 3333333333333333333333333333333333333333333333333333333 +3084 3333333333333333333333333333333333333333333333333333333 +3085 3333333333333333333333333333333333333333333333333333333 +3086 3333333333333333333333333333333333333333333333333333333 +3087 3333333333333333333333333333333333333333333333333333333 +3088 3333333333333333333333333333333333333333333333333333333 +3089 3333333333333333333333333333333333333333333333333333333 +3090 3333333333333333333333333333333333333333333333333333333 +3091 3333333333333333333333333333333333333333333333333333333 +3092 3333333333333333333333333333333333333333333333333333333 +3093 3333333333333333333333333333333333333333333333333333333 +3094 3333333333333333333333333333333333333333333333333333333 +3095 3333333333333333333333333333333333333333333333333333333 +3096 3333333333333333333333333333333333333333333333333333333 +3097 3333333333333333333333333333333333333333333333333333333 +3098 3333333333333333333333333333333333333333333333333333333 +3099 3333333333333333333333333333333333333333333333333333333 +3100 3333333333333333333333333333333333333333333333333333333 +3101 3333333333333333333333333333333333333333333333333333333 +3102 3333333333333333333333333333333333333333333333333333333 +3103 3333333333333333333333333333333333333333333333333333333 +3104 3333333333333333333333333333333333333333333333333333333 +3105 3333333333333333333333333333333333333333333333333333333 +3106 3333333333333333333333333333333333333333333333333333333 +3107 3333333333333333333333333333333333333333333333333333333 +3108 3333333333333333333333333333333333333333333333333333333 +3109 3333333333333333333333333333333333333333333333333333333 +3110 3333333333333333333333333333333333333333333333333333333 +3111 3333333333333333333333333333333333333333333333333333333 +3112 3333333333333333333333333333333333333333333333333333333 +3113 3333333333333333333333333333333333333333333333333333333 +3114 3333333333333333333333333333333333333333333333333333333 +3115 3333333333333333333333333333333333333333333333333333333 +3116 3333333333333333333333333333333333333333333333333333333 +3117 3333333333333333333333333333333333333333333333333333333 +3118 3333333333333333333333333333333333333333333333333333333 +3119 3333333333333333333333333333333333333333333333333333333 +3120 3333333333333333333333333333333333333333333333333333333 +3121 3333333333333333333333333333333333333333333333333333333 +3122 3333333333333333333333333333333333333333333333333333333 +3123 3333333333333333333333333333333333333333333333333333333 +3124 3333333333333333333333333333333333333333333333333333333 +3125 3333333333333333333333333333333333333333333333333333333 +3126 3333333333333333333333333333333333333333333333333333333 +3127 3333333333333333333333333333333333333333333333333333333 +3128 3333333333333333333333333333333333333333333333333333333 +3129 3333333333333333333333333333333333333333333333333333333 +3130 3333333333333333333333333333333333333333333333333333333 +3131 3333333333333333333333333333333333333333333333333333333 +3132 3333333333333333333333333333333333333333333333333333333 +3133 3333333333333333333333333333333333333333333333333333333 +3134 3333333333333333333333333333333333333333333333333333333 +3135 3333333333333333333333333333333333333333333333333333333 +3136 3333333333333333333333333333333333333333333333333333333 +3137 3333333333333333333333333333333333333333333333333333333 +3138 3333333333333333333333333333333333333333333333333333333 +3139 3333333333333333333333333333333333333333333333333333333 +3140 3333333333333333333333333333333333333333333333333333333 +3141 3333333333333333333333333333333333333333333333333333333 +3142 3333333333333333333333333333333333333333333333333333333 +3143 3333333333333333333333333333333333333333333333333333333 +3144 3333333333333333333333333333333333333333333333333333333 +3145 3333333333333333333333333333333333333333333333333333333 +3146 3333333333333333333333333333333333333333333333333333333 +3147 3333333333333333333333333333333333333333333333333333333 +3148 3333333333333333333333333333333333333333333333333333333 +3149 3333333333333333333333333333333333333333333333333333333 +3150 3333333333333333333333333333333333333333333333333333333 +3151 3333333333333333333333333333333333333333333333333333333 +3152 3333333333333333333333333333333333333333333333333333333 +3153 3333333333333333333333333333333333333333333333333333333 +3154 3333333333333333333333333333333333333333333333333333333 +3155 3333333333333333333333333333333333333333333333333333333 +3156 3333333333333333333333333333333333333333333333333333333 +3157 3333333333333333333333333333333333333333333333333333333 +3158 3333333333333333333333333333333333333333333333333333333 +3159 3333333333333333333333333333333333333333333333333333333 +3160 3333333333333333333333333333333333333333333333333333333 +3161 3333333333333333333333333333333333333333333333333333333 +3162 3333333333333333333333333333333333333333333333333333333 +3163 3333333333333333333333333333333333333333333333333333333 +3164 3333333333333333333333333333333333333333333333333333333 +3165 3333333333333333333333333333333333333333333333333333333 +3166 3333333333333333333333333333333333333333333333333333333 +3167 3333333333333333333333333333333333333333333333333333333 +3168 3333333333333333333333333333333333333333333333333333333 +3169 3333333333333333333333333333333333333333333333333333333 +3170 3333333333333333333333333333333333333333333333333333333 +3171 3333333333333333333333333333333333333333333333333333333 +3172 3333333333333333333333333333333333333333333333333333333 +3173 3333333333333333333333333333333333333333333333333333333 +3174 3333333333333333333333333333333333333333333333333333333 +3175 3333333333333333333333333333333333333333333333333333333 +3176 3333333333333333333333333333333333333333333333333333333 +3177 3333333333333333333333333333333333333333333333333333333 +3178 3333333333333333333333333333333333333333333333333333333 +3179 3333333333333333333333333333333333333333333333333333333 +3180 3333333333333333333333333333333333333333333333333333333 +3181 3333333333333333333333333333333333333333333333333333333 +3182 3333333333333333333333333333333333333333333333333333333 +3183 3333333333333333333333333333333333333333333333333333333 +3184 3333333333333333333333333333333333333333333333333333333 +3185 3333333333333333333333333333333333333333333333333333333 +3186 3333333333333333333333333333333333333333333333333333333 +3187 3333333333333333333333333333333333333333333333333333333 +3188 3333333333333333333333333333333333333333333333333333333 +3189 3333333333333333333333333333333333333333333333333333333 +3190 3333333333333333333333333333333333333333333333333333333 +3191 3333333333333333333333333333333333333333333333333333333 +3192 3333333333333333333333333333333333333333333333333333333 +3193 3333333333333333333333333333333333333333333333333333333 +3194 3333333333333333333333333333333333333333333333333333333 +3195 3333333333333333333333333333333333333333333333333333333 +3196 3333333333333333333333333333333333333333333333333333333 +3197 3333333333333333333333333333333333333333333333333333333 +3198 3333333333333333333333333333333333333333333333333333333 +3199 3333333333333333333333333333333333333333333333333333333 +3200 3333333333333333333333333333333333333333333333333333333 +3201 3333333333333333333333333333333333333333333333333333333 +3202 3333333333333333333333333333333333333333333333333333333 +3203 3333333333333333333333333333333333333333333333333333333 +3204 3333333333333333333333333333333333333333333333333333333 +3205 3333333333333333333333333333333333333333333333333333333 +3206 3333333333333333333333333333333333333333333333333333333 +3207 3333333333333333333333333333333333333333333333333333333 +3208 3333333333333333333333333333333333333333333333333333333 +3209 3333333333333333333333333333333333333333333333333333333 +3210 3333333333333333333333333333333333333333333333333333333 +3211 3333333333333333333333333333333333333333333333333333333 +3212 3333333333333333333333333333333333333333333333333333333 +3213 3333333333333333333333333333333333333333333333333333333 +3214 3333333333333333333333333333333333333333333333333333333 +3215 3333333333333333333333333333333333333333333333333333333 +3216 3333333333333333333333333333333333333333333333333333333 +3217 3333333333333333333333333333333333333333333333333333333 +3218 3333333333333333333333333333333333333333333333333333333 +3219 3333333333333333333333333333333333333333333333333333333 +3220 3333333333333333333333333333333333333333333333333333333 +3221 3333333333333333333333333333333333333333333333333333333 +3222 3333333333333333333333333333333333333333333333333333333 +3223 3333333333333333333333333333333333333333333333333333333 +3224 3333333333333333333333333333333333333333333333333333333 +3225 3333333333333333333333333333333333333333333333333333333 +3226 3333333333333333333333333333333333333333333333333333333 +3227 3333333333333333333333333333333333333333333333333333333 +3228 3333333333333333333333333333333333333333333333333333333 +3229 3333333333333333333333333333333333333333333333333333333 +3230 3333333333333333333333333333333333333333333333333333333 +3231 3333333333333333333333333333333333333333333333333333333 +3232 3333333333333333333333333333333333333333333333333333333 +3233 3333333333333333333333333333333333333333333333333333333 +3234 3333333333333333333333333333333333333333333333333333333 +3235 3333333333333333333333333333333333333333333333333333333 +3236 3333333333333333333333333333333333333333333333333333333 +3237 3333333333333333333333333333333333333333333333333333333 +3238 3333333333333333333333333333333333333333333333333333333 +3239 3333333333333333333333333333333333333333333333333333333 +3240 3333333333333333333333333333333333333333333333333333333 +3241 3333333333333333333333333333333333333333333333333333333 +3242 3333333333333333333333333333333333333333333333333333333 +3243 3333333333333333333333333333333333333333333333333333333 +3244 3333333333333333333333333333333333333333333333333333333 +3245 3333333333333333333333333333333333333333333333333333333 +3246 3333333333333333333333333333333333333333333333333333333 +3247 3333333333333333333333333333333333333333333333333333333 +3248 3333333333333333333333333333333333333333333333333333333 +3249 3333333333333333333333333333333333333333333333333333333 +3250 3333333333333333333333333333333333333333333333333333333 +3251 3333333333333333333333333333333333333333333333333333333 +3252 3333333333333333333333333333333333333333333333333333333 +3253 3333333333333333333333333333333333333333333333333333333 +3254 3333333333333333333333333333333333333333333333333333333 +3255 3333333333333333333333333333333333333333333333333333333 +3256 3333333333333333333333333333333333333333333333333333333 +3257 3333333333333333333333333333333333333333333333333333333 +3258 3333333333333333333333333333333333333333333333333333333 +3259 3333333333333333333333333333333333333333333333333333333 +3260 3333333333333333333333333333333333333333333333333333333 +3261 3333333333333333333333333333333333333333333333333333333 +3262 3333333333333333333333333333333333333333333333333333333 +3263 3333333333333333333333333333333333333333333333333333333 +3264 3333333333333333333333333333333333333333333333333333333 +3265 3333333333333333333333333333333333333333333333333333333 +3266 3333333333333333333333333333333333333333333333333333333 +3267 3333333333333333333333333333333333333333333333333333333 +3268 3333333333333333333333333333333333333333333333333333333 +3269 3333333333333333333333333333333333333333333333333333333 +3270 3333333333333333333333333333333333333333333333333333333 +3271 3333333333333333333333333333333333333333333333333333333 +3272 3333333333333333333333333333333333333333333333333333333 +3273 3333333333333333333333333333333333333333333333333333333 +3274 3333333333333333333333333333333333333333333333333333333 +3275 3333333333333333333333333333333333333333333333333333333 +3276 3333333333333333333333333333333333333333333333333333333 +3277 3333333333333333333333333333333333333333333333333333333 +3278 3333333333333333333333333333333333333333333333333333333 +3279 3333333333333333333333333333333333333333333333333333333 +3280 3333333333333333333333333333333333333333333333333333333 +3281 3333333333333333333333333333333333333333333333333333333 +3282 3333333333333333333333333333333333333333333333333333333 +3283 3333333333333333333333333333333333333333333333333333333 +3284 3333333333333333333333333333333333333333333333333333333 +3285 3333333333333333333333333333333333333333333333333333333 +3286 3333333333333333333333333333333333333333333333333333333 +3287 3333333333333333333333333333333333333333333333333333333 +3288 3333333333333333333333333333333333333333333333333333333 +3289 3333333333333333333333333333333333333333333333333333333 +3290 3333333333333333333333333333333333333333333333333333333 +3291 3333333333333333333333333333333333333333333333333333333 +3292 3333333333333333333333333333333333333333333333333333333 +3293 3333333333333333333333333333333333333333333333333333333 +3294 3333333333333333333333333333333333333333333333333333333 +3295 3333333333333333333333333333333333333333333333333333333 +3296 3333333333333333333333333333333333333333333333333333333 +3297 3333333333333333333333333333333333333333333333333333333 +3298 3333333333333333333333333333333333333333333333333333333 +3299 3333333333333333333333333333333333333333333333333333333 +3300 3333333333333333333333333333333333333333333333333333333 +3301 3333333333333333333333333333333333333333333333333333333 +3302 3333333333333333333333333333333333333333333333333333333 +3303 3333333333333333333333333333333333333333333333333333333 +3304 3333333333333333333333333333333333333333333333333333333 +3305 3333333333333333333333333333333333333333333333333333333 +3306 3333333333333333333333333333333333333333333333333333333 +3307 3333333333333333333333333333333333333333333333333333333 +3308 3333333333333333333333333333333333333333333333333333333 +3309 3333333333333333333333333333333333333333333333333333333 +3310 3333333333333333333333333333333333333333333333333333333 +3311 3333333333333333333333333333333333333333333333333333333 +3312 3333333333333333333333333333333333333333333333333333333 +3313 3333333333333333333333333333333333333333333333333333333 +3314 3333333333333333333333333333333333333333333333333333333 +3315 3333333333333333333333333333333333333333333333333333333 +3316 3333333333333333333333333333333333333333333333333333333 +3317 3333333333333333333333333333333333333333333333333333333 +3318 3333333333333333333333333333333333333333333333333333333 +3319 3333333333333333333333333333333333333333333333333333333 +3320 3333333333333333333333333333333333333333333333333333333 +3321 3333333333333333333333333333333333333333333333333333333 +3322 3333333333333333333333333333333333333333333333333333333 +3323 3333333333333333333333333333333333333333333333333333333 +3324 3333333333333333333333333333333333333333333333333333333 +3325 3333333333333333333333333333333333333333333333333333333 +3326 3333333333333333333333333333333333333333333333333333333 +3327 3333333333333333333333333333333333333333333333333333333 +3328 3333333333333333333333333333333333333333333333333333333 +3329 3333333333333333333333333333333333333333333333333333333 +3330 3333333333333333333333333333333333333333333333333333333 +3331 3333333333333333333333333333333333333333333333333333333 +3332 3333333333333333333333333333333333333333333333333333333 +3333 3333333333333333333333333333333333333333333333333333333 +3334 3333333333333333333333333333333333333333333333333333333 +3335 3333333333333333333333333333333333333333333333333333333 +3336 3333333333333333333333333333333333333333333333333333333 +3337 3333333333333333333333333333333333333333333333333333333 +3338 3333333333333333333333333333333333333333333333333333333 +3339 3333333333333333333333333333333333333333333333333333333 +3340 3333333333333333333333333333333333333333333333333333333 +3341 3333333333333333333333333333333333333333333333333333333 +3342 3333333333333333333333333333333333333333333333333333333 +3343 3333333333333333333333333333333333333333333333333333333 +3344 3333333333333333333333333333333333333333333333333333333 +3345 3333333333333333333333333333333333333333333333333333333 +3346 3333333333333333333333333333333333333333333333333333333 +3347 3333333333333333333333333333333333333333333333333333333 +3348 3333333333333333333333333333333333333333333333333333333 +3349 3333333333333333333333333333333333333333333333333333333 +3350 3333333333333333333333333333333333333333333333333333333 +3351 3333333333333333333333333333333333333333333333333333333 +3352 3333333333333333333333333333333333333333333333333333333 +3353 3333333333333333333333333333333333333333333333333333333 +3354 3333333333333333333333333333333333333333333333333333333 +3355 3333333333333333333333333333333333333333333333333333333 +3356 3333333333333333333333333333333333333333333333333333333 +3357 3333333333333333333333333333333333333333333333333333333 +3358 3333333333333333333333333333333333333333333333333333333 +3359 3333333333333333333333333333333333333333333333333333333 +3360 3333333333333333333333333333333333333333333333333333333 +3361 3333333333333333333333333333333333333333333333333333333 +3362 3333333333333333333333333333333333333333333333333333333 +3363 3333333333333333333333333333333333333333333333333333333 +3364 3333333333333333333333333333333333333333333333333333333 +3365 3333333333333333333333333333333333333333333333333333333 +3366 3333333333333333333333333333333333333333333333333333333 +3367 3333333333333333333333333333333333333333333333333333333 +3368 3333333333333333333333333333333333333333333333333333333 +3369 3333333333333333333333333333333333333333333333333333333 +3370 3333333333333333333333333333333333333333333333333333333 +3371 3333333333333333333333333333333333333333333333333333333 +3372 3333333333333333333333333333333333333333333333333333333 +3373 3333333333333333333333333333333333333333333333333333333 +3374 3333333333333333333333333333333333333333333333333333333 +3375 3333333333333333333333333333333333333333333333333333333 +3376 3333333333333333333333333333333333333333333333333333333 +3377 3333333333333333333333333333333333333333333333333333333 +3378 3333333333333333333333333333333333333333333333333333333 +3379 3333333333333333333333333333333333333333333333333333333 +3380 3333333333333333333333333333333333333333333333333333333 +3381 3333333333333333333333333333333333333333333333333333333 +3382 3333333333333333333333333333333333333333333333333333333 +3383 3333333333333333333333333333333333333333333333333333333 +3384 3333333333333333333333333333333333333333333333333333333 +3385 3333333333333333333333333333333333333333333333333333333 +3386 3333333333333333333333333333333333333333333333333333333 +3387 3333333333333333333333333333333333333333333333333333333 +3388 3333333333333333333333333333333333333333333333333333333 +3389 3333333333333333333333333333333333333333333333333333333 +3390 3333333333333333333333333333333333333333333333333333333 +3391 3333333333333333333333333333333333333333333333333333333 +3392 3333333333333333333333333333333333333333333333333333333 +3393 3333333333333333333333333333333333333333333333333333333 +3394 3333333333333333333333333333333333333333333333333333333 +3395 3333333333333333333333333333333333333333333333333333333 +3396 3333333333333333333333333333333333333333333333333333333 +3397 3333333333333333333333333333333333333333333333333333333 +3398 3333333333333333333333333333333333333333333333333333333 +3399 3333333333333333333333333333333333333333333333333333333 +3400 3333333333333333333333333333333333333333333333333333333 +3401 3333333333333333333333333333333333333333333333333333333 +3402 3333333333333333333333333333333333333333333333333333333 +3403 3333333333333333333333333333333333333333333333333333333 +3404 3333333333333333333333333333333333333333333333333333333 +3405 3333333333333333333333333333333333333333333333333333333 +3406 3333333333333333333333333333333333333333333333333333333 +3407 3333333333333333333333333333333333333333333333333333333 +3408 3333333333333333333333333333333333333333333333333333333 +3409 3333333333333333333333333333333333333333333333333333333 +3410 3333333333333333333333333333333333333333333333333333333 +3411 3333333333333333333333333333333333333333333333333333333 +3412 3333333333333333333333333333333333333333333333333333333 +3413 3333333333333333333333333333333333333333333333333333333 +3414 3333333333333333333333333333333333333333333333333333333 +3415 3333333333333333333333333333333333333333333333333333333 +3416 3333333333333333333333333333333333333333333333333333333 +3417 3333333333333333333333333333333333333333333333333333333 +3418 3333333333333333333333333333333333333333333333333333333 +3419 3333333333333333333333333333333333333333333333333333333 +3420 3333333333333333333333333333333333333333333333333333333 +3421 3333333333333333333333333333333333333333333333333333333 +3422 3333333333333333333333333333333333333333333333333333333 +3423 3333333333333333333333333333333333333333333333333333333 +3424 3333333333333333333333333333333333333333333333333333333 +3425 3333333333333333333333333333333333333333333333333333333 +3426 3333333333333333333333333333333333333333333333333333333 +3427 3333333333333333333333333333333333333333333333333333333 +3428 3333333333333333333333333333333333333333333333333333333 +3429 3333333333333333333333333333333333333333333333333333333 +3430 3333333333333333333333333333333333333333333333333333333 +3431 3333333333333333333333333333333333333333333333333333333 +3432 3333333333333333333333333333333333333333333333333333333 +3433 3333333333333333333333333333333333333333333333333333333 +3434 3333333333333333333333333333333333333333333333333333333 +3435 3333333333333333333333333333333333333333333333333333333 +3436 3333333333333333333333333333333333333333333333333333333 +3437 3333333333333333333333333333333333333333333333333333333 +3438 3333333333333333333333333333333333333333333333333333333 +3439 3333333333333333333333333333333333333333333333333333333 +3440 3333333333333333333333333333333333333333333333333333333 +3441 3333333333333333333333333333333333333333333333333333333 +3442 3333333333333333333333333333333333333333333333333333333 +3443 3333333333333333333333333333333333333333333333333333333 +3444 3333333333333333333333333333333333333333333333333333333 +3445 3333333333333333333333333333333333333333333333333333333 +3446 3333333333333333333333333333333333333333333333333333333 +3447 3333333333333333333333333333333333333333333333333333333 +3448 3333333333333333333333333333333333333333333333333333333 +3449 3333333333333333333333333333333333333333333333333333333 +3450 3333333333333333333333333333333333333333333333333333333 +3451 3333333333333333333333333333333333333333333333333333333 +3452 3333333333333333333333333333333333333333333333333333333 +3453 3333333333333333333333333333333333333333333333333333333 +3454 3333333333333333333333333333333333333333333333333333333 +3455 3333333333333333333333333333333333333333333333333333333 +3456 3333333333333333333333333333333333333333333333333333333 +3457 3333333333333333333333333333333333333333333333333333333 +3458 3333333333333333333333333333333333333333333333333333333 +3459 3333333333333333333333333333333333333333333333333333333 +3460 3333333333333333333333333333333333333333333333333333333 +3461 3333333333333333333333333333333333333333333333333333333 +3462 3333333333333333333333333333333333333333333333333333333 +3463 3333333333333333333333333333333333333333333333333333333 +3464 3333333333333333333333333333333333333333333333333333333 +3465 3333333333333333333333333333333333333333333333333333333 +3466 3333333333333333333333333333333333333333333333333333333 +3467 3333333333333333333333333333333333333333333333333333333 +3468 3333333333333333333333333333333333333333333333333333333 +3469 3333333333333333333333333333333333333333333333333333333 +3470 3333333333333333333333333333333333333333333333333333333 +3471 3333333333333333333333333333333333333333333333333333333 +3472 3333333333333333333333333333333333333333333333333333333 +3473 3333333333333333333333333333333333333333333333333333333 +3474 3333333333333333333333333333333333333333333333333333333 +3475 3333333333333333333333333333333333333333333333333333333 +3476 3333333333333333333333333333333333333333333333333333333 +3477 3333333333333333333333333333333333333333333333333333333 +3478 3333333333333333333333333333333333333333333333333333333 +3479 3333333333333333333333333333333333333333333333333333333 +3480 3333333333333333333333333333333333333333333333333333333 +3481 3333333333333333333333333333333333333333333333333333333 +3482 3333333333333333333333333333333333333333333333333333333 +3483 3333333333333333333333333333333333333333333333333333333 +3484 3333333333333333333333333333333333333333333333333333333 +3485 3333333333333333333333333333333333333333333333333333333 +3486 3333333333333333333333333333333333333333333333333333333 +3487 3333333333333333333333333333333333333333333333333333333 +3488 3333333333333333333333333333333333333333333333333333333 +3489 3333333333333333333333333333333333333333333333333333333 +3490 3333333333333333333333333333333333333333333333333333333 +3491 3333333333333333333333333333333333333333333333333333333 +3492 3333333333333333333333333333333333333333333333333333333 +3493 3333333333333333333333333333333333333333333333333333333 +3494 3333333333333333333333333333333333333333333333333333333 +3495 3333333333333333333333333333333333333333333333333333333 +3496 3333333333333333333333333333333333333333333333333333333 +3497 3333333333333333333333333333333333333333333333333333333 +3498 3333333333333333333333333333333333333333333333333333333 +3499 3333333333333333333333333333333333333333333333333333333 +3500 3333333333333333333333333333333333333333333333333333333 +3501 3333333333333333333333333333333333333333333333333333333 +3502 3333333333333333333333333333333333333333333333333333333 +3503 3333333333333333333333333333333333333333333333333333333 +3504 3333333333333333333333333333333333333333333333333333333 +3505 3333333333333333333333333333333333333333333333333333333 +3506 3333333333333333333333333333333333333333333333333333333 +3507 3333333333333333333333333333333333333333333333333333333 +3508 3333333333333333333333333333333333333333333333333333333 +3509 3333333333333333333333333333333333333333333333333333333 +3510 3333333333333333333333333333333333333333333333333333333 +3511 3333333333333333333333333333333333333333333333333333333 +3512 3333333333333333333333333333333333333333333333333333333 +3513 3333333333333333333333333333333333333333333333333333333 +3514 3333333333333333333333333333333333333333333333333333333 +3515 3333333333333333333333333333333333333333333333333333333 +3516 3333333333333333333333333333333333333333333333333333333 +3517 3333333333333333333333333333333333333333333333333333333 +3518 3333333333333333333333333333333333333333333333333333333 +3519 3333333333333333333333333333333333333333333333333333333 +3520 3333333333333333333333333333333333333333333333333333333 +3521 3333333333333333333333333333333333333333333333333333333 +3522 3333333333333333333333333333333333333333333333333333333 +3523 3333333333333333333333333333333333333333333333333333333 +3524 3333333333333333333333333333333333333333333333333333333 +3525 3333333333333333333333333333333333333333333333333333333 +3526 3333333333333333333333333333333333333333333333333333333 +3527 3333333333333333333333333333333333333333333333333333333 +3528 3333333333333333333333333333333333333333333333333333333 +3529 3333333333333333333333333333333333333333333333333333333 +3530 3333333333333333333333333333333333333333333333333333333 +3531 3333333333333333333333333333333333333333333333333333333 +3532 3333333333333333333333333333333333333333333333333333333 +3533 3333333333333333333333333333333333333333333333333333333 +3534 3333333333333333333333333333333333333333333333333333333 +3535 3333333333333333333333333333333333333333333333333333333 +3536 3333333333333333333333333333333333333333333333333333333 +3537 3333333333333333333333333333333333333333333333333333333 +3538 3333333333333333333333333333333333333333333333333333333 +3539 3333333333333333333333333333333333333333333333333333333 +3540 3333333333333333333333333333333333333333333333333333333 +3541 3333333333333333333333333333333333333333333333333333333 +3542 3333333333333333333333333333333333333333333333333333333 +3543 3333333333333333333333333333333333333333333333333333333 +3544 3333333333333333333333333333333333333333333333333333333 +3545 3333333333333333333333333333333333333333333333333333333 +3546 3333333333333333333333333333333333333333333333333333333 +3547 3333333333333333333333333333333333333333333333333333333 +3548 3333333333333333333333333333333333333333333333333333333 +3549 3333333333333333333333333333333333333333333333333333333 +3550 3333333333333333333333333333333333333333333333333333333 +3551 3333333333333333333333333333333333333333333333333333333 +3552 3333333333333333333333333333333333333333333333333333333 +3553 3333333333333333333333333333333333333333333333333333333 +3554 3333333333333333333333333333333333333333333333333333333 +3555 3333333333333333333333333333333333333333333333333333333 +3556 3333333333333333333333333333333333333333333333333333333 +3557 3333333333333333333333333333333333333333333333333333333 +3558 3333333333333333333333333333333333333333333333333333333 +3559 3333333333333333333333333333333333333333333333333333333 +3560 3333333333333333333333333333333333333333333333333333333 +3561 3333333333333333333333333333333333333333333333333333333 +3562 3333333333333333333333333333333333333333333333333333333 +3563 3333333333333333333333333333333333333333333333333333333 +3564 3333333333333333333333333333333333333333333333333333333 +3565 3333333333333333333333333333333333333333333333333333333 +3566 3333333333333333333333333333333333333333333333333333333 +3567 3333333333333333333333333333333333333333333333333333333 +3568 3333333333333333333333333333333333333333333333333333333 +3569 3333333333333333333333333333333333333333333333333333333 +3570 3333333333333333333333333333333333333333333333333333333 +3571 3333333333333333333333333333333333333333333333333333333 +3572 3333333333333333333333333333333333333333333333333333333 +3573 3333333333333333333333333333333333333333333333333333333 +3574 3333333333333333333333333333333333333333333333333333333 +3575 3333333333333333333333333333333333333333333333333333333 +3576 3333333333333333333333333333333333333333333333333333333 +3577 3333333333333333333333333333333333333333333333333333333 +3578 3333333333333333333333333333333333333333333333333333333 +3579 3333333333333333333333333333333333333333333333333333333 +3580 3333333333333333333333333333333333333333333333333333333 +3581 3333333333333333333333333333333333333333333333333333333 +3582 3333333333333333333333333333333333333333333333333333333 +3583 3333333333333333333333333333333333333333333333333333333 +3584 3333333333333333333333333333333333333333333333333333333 +3585 3333333333333333333333333333333333333333333333333333333 +3586 3333333333333333333333333333333333333333333333333333333 +3587 3333333333333333333333333333333333333333333333333333333 +3588 3333333333333333333333333333333333333333333333333333333 +3589 3333333333333333333333333333333333333333333333333333333 +3590 3333333333333333333333333333333333333333333333333333333 +3591 3333333333333333333333333333333333333333333333333333333 +3592 3333333333333333333333333333333333333333333333333333333 +3593 3333333333333333333333333333333333333333333333333333333 +3594 3333333333333333333333333333333333333333333333333333333 +3595 3333333333333333333333333333333333333333333333333333333 +3596 3333333333333333333333333333333333333333333333333333333 +3597 3333333333333333333333333333333333333333333333333333333 +3598 3333333333333333333333333333333333333333333333333333333 +3599 3333333333333333333333333333333333333333333333333333333 +3600 3333333333333333333333333333333333333333333333333333333 +3601 3333333333333333333333333333333333333333333333333333333 +3602 3333333333333333333333333333333333333333333333333333333 +3603 3333333333333333333333333333333333333333333333333333333 +3604 3333333333333333333333333333333333333333333333333333333 +3605 3333333333333333333333333333333333333333333333333333333 +3606 3333333333333333333333333333333333333333333333333333333 +3607 3333333333333333333333333333333333333333333333333333333 +3608 3333333333333333333333333333333333333333333333333333333 +3609 3333333333333333333333333333333333333333333333333333333 +3610 3333333333333333333333333333333333333333333333333333333 +3611 3333333333333333333333333333333333333333333333333333333 +3612 3333333333333333333333333333333333333333333333333333333 +3613 3333333333333333333333333333333333333333333333333333333 +3614 3333333333333333333333333333333333333333333333333333333 +3615 3333333333333333333333333333333333333333333333333333333 +3616 3333333333333333333333333333333333333333333333333333333 +3617 3333333333333333333333333333333333333333333333333333333 +3618 3333333333333333333333333333333333333333333333333333333 +3619 3333333333333333333333333333333333333333333333333333333 +3620 3333333333333333333333333333333333333333333333333333333 +3621 3333333333333333333333333333333333333333333333333333333 +3622 3333333333333333333333333333333333333333333333333333333 +3623 3333333333333333333333333333333333333333333333333333333 +3624 3333333333333333333333333333333333333333333333333333333 +3625 3333333333333333333333333333333333333333333333333333333 +3626 3333333333333333333333333333333333333333333333333333333 +3627 3333333333333333333333333333333333333333333333333333333 +3628 3333333333333333333333333333333333333333333333333333333 +3629 3333333333333333333333333333333333333333333333333333333 +3630 3333333333333333333333333333333333333333333333333333333 +3631 3333333333333333333333333333333333333333333333333333333 +3632 3333333333333333333333333333333333333333333333333333333 +3633 3333333333333333333333333333333333333333333333333333333 +3634 3333333333333333333333333333333333333333333333333333333 +3635 3333333333333333333333333333333333333333333333333333333 +3636 3333333333333333333333333333333333333333333333333333333 +3637 3333333333333333333333333333333333333333333333333333333 +3638 3333333333333333333333333333333333333333333333333333333 +3639 3333333333333333333333333333333333333333333333333333333 +3640 3333333333333333333333333333333333333333333333333333333 +3641 3333333333333333333333333333333333333333333333333333333 +3642 3333333333333333333333333333333333333333333333333333333 +3643 3333333333333333333333333333333333333333333333333333333 +3644 3333333333333333333333333333333333333333333333333333333 +3645 3333333333333333333333333333333333333333333333333333333 +3646 3333333333333333333333333333333333333333333333333333333 +3647 3333333333333333333333333333333333333333333333333333333 +3648 3333333333333333333333333333333333333333333333333333333 +3649 3333333333333333333333333333333333333333333333333333333 +3650 3333333333333333333333333333333333333333333333333333333 +3651 3333333333333333333333333333333333333333333333333333333 +3652 3333333333333333333333333333333333333333333333333333333 +3653 3333333333333333333333333333333333333333333333333333333 +3654 3333333333333333333333333333333333333333333333333333333 +3655 3333333333333333333333333333333333333333333333333333333 +3656 3333333333333333333333333333333333333333333333333333333 +3657 3333333333333333333333333333333333333333333333333333333 +3658 3333333333333333333333333333333333333333333333333333333 +3659 3333333333333333333333333333333333333333333333333333333 +3660 3333333333333333333333333333333333333333333333333333333 +3661 3333333333333333333333333333333333333333333333333333333 +3662 3333333333333333333333333333333333333333333333333333333 +3663 3333333333333333333333333333333333333333333333333333333 +3664 3333333333333333333333333333333333333333333333333333333 +3665 3333333333333333333333333333333333333333333333333333333 +3666 3333333333333333333333333333333333333333333333333333333 +3667 3333333333333333333333333333333333333333333333333333333 +3668 3333333333333333333333333333333333333333333333333333333 +3669 3333333333333333333333333333333333333333333333333333333 +3670 3333333333333333333333333333333333333333333333333333333 +3671 3333333333333333333333333333333333333333333333333333333 +3672 3333333333333333333333333333333333333333333333333333333 +3673 3333333333333333333333333333333333333333333333333333333 +3674 3333333333333333333333333333333333333333333333333333333 +3675 3333333333333333333333333333333333333333333333333333333 +3676 3333333333333333333333333333333333333333333333333333333 +3677 3333333333333333333333333333333333333333333333333333333 +3678 3333333333333333333333333333333333333333333333333333333 +3679 3333333333333333333333333333333333333333333333333333333 +3680 3333333333333333333333333333333333333333333333333333333 +3681 3333333333333333333333333333333333333333333333333333333 +3682 3333333333333333333333333333333333333333333333333333333 +3683 3333333333333333333333333333333333333333333333333333333 +3684 3333333333333333333333333333333333333333333333333333333 +3685 3333333333333333333333333333333333333333333333333333333 +3686 3333333333333333333333333333333333333333333333333333333 +3687 3333333333333333333333333333333333333333333333333333333 +3688 3333333333333333333333333333333333333333333333333333333 +3689 3333333333333333333333333333333333333333333333333333333 +3690 3333333333333333333333333333333333333333333333333333333 +3691 3333333333333333333333333333333333333333333333333333333 +3692 3333333333333333333333333333333333333333333333333333333 +3693 3333333333333333333333333333333333333333333333333333333 +3694 3333333333333333333333333333333333333333333333333333333 +3695 3333333333333333333333333333333333333333333333333333333 +3696 3333333333333333333333333333333333333333333333333333333 +3697 3333333333333333333333333333333333333333333333333333333 +3698 3333333333333333333333333333333333333333333333333333333 +3699 3333333333333333333333333333333333333333333333333333333 +3700 3333333333333333333333333333333333333333333333333333333 +3701 3333333333333333333333333333333333333333333333333333333 +3702 3333333333333333333333333333333333333333333333333333333 +3703 3333333333333333333333333333333333333333333333333333333 +3704 3333333333333333333333333333333333333333333333333333333 +3705 3333333333333333333333333333333333333333333333333333333 +3706 3333333333333333333333333333333333333333333333333333333 +3707 3333333333333333333333333333333333333333333333333333333 +3708 3333333333333333333333333333333333333333333333333333333 +3709 3333333333333333333333333333333333333333333333333333333 +3710 3333333333333333333333333333333333333333333333333333333 +3711 3333333333333333333333333333333333333333333333333333333 +3712 3333333333333333333333333333333333333333333333333333333 +3713 3333333333333333333333333333333333333333333333333333333 +3714 3333333333333333333333333333333333333333333333333333333 +3715 3333333333333333333333333333333333333333333333333333333 +3716 3333333333333333333333333333333333333333333333333333333 +3717 3333333333333333333333333333333333333333333333333333333 +3718 3333333333333333333333333333333333333333333333333333333 +3719 3333333333333333333333333333333333333333333333333333333 +3720 3333333333333333333333333333333333333333333333333333333 +3721 3333333333333333333333333333333333333333333333333333333 +3722 3333333333333333333333333333333333333333333333333333333 +3723 3333333333333333333333333333333333333333333333333333333 +3724 3333333333333333333333333333333333333333333333333333333 +3725 3333333333333333333333333333333333333333333333333333333 +3726 3333333333333333333333333333333333333333333333333333333 +3727 3333333333333333333333333333333333333333333333333333333 +3728 3333333333333333333333333333333333333333333333333333333 +3729 3333333333333333333333333333333333333333333333333333333 +3730 3333333333333333333333333333333333333333333333333333333 +3731 3333333333333333333333333333333333333333333333333333333 +3732 3333333333333333333333333333333333333333333333333333333 +3733 3333333333333333333333333333333333333333333333333333333 +3734 3333333333333333333333333333333333333333333333333333333 +3735 3333333333333333333333333333333333333333333333333333333 +3736 3333333333333333333333333333333333333333333333333333333 +3737 3333333333333333333333333333333333333333333333333333333 +3738 3333333333333333333333333333333333333333333333333333333 +3739 3333333333333333333333333333333333333333333333333333333 +3740 3333333333333333333333333333333333333333333333333333333 +3741 3333333333333333333333333333333333333333333333333333333 +3742 3333333333333333333333333333333333333333333333333333333 +3743 3333333333333333333333333333333333333333333333333333333 +3744 3333333333333333333333333333333333333333333333333333333 +3745 3333333333333333333333333333333333333333333333333333333 +3746 3333333333333333333333333333333333333333333333333333333 +3747 3333333333333333333333333333333333333333333333333333333 +3748 3333333333333333333333333333333333333333333333333333333 +3749 3333333333333333333333333333333333333333333333333333333 +3750 3333333333333333333333333333333333333333333333333333333 +3751 3333333333333333333333333333333333333333333333333333333 +3752 3333333333333333333333333333333333333333333333333333333 +3753 3333333333333333333333333333333333333333333333333333333 +3754 3333333333333333333333333333333333333333333333333333333 +3755 3333333333333333333333333333333333333333333333333333333 +3756 3333333333333333333333333333333333333333333333333333333 +3757 3333333333333333333333333333333333333333333333333333333 +3758 3333333333333333333333333333333333333333333333333333333 +3759 3333333333333333333333333333333333333333333333333333333 +3760 3333333333333333333333333333333333333333333333333333333 +3761 3333333333333333333333333333333333333333333333333333333 +3762 3333333333333333333333333333333333333333333333333333333 +3763 3333333333333333333333333333333333333333333333333333333 +3764 3333333333333333333333333333333333333333333333333333333 +3765 3333333333333333333333333333333333333333333333333333333 +3766 3333333333333333333333333333333333333333333333333333333 +3767 3333333333333333333333333333333333333333333333333333333 +3768 3333333333333333333333333333333333333333333333333333333 +3769 3333333333333333333333333333333333333333333333333333333 +3770 3333333333333333333333333333333333333333333333333333333 +3771 3333333333333333333333333333333333333333333333333333333 +3772 3333333333333333333333333333333333333333333333333333333 +3773 3333333333333333333333333333333333333333333333333333333 +3774 3333333333333333333333333333333333333333333333333333333 +3775 3333333333333333333333333333333333333333333333333333333 +3776 3333333333333333333333333333333333333333333333333333333 +3777 3333333333333333333333333333333333333333333333333333333 +3778 3333333333333333333333333333333333333333333333333333333 +3779 3333333333333333333333333333333333333333333333333333333 +3780 3333333333333333333333333333333333333333333333333333333 +3781 3333333333333333333333333333333333333333333333333333333 +3782 3333333333333333333333333333333333333333333333333333333 +3783 3333333333333333333333333333333333333333333333333333333 +3784 3333333333333333333333333333333333333333333333333333333 +3785 3333333333333333333333333333333333333333333333333333333 +3786 3333333333333333333333333333333333333333333333333333333 +3787 3333333333333333333333333333333333333333333333333333333 +3788 3333333333333333333333333333333333333333333333333333333 +3789 3333333333333333333333333333333333333333333333333333333 +3790 3333333333333333333333333333333333333333333333333333333 +3791 3333333333333333333333333333333333333333333333333333333 +3792 3333333333333333333333333333333333333333333333333333333 +3793 3333333333333333333333333333333333333333333333333333333 +3794 3333333333333333333333333333333333333333333333333333333 +3795 3333333333333333333333333333333333333333333333333333333 +3796 3333333333333333333333333333333333333333333333333333333 +3797 3333333333333333333333333333333333333333333333333333333 +3798 3333333333333333333333333333333333333333333333333333333 +3799 3333333333333333333333333333333333333333333333333333333 +3800 3333333333333333333333333333333333333333333333333333333 +3801 3333333333333333333333333333333333333333333333333333333 +3802 3333333333333333333333333333333333333333333333333333333 +3803 3333333333333333333333333333333333333333333333333333333 +3804 3333333333333333333333333333333333333333333333333333333 +3805 3333333333333333333333333333333333333333333333333333333 +3806 3333333333333333333333333333333333333333333333333333333 +3807 3333333333333333333333333333333333333333333333333333333 +3808 3333333333333333333333333333333333333333333333333333333 +3809 3333333333333333333333333333333333333333333333333333333 +3810 3333333333333333333333333333333333333333333333333333333 +3811 3333333333333333333333333333333333333333333333333333333 +3812 3333333333333333333333333333333333333333333333333333333 +3813 3333333333333333333333333333333333333333333333333333333 +3814 3333333333333333333333333333333333333333333333333333333 +3815 3333333333333333333333333333333333333333333333333333333 +3816 3333333333333333333333333333333333333333333333333333333 +3817 3333333333333333333333333333333333333333333333333333333 +3818 3333333333333333333333333333333333333333333333333333333 +3819 3333333333333333333333333333333333333333333333333333333 +3820 3333333333333333333333333333333333333333333333333333333 +3821 3333333333333333333333333333333333333333333333333333333 +3822 3333333333333333333333333333333333333333333333333333333 +3823 3333333333333333333333333333333333333333333333333333333 +3824 3333333333333333333333333333333333333333333333333333333 +3825 3333333333333333333333333333333333333333333333333333333 +3826 3333333333333333333333333333333333333333333333333333333 +3827 3333333333333333333333333333333333333333333333333333333 +3828 3333333333333333333333333333333333333333333333333333333 +3829 3333333333333333333333333333333333333333333333333333333 +3830 3333333333333333333333333333333333333333333333333333333 +3831 3333333333333333333333333333333333333333333333333333333 +3832 3333333333333333333333333333333333333333333333333333333 +3833 3333333333333333333333333333333333333333333333333333333 +3834 3333333333333333333333333333333333333333333333333333333 +3835 3333333333333333333333333333333333333333333333333333333 +3836 3333333333333333333333333333333333333333333333333333333 +3837 3333333333333333333333333333333333333333333333333333333 +3838 3333333333333333333333333333333333333333333333333333333 +3839 3333333333333333333333333333333333333333333333333333333 +3840 3333333333333333333333333333333333333333333333333333333 +3841 3333333333333333333333333333333333333333333333333333333 +3842 3333333333333333333333333333333333333333333333333333333 +3843 3333333333333333333333333333333333333333333333333333333 +3844 3333333333333333333333333333333333333333333333333333333 +3845 3333333333333333333333333333333333333333333333333333333 +3846 3333333333333333333333333333333333333333333333333333333 +3847 3333333333333333333333333333333333333333333333333333333 +3848 3333333333333333333333333333333333333333333333333333333 +3849 3333333333333333333333333333333333333333333333333333333 +3850 3333333333333333333333333333333333333333333333333333333 +3851 3333333333333333333333333333333333333333333333333333333 +3852 3333333333333333333333333333333333333333333333333333333 +3853 3333333333333333333333333333333333333333333333333333333 +3854 3333333333333333333333333333333333333333333333333333333 +3855 3333333333333333333333333333333333333333333333333333333 +3856 3333333333333333333333333333333333333333333333333333333 +3857 3333333333333333333333333333333333333333333333333333333 +3858 3333333333333333333333333333333333333333333333333333333 +3859 3333333333333333333333333333333333333333333333333333333 +3860 3333333333333333333333333333333333333333333333333333333 +3861 3333333333333333333333333333333333333333333333333333333 +3862 3333333333333333333333333333333333333333333333333333333 +3863 3333333333333333333333333333333333333333333333333333333 +3864 3333333333333333333333333333333333333333333333333333333 +3865 3333333333333333333333333333333333333333333333333333333 +3866 3333333333333333333333333333333333333333333333333333333 +3867 3333333333333333333333333333333333333333333333333333333 +3868 3333333333333333333333333333333333333333333333333333333 +3869 3333333333333333333333333333333333333333333333333333333 +3870 3333333333333333333333333333333333333333333333333333333 +3871 3333333333333333333333333333333333333333333333333333333 +3872 3333333333333333333333333333333333333333333333333333333 +3873 3333333333333333333333333333333333333333333333333333333 +3874 3333333333333333333333333333333333333333333333333333333 +3875 3333333333333333333333333333333333333333333333333333333 +3876 3333333333333333333333333333333333333333333333333333333 +3877 3333333333333333333333333333333333333333333333333333333 +3878 3333333333333333333333333333333333333333333333333333333 +3879 3333333333333333333333333333333333333333333333333333333 +3880 3333333333333333333333333333333333333333333333333333333 +3881 3333333333333333333333333333333333333333333333333333333 +3882 3333333333333333333333333333333333333333333333333333333 +3883 3333333333333333333333333333333333333333333333333333333 +3884 3333333333333333333333333333333333333333333333333333333 +3885 3333333333333333333333333333333333333333333333333333333 +3886 3333333333333333333333333333333333333333333333333333333 +3887 3333333333333333333333333333333333333333333333333333333 +3888 3333333333333333333333333333333333333333333333333333333 +3889 3333333333333333333333333333333333333333333333333333333 +3890 3333333333333333333333333333333333333333333333333333333 +3891 3333333333333333333333333333333333333333333333333333333 +3892 3333333333333333333333333333333333333333333333333333333 +3893 3333333333333333333333333333333333333333333333333333333 +3894 3333333333333333333333333333333333333333333333333333333 +3895 3333333333333333333333333333333333333333333333333333333 +3896 3333333333333333333333333333333333333333333333333333333 +3897 3333333333333333333333333333333333333333333333333333333 +3898 3333333333333333333333333333333333333333333333333333333 +3899 3333333333333333333333333333333333333333333333333333333 +3900 3333333333333333333333333333333333333333333333333333333 +3901 3333333333333333333333333333333333333333333333333333333 +3902 3333333333333333333333333333333333333333333333333333333 +3903 3333333333333333333333333333333333333333333333333333333 +3904 3333333333333333333333333333333333333333333333333333333 +3905 3333333333333333333333333333333333333333333333333333333 +3906 3333333333333333333333333333333333333333333333333333333 +3907 3333333333333333333333333333333333333333333333333333333 +3908 3333333333333333333333333333333333333333333333333333333 +3909 3333333333333333333333333333333333333333333333333333333 +3910 3333333333333333333333333333333333333333333333333333333 +3911 3333333333333333333333333333333333333333333333333333333 +3912 3333333333333333333333333333333333333333333333333333333 +3913 3333333333333333333333333333333333333333333333333333333 +3914 3333333333333333333333333333333333333333333333333333333 +3915 3333333333333333333333333333333333333333333333333333333 +3916 3333333333333333333333333333333333333333333333333333333 +3917 3333333333333333333333333333333333333333333333333333333 +3918 3333333333333333333333333333333333333333333333333333333 +3919 3333333333333333333333333333333333333333333333333333333 +3920 3333333333333333333333333333333333333333333333333333333 +3921 3333333333333333333333333333333333333333333333333333333 +3922 3333333333333333333333333333333333333333333333333333333 +3923 3333333333333333333333333333333333333333333333333333333 +3924 3333333333333333333333333333333333333333333333333333333 +3925 3333333333333333333333333333333333333333333333333333333 +3926 3333333333333333333333333333333333333333333333333333333 +3927 3333333333333333333333333333333333333333333333333333333 +3928 3333333333333333333333333333333333333333333333333333333 +3929 3333333333333333333333333333333333333333333333333333333 +3930 3333333333333333333333333333333333333333333333333333333 +3931 3333333333333333333333333333333333333333333333333333333 +3932 3333333333333333333333333333333333333333333333333333333 +3933 3333333333333333333333333333333333333333333333333333333 +3934 3333333333333333333333333333333333333333333333333333333 +3935 3333333333333333333333333333333333333333333333333333333 +3936 3333333333333333333333333333333333333333333333333333333 +3937 3333333333333333333333333333333333333333333333333333333 +3938 3333333333333333333333333333333333333333333333333333333 +3939 3333333333333333333333333333333333333333333333333333333 +3940 3333333333333333333333333333333333333333333333333333333 +3941 3333333333333333333333333333333333333333333333333333333 +3942 3333333333333333333333333333333333333333333333333333333 +3943 3333333333333333333333333333333333333333333333333333333 +3944 3333333333333333333333333333333333333333333333333333333 +3945 3333333333333333333333333333333333333333333333333333333 +3946 3333333333333333333333333333333333333333333333333333333 +3947 3333333333333333333333333333333333333333333333333333333 +3948 3333333333333333333333333333333333333333333333333333333 +3949 3333333333333333333333333333333333333333333333333333333 +3950 3333333333333333333333333333333333333333333333333333333 +3951 3333333333333333333333333333333333333333333333333333333 +3952 3333333333333333333333333333333333333333333333333333333 +3953 3333333333333333333333333333333333333333333333333333333 +3954 3333333333333333333333333333333333333333333333333333333 +3955 3333333333333333333333333333333333333333333333333333333 +3956 3333333333333333333333333333333333333333333333333333333 +3957 3333333333333333333333333333333333333333333333333333333 +3958 3333333333333333333333333333333333333333333333333333333 +3959 3333333333333333333333333333333333333333333333333333333 +3960 3333333333333333333333333333333333333333333333333333333 +3961 3333333333333333333333333333333333333333333333333333333 +3962 3333333333333333333333333333333333333333333333333333333 +3963 3333333333333333333333333333333333333333333333333333333 +3964 3333333333333333333333333333333333333333333333333333333 +3965 3333333333333333333333333333333333333333333333333333333 +3966 3333333333333333333333333333333333333333333333333333333 +3967 3333333333333333333333333333333333333333333333333333333 +3968 3333333333333333333333333333333333333333333333333333333 +3969 3333333333333333333333333333333333333333333333333333333 +3970 3333333333333333333333333333333333333333333333333333333 +3971 3333333333333333333333333333333333333333333333333333333 +3972 3333333333333333333333333333333333333333333333333333333 +3973 3333333333333333333333333333333333333333333333333333333 +3974 3333333333333333333333333333333333333333333333333333333 +3975 3333333333333333333333333333333333333333333333333333333 +3976 3333333333333333333333333333333333333333333333333333333 +3977 3333333333333333333333333333333333333333333333333333333 +3978 3333333333333333333333333333333333333333333333333333333 +3979 3333333333333333333333333333333333333333333333333333333 +3980 3333333333333333333333333333333333333333333333333333333 +3981 3333333333333333333333333333333333333333333333333333333 +3982 3333333333333333333333333333333333333333333333333333333 +3983 3333333333333333333333333333333333333333333333333333333 +3984 3333333333333333333333333333333333333333333333333333333 +3985 3333333333333333333333333333333333333333333333333333333 +3986 3333333333333333333333333333333333333333333333333333333 +3987 3333333333333333333333333333333333333333333333333333333 +3988 3333333333333333333333333333333333333333333333333333333 +3989 3333333333333333333333333333333333333333333333333333333 +3990 3333333333333333333333333333333333333333333333333333333 +3991 3333333333333333333333333333333333333333333333333333333 +3992 3333333333333333333333333333333333333333333333333333333 +3993 3333333333333333333333333333333333333333333333333333333 +3994 3333333333333333333333333333333333333333333333333333333 +3995 3333333333333333333333333333333333333333333333333333333 +3996 3333333333333333333333333333333333333333333333333333333 +3997 3333333333333333333333333333333333333333333333333333333 +3998 3333333333333333333333333333333333333333333333333333333 +3999 3333333333333333333333333333333333333333333333333333333 +4000 3333333333333333333333333333333333333333333333333333333 +4001 3333333333333333333333333333333333333333333333333333333 +4002 3333333333333333333333333333333333333333333333333333333 +4003 3333333333333333333333333333333333333333333333333333333 +4004 3333333333333333333333333333333333333333333333333333333 +4005 3333333333333333333333333333333333333333333333333333333 +4006 3333333333333333333333333333333333333333333333333333333 +4007 3333333333333333333333333333333333333333333333333333333 +4008 3333333333333333333333333333333333333333333333333333333 +4009 3333333333333333333333333333333333333333333333333333333 +4010 3333333333333333333333333333333333333333333333333333333 +4011 3333333333333333333333333333333333333333333333333333333 +4012 3333333333333333333333333333333333333333333333333333333 +4013 3333333333333333333333333333333333333333333333333333333 +4014 3333333333333333333333333333333333333333333333333333333 +4015 3333333333333333333333333333333333333333333333333333333 +4016 3333333333333333333333333333333333333333333333333333333 +4017 3333333333333333333333333333333333333333333333333333333 +4018 3333333333333333333333333333333333333333333333333333333 +4019 3333333333333333333333333333333333333333333333333333333 +4020 3333333333333333333333333333333333333333333333333333333 +4021 3333333333333333333333333333333333333333333333333333333 +4022 3333333333333333333333333333333333333333333333333333333 +4023 3333333333333333333333333333333333333333333333333333333 +4024 3333333333333333333333333333333333333333333333333333333 +4025 3333333333333333333333333333333333333333333333333333333 +4026 3333333333333333333333333333333333333333333333333333333 +4027 3333333333333333333333333333333333333333333333333333333 +4028 3333333333333333333333333333333333333333333333333333333 +4029 3333333333333333333333333333333333333333333333333333333 +4030 3333333333333333333333333333333333333333333333333333333 +4031 3333333333333333333333333333333333333333333333333333333 +4032 3333333333333333333333333333333333333333333333333333333 +4033 3333333333333333333333333333333333333333333333333333333 +4034 3333333333333333333333333333333333333333333333333333333 +4035 3333333333333333333333333333333333333333333333333333333 +4036 3333333333333333333333333333333333333333333333333333333 +4037 3333333333333333333333333333333333333333333333333333333 +4038 3333333333333333333333333333333333333333333333333333333 +4039 3333333333333333333333333333333333333333333333333333333 +4040 3333333333333333333333333333333333333333333333333333333 +4041 3333333333333333333333333333333333333333333333333333333 +4042 3333333333333333333333333333333333333333333333333333333 +4043 3333333333333333333333333333333333333333333333333333333 +4044 3333333333333333333333333333333333333333333333333333333 +4045 3333333333333333333333333333333333333333333333333333333 +4046 3333333333333333333333333333333333333333333333333333333 +4047 3333333333333333333333333333333333333333333333333333333 +4048 3333333333333333333333333333333333333333333333333333333 +4049 3333333333333333333333333333333333333333333333333333333 +4050 3333333333333333333333333333333333333333333333333333333 +4051 3333333333333333333333333333333333333333333333333333333 +4052 3333333333333333333333333333333333333333333333333333333 +4053 3333333333333333333333333333333333333333333333333333333 +4054 3333333333333333333333333333333333333333333333333333333 +4055 3333333333333333333333333333333333333333333333333333333 +4056 3333333333333333333333333333333333333333333333333333333 +4057 3333333333333333333333333333333333333333333333333333333 +4058 3333333333333333333333333333333333333333333333333333333 +4059 3333333333333333333333333333333333333333333333333333333 +4060 3333333333333333333333333333333333333333333333333333333 +4061 3333333333333333333333333333333333333333333333333333333 +4062 3333333333333333333333333333333333333333333333333333333 +4063 3333333333333333333333333333333333333333333333333333333 +4064 3333333333333333333333333333333333333333333333333333333 +4065 3333333333333333333333333333333333333333333333333333333 +4066 3333333333333333333333333333333333333333333333333333333 +4067 3333333333333333333333333333333333333333333333333333333 +4068 3333333333333333333333333333333333333333333333333333333 +4069 3333333333333333333333333333333333333333333333333333333 +4070 3333333333333333333333333333333333333333333333333333333 +4071 3333333333333333333333333333333333333333333333333333333 +4072 3333333333333333333333333333333333333333333333333333333 +4073 3333333333333333333333333333333333333333333333333333333 +4074 3333333333333333333333333333333333333333333333333333333 +4075 3333333333333333333333333333333333333333333333333333333 +4076 3333333333333333333333333333333333333333333333333333333 +4077 3333333333333333333333333333333333333333333333333333333 +4078 3333333333333333333333333333333333333333333333333333333 +4079 3333333333333333333333333333333333333333333333333333333 +4080 3333333333333333333333333333333333333333333333333333333 +4081 3333333333333333333333333333333333333333333333333333333 +4082 3333333333333333333333333333333333333333333333333333333 +4083 3333333333333333333333333333333333333333333333333333333 +4084 3333333333333333333333333333333333333333333333333333333 +4085 3333333333333333333333333333333333333333333333333333333 +4086 3333333333333333333333333333333333333333333333333333333 +4087 3333333333333333333333333333333333333333333333333333333 +4088 3333333333333333333333333333333333333333333333333333333 +4089 3333333333333333333333333333333333333333333333333333333 +4090 3333333333333333333333333333333333333333333333333333333 +4091 3333333333333333333333333333333333333333333333333333333 +4092 3333333333333333333333333333333333333333333333333333333 +4093 3333333333333333333333333333333333333333333333333333333 +4094 3333333333333333333333333333333333333333333333333333333 +4095 3333333333333333333333333333333333333333333333333333333 +4096 3333333333333333333333333333333333333333333333333333333 +4097 3333333333333333333333333333333333333333333333333333333 +4098 3333333333333333333333333333333333333333333333333333333 +4099 3333333333333333333333333333333333333333333333333333333 +4100 3333333333333333333333333333333333333333333333333333333 +4101 3333333333333333333333333333333333333333333333333333333 +4102 3333333333333333333333333333333333333333333333333333333 +4103 3333333333333333333333333333333333333333333333333333333 +4104 3333333333333333333333333333333333333333333333333333333 +4105 3333333333333333333333333333333333333333333333333333333 +4106 3333333333333333333333333333333333333333333333333333333 +4107 3333333333333333333333333333333333333333333333333333333 +4108 3333333333333333333333333333333333333333333333333333333 +4109 3333333333333333333333333333333333333333333333333333333 +4110 3333333333333333333333333333333333333333333333333333333 +4111 3333333333333333333333333333333333333333333333333333333 +4112 3333333333333333333333333333333333333333333333333333333 +4113 3333333333333333333333333333333333333333333333333333333 +4114 3333333333333333333333333333333333333333333333333333333 +4115 3333333333333333333333333333333333333333333333333333333 +4116 3333333333333333333333333333333333333333333333333333333 +4117 3333333333333333333333333333333333333333333333333333333 +4118 3333333333333333333333333333333333333333333333333333333 +4119 3333333333333333333333333333333333333333333333333333333 +4120 3333333333333333333333333333333333333333333333333333333 +4121 3333333333333333333333333333333333333333333333333333333 +4122 3333333333333333333333333333333333333333333333333333333 +4123 3333333333333333333333333333333333333333333333333333333 +4124 3333333333333333333333333333333333333333333333333333333 +4125 3333333333333333333333333333333333333333333333333333333 +4126 3333333333333333333333333333333333333333333333333333333 +4127 3333333333333333333333333333333333333333333333333333333 +4128 3333333333333333333333333333333333333333333333333333333 +4129 3333333333333333333333333333333333333333333333333333333 +4130 3333333333333333333333333333333333333333333333333333333 +4131 3333333333333333333333333333333333333333333333333333333 +4132 3333333333333333333333333333333333333333333333333333333 +4133 3333333333333333333333333333333333333333333333333333333 +4134 3333333333333333333333333333333333333333333333333333333 +4135 3333333333333333333333333333333333333333333333333333333 +4136 3333333333333333333333333333333333333333333333333333333 +4137 3333333333333333333333333333333333333333333333333333333 +4138 3333333333333333333333333333333333333333333333333333333 +4139 3333333333333333333333333333333333333333333333333333333 +4140 3333333333333333333333333333333333333333333333333333333 +4141 3333333333333333333333333333333333333333333333333333333 +4142 3333333333333333333333333333333333333333333333333333333 +4143 3333333333333333333333333333333333333333333333333333333 +4144 3333333333333333333333333333333333333333333333333333333 +4145 3333333333333333333333333333333333333333333333333333333 +4146 3333333333333333333333333333333333333333333333333333333 +4147 3333333333333333333333333333333333333333333333333333333 +4148 3333333333333333333333333333333333333333333333333333333 +4149 3333333333333333333333333333333333333333333333333333333 +4150 3333333333333333333333333333333333333333333333333333333 +4151 3333333333333333333333333333333333333333333333333333333 +4152 3333333333333333333333333333333333333333333333333333333 +4153 3333333333333333333333333333333333333333333333333333333 +4154 3333333333333333333333333333333333333333333333333333333 +4155 3333333333333333333333333333333333333333333333333333333 +4156 3333333333333333333333333333333333333333333333333333333 +4157 3333333333333333333333333333333333333333333333333333333 +4158 3333333333333333333333333333333333333333333333333333333 +4159 3333333333333333333333333333333333333333333333333333333 +4160 3333333333333333333333333333333333333333333333333333333 +4161 3333333333333333333333333333333333333333333333333333333 +4162 3333333333333333333333333333333333333333333333333333333 +4163 3333333333333333333333333333333333333333333333333333333 +4164 3333333333333333333333333333333333333333333333333333333 +4165 3333333333333333333333333333333333333333333333333333333 +4166 3333333333333333333333333333333333333333333333333333333 +4167 3333333333333333333333333333333333333333333333333333333 +4168 3333333333333333333333333333333333333333333333333333333 +4169 3333333333333333333333333333333333333333333333333333333 +4170 3333333333333333333333333333333333333333333333333333333 +4171 3333333333333333333333333333333333333333333333333333333 +4172 3333333333333333333333333333333333333333333333333333333 +4173 3333333333333333333333333333333333333333333333333333333 +4174 3333333333333333333333333333333333333333333333333333333 +4175 3333333333333333333333333333333333333333333333333333333 +4176 3333333333333333333333333333333333333333333333333333333 +4177 3333333333333333333333333333333333333333333333333333333 +4178 3333333333333333333333333333333333333333333333333333333 +4179 3333333333333333333333333333333333333333333333333333333 +4180 3333333333333333333333333333333333333333333333333333333 +4181 3333333333333333333333333333333333333333333333333333333 +4182 3333333333333333333333333333333333333333333333333333333 +4183 3333333333333333333333333333333333333333333333333333333 +4184 3333333333333333333333333333333333333333333333333333333 +4185 3333333333333333333333333333333333333333333333333333333 +4186 3333333333333333333333333333333333333333333333333333333 +4187 3333333333333333333333333333333333333333333333333333333 +4188 3333333333333333333333333333333333333333333333333333333 +4189 3333333333333333333333333333333333333333333333333333333 +4190 3333333333333333333333333333333333333333333333333333333 +4191 3333333333333333333333333333333333333333333333333333333 +4192 3333333333333333333333333333333333333333333333333333333 +4193 3333333333333333333333333333333333333333333333333333333 +4194 3333333333333333333333333333333333333333333333333333333 +4195 3333333333333333333333333333333333333333333333333333333 +4196 3333333333333333333333333333333333333333333333333333333 +4197 3333333333333333333333333333333333333333333333333333333 +4198 3333333333333333333333333333333333333333333333333333333 +4199 3333333333333333333333333333333333333333333333333333333 +4200 3333333333333333333333333333333333333333333333333333333 +4201 3333333333333333333333333333333333333333333333333333333 +4202 3333333333333333333333333333333333333333333333333333333 +4203 3333333333333333333333333333333333333333333333333333333 +4204 3333333333333333333333333333333333333333333333333333333 +4205 3333333333333333333333333333333333333333333333333333333 +4206 3333333333333333333333333333333333333333333333333333333 +4207 3333333333333333333333333333333333333333333333333333333 +4208 3333333333333333333333333333333333333333333333333333333 +4209 3333333333333333333333333333333333333333333333333333333 +4210 3333333333333333333333333333333333333333333333333333333 +4211 3333333333333333333333333333333333333333333333333333333 +4212 3333333333333333333333333333333333333333333333333333333 +4213 3333333333333333333333333333333333333333333333333333333 +4214 3333333333333333333333333333333333333333333333333333333 +4215 3333333333333333333333333333333333333333333333333333333 +4216 3333333333333333333333333333333333333333333333333333333 +4217 3333333333333333333333333333333333333333333333333333333 +4218 3333333333333333333333333333333333333333333333333333333 +4219 3333333333333333333333333333333333333333333333333333333 +4220 3333333333333333333333333333333333333333333333333333333 +4221 3333333333333333333333333333333333333333333333333333333 +4222 3333333333333333333333333333333333333333333333333333333 +4223 3333333333333333333333333333333333333333333333333333333 +4224 3333333333333333333333333333333333333333333333333333333 +4225 3333333333333333333333333333333333333333333333333333333 +4226 3333333333333333333333333333333333333333333333333333333 +4227 3333333333333333333333333333333333333333333333333333333 +4228 3333333333333333333333333333333333333333333333333333333 +4229 3333333333333333333333333333333333333333333333333333333 +4230 3333333333333333333333333333333333333333333333333333333 +4231 3333333333333333333333333333333333333333333333333333333 +4232 3333333333333333333333333333333333333333333333333333333 +4233 3333333333333333333333333333333333333333333333333333333 +4234 3333333333333333333333333333333333333333333333333333333 +4235 3333333333333333333333333333333333333333333333333333333 +4236 3333333333333333333333333333333333333333333333333333333 +4237 3333333333333333333333333333333333333333333333333333333 +4238 3333333333333333333333333333333333333333333333333333333 +4239 3333333333333333333333333333333333333333333333333333333 +4240 3333333333333333333333333333333333333333333333333333333 +4241 3333333333333333333333333333333333333333333333333333333 +4242 3333333333333333333333333333333333333333333333333333333 +4243 3333333333333333333333333333333333333333333333333333333 +4244 3333333333333333333333333333333333333333333333333333333 +4245 3333333333333333333333333333333333333333333333333333333 +4246 3333333333333333333333333333333333333333333333333333333 +4247 3333333333333333333333333333333333333333333333333333333 +4248 3333333333333333333333333333333333333333333333333333333 +4249 3333333333333333333333333333333333333333333333333333333 +4250 3333333333333333333333333333333333333333333333333333333 +4251 3333333333333333333333333333333333333333333333333333333 +4252 3333333333333333333333333333333333333333333333333333333 +4253 3333333333333333333333333333333333333333333333333333333 +4254 3333333333333333333333333333333333333333333333333333333 +4255 3333333333333333333333333333333333333333333333333333333 +4256 3333333333333333333333333333333333333333333333333333333 +4257 3333333333333333333333333333333333333333333333333333333 +4258 3333333333333333333333333333333333333333333333333333333 +4259 3333333333333333333333333333333333333333333333333333333 +4260 3333333333333333333333333333333333333333333333333333333 +4261 3333333333333333333333333333333333333333333333333333333 +4262 3333333333333333333333333333333333333333333333333333333 +4263 3333333333333333333333333333333333333333333333333333333 +4264 3333333333333333333333333333333333333333333333333333333 +4265 3333333333333333333333333333333333333333333333333333333 +4266 3333333333333333333333333333333333333333333333333333333 +4267 3333333333333333333333333333333333333333333333333333333 +4268 3333333333333333333333333333333333333333333333333333333 +4269 3333333333333333333333333333333333333333333333333333333 +4270 3333333333333333333333333333333333333333333333333333333 +4271 3333333333333333333333333333333333333333333333333333333 +4272 3333333333333333333333333333333333333333333333333333333 +4273 3333333333333333333333333333333333333333333333333333333 +4274 3333333333333333333333333333333333333333333333333333333 +4275 3333333333333333333333333333333333333333333333333333333 +4276 3333333333333333333333333333333333333333333333333333333 +4277 3333333333333333333333333333333333333333333333333333333 +4278 3333333333333333333333333333333333333333333333333333333 +4279 3333333333333333333333333333333333333333333333333333333 +4280 3333333333333333333333333333333333333333333333333333333 +4281 3333333333333333333333333333333333333333333333333333333 +4282 3333333333333333333333333333333333333333333333333333333 +4283 3333333333333333333333333333333333333333333333333333333 +4284 3333333333333333333333333333333333333333333333333333333 +4285 3333333333333333333333333333333333333333333333333333333 +4286 3333333333333333333333333333333333333333333333333333333 +4287 3333333333333333333333333333333333333333333333333333333 +4288 3333333333333333333333333333333333333333333333333333333 +4289 3333333333333333333333333333333333333333333333333333333 +4290 3333333333333333333333333333333333333333333333333333333 +4291 3333333333333333333333333333333333333333333333333333333 +4292 3333333333333333333333333333333333333333333333333333333 +4293 3333333333333333333333333333333333333333333333333333333 +4294 3333333333333333333333333333333333333333333333333333333 +4295 3333333333333333333333333333333333333333333333333333333 +4296 3333333333333333333333333333333333333333333333333333333 +4297 3333333333333333333333333333333333333333333333333333333 +4298 3333333333333333333333333333333333333333333333333333333 +4299 3333333333333333333333333333333333333333333333333333333 +4300 3333333333333333333333333333333333333333333333333333333 +4301 3333333333333333333333333333333333333333333333333333333 +4302 3333333333333333333333333333333333333333333333333333333 +4303 3333333333333333333333333333333333333333333333333333333 +4304 3333333333333333333333333333333333333333333333333333333 +4305 3333333333333333333333333333333333333333333333333333333 +4306 3333333333333333333333333333333333333333333333333333333 +4307 3333333333333333333333333333333333333333333333333333333 +4308 3333333333333333333333333333333333333333333333333333333 +4309 3333333333333333333333333333333333333333333333333333333 +4310 3333333333333333333333333333333333333333333333333333333 +4311 3333333333333333333333333333333333333333333333333333333 +4312 3333333333333333333333333333333333333333333333333333333 +4313 3333333333333333333333333333333333333333333333333333333 +4314 3333333333333333333333333333333333333333333333333333333 +4315 3333333333333333333333333333333333333333333333333333333 +4316 3333333333333333333333333333333333333333333333333333333 +4317 3333333333333333333333333333333333333333333333333333333 +4318 3333333333333333333333333333333333333333333333333333333 +4319 3333333333333333333333333333333333333333333333333333333 +4320 3333333333333333333333333333333333333333333333333333333 +4321 3333333333333333333333333333333333333333333333333333333 +4322 3333333333333333333333333333333333333333333333333333333 +4323 3333333333333333333333333333333333333333333333333333333 +4324 3333333333333333333333333333333333333333333333333333333 +4325 3333333333333333333333333333333333333333333333333333333 +4326 3333333333333333333333333333333333333333333333333333333 +4327 3333333333333333333333333333333333333333333333333333333 +4328 3333333333333333333333333333333333333333333333333333333 +4329 3333333333333333333333333333333333333333333333333333333 +4330 3333333333333333333333333333333333333333333333333333333 +4331 3333333333333333333333333333333333333333333333333333333 +4332 3333333333333333333333333333333333333333333333333333333 +4333 3333333333333333333333333333333333333333333333333333333 +4334 3333333333333333333333333333333333333333333333333333333 +4335 3333333333333333333333333333333333333333333333333333333 +4336 3333333333333333333333333333333333333333333333333333333 +4337 3333333333333333333333333333333333333333333333333333333 +4338 3333333333333333333333333333333333333333333333333333333 +4339 3333333333333333333333333333333333333333333333333333333 +4340 3333333333333333333333333333333333333333333333333333333 +4341 3333333333333333333333333333333333333333333333333333333 +4342 3333333333333333333333333333333333333333333333333333333 +4343 3333333333333333333333333333333333333333333333333333333 +4344 3333333333333333333333333333333333333333333333333333333 +4345 3333333333333333333333333333333333333333333333333333333 +4346 3333333333333333333333333333333333333333333333333333333 +4347 3333333333333333333333333333333333333333333333333333333 +4348 3333333333333333333333333333333333333333333333333333333 +4349 3333333333333333333333333333333333333333333333333333333 +4350 3333333333333333333333333333333333333333333333333333333 +4351 3333333333333333333333333333333333333333333333333333333 +4352 3333333333333333333333333333333333333333333333333333333 +4353 3333333333333333333333333333333333333333333333333333333 +4354 3333333333333333333333333333333333333333333333333333333 +4355 3333333333333333333333333333333333333333333333333333333 +4356 3333333333333333333333333333333333333333333333333333333 +4357 3333333333333333333333333333333333333333333333333333333 +4358 3333333333333333333333333333333333333333333333333333333 +4359 3333333333333333333333333333333333333333333333333333333 +4360 3333333333333333333333333333333333333333333333333333333 +4361 3333333333333333333333333333333333333333333333333333333 +4362 3333333333333333333333333333333333333333333333333333333 +4363 3333333333333333333333333333333333333333333333333333333 +4364 3333333333333333333333333333333333333333333333333333333 +4365 3333333333333333333333333333333333333333333333333333333 +4366 3333333333333333333333333333333333333333333333333333333 +4367 3333333333333333333333333333333333333333333333333333333 +4368 3333333333333333333333333333333333333333333333333333333 +4369 3333333333333333333333333333333333333333333333333333333 +4370 3333333333333333333333333333333333333333333333333333333 +4371 3333333333333333333333333333333333333333333333333333333 +4372 3333333333333333333333333333333333333333333333333333333 +4373 3333333333333333333333333333333333333333333333333333333 +4374 3333333333333333333333333333333333333333333333333333333 +4375 3333333333333333333333333333333333333333333333333333333 +4376 3333333333333333333333333333333333333333333333333333333 +4377 3333333333333333333333333333333333333333333333333333333 +4378 3333333333333333333333333333333333333333333333333333333 +4379 3333333333333333333333333333333333333333333333333333333 +4380 3333333333333333333333333333333333333333333333333333333 +4381 3333333333333333333333333333333333333333333333333333333 +4382 3333333333333333333333333333333333333333333333333333333 +4383 3333333333333333333333333333333333333333333333333333333 +4384 3333333333333333333333333333333333333333333333333333333 +4385 3333333333333333333333333333333333333333333333333333333 +4386 3333333333333333333333333333333333333333333333333333333 +4387 3333333333333333333333333333333333333333333333333333333 +4388 3333333333333333333333333333333333333333333333333333333 +4389 3333333333333333333333333333333333333333333333333333333 +4390 3333333333333333333333333333333333333333333333333333333 +4391 3333333333333333333333333333333333333333333333333333333 +4392 3333333333333333333333333333333333333333333333333333333 +4393 3333333333333333333333333333333333333333333333333333333 +4394 3333333333333333333333333333333333333333333333333333333 +4395 3333333333333333333333333333333333333333333333333333333 +4396 3333333333333333333333333333333333333333333333333333333 +4397 3333333333333333333333333333333333333333333333333333333 +4398 3333333333333333333333333333333333333333333333333333333 +4399 3333333333333333333333333333333333333333333333333333333 +4400 3333333333333333333333333333333333333333333333333333333 +4401 3333333333333333333333333333333333333333333333333333333 +4402 3333333333333333333333333333333333333333333333333333333 +4403 3333333333333333333333333333333333333333333333333333333 +4404 3333333333333333333333333333333333333333333333333333333 +4405 3333333333333333333333333333333333333333333333333333333 +4406 3333333333333333333333333333333333333333333333333333333 +4407 3333333333333333333333333333333333333333333333333333333 +4408 3333333333333333333333333333333333333333333333333333333 +4409 3333333333333333333333333333333333333333333333333333333 +4410 3333333333333333333333333333333333333333333333333333333 +4411 3333333333333333333333333333333333333333333333333333333 +4412 3333333333333333333333333333333333333333333333333333333 +4413 3333333333333333333333333333333333333333333333333333333 +4414 3333333333333333333333333333333333333333333333333333333 +4415 3333333333333333333333333333333333333333333333333333333 +4416 3333333333333333333333333333333333333333333333333333333 +4417 3333333333333333333333333333333333333333333333333333333 +4418 3333333333333333333333333333333333333333333333333333333 +4419 3333333333333333333333333333333333333333333333333333333 +4420 3333333333333333333333333333333333333333333333333333333 +4421 3333333333333333333333333333333333333333333333333333333 +4422 3333333333333333333333333333333333333333333333333333333 +4423 3333333333333333333333333333333333333333333333333333333 +4424 3333333333333333333333333333333333333333333333333333333 +4425 3333333333333333333333333333333333333333333333333333333 +4426 3333333333333333333333333333333333333333333333333333333 +4427 3333333333333333333333333333333333333333333333333333333 +4428 3333333333333333333333333333333333333333333333333333333 +4429 3333333333333333333333333333333333333333333333333333333 +4430 3333333333333333333333333333333333333333333333333333333 +4431 3333333333333333333333333333333333333333333333333333333 +4432 3333333333333333333333333333333333333333333333333333333 +4433 3333333333333333333333333333333333333333333333333333333 +4434 3333333333333333333333333333333333333333333333333333333 +4435 3333333333333333333333333333333333333333333333333333333 +4436 3333333333333333333333333333333333333333333333333333333 +4437 3333333333333333333333333333333333333333333333333333333 +4438 3333333333333333333333333333333333333333333333333333333 +4439 3333333333333333333333333333333333333333333333333333333 +4440 3333333333333333333333333333333333333333333333333333333 +4441 3333333333333333333333333333333333333333333333333333333 +4442 3333333333333333333333333333333333333333333333333333333 +4443 3333333333333333333333333333333333333333333333333333333 +4444 3333333333333333333333333333333333333333333333333333333 +4445 3333333333333333333333333333333333333333333333333333333 +4446 3333333333333333333333333333333333333333333333333333333 +4447 3333333333333333333333333333333333333333333333333333333 +4448 3333333333333333333333333333333333333333333333333333333 +4449 3333333333333333333333333333333333333333333333333333333 +4450 3333333333333333333333333333333333333333333333333333333 +4451 3333333333333333333333333333333333333333333333333333333 +4452 3333333333333333333333333333333333333333333333333333333 +4453 3333333333333333333333333333333333333333333333333333333 +4454 3333333333333333333333333333333333333333333333333333333 +4455 3333333333333333333333333333333333333333333333333333333 +4456 3333333333333333333333333333333333333333333333333333333 +4457 3333333333333333333333333333333333333333333333333333333 +4458 3333333333333333333333333333333333333333333333333333333 +4459 3333333333333333333333333333333333333333333333333333333 +4460 3333333333333333333333333333333333333333333333333333333 +4461 3333333333333333333333333333333333333333333333333333333 +4462 3333333333333333333333333333333333333333333333333333333 +4463 3333333333333333333333333333333333333333333333333333333 +4464 3333333333333333333333333333333333333333333333333333333 +4465 3333333333333333333333333333333333333333333333333333333 +4466 3333333333333333333333333333333333333333333333333333333 +4467 3333333333333333333333333333333333333333333333333333333 +4468 3333333333333333333333333333333333333333333333333333333 +4469 3333333333333333333333333333333333333333333333333333333 +4470 3333333333333333333333333333333333333333333333333333333 +4471 3333333333333333333333333333333333333333333333333333333 +4472 3333333333333333333333333333333333333333333333333333333 +4473 3333333333333333333333333333333333333333333333333333333 +4474 3333333333333333333333333333333333333333333333333333333 +4475 3333333333333333333333333333333333333333333333333333333 +4476 3333333333333333333333333333333333333333333333333333333 +4477 3333333333333333333333333333333333333333333333333333333 +4478 3333333333333333333333333333333333333333333333333333333 +4479 3333333333333333333333333333333333333333333333333333333 +4480 3333333333333333333333333333333333333333333333333333333 +4481 3333333333333333333333333333333333333333333333333333333 +4482 3333333333333333333333333333333333333333333333333333333 +4483 3333333333333333333333333333333333333333333333333333333 +4484 3333333333333333333333333333333333333333333333333333333 +4485 3333333333333333333333333333333333333333333333333333333 +4486 3333333333333333333333333333333333333333333333333333333 +4487 3333333333333333333333333333333333333333333333333333333 +4488 3333333333333333333333333333333333333333333333333333333 +4489 3333333333333333333333333333333333333333333333333333333 +4490 3333333333333333333333333333333333333333333333333333333 +4491 3333333333333333333333333333333333333333333333333333333 +4492 3333333333333333333333333333333333333333333333333333333 +4493 3333333333333333333333333333333333333333333333333333333 +4494 3333333333333333333333333333333333333333333333333333333 +4495 3333333333333333333333333333333333333333333333333333333 +4496 3333333333333333333333333333333333333333333333333333333 +4497 3333333333333333333333333333333333333333333333333333333 +4498 3333333333333333333333333333333333333333333333333333333 +4499 3333333333333333333333333333333333333333333333333333333 +4500 3333333333333333333333333333333333333333333333333333333 +4501 3333333333333333333333333333333333333333333333333333333 +4502 3333333333333333333333333333333333333333333333333333333 +4503 3333333333333333333333333333333333333333333333333333333 +4504 3333333333333333333333333333333333333333333333333333333 +4505 3333333333333333333333333333333333333333333333333333333 +4506 3333333333333333333333333333333333333333333333333333333 +4507 3333333333333333333333333333333333333333333333333333333 +4508 3333333333333333333333333333333333333333333333333333333 +4509 3333333333333333333333333333333333333333333333333333333 +4510 3333333333333333333333333333333333333333333333333333333 +4511 3333333333333333333333333333333333333333333333333333333 +4512 3333333333333333333333333333333333333333333333333333333 +4513 3333333333333333333333333333333333333333333333333333333 +4514 3333333333333333333333333333333333333333333333333333333 +4515 3333333333333333333333333333333333333333333333333333333 +4516 3333333333333333333333333333333333333333333333333333333 +4517 3333333333333333333333333333333333333333333333333333333 +4518 3333333333333333333333333333333333333333333333333333333 +4519 3333333333333333333333333333333333333333333333333333333 +4520 3333333333333333333333333333333333333333333333333333333 +4521 3333333333333333333333333333333333333333333333333333333 +4522 3333333333333333333333333333333333333333333333333333333 +4523 3333333333333333333333333333333333333333333333333333333 +4524 3333333333333333333333333333333333333333333333333333333 +4525 3333333333333333333333333333333333333333333333333333333 +4526 3333333333333333333333333333333333333333333333333333333 +4527 3333333333333333333333333333333333333333333333333333333 +4528 3333333333333333333333333333333333333333333333333333333 +4529 3333333333333333333333333333333333333333333333333333333 +4530 3333333333333333333333333333333333333333333333333333333 +4531 3333333333333333333333333333333333333333333333333333333 +4532 3333333333333333333333333333333333333333333333333333333 +4533 3333333333333333333333333333333333333333333333333333333 +4534 3333333333333333333333333333333333333333333333333333333 +4535 3333333333333333333333333333333333333333333333333333333 +4536 3333333333333333333333333333333333333333333333333333333 +4537 3333333333333333333333333333333333333333333333333333333 +4538 3333333333333333333333333333333333333333333333333333333 +4539 3333333333333333333333333333333333333333333333333333333 +4540 3333333333333333333333333333333333333333333333333333333 +4541 3333333333333333333333333333333333333333333333333333333 +4542 3333333333333333333333333333333333333333333333333333333 +4543 3333333333333333333333333333333333333333333333333333333 +4544 3333333333333333333333333333333333333333333333333333333 +4545 3333333333333333333333333333333333333333333333333333333 +4546 3333333333333333333333333333333333333333333333333333333 +4547 3333333333333333333333333333333333333333333333333333333 +4548 3333333333333333333333333333333333333333333333333333333 +4549 3333333333333333333333333333333333333333333333333333333 +4550 3333333333333333333333333333333333333333333333333333333 +4551 3333333333333333333333333333333333333333333333333333333 +4552 3333333333333333333333333333333333333333333333333333333 +4553 3333333333333333333333333333333333333333333333333333333 +4554 3333333333333333333333333333333333333333333333333333333 +4555 3333333333333333333333333333333333333333333333333333333 +4556 3333333333333333333333333333333333333333333333333333333 +4557 3333333333333333333333333333333333333333333333333333333 +4558 3333333333333333333333333333333333333333333333333333333 +4559 3333333333333333333333333333333333333333333333333333333 +4560 3333333333333333333333333333333333333333333333333333333 +4561 3333333333333333333333333333333333333333333333333333333 +4562 3333333333333333333333333333333333333333333333333333333 +4563 3333333333333333333333333333333333333333333333333333333 +4564 3333333333333333333333333333333333333333333333333333333 +4565 3333333333333333333333333333333333333333333333333333333 +4566 3333333333333333333333333333333333333333333333333333333 +4567 3333333333333333333333333333333333333333333333333333333 +4568 3333333333333333333333333333333333333333333333333333333 +4569 3333333333333333333333333333333333333333333333333333333 +4570 3333333333333333333333333333333333333333333333333333333 +4571 3333333333333333333333333333333333333333333333333333333 +4572 3333333333333333333333333333333333333333333333333333333 +4573 3333333333333333333333333333333333333333333333333333333 +4574 3333333333333333333333333333333333333333333333333333333 +4575 3333333333333333333333333333333333333333333333333333333 +4576 3333333333333333333333333333333333333333333333333333333 +4577 3333333333333333333333333333333333333333333333333333333 +4578 3333333333333333333333333333333333333333333333333333333 +4579 3333333333333333333333333333333333333333333333333333333 +4580 3333333333333333333333333333333333333333333333333333333 +4581 3333333333333333333333333333333333333333333333333333333 +4582 3333333333333333333333333333333333333333333333333333333 +4583 3333333333333333333333333333333333333333333333333333333 +4584 3333333333333333333333333333333333333333333333333333333 +4585 3333333333333333333333333333333333333333333333333333333 +4586 3333333333333333333333333333333333333333333333333333333 +4587 3333333333333333333333333333333333333333333333333333333 +4588 3333333333333333333333333333333333333333333333333333333 +4589 3333333333333333333333333333333333333333333333333333333 +4590 3333333333333333333333333333333333333333333333333333333 +4591 3333333333333333333333333333333333333333333333333333333 +4592 3333333333333333333333333333333333333333333333333333333 +4593 3333333333333333333333333333333333333333333333333333333 +4594 3333333333333333333333333333333333333333333333333333333 +4595 3333333333333333333333333333333333333333333333333333333 +4596 3333333333333333333333333333333333333333333333333333333 +4597 3333333333333333333333333333333333333333333333333333333 +4598 3333333333333333333333333333333333333333333333333333333 +4599 3333333333333333333333333333333333333333333333333333333 +4600 3333333333333333333333333333333333333333333333333333333 +4601 3333333333333333333333333333333333333333333333333333333 +4602 3333333333333333333333333333333333333333333333333333333 +4603 3333333333333333333333333333333333333333333333333333333 +4604 3333333333333333333333333333333333333333333333333333333 +4605 3333333333333333333333333333333333333333333333333333333 +4606 3333333333333333333333333333333333333333333333333333333 +4607 3333333333333333333333333333333333333333333333333333333 +4608 3333333333333333333333333333333333333333333333333333333 +4609 3333333333333333333333333333333333333333333333333333333 +4610 3333333333333333333333333333333333333333333333333333333 +4611 3333333333333333333333333333333333333333333333333333333 +4612 3333333333333333333333333333333333333333333333333333333 +4613 3333333333333333333333333333333333333333333333333333333 +4614 3333333333333333333333333333333333333333333333333333333 +4615 3333333333333333333333333333333333333333333333333333333 +4616 3333333333333333333333333333333333333333333333333333333 +4617 3333333333333333333333333333333333333333333333333333333 +4618 3333333333333333333333333333333333333333333333333333333 +4619 3333333333333333333333333333333333333333333333333333333 +4620 3333333333333333333333333333333333333333333333333333333 +4621 3333333333333333333333333333333333333333333333333333333 +4622 3333333333333333333333333333333333333333333333333333333 +4623 3333333333333333333333333333333333333333333333333333333 +4624 3333333333333333333333333333333333333333333333333333333 +4625 3333333333333333333333333333333333333333333333333333333 +4626 3333333333333333333333333333333333333333333333333333333 +4627 3333333333333333333333333333333333333333333333333333333 +4628 3333333333333333333333333333333333333333333333333333333 +4629 3333333333333333333333333333333333333333333333333333333 +4630 3333333333333333333333333333333333333333333333333333333 +4631 3333333333333333333333333333333333333333333333333333333 +4632 3333333333333333333333333333333333333333333333333333333 +4633 3333333333333333333333333333333333333333333333333333333 +4634 3333333333333333333333333333333333333333333333333333333 +4635 3333333333333333333333333333333333333333333333333333333 +4636 3333333333333333333333333333333333333333333333333333333 +4637 3333333333333333333333333333333333333333333333333333333 +4638 3333333333333333333333333333333333333333333333333333333 +4639 3333333333333333333333333333333333333333333333333333333 +4640 3333333333333333333333333333333333333333333333333333333 +4641 3333333333333333333333333333333333333333333333333333333 +4642 3333333333333333333333333333333333333333333333333333333 +4643 3333333333333333333333333333333333333333333333333333333 +4644 3333333333333333333333333333333333333333333333333333333 +4645 3333333333333333333333333333333333333333333333333333333 +4646 3333333333333333333333333333333333333333333333333333333 +4647 3333333333333333333333333333333333333333333333333333333 +4648 3333333333333333333333333333333333333333333333333333333 +4649 3333333333333333333333333333333333333333333333333333333 +4650 3333333333333333333333333333333333333333333333333333333 +4651 3333333333333333333333333333333333333333333333333333333 +4652 3333333333333333333333333333333333333333333333333333333 +4653 3333333333333333333333333333333333333333333333333333333 +4654 3333333333333333333333333333333333333333333333333333333 +4655 3333333333333333333333333333333333333333333333333333333 +4656 3333333333333333333333333333333333333333333333333333333 +4657 3333333333333333333333333333333333333333333333333333333 +4658 3333333333333333333333333333333333333333333333333333333 +4659 3333333333333333333333333333333333333333333333333333333 +4660 3333333333333333333333333333333333333333333333333333333 +4661 3333333333333333333333333333333333333333333333333333333 +4662 3333333333333333333333333333333333333333333333333333333 +4663 3333333333333333333333333333333333333333333333333333333 +4664 3333333333333333333333333333333333333333333333333333333 +4665 3333333333333333333333333333333333333333333333333333333 +4666 3333333333333333333333333333333333333333333333333333333 +4667 3333333333333333333333333333333333333333333333333333333 +4668 3333333333333333333333333333333333333333333333333333333 +4669 3333333333333333333333333333333333333333333333333333333 +4670 3333333333333333333333333333333333333333333333333333333 +4671 3333333333333333333333333333333333333333333333333333333 +4672 3333333333333333333333333333333333333333333333333333333 +4673 3333333333333333333333333333333333333333333333333333333 +4674 3333333333333333333333333333333333333333333333333333333 +4675 3333333333333333333333333333333333333333333333333333333 +4676 3333333333333333333333333333333333333333333333333333333 +4677 3333333333333333333333333333333333333333333333333333333 +4678 3333333333333333333333333333333333333333333333333333333 +4679 3333333333333333333333333333333333333333333333333333333 +4680 3333333333333333333333333333333333333333333333333333333 +4681 3333333333333333333333333333333333333333333333333333333 +4682 3333333333333333333333333333333333333333333333333333333 +4683 3333333333333333333333333333333333333333333333333333333 +4684 3333333333333333333333333333333333333333333333333333333 +4685 3333333333333333333333333333333333333333333333333333333 +4686 3333333333333333333333333333333333333333333333333333333 +4687 3333333333333333333333333333333333333333333333333333333 +4688 3333333333333333333333333333333333333333333333333333333 +4689 3333333333333333333333333333333333333333333333333333333 +4690 3333333333333333333333333333333333333333333333333333333 +4691 3333333333333333333333333333333333333333333333333333333 +4692 3333333333333333333333333333333333333333333333333333333 +4693 3333333333333333333333333333333333333333333333333333333 +4694 3333333333333333333333333333333333333333333333333333333 +4695 3333333333333333333333333333333333333333333333333333333 +4696 3333333333333333333333333333333333333333333333333333333 +4697 3333333333333333333333333333333333333333333333333333333 +4698 3333333333333333333333333333333333333333333333333333333 +4699 3333333333333333333333333333333333333333333333333333333 +4700 3333333333333333333333333333333333333333333333333333333 +4701 3333333333333333333333333333333333333333333333333333333 +4702 3333333333333333333333333333333333333333333333333333333 +4703 3333333333333333333333333333333333333333333333333333333 +4704 3333333333333333333333333333333333333333333333333333333 +4705 3333333333333333333333333333333333333333333333333333333 +4706 3333333333333333333333333333333333333333333333333333333 +4707 3333333333333333333333333333333333333333333333333333333 +4708 3333333333333333333333333333333333333333333333333333333 +4709 3333333333333333333333333333333333333333333333333333333 +4710 3333333333333333333333333333333333333333333333333333333 +4711 3333333333333333333333333333333333333333333333333333333 +4712 3333333333333333333333333333333333333333333333333333333 +4713 3333333333333333333333333333333333333333333333333333333 +4714 3333333333333333333333333333333333333333333333333333333 +4715 3333333333333333333333333333333333333333333333333333333 +4716 3333333333333333333333333333333333333333333333333333333 +4717 3333333333333333333333333333333333333333333333333333333 +4718 3333333333333333333333333333333333333333333333333333333 +4719 3333333333333333333333333333333333333333333333333333333 +4720 3333333333333333333333333333333333333333333333333333333 +4721 3333333333333333333333333333333333333333333333333333333 +4722 3333333333333333333333333333333333333333333333333333333 +4723 3333333333333333333333333333333333333333333333333333333 +4724 3333333333333333333333333333333333333333333333333333333 +4725 3333333333333333333333333333333333333333333333333333333 +4726 3333333333333333333333333333333333333333333333333333333 +4727 3333333333333333333333333333333333333333333333333333333 +4728 3333333333333333333333333333333333333333333333333333333 +4729 3333333333333333333333333333333333333333333333333333333 +4730 3333333333333333333333333333333333333333333333333333333 +4731 3333333333333333333333333333333333333333333333333333333 +4732 3333333333333333333333333333333333333333333333333333333 +4733 3333333333333333333333333333333333333333333333333333333 +4734 3333333333333333333333333333333333333333333333333333333 +4735 3333333333333333333333333333333333333333333333333333333 +4736 3333333333333333333333333333333333333333333333333333333 +4737 3333333333333333333333333333333333333333333333333333333 +4738 3333333333333333333333333333333333333333333333333333333 +4739 3333333333333333333333333333333333333333333333333333333 +4740 3333333333333333333333333333333333333333333333333333333 +4741 3333333333333333333333333333333333333333333333333333333 +4742 3333333333333333333333333333333333333333333333333333333 +4743 3333333333333333333333333333333333333333333333333333333 +4744 3333333333333333333333333333333333333333333333333333333 +4745 3333333333333333333333333333333333333333333333333333333 +4746 3333333333333333333333333333333333333333333333333333333 +4747 3333333333333333333333333333333333333333333333333333333 +4748 3333333333333333333333333333333333333333333333333333333 +4749 3333333333333333333333333333333333333333333333333333333 +4750 3333333333333333333333333333333333333333333333333333333 +4751 3333333333333333333333333333333333333333333333333333333 +4752 3333333333333333333333333333333333333333333333333333333 +4753 3333333333333333333333333333333333333333333333333333333 +4754 3333333333333333333333333333333333333333333333333333333 +4755 3333333333333333333333333333333333333333333333333333333 +4756 3333333333333333333333333333333333333333333333333333333 +4757 3333333333333333333333333333333333333333333333333333333 +4758 3333333333333333333333333333333333333333333333333333333 +4759 3333333333333333333333333333333333333333333333333333333 +4760 3333333333333333333333333333333333333333333333333333333 +4761 3333333333333333333333333333333333333333333333333333333 +4762 3333333333333333333333333333333333333333333333333333333 +4763 3333333333333333333333333333333333333333333333333333333 +4764 3333333333333333333333333333333333333333333333333333333 +4765 3333333333333333333333333333333333333333333333333333333 +4766 3333333333333333333333333333333333333333333333333333333 +4767 3333333333333333333333333333333333333333333333333333333 +4768 3333333333333333333333333333333333333333333333333333333 +4769 3333333333333333333333333333333333333333333333333333333 +4770 3333333333333333333333333333333333333333333333333333333 +4771 3333333333333333333333333333333333333333333333333333333 +4772 3333333333333333333333333333333333333333333333333333333 +4773 3333333333333333333333333333333333333333333333333333333 +4774 3333333333333333333333333333333333333333333333333333333 +4775 3333333333333333333333333333333333333333333333333333333 +4776 3333333333333333333333333333333333333333333333333333333 +4777 3333333333333333333333333333333333333333333333333333333 +4778 3333333333333333333333333333333333333333333333333333333 +4779 3333333333333333333333333333333333333333333333333333333 +4780 3333333333333333333333333333333333333333333333333333333 +4781 3333333333333333333333333333333333333333333333333333333 +4782 3333333333333333333333333333333333333333333333333333333 +4783 3333333333333333333333333333333333333333333333333333333 +4784 3333333333333333333333333333333333333333333333333333333 +4785 3333333333333333333333333333333333333333333333333333333 +4786 3333333333333333333333333333333333333333333333333333333 +4787 3333333333333333333333333333333333333333333333333333333 +4788 3333333333333333333333333333333333333333333333333333333 +4789 3333333333333333333333333333333333333333333333333333333 +4790 3333333333333333333333333333333333333333333333333333333 +4791 3333333333333333333333333333333333333333333333333333333 +4792 3333333333333333333333333333333333333333333333333333333 +4793 3333333333333333333333333333333333333333333333333333333 +4794 3333333333333333333333333333333333333333333333333333333 +4795 3333333333333333333333333333333333333333333333333333333 +4796 3333333333333333333333333333333333333333333333333333333 +4797 3333333333333333333333333333333333333333333333333333333 +4798 3333333333333333333333333333333333333333333333333333333 +4799 3333333333333333333333333333333333333333333333333333333 +4800 3333333333333333333333333333333333333333333333333333333 +4801 3333333333333333333333333333333333333333333333333333333 +4802 3333333333333333333333333333333333333333333333333333333 +4803 3333333333333333333333333333333333333333333333333333333 +4804 3333333333333333333333333333333333333333333333333333333 +4805 3333333333333333333333333333333333333333333333333333333 +4806 3333333333333333333333333333333333333333333333333333333 +4807 3333333333333333333333333333333333333333333333333333333 +4808 3333333333333333333333333333333333333333333333333333333 +4809 3333333333333333333333333333333333333333333333333333333 +4810 3333333333333333333333333333333333333333333333333333333 +4811 3333333333333333333333333333333333333333333333333333333 +4812 3333333333333333333333333333333333333333333333333333333 +4813 3333333333333333333333333333333333333333333333333333333 +4814 3333333333333333333333333333333333333333333333333333333 +4815 3333333333333333333333333333333333333333333333333333333 +4816 3333333333333333333333333333333333333333333333333333333 +4817 3333333333333333333333333333333333333333333333333333333 +4818 3333333333333333333333333333333333333333333333333333333 +4819 3333333333333333333333333333333333333333333333333333333 +4820 3333333333333333333333333333333333333333333333333333333 +4821 3333333333333333333333333333333333333333333333333333333 +4822 3333333333333333333333333333333333333333333333333333333 +4823 3333333333333333333333333333333333333333333333333333333 +4824 3333333333333333333333333333333333333333333333333333333 +4825 3333333333333333333333333333333333333333333333333333333 +4826 3333333333333333333333333333333333333333333333333333333 +4827 3333333333333333333333333333333333333333333333333333333 +4828 3333333333333333333333333333333333333333333333333333333 +4829 3333333333333333333333333333333333333333333333333333333 +4830 3333333333333333333333333333333333333333333333333333333 +4831 3333333333333333333333333333333333333333333333333333333 +4832 3333333333333333333333333333333333333333333333333333333 +4833 3333333333333333333333333333333333333333333333333333333 +4834 3333333333333333333333333333333333333333333333333333333 +4835 3333333333333333333333333333333333333333333333333333333 +4836 3333333333333333333333333333333333333333333333333333333 +4837 3333333333333333333333333333333333333333333333333333333 +4838 3333333333333333333333333333333333333333333333333333333 +4839 3333333333333333333333333333333333333333333333333333333 +4840 3333333333333333333333333333333333333333333333333333333 +4841 3333333333333333333333333333333333333333333333333333333 +4842 3333333333333333333333333333333333333333333333333333333 +4843 3333333333333333333333333333333333333333333333333333333 +4844 3333333333333333333333333333333333333333333333333333333 +4845 3333333333333333333333333333333333333333333333333333333 +4846 3333333333333333333333333333333333333333333333333333333 +4847 3333333333333333333333333333333333333333333333333333333 +4848 3333333333333333333333333333333333333333333333333333333 +4849 3333333333333333333333333333333333333333333333333333333 +4850 3333333333333333333333333333333333333333333333333333333 +4851 3333333333333333333333333333333333333333333333333333333 +4852 3333333333333333333333333333333333333333333333333333333 +4853 3333333333333333333333333333333333333333333333333333333 +4854 3333333333333333333333333333333333333333333333333333333 +4855 3333333333333333333333333333333333333333333333333333333 +4856 3333333333333333333333333333333333333333333333333333333 +4857 3333333333333333333333333333333333333333333333333333333 +4858 3333333333333333333333333333333333333333333333333333333 +4859 3333333333333333333333333333333333333333333333333333333 +4860 3333333333333333333333333333333333333333333333333333333 +4861 3333333333333333333333333333333333333333333333333333333 +4862 3333333333333333333333333333333333333333333333333333333 +4863 3333333333333333333333333333333333333333333333333333333 +4864 3333333333333333333333333333333333333333333333333333333 +4865 3333333333333333333333333333333333333333333333333333333 +4866 3333333333333333333333333333333333333333333333333333333 +4867 3333333333333333333333333333333333333333333333333333333 +4868 3333333333333333333333333333333333333333333333333333333 +4869 3333333333333333333333333333333333333333333333333333333 +4870 3333333333333333333333333333333333333333333333333333333 +4871 3333333333333333333333333333333333333333333333333333333 +4872 3333333333333333333333333333333333333333333333333333333 +4873 3333333333333333333333333333333333333333333333333333333 +4874 3333333333333333333333333333333333333333333333333333333 +4875 3333333333333333333333333333333333333333333333333333333 +4876 3333333333333333333333333333333333333333333333333333333 +4877 3333333333333333333333333333333333333333333333333333333 +4878 3333333333333333333333333333333333333333333333333333333 +4879 3333333333333333333333333333333333333333333333333333333 +4880 3333333333333333333333333333333333333333333333333333333 +4881 3333333333333333333333333333333333333333333333333333333 +4882 3333333333333333333333333333333333333333333333333333333 +4883 3333333333333333333333333333333333333333333333333333333 +4884 3333333333333333333333333333333333333333333333333333333 +4885 3333333333333333333333333333333333333333333333333333333 +4886 3333333333333333333333333333333333333333333333333333333 +4887 3333333333333333333333333333333333333333333333333333333 +4888 3333333333333333333333333333333333333333333333333333333 +4889 3333333333333333333333333333333333333333333333333333333 +4890 3333333333333333333333333333333333333333333333333333333 +4891 3333333333333333333333333333333333333333333333333333333 +4892 3333333333333333333333333333333333333333333333333333333 +4893 3333333333333333333333333333333333333333333333333333333 +4894 3333333333333333333333333333333333333333333333333333333 +4895 3333333333333333333333333333333333333333333333333333333 +4896 3333333333333333333333333333333333333333333333333333333 +4897 3333333333333333333333333333333333333333333333333333333 +4898 3333333333333333333333333333333333333333333333333333333 +4899 3333333333333333333333333333333333333333333333333333333 +4900 3333333333333333333333333333333333333333333333333333333 +4901 3333333333333333333333333333333333333333333333333333333 +4902 3333333333333333333333333333333333333333333333333333333 +4903 3333333333333333333333333333333333333333333333333333333 +4904 3333333333333333333333333333333333333333333333333333333 +4905 3333333333333333333333333333333333333333333333333333333 +4906 3333333333333333333333333333333333333333333333333333333 +4907 3333333333333333333333333333333333333333333333333333333 +4908 3333333333333333333333333333333333333333333333333333333 +4909 3333333333333333333333333333333333333333333333333333333 +4910 3333333333333333333333333333333333333333333333333333333 +4911 3333333333333333333333333333333333333333333333333333333 +4912 3333333333333333333333333333333333333333333333333333333 +4913 3333333333333333333333333333333333333333333333333333333 +4914 3333333333333333333333333333333333333333333333333333333 +4915 3333333333333333333333333333333333333333333333333333333 +4916 3333333333333333333333333333333333333333333333333333333 +4917 3333333333333333333333333333333333333333333333333333333 +4918 3333333333333333333333333333333333333333333333333333333 +4919 3333333333333333333333333333333333333333333333333333333 +4920 3333333333333333333333333333333333333333333333333333333 +4921 3333333333333333333333333333333333333333333333333333333 +4922 3333333333333333333333333333333333333333333333333333333 +4923 3333333333333333333333333333333333333333333333333333333 +4924 3333333333333333333333333333333333333333333333333333333 +4925 3333333333333333333333333333333333333333333333333333333 +4926 3333333333333333333333333333333333333333333333333333333 +4927 3333333333333333333333333333333333333333333333333333333 +4928 3333333333333333333333333333333333333333333333333333333 +4929 3333333333333333333333333333333333333333333333333333333 +4930 3333333333333333333333333333333333333333333333333333333 +4931 3333333333333333333333333333333333333333333333333333333 +4932 3333333333333333333333333333333333333333333333333333333 +4933 3333333333333333333333333333333333333333333333333333333 +4934 3333333333333333333333333333333333333333333333333333333 +4935 3333333333333333333333333333333333333333333333333333333 +4936 3333333333333333333333333333333333333333333333333333333 +4937 3333333333333333333333333333333333333333333333333333333 +4938 3333333333333333333333333333333333333333333333333333333 +4939 3333333333333333333333333333333333333333333333333333333 +4940 3333333333333333333333333333333333333333333333333333333 +4941 3333333333333333333333333333333333333333333333333333333 +4942 3333333333333333333333333333333333333333333333333333333 +4943 3333333333333333333333333333333333333333333333333333333 +4944 3333333333333333333333333333333333333333333333333333333 +4945 3333333333333333333333333333333333333333333333333333333 +4946 3333333333333333333333333333333333333333333333333333333 +4947 3333333333333333333333333333333333333333333333333333333 +4948 3333333333333333333333333333333333333333333333333333333 +4949 3333333333333333333333333333333333333333333333333333333 +4950 3333333333333333333333333333333333333333333333333333333 +4951 3333333333333333333333333333333333333333333333333333333 +4952 3333333333333333333333333333333333333333333333333333333 +4953 3333333333333333333333333333333333333333333333333333333 +4954 3333333333333333333333333333333333333333333333333333333 +4955 3333333333333333333333333333333333333333333333333333333 +4956 3333333333333333333333333333333333333333333333333333333 +4957 3333333333333333333333333333333333333333333333333333333 +4958 3333333333333333333333333333333333333333333333333333333 +4959 3333333333333333333333333333333333333333333333333333333 +4960 3333333333333333333333333333333333333333333333333333333 +4961 3333333333333333333333333333333333333333333333333333333 +4962 3333333333333333333333333333333333333333333333333333333 +4963 3333333333333333333333333333333333333333333333333333333 +4964 3333333333333333333333333333333333333333333333333333333 +4965 3333333333333333333333333333333333333333333333333333333 +4966 3333333333333333333333333333333333333333333333333333333 +4967 3333333333333333333333333333333333333333333333333333333 +4968 3333333333333333333333333333333333333333333333333333333 +4969 3333333333333333333333333333333333333333333333333333333 +4970 3333333333333333333333333333333333333333333333333333333 +4971 3333333333333333333333333333333333333333333333333333333 +4972 3333333333333333333333333333333333333333333333333333333 +4973 3333333333333333333333333333333333333333333333333333333 +4974 3333333333333333333333333333333333333333333333333333333 +4975 3333333333333333333333333333333333333333333333333333333 +4976 3333333333333333333333333333333333333333333333333333333 +4977 3333333333333333333333333333333333333333333333333333333 +4978 3333333333333333333333333333333333333333333333333333333 +4979 3333333333333333333333333333333333333333333333333333333 +4980 3333333333333333333333333333333333333333333333333333333 +4981 3333333333333333333333333333333333333333333333333333333 +4982 3333333333333333333333333333333333333333333333333333333 +4983 3333333333333333333333333333333333333333333333333333333 +4984 3333333333333333333333333333333333333333333333333333333 +4985 3333333333333333333333333333333333333333333333333333333 +4986 3333333333333333333333333333333333333333333333333333333 +4987 3333333333333333333333333333333333333333333333333333333 +4988 3333333333333333333333333333333333333333333333333333333 +4989 3333333333333333333333333333333333333333333333333333333 +4990 3333333333333333333333333333333333333333333333333333333 +4991 3333333333333333333333333333333333333333333333333333333 +4992 3333333333333333333333333333333333333333333333333333333 +4993 3333333333333333333333333333333333333333333333333333333 +4994 3333333333333333333333333333333333333333333333333333333 +4995 3333333333333333333333333333333333333333333333333333333 +4996 3333333333333333333333333333333333333333333333333333333 +4997 3333333333333333333333333333333333333333333333333333333 +4998 3333333333333333333333333333333333333333333333333333333 +4999 3333333333333333333333333333333333333333333333333333333 +5000 3333333333333333333333333333333333333333333333333333333 +5001 3333333333333333333333333333333333333333333333333333333 +5002 3333333333333333333333333333333333333333333333333333333 +5003 3333333333333333333333333333333333333333333333333333333 +5004 3333333333333333333333333333333333333333333333333333333 +5005 3333333333333333333333333333333333333333333333333333333 +5006 3333333333333333333333333333333333333333333333333333333 +5007 3333333333333333333333333333333333333333333333333333333 +5008 3333333333333333333333333333333333333333333333333333333 +5009 3333333333333333333333333333333333333333333333333333333 +5010 3333333333333333333333333333333333333333333333333333333 +5011 3333333333333333333333333333333333333333333333333333333 +5012 3333333333333333333333333333333333333333333333333333333 +5013 3333333333333333333333333333333333333333333333333333333 +5014 3333333333333333333333333333333333333333333333333333333 +5015 3333333333333333333333333333333333333333333333333333333 +5016 3333333333333333333333333333333333333333333333333333333 +5017 3333333333333333333333333333333333333333333333333333333 +5018 3333333333333333333333333333333333333333333333333333333 +5019 3333333333333333333333333333333333333333333333333333333 +5020 3333333333333333333333333333333333333333333333333333333 +5021 3333333333333333333333333333333333333333333333333333333 +5022 3333333333333333333333333333333333333333333333333333333 +5023 3333333333333333333333333333333333333333333333333333333 +5024 3333333333333333333333333333333333333333333333333333333 +5025 3333333333333333333333333333333333333333333333333333333 +5026 3333333333333333333333333333333333333333333333333333333 +5027 3333333333333333333333333333333333333333333333333333333 +5028 3333333333333333333333333333333333333333333333333333333 +5029 3333333333333333333333333333333333333333333333333333333 +5030 3333333333333333333333333333333333333333333333333333333 +5031 3333333333333333333333333333333333333333333333333333333 +5032 3333333333333333333333333333333333333333333333333333333 +5033 3333333333333333333333333333333333333333333333333333333 +5034 3333333333333333333333333333333333333333333333333333333 +5035 3333333333333333333333333333333333333333333333333333333 +5036 3333333333333333333333333333333333333333333333333333333 +5037 3333333333333333333333333333333333333333333333333333333 +5038 3333333333333333333333333333333333333333333333333333333 +5039 3333333333333333333333333333333333333333333333333333333 +5040 3333333333333333333333333333333333333333333333333333333 +5041 3333333333333333333333333333333333333333333333333333333 +5042 3333333333333333333333333333333333333333333333333333333 +5043 3333333333333333333333333333333333333333333333333333333 +5044 3333333333333333333333333333333333333333333333333333333 +5045 3333333333333333333333333333333333333333333333333333333 +5046 3333333333333333333333333333333333333333333333333333333 +5047 3333333333333333333333333333333333333333333333333333333 +5048 3333333333333333333333333333333333333333333333333333333 +5049 3333333333333333333333333333333333333333333333333333333 +5050 3333333333333333333333333333333333333333333333333333333 +5051 3333333333333333333333333333333333333333333333333333333 +5052 3333333333333333333333333333333333333333333333333333333 +5053 3333333333333333333333333333333333333333333333333333333 +5054 3333333333333333333333333333333333333333333333333333333 +5055 3333333333333333333333333333333333333333333333333333333 +5056 3333333333333333333333333333333333333333333333333333333 +5057 3333333333333333333333333333333333333333333333333333333 +5058 3333333333333333333333333333333333333333333333333333333 +5059 3333333333333333333333333333333333333333333333333333333 +5060 3333333333333333333333333333333333333333333333333333333 +5061 3333333333333333333333333333333333333333333333333333333 +5062 3333333333333333333333333333333333333333333333333333333 +5063 3333333333333333333333333333333333333333333333333333333 +5064 3333333333333333333333333333333333333333333333333333333 +5065 3333333333333333333333333333333333333333333333333333333 +5066 3333333333333333333333333333333333333333333333333333333 +5067 3333333333333333333333333333333333333333333333333333333 +5068 3333333333333333333333333333333333333333333333333333333 +5069 3333333333333333333333333333333333333333333333333333333 +5070 3333333333333333333333333333333333333333333333333333333 +5071 3333333333333333333333333333333333333333333333333333333 +5072 3333333333333333333333333333333333333333333333333333333 +5073 3333333333333333333333333333333333333333333333333333333 +5074 3333333333333333333333333333333333333333333333333333333 +5075 3333333333333333333333333333333333333333333333333333333 +5076 3333333333333333333333333333333333333333333333333333333 +5077 3333333333333333333333333333333333333333333333333333333 +5078 3333333333333333333333333333333333333333333333333333333 +5079 3333333333333333333333333333333333333333333333333333333 +5080 3333333333333333333333333333333333333333333333333333333 +5081 3333333333333333333333333333333333333333333333333333333 +5082 3333333333333333333333333333333333333333333333333333333 +5083 3333333333333333333333333333333333333333333333333333333 +5084 3333333333333333333333333333333333333333333333333333333 +5085 3333333333333333333333333333333333333333333333333333333 +5086 3333333333333333333333333333333333333333333333333333333 +5087 3333333333333333333333333333333333333333333333333333333 +5088 3333333333333333333333333333333333333333333333333333333 +5089 3333333333333333333333333333333333333333333333333333333 +5090 3333333333333333333333333333333333333333333333333333333 +5091 3333333333333333333333333333333333333333333333333333333 +5092 3333333333333333333333333333333333333333333333333333333 +5093 3333333333333333333333333333333333333333333333333333333 +5094 3333333333333333333333333333333333333333333333333333333 +5095 3333333333333333333333333333333333333333333333333333333 +5096 3333333333333333333333333333333333333333333333333333333 +5097 3333333333333333333333333333333333333333333333333333333 +5098 3333333333333333333333333333333333333333333333333333333 +5099 3333333333333333333333333333333333333333333333333333333 +5100 3333333333333333333333333333333333333333333333333333333 +5101 3333333333333333333333333333333333333333333333333333333 +5102 3333333333333333333333333333333333333333333333333333333 +5103 3333333333333333333333333333333333333333333333333333333 +5104 3333333333333333333333333333333333333333333333333333333 +5105 3333333333333333333333333333333333333333333333333333333 +5106 3333333333333333333333333333333333333333333333333333333 +5107 3333333333333333333333333333333333333333333333333333333 +5108 3333333333333333333333333333333333333333333333333333333 +5109 3333333333333333333333333333333333333333333333333333333 +5110 3333333333333333333333333333333333333333333333333333333 +5111 3333333333333333333333333333333333333333333333333333333 +5112 3333333333333333333333333333333333333333333333333333333 +5113 3333333333333333333333333333333333333333333333333333333 +5114 3333333333333333333333333333333333333333333333333333333 +5115 3333333333333333333333333333333333333333333333333333333 +5116 3333333333333333333333333333333333333333333333333333333 +5117 3333333333333333333333333333333333333333333333333333333 +5118 3333333333333333333333333333333333333333333333333333333 +5119 3333333333333333333333333333333333333333333333333333333 +5120 3333333333333333333333333333333333333333333333333333333 +5121 3333333333333333333333333333333333333333333333333333333 +5122 3333333333333333333333333333333333333333333333333333333 +5123 3333333333333333333333333333333333333333333333333333333 +5124 3333333333333333333333333333333333333333333333333333333 +5125 3333333333333333333333333333333333333333333333333333333 +5126 3333333333333333333333333333333333333333333333333333333 +5127 3333333333333333333333333333333333333333333333333333333 +5128 3333333333333333333333333333333333333333333333333333333 +5129 3333333333333333333333333333333333333333333333333333333 +5130 3333333333333333333333333333333333333333333333333333333 +5131 3333333333333333333333333333333333333333333333333333333 +5132 3333333333333333333333333333333333333333333333333333333 +5133 3333333333333333333333333333333333333333333333333333333 +5134 3333333333333333333333333333333333333333333333333333333 +5135 3333333333333333333333333333333333333333333333333333333 +5136 3333333333333333333333333333333333333333333333333333333 +5137 3333333333333333333333333333333333333333333333333333333 +5138 3333333333333333333333333333333333333333333333333333333 +5139 3333333333333333333333333333333333333333333333333333333 +5140 3333333333333333333333333333333333333333333333333333333 +5141 3333333333333333333333333333333333333333333333333333333 +5142 3333333333333333333333333333333333333333333333333333333 +5143 3333333333333333333333333333333333333333333333333333333 +5144 3333333333333333333333333333333333333333333333333333333 +5145 3333333333333333333333333333333333333333333333333333333 +5146 3333333333333333333333333333333333333333333333333333333 +5147 3333333333333333333333333333333333333333333333333333333 +5148 3333333333333333333333333333333333333333333333333333333 +5149 3333333333333333333333333333333333333333333333333333333 +5150 3333333333333333333333333333333333333333333333333333333 +5151 3333333333333333333333333333333333333333333333333333333 +5152 3333333333333333333333333333333333333333333333333333333 +5153 3333333333333333333333333333333333333333333333333333333 +5154 3333333333333333333333333333333333333333333333333333333 +5155 3333333333333333333333333333333333333333333333333333333 +5156 3333333333333333333333333333333333333333333333333333333 +5157 3333333333333333333333333333333333333333333333333333333 +5158 3333333333333333333333333333333333333333333333333333333 +5159 3333333333333333333333333333333333333333333333333333333 +5160 3333333333333333333333333333333333333333333333333333333 +5161 3333333333333333333333333333333333333333333333333333333 +5162 3333333333333333333333333333333333333333333333333333333 +5163 3333333333333333333333333333333333333333333333333333333 +5164 3333333333333333333333333333333333333333333333333333333 +5165 3333333333333333333333333333333333333333333333333333333 +5166 3333333333333333333333333333333333333333333333333333333 +5167 3333333333333333333333333333333333333333333333333333333 +5168 3333333333333333333333333333333333333333333333333333333 +5169 3333333333333333333333333333333333333333333333333333333 +5170 3333333333333333333333333333333333333333333333333333333 +5171 3333333333333333333333333333333333333333333333333333333 +5172 3333333333333333333333333333333333333333333333333333333 +5173 3333333333333333333333333333333333333333333333333333333 +5174 3333333333333333333333333333333333333333333333333333333 +5175 3333333333333333333333333333333333333333333333333333333 +5176 3333333333333333333333333333333333333333333333333333333 +5177 3333333333333333333333333333333333333333333333333333333 +5178 3333333333333333333333333333333333333333333333333333333 +5179 3333333333333333333333333333333333333333333333333333333 +5180 3333333333333333333333333333333333333333333333333333333 +5181 3333333333333333333333333333333333333333333333333333333 +5182 3333333333333333333333333333333333333333333333333333333 +5183 3333333333333333333333333333333333333333333333333333333 +5184 3333333333333333333333333333333333333333333333333333333 +5185 3333333333333333333333333333333333333333333333333333333 +5186 3333333333333333333333333333333333333333333333333333333 +5187 3333333333333333333333333333333333333333333333333333333 +5188 3333333333333333333333333333333333333333333333333333333 +5189 3333333333333333333333333333333333333333333333333333333 +5190 3333333333333333333333333333333333333333333333333333333 +5191 3333333333333333333333333333333333333333333333333333333 +5192 3333333333333333333333333333333333333333333333333333333 +5193 3333333333333333333333333333333333333333333333333333333 +5194 3333333333333333333333333333333333333333333333333333333 +5195 3333333333333333333333333333333333333333333333333333333 +5196 3333333333333333333333333333333333333333333333333333333 +5197 3333333333333333333333333333333333333333333333333333333 +5198 3333333333333333333333333333333333333333333333333333333 +5199 3333333333333333333333333333333333333333333333333333333 +5200 3333333333333333333333333333333333333333333333333333333 +5201 3333333333333333333333333333333333333333333333333333333 +5202 3333333333333333333333333333333333333333333333333333333 +5203 3333333333333333333333333333333333333333333333333333333 +5204 3333333333333333333333333333333333333333333333333333333 +5205 3333333333333333333333333333333333333333333333333333333 +5206 3333333333333333333333333333333333333333333333333333333 +5207 3333333333333333333333333333333333333333333333333333333 +5208 3333333333333333333333333333333333333333333333333333333 +5209 3333333333333333333333333333333333333333333333333333333 +5210 3333333333333333333333333333333333333333333333333333333 +5211 3333333333333333333333333333333333333333333333333333333 +5212 3333333333333333333333333333333333333333333333333333333 +5213 3333333333333333333333333333333333333333333333333333333 +5214 3333333333333333333333333333333333333333333333333333333 +5215 3333333333333333333333333333333333333333333333333333333 +5216 3333333333333333333333333333333333333333333333333333333 +5217 3333333333333333333333333333333333333333333333333333333 +5218 3333333333333333333333333333333333333333333333333333333 +5219 3333333333333333333333333333333333333333333333333333333 +5220 3333333333333333333333333333333333333333333333333333333 +5221 3333333333333333333333333333333333333333333333333333333 +5222 3333333333333333333333333333333333333333333333333333333 +5223 3333333333333333333333333333333333333333333333333333333 +5224 3333333333333333333333333333333333333333333333333333333 +5225 3333333333333333333333333333333333333333333333333333333 +5226 3333333333333333333333333333333333333333333333333333333 +5227 3333333333333333333333333333333333333333333333333333333 +5228 3333333333333333333333333333333333333333333333333333333 +5229 3333333333333333333333333333333333333333333333333333333 +5230 3333333333333333333333333333333333333333333333333333333 +5231 3333333333333333333333333333333333333333333333333333333 +5232 3333333333333333333333333333333333333333333333333333333 +5233 3333333333333333333333333333333333333333333333333333333 +5234 3333333333333333333333333333333333333333333333333333333 +5235 3333333333333333333333333333333333333333333333333333333 +5236 3333333333333333333333333333333333333333333333333333333 +5237 3333333333333333333333333333333333333333333333333333333 +5238 3333333333333333333333333333333333333333333333333333333 +5239 3333333333333333333333333333333333333333333333333333333 +5240 3333333333333333333333333333333333333333333333333333333 +5241 3333333333333333333333333333333333333333333333333333333 +5242 3333333333333333333333333333333333333333333333333333333 +5243 3333333333333333333333333333333333333333333333333333333 +5244 3333333333333333333333333333333333333333333333333333333 +5245 3333333333333333333333333333333333333333333333333333333 +5246 3333333333333333333333333333333333333333333333333333333 +5247 3333333333333333333333333333333333333333333333333333333 +5248 3333333333333333333333333333333333333333333333333333333 +5249 3333333333333333333333333333333333333333333333333333333 +5250 3333333333333333333333333333333333333333333333333333333 +5251 3333333333333333333333333333333333333333333333333333333 +5252 3333333333333333333333333333333333333333333333333333333 +5253 3333333333333333333333333333333333333333333333333333333 +5254 3333333333333333333333333333333333333333333333333333333 +5255 3333333333333333333333333333333333333333333333333333333 +5256 3333333333333333333333333333333333333333333333333333333 +5257 3333333333333333333333333333333333333333333333333333333 +5258 3333333333333333333333333333333333333333333333333333333 +5259 3333333333333333333333333333333333333333333333333333333 +5260 3333333333333333333333333333333333333333333333333333333 +5261 3333333333333333333333333333333333333333333333333333333 +5262 3333333333333333333333333333333333333333333333333333333 +5263 3333333333333333333333333333333333333333333333333333333 +5264 3333333333333333333333333333333333333333333333333333333 +5265 3333333333333333333333333333333333333333333333333333333 +5266 3333333333333333333333333333333333333333333333333333333 +5267 3333333333333333333333333333333333333333333333333333333 +5268 3333333333333333333333333333333333333333333333333333333 +5269 3333333333333333333333333333333333333333333333333333333 +5270 3333333333333333333333333333333333333333333333333333333 +5271 3333333333333333333333333333333333333333333333333333333 +5272 3333333333333333333333333333333333333333333333333333333 +5273 3333333333333333333333333333333333333333333333333333333 +5274 3333333333333333333333333333333333333333333333333333333 +5275 3333333333333333333333333333333333333333333333333333333 +5276 3333333333333333333333333333333333333333333333333333333 +5277 3333333333333333333333333333333333333333333333333333333 +5278 3333333333333333333333333333333333333333333333333333333 +5279 3333333333333333333333333333333333333333333333333333333 +5280 3333333333333333333333333333333333333333333333333333333 +5281 3333333333333333333333333333333333333333333333333333333 +5282 3333333333333333333333333333333333333333333333333333333 +5283 3333333333333333333333333333333333333333333333333333333 +5284 3333333333333333333333333333333333333333333333333333333 +5285 3333333333333333333333333333333333333333333333333333333 +5286 3333333333333333333333333333333333333333333333333333333 +5287 3333333333333333333333333333333333333333333333333333333 +5288 3333333333333333333333333333333333333333333333333333333 +5289 3333333333333333333333333333333333333333333333333333333 +5290 3333333333333333333333333333333333333333333333333333333 +5291 3333333333333333333333333333333333333333333333333333333 +5292 3333333333333333333333333333333333333333333333333333333 +5293 3333333333333333333333333333333333333333333333333333333 +5294 3333333333333333333333333333333333333333333333333333333 +5295 3333333333333333333333333333333333333333333333333333333 +5296 3333333333333333333333333333333333333333333333333333333 +5297 3333333333333333333333333333333333333333333333333333333 +5298 3333333333333333333333333333333333333333333333333333333 +5299 3333333333333333333333333333333333333333333333333333333 +5300 3333333333333333333333333333333333333333333333333333333 +5301 3333333333333333333333333333333333333333333333333333333 +5302 3333333333333333333333333333333333333333333333333333333 +5303 3333333333333333333333333333333333333333333333333333333 +5304 3333333333333333333333333333333333333333333333333333333 +5305 3333333333333333333333333333333333333333333333333333333 +5306 3333333333333333333333333333333333333333333333333333333 +5307 3333333333333333333333333333333333333333333333333333333 +5308 3333333333333333333333333333333333333333333333333333333 +5309 3333333333333333333333333333333333333333333333333333333 +5310 3333333333333333333333333333333333333333333333333333333 +5311 3333333333333333333333333333333333333333333333333333333 +5312 3333333333333333333333333333333333333333333333333333333 +5313 3333333333333333333333333333333333333333333333333333333 +5314 3333333333333333333333333333333333333333333333333333333 +5315 3333333333333333333333333333333333333333333333333333333 +5316 3333333333333333333333333333333333333333333333333333333 +5317 3333333333333333333333333333333333333333333333333333333 +5318 3333333333333333333333333333333333333333333333333333333 +5319 3333333333333333333333333333333333333333333333333333333 +5320 3333333333333333333333333333333333333333333333333333333 +5321 3333333333333333333333333333333333333333333333333333333 +5322 3333333333333333333333333333333333333333333333333333333 +5323 3333333333333333333333333333333333333333333333333333333 +5324 3333333333333333333333333333333333333333333333333333333 +5325 3333333333333333333333333333333333333333333333333333333 +5326 3333333333333333333333333333333333333333333333333333333 +5327 3333333333333333333333333333333333333333333333333333333 +5328 3333333333333333333333333333333333333333333333333333333 +5329 3333333333333333333333333333333333333333333333333333333 +5330 3333333333333333333333333333333333333333333333333333333 +5331 3333333333333333333333333333333333333333333333333333333 +5332 3333333333333333333333333333333333333333333333333333333 +5333 3333333333333333333333333333333333333333333333333333333 +5334 3333333333333333333333333333333333333333333333333333333 +5335 3333333333333333333333333333333333333333333333333333333 +5336 3333333333333333333333333333333333333333333333333333333 +5337 3333333333333333333333333333333333333333333333333333333 +5338 3333333333333333333333333333333333333333333333333333333 +5339 3333333333333333333333333333333333333333333333333333333 +5340 3333333333333333333333333333333333333333333333333333333 +5341 3333333333333333333333333333333333333333333333333333333 +5342 3333333333333333333333333333333333333333333333333333333 +5343 3333333333333333333333333333333333333333333333333333333 +5344 3333333333333333333333333333333333333333333333333333333 +5345 3333333333333333333333333333333333333333333333333333333 +5346 3333333333333333333333333333333333333333333333333333333 +5347 3333333333333333333333333333333333333333333333333333333 +5348 3333333333333333333333333333333333333333333333333333333 +5349 3333333333333333333333333333333333333333333333333333333 +5350 3333333333333333333333333333333333333333333333333333333 +5351 3333333333333333333333333333333333333333333333333333333 +5352 3333333333333333333333333333333333333333333333333333333 +5353 3333333333333333333333333333333333333333333333333333333 +5354 3333333333333333333333333333333333333333333333333333333 +5355 3333333333333333333333333333333333333333333333333333333 +5356 3333333333333333333333333333333333333333333333333333333 +5357 3333333333333333333333333333333333333333333333333333333 +5358 3333333333333333333333333333333333333333333333333333333 +5359 3333333333333333333333333333333333333333333333333333333 +5360 3333333333333333333333333333333333333333333333333333333 +5361 3333333333333333333333333333333333333333333333333333333 +5362 3333333333333333333333333333333333333333333333333333333 +5363 3333333333333333333333333333333333333333333333333333333 +5364 3333333333333333333333333333333333333333333333333333333 +5365 3333333333333333333333333333333333333333333333333333333 +5366 3333333333333333333333333333333333333333333333333333333 +5367 3333333333333333333333333333333333333333333333333333333 +5368 3333333333333333333333333333333333333333333333333333333 +5369 3333333333333333333333333333333333333333333333333333333 +5370 3333333333333333333333333333333333333333333333333333333 +5371 3333333333333333333333333333333333333333333333333333333 +5372 3333333333333333333333333333333333333333333333333333333 +5373 3333333333333333333333333333333333333333333333333333333 +5374 3333333333333333333333333333333333333333333333333333333 +5375 3333333333333333333333333333333333333333333333333333333 +5376 3333333333333333333333333333333333333333333333333333333 +5377 3333333333333333333333333333333333333333333333333333333 +5378 3333333333333333333333333333333333333333333333333333333 +5379 3333333333333333333333333333333333333333333333333333333 +5380 3333333333333333333333333333333333333333333333333333333 +5381 3333333333333333333333333333333333333333333333333333333 +5382 3333333333333333333333333333333333333333333333333333333 +5383 3333333333333333333333333333333333333333333333333333333 +5384 3333333333333333333333333333333333333333333333333333333 +5385 3333333333333333333333333333333333333333333333333333333 +5386 3333333333333333333333333333333333333333333333333333333 +5387 3333333333333333333333333333333333333333333333333333333 +5388 3333333333333333333333333333333333333333333333333333333 +5389 3333333333333333333333333333333333333333333333333333333 +5390 3333333333333333333333333333333333333333333333333333333 +5391 3333333333333333333333333333333333333333333333333333333 +5392 3333333333333333333333333333333333333333333333333333333 +5393 3333333333333333333333333333333333333333333333333333333 +5394 3333333333333333333333333333333333333333333333333333333 +5395 3333333333333333333333333333333333333333333333333333333 +5396 3333333333333333333333333333333333333333333333333333333 +5397 3333333333333333333333333333333333333333333333333333333 +5398 3333333333333333333333333333333333333333333333333333333 +5399 3333333333333333333333333333333333333333333333333333333 +5400 3333333333333333333333333333333333333333333333333333333 +5401 3333333333333333333333333333333333333333333333333333333 +5402 3333333333333333333333333333333333333333333333333333333 +5403 3333333333333333333333333333333333333333333333333333333 +5404 3333333333333333333333333333333333333333333333333333333 +5405 3333333333333333333333333333333333333333333333333333333 +5406 3333333333333333333333333333333333333333333333333333333 +5407 3333333333333333333333333333333333333333333333333333333 +5408 3333333333333333333333333333333333333333333333333333333 +5409 3333333333333333333333333333333333333333333333333333333 +5410 3333333333333333333333333333333333333333333333333333333 +5411 3333333333333333333333333333333333333333333333333333333 +5412 3333333333333333333333333333333333333333333333333333333 +5413 3333333333333333333333333333333333333333333333333333333 +5414 3333333333333333333333333333333333333333333333333333333 +5415 3333333333333333333333333333333333333333333333333333333 +5416 3333333333333333333333333333333333333333333333333333333 +5417 3333333333333333333333333333333333333333333333333333333 +5418 3333333333333333333333333333333333333333333333333333333 +5419 3333333333333333333333333333333333333333333333333333333 +5420 3333333333333333333333333333333333333333333333333333333 +5421 3333333333333333333333333333333333333333333333333333333 +5422 3333333333333333333333333333333333333333333333333333333 +5423 3333333333333333333333333333333333333333333333333333333 +5424 3333333333333333333333333333333333333333333333333333333 +5425 3333333333333333333333333333333333333333333333333333333 +5426 3333333333333333333333333333333333333333333333333333333 +5427 3333333333333333333333333333333333333333333333333333333 +5428 3333333333333333333333333333333333333333333333333333333 +5429 3333333333333333333333333333333333333333333333333333333 +5430 3333333333333333333333333333333333333333333333333333333 +5431 3333333333333333333333333333333333333333333333333333333 +5432 3333333333333333333333333333333333333333333333333333333 +5433 3333333333333333333333333333333333333333333333333333333 +5434 3333333333333333333333333333333333333333333333333333333 +5435 3333333333333333333333333333333333333333333333333333333 +5436 3333333333333333333333333333333333333333333333333333333 +5437 3333333333333333333333333333333333333333333333333333333 +5438 3333333333333333333333333333333333333333333333333333333 +5439 3333333333333333333333333333333333333333333333333333333 +5440 3333333333333333333333333333333333333333333333333333333 +5441 3333333333333333333333333333333333333333333333333333333 +5442 3333333333333333333333333333333333333333333333333333333 +5443 3333333333333333333333333333333333333333333333333333333 +5444 3333333333333333333333333333333333333333333333333333333 +5445 3333333333333333333333333333333333333333333333333333333 +5446 3333333333333333333333333333333333333333333333333333333 +5447 3333333333333333333333333333333333333333333333333333333 +5448 3333333333333333333333333333333333333333333333333333333 +5449 3333333333333333333333333333333333333333333333333333333 +5450 3333333333333333333333333333333333333333333333333333333 +5451 3333333333333333333333333333333333333333333333333333333 +5452 3333333333333333333333333333333333333333333333333333333 +5453 3333333333333333333333333333333333333333333333333333333 +5454 3333333333333333333333333333333333333333333333333333333 +5455 3333333333333333333333333333333333333333333333333333333 +5456 3333333333333333333333333333333333333333333333333333333 +5457 3333333333333333333333333333333333333333333333333333333 +5458 3333333333333333333333333333333333333333333333333333333 +5459 3333333333333333333333333333333333333333333333333333333 +5460 3333333333333333333333333333333333333333333333333333333 +5461 3333333333333333333333333333333333333333333333333333333 +5462 3333333333333333333333333333333333333333333333333333333 +5463 3333333333333333333333333333333333333333333333333333333 +5464 3333333333333333333333333333333333333333333333333333333 +5465 3333333333333333333333333333333333333333333333333333333 +5466 3333333333333333333333333333333333333333333333333333333 +5467 3333333333333333333333333333333333333333333333333333333 +5468 3333333333333333333333333333333333333333333333333333333 +5469 3333333333333333333333333333333333333333333333333333333 +5470 3333333333333333333333333333333333333333333333333333333 +5471 3333333333333333333333333333333333333333333333333333333 +5472 3333333333333333333333333333333333333333333333333333333 +5473 3333333333333333333333333333333333333333333333333333333 +5474 3333333333333333333333333333333333333333333333333333333 +5475 3333333333333333333333333333333333333333333333333333333 +5476 3333333333333333333333333333333333333333333333333333333 +5477 3333333333333333333333333333333333333333333333333333333 +5478 3333333333333333333333333333333333333333333333333333333 +5479 3333333333333333333333333333333333333333333333333333333 +5480 3333333333333333333333333333333333333333333333333333333 +5481 3333333333333333333333333333333333333333333333333333333 +5482 3333333333333333333333333333333333333333333333333333333 +5483 3333333333333333333333333333333333333333333333333333333 +5484 3333333333333333333333333333333333333333333333333333333 +5485 3333333333333333333333333333333333333333333333333333333 +5486 3333333333333333333333333333333333333333333333333333333 +5487 3333333333333333333333333333333333333333333333333333333 +5488 3333333333333333333333333333333333333333333333333333333 +5489 3333333333333333333333333333333333333333333333333333333 +5490 3333333333333333333333333333333333333333333333333333333 +5491 3333333333333333333333333333333333333333333333333333333 +5492 3333333333333333333333333333333333333333333333333333333 +5493 3333333333333333333333333333333333333333333333333333333 +5494 3333333333333333333333333333333333333333333333333333333 +5495 3333333333333333333333333333333333333333333333333333333 +5496 3333333333333333333333333333333333333333333333333333333 +5497 3333333333333333333333333333333333333333333333333333333 +5498 3333333333333333333333333333333333333333333333333333333 +5499 3333333333333333333333333333333333333333333333333333333 +5500 3333333333333333333333333333333333333333333333333333333 +5501 3333333333333333333333333333333333333333333333333333333 +5502 3333333333333333333333333333333333333333333333333333333 +5503 3333333333333333333333333333333333333333333333333333333 +5504 3333333333333333333333333333333333333333333333333333333 +5505 3333333333333333333333333333333333333333333333333333333 +5506 3333333333333333333333333333333333333333333333333333333 +5507 3333333333333333333333333333333333333333333333333333333 +5508 3333333333333333333333333333333333333333333333333333333 +5509 3333333333333333333333333333333333333333333333333333333 +5510 3333333333333333333333333333333333333333333333333333333 +5511 3333333333333333333333333333333333333333333333333333333 +5512 3333333333333333333333333333333333333333333333333333333 +5513 3333333333333333333333333333333333333333333333333333333 +5514 3333333333333333333333333333333333333333333333333333333 +5515 3333333333333333333333333333333333333333333333333333333 +5516 3333333333333333333333333333333333333333333333333333333 +5517 3333333333333333333333333333333333333333333333333333333 +5518 3333333333333333333333333333333333333333333333333333333 +5519 3333333333333333333333333333333333333333333333333333333 +5520 3333333333333333333333333333333333333333333333333333333 +5521 3333333333333333333333333333333333333333333333333333333 +5522 3333333333333333333333333333333333333333333333333333333 +5523 3333333333333333333333333333333333333333333333333333333 +5524 3333333333333333333333333333333333333333333333333333333 +5525 3333333333333333333333333333333333333333333333333333333 +5526 3333333333333333333333333333333333333333333333333333333 +5527 3333333333333333333333333333333333333333333333333333333 +5528 3333333333333333333333333333333333333333333333333333333 +5529 3333333333333333333333333333333333333333333333333333333 +5530 3333333333333333333333333333333333333333333333333333333 +5531 3333333333333333333333333333333333333333333333333333333 +5532 3333333333333333333333333333333333333333333333333333333 +5533 3333333333333333333333333333333333333333333333333333333 +5534 3333333333333333333333333333333333333333333333333333333 +5535 3333333333333333333333333333333333333333333333333333333 +5536 3333333333333333333333333333333333333333333333333333333 +5537 3333333333333333333333333333333333333333333333333333333 +5538 3333333333333333333333333333333333333333333333333333333 +5539 3333333333333333333333333333333333333333333333333333333 +5540 3333333333333333333333333333333333333333333333333333333 +5541 3333333333333333333333333333333333333333333333333333333 +5542 3333333333333333333333333333333333333333333333333333333 +5543 3333333333333333333333333333333333333333333333333333333 +5544 3333333333333333333333333333333333333333333333333333333 +5545 3333333333333333333333333333333333333333333333333333333 +5546 3333333333333333333333333333333333333333333333333333333 +5547 3333333333333333333333333333333333333333333333333333333 +5548 3333333333333333333333333333333333333333333333333333333 +5549 3333333333333333333333333333333333333333333333333333333 +5550 3333333333333333333333333333333333333333333333333333333 +5551 3333333333333333333333333333333333333333333333333333333 +5552 3333333333333333333333333333333333333333333333333333333 +5553 3333333333333333333333333333333333333333333333333333333 +5554 3333333333333333333333333333333333333333333333333333333 +5555 3333333333333333333333333333333333333333333333333333333 +5556 3333333333333333333333333333333333333333333333333333333 +5557 3333333333333333333333333333333333333333333333333333333 +5558 3333333333333333333333333333333333333333333333333333333 +5559 3333333333333333333333333333333333333333333333333333333 +5560 3333333333333333333333333333333333333333333333333333333 +5561 3333333333333333333333333333333333333333333333333333333 +5562 3333333333333333333333333333333333333333333333333333333 +5563 3333333333333333333333333333333333333333333333333333333 +5564 3333333333333333333333333333333333333333333333333333333 +5565 3333333333333333333333333333333333333333333333333333333 +5566 3333333333333333333333333333333333333333333333333333333 +5567 3333333333333333333333333333333333333333333333333333333 +5568 3333333333333333333333333333333333333333333333333333333 +5569 3333333333333333333333333333333333333333333333333333333 +5570 3333333333333333333333333333333333333333333333333333333 +5571 3333333333333333333333333333333333333333333333333333333 +5572 3333333333333333333333333333333333333333333333333333333 +5573 3333333333333333333333333333333333333333333333333333333 +5574 3333333333333333333333333333333333333333333333333333333 +5575 3333333333333333333333333333333333333333333333333333333 +5576 3333333333333333333333333333333333333333333333333333333 +5577 3333333333333333333333333333333333333333333333333333333 +5578 3333333333333333333333333333333333333333333333333333333 +5579 3333333333333333333333333333333333333333333333333333333 +5580 3333333333333333333333333333333333333333333333333333333 +5581 3333333333333333333333333333333333333333333333333333333 +5582 3333333333333333333333333333333333333333333333333333333 +5583 3333333333333333333333333333333333333333333333333333333 +5584 3333333333333333333333333333333333333333333333333333333 +5585 3333333333333333333333333333333333333333333333333333333 +5586 3333333333333333333333333333333333333333333333333333333 +5587 3333333333333333333333333333333333333333333333333333333 +5588 3333333333333333333333333333333333333333333333333333333 +5589 3333333333333333333333333333333333333333333333333333333 +5590 3333333333333333333333333333333333333333333333333333333 +5591 3333333333333333333333333333333333333333333333333333333 +5592 3333333333333333333333333333333333333333333333333333333 +5593 3333333333333333333333333333333333333333333333333333333 +5594 3333333333333333333333333333333333333333333333333333333 +5595 3333333333333333333333333333333333333333333333333333333 +5596 3333333333333333333333333333333333333333333333333333333 +5597 3333333333333333333333333333333333333333333333333333333 +5598 3333333333333333333333333333333333333333333333333333333 +5599 3333333333333333333333333333333333333333333333333333333 +5600 3333333333333333333333333333333333333333333333333333333 +5601 3333333333333333333333333333333333333333333333333333333 +5602 3333333333333333333333333333333333333333333333333333333 +5603 3333333333333333333333333333333333333333333333333333333 +5604 3333333333333333333333333333333333333333333333333333333 +5605 3333333333333333333333333333333333333333333333333333333 +5606 3333333333333333333333333333333333333333333333333333333 +5607 3333333333333333333333333333333333333333333333333333333 +5608 3333333333333333333333333333333333333333333333333333333 +5609 3333333333333333333333333333333333333333333333333333333 +5610 3333333333333333333333333333333333333333333333333333333 +5611 3333333333333333333333333333333333333333333333333333333 +5612 3333333333333333333333333333333333333333333333333333333 +5613 3333333333333333333333333333333333333333333333333333333 +5614 3333333333333333333333333333333333333333333333333333333 +5615 3333333333333333333333333333333333333333333333333333333 +5616 3333333333333333333333333333333333333333333333333333333 +5617 3333333333333333333333333333333333333333333333333333333 +5618 3333333333333333333333333333333333333333333333333333333 +5619 3333333333333333333333333333333333333333333333333333333 +5620 3333333333333333333333333333333333333333333333333333333 +5621 3333333333333333333333333333333333333333333333333333333 +5622 3333333333333333333333333333333333333333333333333333333 +5623 3333333333333333333333333333333333333333333333333333333 +5624 3333333333333333333333333333333333333333333333333333333 +5625 3333333333333333333333333333333333333333333333333333333 +5626 3333333333333333333333333333333333333333333333333333333 +5627 3333333333333333333333333333333333333333333333333333333 +5628 3333333333333333333333333333333333333333333333333333333 +5629 3333333333333333333333333333333333333333333333333333333 +5630 3333333333333333333333333333333333333333333333333333333 +5631 3333333333333333333333333333333333333333333333333333333 +5632 3333333333333333333333333333333333333333333333333333333 +5633 3333333333333333333333333333333333333333333333333333333 +5634 3333333333333333333333333333333333333333333333333333333 +5635 3333333333333333333333333333333333333333333333333333333 +5636 3333333333333333333333333333333333333333333333333333333 +5637 3333333333333333333333333333333333333333333333333333333 +5638 3333333333333333333333333333333333333333333333333333333 +5639 3333333333333333333333333333333333333333333333333333333 +5640 3333333333333333333333333333333333333333333333333333333 +5641 3333333333333333333333333333333333333333333333333333333 +5642 3333333333333333333333333333333333333333333333333333333 +5643 3333333333333333333333333333333333333333333333333333333 +5644 3333333333333333333333333333333333333333333333333333333 +5645 3333333333333333333333333333333333333333333333333333333 +5646 3333333333333333333333333333333333333333333333333333333 +5647 3333333333333333333333333333333333333333333333333333333 +5648 3333333333333333333333333333333333333333333333333333333 +5649 3333333333333333333333333333333333333333333333333333333 +5650 3333333333333333333333333333333333333333333333333333333 +5651 3333333333333333333333333333333333333333333333333333333 +5652 3333333333333333333333333333333333333333333333333333333 +5653 3333333333333333333333333333333333333333333333333333333 +5654 3333333333333333333333333333333333333333333333333333333 +5655 3333333333333333333333333333333333333333333333333333333 +5656 3333333333333333333333333333333333333333333333333333333 +5657 3333333333333333333333333333333333333333333333333333333 +5658 3333333333333333333333333333333333333333333333333333333 +5659 3333333333333333333333333333333333333333333333333333333 +5660 3333333333333333333333333333333333333333333333333333333 +5661 3333333333333333333333333333333333333333333333333333333 +5662 3333333333333333333333333333333333333333333333333333333 +5663 3333333333333333333333333333333333333333333333333333333 +5664 3333333333333333333333333333333333333333333333333333333 +5665 3333333333333333333333333333333333333333333333333333333 +5666 3333333333333333333333333333333333333333333333333333333 +5667 3333333333333333333333333333333333333333333333333333333 +5668 3333333333333333333333333333333333333333333333333333333 +5669 3333333333333333333333333333333333333333333333333333333 +5670 3333333333333333333333333333333333333333333333333333333 +5671 3333333333333333333333333333333333333333333333333333333 +5672 3333333333333333333333333333333333333333333333333333333 +5673 3333333333333333333333333333333333333333333333333333333 +5674 3333333333333333333333333333333333333333333333333333333 +5675 3333333333333333333333333333333333333333333333333333333 +5676 3333333333333333333333333333333333333333333333333333333 +5677 3333333333333333333333333333333333333333333333333333333 +5678 3333333333333333333333333333333333333333333333333333333 +5679 3333333333333333333333333333333333333333333333333333333 +5680 3333333333333333333333333333333333333333333333333333333 +5681 3333333333333333333333333333333333333333333333333333333 +5682 3333333333333333333333333333333333333333333333333333333 +5683 3333333333333333333333333333333333333333333333333333333 +5684 3333333333333333333333333333333333333333333333333333333 +5685 3333333333333333333333333333333333333333333333333333333 +5686 3333333333333333333333333333333333333333333333333333333 +5687 3333333333333333333333333333333333333333333333333333333 +5688 3333333333333333333333333333333333333333333333333333333 +5689 3333333333333333333333333333333333333333333333333333333 +5690 3333333333333333333333333333333333333333333333333333333 +5691 3333333333333333333333333333333333333333333333333333333 +5692 3333333333333333333333333333333333333333333333333333333 +5693 3333333333333333333333333333333333333333333333333333333 +5694 3333333333333333333333333333333333333333333333333333333 +5695 3333333333333333333333333333333333333333333333333333333 +5696 3333333333333333333333333333333333333333333333333333333 +5697 3333333333333333333333333333333333333333333333333333333 +5698 3333333333333333333333333333333333333333333333333333333 +5699 3333333333333333333333333333333333333333333333333333333 +5700 3333333333333333333333333333333333333333333333333333333 +5701 3333333333333333333333333333333333333333333333333333333 +5702 3333333333333333333333333333333333333333333333333333333 +5703 3333333333333333333333333333333333333333333333333333333 +5704 3333333333333333333333333333333333333333333333333333333 +5705 3333333333333333333333333333333333333333333333333333333 +5706 3333333333333333333333333333333333333333333333333333333 +5707 3333333333333333333333333333333333333333333333333333333 +5708 3333333333333333333333333333333333333333333333333333333 +5709 3333333333333333333333333333333333333333333333333333333 +5710 3333333333333333333333333333333333333333333333333333333 +5711 3333333333333333333333333333333333333333333333333333333 +5712 3333333333333333333333333333333333333333333333333333333 +5713 3333333333333333333333333333333333333333333333333333333 +5714 3333333333333333333333333333333333333333333333333333333 +5715 3333333333333333333333333333333333333333333333333333333 +5716 3333333333333333333333333333333333333333333333333333333 +5717 3333333333333333333333333333333333333333333333333333333 +5718 3333333333333333333333333333333333333333333333333333333 +5719 3333333333333333333333333333333333333333333333333333333 +5720 3333333333333333333333333333333333333333333333333333333 +5721 3333333333333333333333333333333333333333333333333333333 +5722 3333333333333333333333333333333333333333333333333333333 +5723 3333333333333333333333333333333333333333333333333333333 +5724 3333333333333333333333333333333333333333333333333333333 +5725 3333333333333333333333333333333333333333333333333333333 +5726 3333333333333333333333333333333333333333333333333333333 +5727 3333333333333333333333333333333333333333333333333333333 +5728 3333333333333333333333333333333333333333333333333333333 +5729 3333333333333333333333333333333333333333333333333333333 +5730 3333333333333333333333333333333333333333333333333333333 +5731 3333333333333333333333333333333333333333333333333333333 +5732 3333333333333333333333333333333333333333333333333333333 +5733 3333333333333333333333333333333333333333333333333333333 +5734 3333333333333333333333333333333333333333333333333333333 +5735 3333333333333333333333333333333333333333333333333333333 +5736 3333333333333333333333333333333333333333333333333333333 +5737 3333333333333333333333333333333333333333333333333333333 +5738 3333333333333333333333333333333333333333333333333333333 +5739 3333333333333333333333333333333333333333333333333333333 +5740 3333333333333333333333333333333333333333333333333333333 +5741 3333333333333333333333333333333333333333333333333333333 +5742 3333333333333333333333333333333333333333333333333333333 +5743 3333333333333333333333333333333333333333333333333333333 +5744 3333333333333333333333333333333333333333333333333333333 +5745 3333333333333333333333333333333333333333333333333333333 +5746 3333333333333333333333333333333333333333333333333333333 +5747 3333333333333333333333333333333333333333333333333333333 +5748 3333333333333333333333333333333333333333333333333333333 +5749 3333333333333333333333333333333333333333333333333333333 +5750 3333333333333333333333333333333333333333333333333333333 +5751 3333333333333333333333333333333333333333333333333333333 +5752 3333333333333333333333333333333333333333333333333333333 +5753 3333333333333333333333333333333333333333333333333333333 +5754 3333333333333333333333333333333333333333333333333333333 +5755 3333333333333333333333333333333333333333333333333333333 +5756 3333333333333333333333333333333333333333333333333333333 +5757 3333333333333333333333333333333333333333333333333333333 +5758 3333333333333333333333333333333333333333333333333333333 +5759 3333333333333333333333333333333333333333333333333333333 +5760 3333333333333333333333333333333333333333333333333333333 +5761 3333333333333333333333333333333333333333333333333333333 +5762 3333333333333333333333333333333333333333333333333333333 +5763 3333333333333333333333333333333333333333333333333333333 +5764 3333333333333333333333333333333333333333333333333333333 +5765 3333333333333333333333333333333333333333333333333333333 +5766 3333333333333333333333333333333333333333333333333333333 +5767 3333333333333333333333333333333333333333333333333333333 +5768 3333333333333333333333333333333333333333333333333333333 +5769 3333333333333333333333333333333333333333333333333333333 +5770 3333333333333333333333333333333333333333333333333333333 +5771 3333333333333333333333333333333333333333333333333333333 +5772 3333333333333333333333333333333333333333333333333333333 +5773 3333333333333333333333333333333333333333333333333333333 +5774 3333333333333333333333333333333333333333333333333333333 +5775 3333333333333333333333333333333333333333333333333333333 +5776 3333333333333333333333333333333333333333333333333333333 +5777 3333333333333333333333333333333333333333333333333333333 +5778 3333333333333333333333333333333333333333333333333333333 +5779 3333333333333333333333333333333333333333333333333333333 +5780 3333333333333333333333333333333333333333333333333333333 +5781 3333333333333333333333333333333333333333333333333333333 +5782 3333333333333333333333333333333333333333333333333333333 +5783 3333333333333333333333333333333333333333333333333333333 +5784 3333333333333333333333333333333333333333333333333333333 +5785 3333333333333333333333333333333333333333333333333333333 +5786 3333333333333333333333333333333333333333333333333333333 +5787 3333333333333333333333333333333333333333333333333333333 +5788 3333333333333333333333333333333333333333333333333333333 +5789 3333333333333333333333333333333333333333333333333333333 +5790 3333333333333333333333333333333333333333333333333333333 +5791 3333333333333333333333333333333333333333333333333333333 +5792 3333333333333333333333333333333333333333333333333333333 +5793 3333333333333333333333333333333333333333333333333333333 +5794 3333333333333333333333333333333333333333333333333333333 +5795 3333333333333333333333333333333333333333333333333333333 +5796 3333333333333333333333333333333333333333333333333333333 +5797 3333333333333333333333333333333333333333333333333333333 +5798 3333333333333333333333333333333333333333333333333333333 +5799 3333333333333333333333333333333333333333333333333333333 +5800 3333333333333333333333333333333333333333333333333333333 +5801 3333333333333333333333333333333333333333333333333333333 +5802 3333333333333333333333333333333333333333333333333333333 +5803 3333333333333333333333333333333333333333333333333333333 +5804 3333333333333333333333333333333333333333333333333333333 +5805 3333333333333333333333333333333333333333333333333333333 +5806 3333333333333333333333333333333333333333333333333333333 +5807 3333333333333333333333333333333333333333333333333333333 +5808 3333333333333333333333333333333333333333333333333333333 +5809 3333333333333333333333333333333333333333333333333333333 +5810 3333333333333333333333333333333333333333333333333333333 +5811 3333333333333333333333333333333333333333333333333333333 +5812 3333333333333333333333333333333333333333333333333333333 +5813 3333333333333333333333333333333333333333333333333333333 +5814 3333333333333333333333333333333333333333333333333333333 +5815 3333333333333333333333333333333333333333333333333333333 +5816 3333333333333333333333333333333333333333333333333333333 +5817 3333333333333333333333333333333333333333333333333333333 +5818 3333333333333333333333333333333333333333333333333333333 +5819 3333333333333333333333333333333333333333333333333333333 +5820 3333333333333333333333333333333333333333333333333333333 +5821 3333333333333333333333333333333333333333333333333333333 +5822 3333333333333333333333333333333333333333333333333333333 +5823 3333333333333333333333333333333333333333333333333333333 +5824 3333333333333333333333333333333333333333333333333333333 +5825 3333333333333333333333333333333333333333333333333333333 +5826 3333333333333333333333333333333333333333333333333333333 +5827 3333333333333333333333333333333333333333333333333333333 +5828 3333333333333333333333333333333333333333333333333333333 +5829 3333333333333333333333333333333333333333333333333333333 +5830 3333333333333333333333333333333333333333333333333333333 +5831 3333333333333333333333333333333333333333333333333333333 +5832 3333333333333333333333333333333333333333333333333333333 +5833 3333333333333333333333333333333333333333333333333333333 +5834 3333333333333333333333333333333333333333333333333333333 +5835 3333333333333333333333333333333333333333333333333333333 +5836 3333333333333333333333333333333333333333333333333333333 +5837 3333333333333333333333333333333333333333333333333333333 +5838 3333333333333333333333333333333333333333333333333333333 +5839 3333333333333333333333333333333333333333333333333333333 +5840 3333333333333333333333333333333333333333333333333333333 +5841 3333333333333333333333333333333333333333333333333333333 +5842 3333333333333333333333333333333333333333333333333333333 +5843 3333333333333333333333333333333333333333333333333333333 +5844 3333333333333333333333333333333333333333333333333333333 +5845 3333333333333333333333333333333333333333333333333333333 +5846 3333333333333333333333333333333333333333333333333333333 +5847 3333333333333333333333333333333333333333333333333333333 +5848 3333333333333333333333333333333333333333333333333333333 +5849 3333333333333333333333333333333333333333333333333333333 +5850 3333333333333333333333333333333333333333333333333333333 +5851 3333333333333333333333333333333333333333333333333333333 +5852 3333333333333333333333333333333333333333333333333333333 +5853 3333333333333333333333333333333333333333333333333333333 +5854 3333333333333333333333333333333333333333333333333333333 +5855 3333333333333333333333333333333333333333333333333333333 +5856 3333333333333333333333333333333333333333333333333333333 +5857 3333333333333333333333333333333333333333333333333333333 +5858 3333333333333333333333333333333333333333333333333333333 +5859 3333333333333333333333333333333333333333333333333333333 +5860 3333333333333333333333333333333333333333333333333333333 +5861 3333333333333333333333333333333333333333333333333333333 +5862 3333333333333333333333333333333333333333333333333333333 +5863 3333333333333333333333333333333333333333333333333333333 +5864 3333333333333333333333333333333333333333333333333333333 +5865 3333333333333333333333333333333333333333333333333333333 +5866 3333333333333333333333333333333333333333333333333333333 +5867 3333333333333333333333333333333333333333333333333333333 +5868 3333333333333333333333333333333333333333333333333333333 +5869 3333333333333333333333333333333333333333333333333333333 +5870 3333333333333333333333333333333333333333333333333333333 +5871 3333333333333333333333333333333333333333333333333333333 +5872 3333333333333333333333333333333333333333333333333333333 +5873 3333333333333333333333333333333333333333333333333333333 +5874 3333333333333333333333333333333333333333333333333333333 +5875 3333333333333333333333333333333333333333333333333333333 +5876 3333333333333333333333333333333333333333333333333333333 +5877 3333333333333333333333333333333333333333333333333333333 +5878 3333333333333333333333333333333333333333333333333333333 +5879 3333333333333333333333333333333333333333333333333333333 +5880 3333333333333333333333333333333333333333333333333333333 +5881 3333333333333333333333333333333333333333333333333333333 +5882 3333333333333333333333333333333333333333333333333333333 +5883 3333333333333333333333333333333333333333333333333333333 +5884 3333333333333333333333333333333333333333333333333333333 +5885 3333333333333333333333333333333333333333333333333333333 +5886 3333333333333333333333333333333333333333333333333333333 +5887 3333333333333333333333333333333333333333333333333333333 +5888 3333333333333333333333333333333333333333333333333333333 +5889 3333333333333333333333333333333333333333333333333333333 +5890 3333333333333333333333333333333333333333333333333333333 +5891 3333333333333333333333333333333333333333333333333333333 +5892 3333333333333333333333333333333333333333333333333333333 +5893 3333333333333333333333333333333333333333333333333333333 +5894 3333333333333333333333333333333333333333333333333333333 +5895 3333333333333333333333333333333333333333333333333333333 +5896 3333333333333333333333333333333333333333333333333333333 +5897 3333333333333333333333333333333333333333333333333333333 +5898 3333333333333333333333333333333333333333333333333333333 +5899 3333333333333333333333333333333333333333333333333333333 +5900 3333333333333333333333333333333333333333333333333333333 +5901 3333333333333333333333333333333333333333333333333333333 +5902 3333333333333333333333333333333333333333333333333333333 +5903 3333333333333333333333333333333333333333333333333333333 +5904 3333333333333333333333333333333333333333333333333333333 +5905 3333333333333333333333333333333333333333333333333333333 +5906 3333333333333333333333333333333333333333333333333333333 +5907 3333333333333333333333333333333333333333333333333333333 +5908 3333333333333333333333333333333333333333333333333333333 +5909 3333333333333333333333333333333333333333333333333333333 +5910 3333333333333333333333333333333333333333333333333333333 +5911 3333333333333333333333333333333333333333333333333333333 +5912 3333333333333333333333333333333333333333333333333333333 +5913 3333333333333333333333333333333333333333333333333333333 +5914 3333333333333333333333333333333333333333333333333333333 +5915 3333333333333333333333333333333333333333333333333333333 +5916 3333333333333333333333333333333333333333333333333333333 +5917 3333333333333333333333333333333333333333333333333333333 +5918 3333333333333333333333333333333333333333333333333333333 +5919 3333333333333333333333333333333333333333333333333333333 +5920 3333333333333333333333333333333333333333333333333333333 +5921 3333333333333333333333333333333333333333333333333333333 +5922 3333333333333333333333333333333333333333333333333333333 +5923 3333333333333333333333333333333333333333333333333333333 +5924 3333333333333333333333333333333333333333333333333333333 +5925 3333333333333333333333333333333333333333333333333333333 +5926 3333333333333333333333333333333333333333333333333333333 +5927 3333333333333333333333333333333333333333333333333333333 +5928 3333333333333333333333333333333333333333333333333333333 +5929 3333333333333333333333333333333333333333333333333333333 +5930 3333333333333333333333333333333333333333333333333333333 +5931 3333333333333333333333333333333333333333333333333333333 +5932 3333333333333333333333333333333333333333333333333333333 +5933 3333333333333333333333333333333333333333333333333333333 +5934 3333333333333333333333333333333333333333333333333333333 +5935 3333333333333333333333333333333333333333333333333333333 +5936 3333333333333333333333333333333333333333333333333333333 +5937 3333333333333333333333333333333333333333333333333333333 +5938 3333333333333333333333333333333333333333333333333333333 +5939 3333333333333333333333333333333333333333333333333333333 +5940 3333333333333333333333333333333333333333333333333333333 +5941 3333333333333333333333333333333333333333333333333333333 +5942 3333333333333333333333333333333333333333333333333333333 +5943 3333333333333333333333333333333333333333333333333333333 +5944 3333333333333333333333333333333333333333333333333333333 +5945 3333333333333333333333333333333333333333333333333333333 +5946 3333333333333333333333333333333333333333333333333333333 +5947 3333333333333333333333333333333333333333333333333333333 +5948 3333333333333333333333333333333333333333333333333333333 +5949 3333333333333333333333333333333333333333333333333333333 +5950 3333333333333333333333333333333333333333333333333333333 +5951 3333333333333333333333333333333333333333333333333333333 +5952 3333333333333333333333333333333333333333333333333333333 +5953 3333333333333333333333333333333333333333333333333333333 +5954 3333333333333333333333333333333333333333333333333333333 +5955 3333333333333333333333333333333333333333333333333333333 +5956 3333333333333333333333333333333333333333333333333333333 +5957 3333333333333333333333333333333333333333333333333333333 +5958 3333333333333333333333333333333333333333333333333333333 +5959 3333333333333333333333333333333333333333333333333333333 +5960 3333333333333333333333333333333333333333333333333333333 +5961 3333333333333333333333333333333333333333333333333333333 +5962 3333333333333333333333333333333333333333333333333333333 +5963 3333333333333333333333333333333333333333333333333333333 +5964 3333333333333333333333333333333333333333333333333333333 +5965 3333333333333333333333333333333333333333333333333333333 +5966 3333333333333333333333333333333333333333333333333333333 +5967 3333333333333333333333333333333333333333333333333333333 +5968 3333333333333333333333333333333333333333333333333333333 +5969 3333333333333333333333333333333333333333333333333333333 +5970 3333333333333333333333333333333333333333333333333333333 +5971 3333333333333333333333333333333333333333333333333333333 +5972 3333333333333333333333333333333333333333333333333333333 +5973 3333333333333333333333333333333333333333333333333333333 +5974 3333333333333333333333333333333333333333333333333333333 +5975 3333333333333333333333333333333333333333333333333333333 +5976 3333333333333333333333333333333333333333333333333333333 +5977 3333333333333333333333333333333333333333333333333333333 +5978 3333333333333333333333333333333333333333333333333333333 +5979 3333333333333333333333333333333333333333333333333333333 +5980 3333333333333333333333333333333333333333333333333333333 +5981 3333333333333333333333333333333333333333333333333333333 +5982 3333333333333333333333333333333333333333333333333333333 +5983 3333333333333333333333333333333333333333333333333333333 +5984 3333333333333333333333333333333333333333333333333333333 +5985 3333333333333333333333333333333333333333333333333333333 +5986 3333333333333333333333333333333333333333333333333333333 +5987 3333333333333333333333333333333333333333333333333333333 +5988 3333333333333333333333333333333333333333333333333333333 +5989 3333333333333333333333333333333333333333333333333333333 +5990 3333333333333333333333333333333333333333333333333333333 +5991 3333333333333333333333333333333333333333333333333333333 +5992 3333333333333333333333333333333333333333333333333333333 +5993 3333333333333333333333333333333333333333333333333333333 +5994 3333333333333333333333333333333333333333333333333333333 +5995 3333333333333333333333333333333333333333333333333333333 +5996 3333333333333333333333333333333333333333333333333333333 +5997 3333333333333333333333333333333333333333333333333333333 +5998 3333333333333333333333333333333333333333333333333333333 +5999 3333333333333333333333333333333333333333333333333333333 +6000 3333333333333333333333333333333333333333333333333333333 +6001 3333333333333333333333333333333333333333333333333333333 +6002 3333333333333333333333333333333333333333333333333333333 +6003 3333333333333333333333333333333333333333333333333333333 +6004 3333333333333333333333333333333333333333333333333333333 +6005 3333333333333333333333333333333333333333333333333333333 +6006 3333333333333333333333333333333333333333333333333333333 +6007 3333333333333333333333333333333333333333333333333333333 +6008 3333333333333333333333333333333333333333333333333333333 +6009 3333333333333333333333333333333333333333333333333333333 +6010 3333333333333333333333333333333333333333333333333333333 +6011 3333333333333333333333333333333333333333333333333333333 +6012 3333333333333333333333333333333333333333333333333333333 +6013 3333333333333333333333333333333333333333333333333333333 +6014 3333333333333333333333333333333333333333333333333333333 +6015 3333333333333333333333333333333333333333333333333333333 +6016 3333333333333333333333333333333333333333333333333333333 +6017 3333333333333333333333333333333333333333333333333333333 +6018 3333333333333333333333333333333333333333333333333333333 +6019 3333333333333333333333333333333333333333333333333333333 +6020 3333333333333333333333333333333333333333333333333333333 +6021 3333333333333333333333333333333333333333333333333333333 +6022 3333333333333333333333333333333333333333333333333333333 +6023 3333333333333333333333333333333333333333333333333333333 +6024 3333333333333333333333333333333333333333333333333333333 +6025 3333333333333333333333333333333333333333333333333333333 +6026 3333333333333333333333333333333333333333333333333333333 +6027 3333333333333333333333333333333333333333333333333333333 +6028 3333333333333333333333333333333333333333333333333333333 +6029 3333333333333333333333333333333333333333333333333333333 +6030 3333333333333333333333333333333333333333333333333333333 +6031 3333333333333333333333333333333333333333333333333333333 +6032 3333333333333333333333333333333333333333333333333333333 +6033 3333333333333333333333333333333333333333333333333333333 +6034 3333333333333333333333333333333333333333333333333333333 +6035 3333333333333333333333333333333333333333333333333333333 +6036 3333333333333333333333333333333333333333333333333333333 +6037 3333333333333333333333333333333333333333333333333333333 +6038 3333333333333333333333333333333333333333333333333333333 +6039 3333333333333333333333333333333333333333333333333333333 +6040 3333333333333333333333333333333333333333333333333333333 +6041 3333333333333333333333333333333333333333333333333333333 +6042 3333333333333333333333333333333333333333333333333333333 +6043 3333333333333333333333333333333333333333333333333333333 +6044 3333333333333333333333333333333333333333333333333333333 +6045 3333333333333333333333333333333333333333333333333333333 +6046 3333333333333333333333333333333333333333333333333333333 +6047 3333333333333333333333333333333333333333333333333333333 +6048 3333333333333333333333333333333333333333333333333333333 +6049 3333333333333333333333333333333333333333333333333333333 +6050 3333333333333333333333333333333333333333333333333333333 +6051 3333333333333333333333333333333333333333333333333333333 +6052 3333333333333333333333333333333333333333333333333333333 +6053 3333333333333333333333333333333333333333333333333333333 +6054 3333333333333333333333333333333333333333333333333333333 +6055 3333333333333333333333333333333333333333333333333333333 +6056 3333333333333333333333333333333333333333333333333333333 +6057 3333333333333333333333333333333333333333333333333333333 +6058 3333333333333333333333333333333333333333333333333333333 +6059 3333333333333333333333333333333333333333333333333333333 +6060 3333333333333333333333333333333333333333333333333333333 +6061 3333333333333333333333333333333333333333333333333333333 +6062 3333333333333333333333333333333333333333333333333333333 +6063 3333333333333333333333333333333333333333333333333333333 +6064 3333333333333333333333333333333333333333333333333333333 +6065 3333333333333333333333333333333333333333333333333333333 +6066 3333333333333333333333333333333333333333333333333333333 +6067 3333333333333333333333333333333333333333333333333333333 +6068 3333333333333333333333333333333333333333333333333333333 +6069 3333333333333333333333333333333333333333333333333333333 +6070 3333333333333333333333333333333333333333333333333333333 +6071 3333333333333333333333333333333333333333333333333333333 +6072 3333333333333333333333333333333333333333333333333333333 +6073 3333333333333333333333333333333333333333333333333333333 +6074 3333333333333333333333333333333333333333333333333333333 +6075 3333333333333333333333333333333333333333333333333333333 +6076 3333333333333333333333333333333333333333333333333333333 +6077 3333333333333333333333333333333333333333333333333333333 +6078 3333333333333333333333333333333333333333333333333333333 +6079 3333333333333333333333333333333333333333333333333333333 +6080 3333333333333333333333333333333333333333333333333333333 +6081 3333333333333333333333333333333333333333333333333333333 +6082 3333333333333333333333333333333333333333333333333333333 +6083 3333333333333333333333333333333333333333333333333333333 +6084 3333333333333333333333333333333333333333333333333333333 +6085 3333333333333333333333333333333333333333333333333333333 +6086 3333333333333333333333333333333333333333333333333333333 +6087 3333333333333333333333333333333333333333333333333333333 +6088 3333333333333333333333333333333333333333333333333333333 +6089 3333333333333333333333333333333333333333333333333333333 +6090 3333333333333333333333333333333333333333333333333333333 +6091 3333333333333333333333333333333333333333333333333333333 +6092 3333333333333333333333333333333333333333333333333333333 +6093 3333333333333333333333333333333333333333333333333333333 +6094 3333333333333333333333333333333333333333333333333333333 +6095 3333333333333333333333333333333333333333333333333333333 +6096 3333333333333333333333333333333333333333333333333333333 +6097 3333333333333333333333333333333333333333333333333333333 +6098 3333333333333333333333333333333333333333333333333333333 +6099 3333333333333333333333333333333333333333333333333333333 +6100 3333333333333333333333333333333333333333333333333333333 +6101 3333333333333333333333333333333333333333333333333333333 +6102 3333333333333333333333333333333333333333333333333333333 +6103 3333333333333333333333333333333333333333333333333333333 +6104 3333333333333333333333333333333333333333333333333333333 +6105 3333333333333333333333333333333333333333333333333333333 +6106 3333333333333333333333333333333333333333333333333333333 +6107 3333333333333333333333333333333333333333333333333333333 +6108 3333333333333333333333333333333333333333333333333333333 +6109 3333333333333333333333333333333333333333333333333333333 +6110 3333333333333333333333333333333333333333333333333333333 +6111 3333333333333333333333333333333333333333333333333333333 +6112 3333333333333333333333333333333333333333333333333333333 +6113 3333333333333333333333333333333333333333333333333333333 +6114 3333333333333333333333333333333333333333333333333333333 +6115 3333333333333333333333333333333333333333333333333333333 +6116 3333333333333333333333333333333333333333333333333333333 +6117 3333333333333333333333333333333333333333333333333333333 +6118 3333333333333333333333333333333333333333333333333333333 +6119 3333333333333333333333333333333333333333333333333333333 +6120 3333333333333333333333333333333333333333333333333333333 +6121 3333333333333333333333333333333333333333333333333333333 +6122 3333333333333333333333333333333333333333333333333333333 +6123 3333333333333333333333333333333333333333333333333333333 +6124 3333333333333333333333333333333333333333333333333333333 +6125 3333333333333333333333333333333333333333333333333333333 +6126 3333333333333333333333333333333333333333333333333333333 +6127 3333333333333333333333333333333333333333333333333333333 +6128 3333333333333333333333333333333333333333333333333333333 +6129 3333333333333333333333333333333333333333333333333333333 +6130 3333333333333333333333333333333333333333333333333333333 +6131 3333333333333333333333333333333333333333333333333333333 +6132 3333333333333333333333333333333333333333333333333333333 +6133 3333333333333333333333333333333333333333333333333333333 +6134 3333333333333333333333333333333333333333333333333333333 +6135 3333333333333333333333333333333333333333333333333333333 +6136 3333333333333333333333333333333333333333333333333333333 +6137 3333333333333333333333333333333333333333333333333333333 +6138 3333333333333333333333333333333333333333333333333333333 +6139 3333333333333333333333333333333333333333333333333333333 +6140 3333333333333333333333333333333333333333333333333333333 +6141 3333333333333333333333333333333333333333333333333333333 +6142 3333333333333333333333333333333333333333333333333333333 +6143 3333333333333333333333333333333333333333333333333333333 +6144 3333333333333333333333333333333333333333333333333333333 +6145 3333333333333333333333333333333333333333333333333333333 +6146 3333333333333333333333333333333333333333333333333333333 +6147 3333333333333333333333333333333333333333333333333333333 +6148 3333333333333333333333333333333333333333333333333333333 +6149 3333333333333333333333333333333333333333333333333333333 +6150 3333333333333333333333333333333333333333333333333333333 +6151 3333333333333333333333333333333333333333333333333333333 +6152 3333333333333333333333333333333333333333333333333333333 +6153 3333333333333333333333333333333333333333333333333333333 +6154 3333333333333333333333333333333333333333333333333333333 +6155 3333333333333333333333333333333333333333333333333333333 +6156 3333333333333333333333333333333333333333333333333333333 +6157 3333333333333333333333333333333333333333333333333333333 +6158 3333333333333333333333333333333333333333333333333333333 +6159 3333333333333333333333333333333333333333333333333333333 +6160 3333333333333333333333333333333333333333333333333333333 +6161 3333333333333333333333333333333333333333333333333333333 +6162 3333333333333333333333333333333333333333333333333333333 +6163 3333333333333333333333333333333333333333333333333333333 +6164 3333333333333333333333333333333333333333333333333333333 +6165 3333333333333333333333333333333333333333333333333333333 +6166 3333333333333333333333333333333333333333333333333333333 +6167 3333333333333333333333333333333333333333333333333333333 +6168 3333333333333333333333333333333333333333333333333333333 +6169 3333333333333333333333333333333333333333333333333333333 +6170 3333333333333333333333333333333333333333333333333333333 +6171 3333333333333333333333333333333333333333333333333333333 +6172 3333333333333333333333333333333333333333333333333333333 +6173 3333333333333333333333333333333333333333333333333333333 +6174 3333333333333333333333333333333333333333333333333333333 +6175 3333333333333333333333333333333333333333333333333333333 +6176 3333333333333333333333333333333333333333333333333333333 +6177 3333333333333333333333333333333333333333333333333333333 +6178 3333333333333333333333333333333333333333333333333333333 +6179 3333333333333333333333333333333333333333333333333333333 +6180 3333333333333333333333333333333333333333333333333333333 +6181 3333333333333333333333333333333333333333333333333333333 +6182 3333333333333333333333333333333333333333333333333333333 +6183 3333333333333333333333333333333333333333333333333333333 +6184 3333333333333333333333333333333333333333333333333333333 +6185 3333333333333333333333333333333333333333333333333333333 +6186 3333333333333333333333333333333333333333333333333333333 +6187 3333333333333333333333333333333333333333333333333333333 +6188 3333333333333333333333333333333333333333333333333333333 +6189 3333333333333333333333333333333333333333333333333333333 +6190 3333333333333333333333333333333333333333333333333333333 +6191 3333333333333333333333333333333333333333333333333333333 +6192 3333333333333333333333333333333333333333333333333333333 +6193 3333333333333333333333333333333333333333333333333333333 +6194 3333333333333333333333333333333333333333333333333333333 +6195 3333333333333333333333333333333333333333333333333333333 +6196 3333333333333333333333333333333333333333333333333333333 +6197 3333333333333333333333333333333333333333333333333333333 +6198 3333333333333333333333333333333333333333333333333333333 +6199 3333333333333333333333333333333333333333333333333333333 +6200 3333333333333333333333333333333333333333333333333333333 +6201 3333333333333333333333333333333333333333333333333333333 +6202 3333333333333333333333333333333333333333333333333333333 +6203 3333333333333333333333333333333333333333333333333333333 +6204 3333333333333333333333333333333333333333333333333333333 +6205 3333333333333333333333333333333333333333333333333333333 +6206 3333333333333333333333333333333333333333333333333333333 +6207 3333333333333333333333333333333333333333333333333333333 +6208 3333333333333333333333333333333333333333333333333333333 +6209 3333333333333333333333333333333333333333333333333333333 +6210 3333333333333333333333333333333333333333333333333333333 +6211 3333333333333333333333333333333333333333333333333333333 +6212 3333333333333333333333333333333333333333333333333333333 +6213 3333333333333333333333333333333333333333333333333333333 +6214 3333333333333333333333333333333333333333333333333333333 +6215 3333333333333333333333333333333333333333333333333333333 +6216 3333333333333333333333333333333333333333333333333333333 +6217 3333333333333333333333333333333333333333333333333333333 +6218 3333333333333333333333333333333333333333333333333333333 +6219 3333333333333333333333333333333333333333333333333333333 +6220 3333333333333333333333333333333333333333333333333333333 +6221 3333333333333333333333333333333333333333333333333333333 +6222 3333333333333333333333333333333333333333333333333333333 +6223 3333333333333333333333333333333333333333333333333333333 +6224 3333333333333333333333333333333333333333333333333333333 +6225 3333333333333333333333333333333333333333333333333333333 +6226 3333333333333333333333333333333333333333333333333333333 +6227 3333333333333333333333333333333333333333333333333333333 +6228 3333333333333333333333333333333333333333333333333333333 +6229 3333333333333333333333333333333333333333333333333333333 +6230 3333333333333333333333333333333333333333333333333333333 +6231 3333333333333333333333333333333333333333333333333333333 +6232 3333333333333333333333333333333333333333333333333333333 +6233 3333333333333333333333333333333333333333333333333333333 +6234 3333333333333333333333333333333333333333333333333333333 +6235 3333333333333333333333333333333333333333333333333333333 +6236 3333333333333333333333333333333333333333333333333333333 +6237 3333333333333333333333333333333333333333333333333333333 +6238 3333333333333333333333333333333333333333333333333333333 +6239 3333333333333333333333333333333333333333333333333333333 +6240 3333333333333333333333333333333333333333333333333333333 +6241 3333333333333333333333333333333333333333333333333333333 +6242 3333333333333333333333333333333333333333333333333333333 +6243 3333333333333333333333333333333333333333333333333333333 +6244 3333333333333333333333333333333333333333333333333333333 +6245 3333333333333333333333333333333333333333333333333333333 +6246 3333333333333333333333333333333333333333333333333333333 +6247 3333333333333333333333333333333333333333333333333333333 +6248 3333333333333333333333333333333333333333333333333333333 +6249 3333333333333333333333333333333333333333333333333333333 +6250 3333333333333333333333333333333333333333333333333333333 +6251 3333333333333333333333333333333333333333333333333333333 +6252 3333333333333333333333333333333333333333333333333333333 +6253 3333333333333333333333333333333333333333333333333333333 +6254 3333333333333333333333333333333333333333333333333333333 +6255 3333333333333333333333333333333333333333333333333333333 +6256 3333333333333333333333333333333333333333333333333333333 +6257 3333333333333333333333333333333333333333333333333333333 +6258 3333333333333333333333333333333333333333333333333333333 +6259 3333333333333333333333333333333333333333333333333333333 +6260 3333333333333333333333333333333333333333333333333333333 +6261 3333333333333333333333333333333333333333333333333333333 +6262 3333333333333333333333333333333333333333333333333333333 +6263 3333333333333333333333333333333333333333333333333333333 +6264 3333333333333333333333333333333333333333333333333333333 +6265 3333333333333333333333333333333333333333333333333333333 +6266 3333333333333333333333333333333333333333333333333333333 +6267 3333333333333333333333333333333333333333333333333333333 +6268 3333333333333333333333333333333333333333333333333333333 +6269 3333333333333333333333333333333333333333333333333333333 +6270 3333333333333333333333333333333333333333333333333333333 +6271 3333333333333333333333333333333333333333333333333333333 +6272 3333333333333333333333333333333333333333333333333333333 +6273 3333333333333333333333333333333333333333333333333333333 +6274 3333333333333333333333333333333333333333333333333333333 +6275 3333333333333333333333333333333333333333333333333333333 +6276 3333333333333333333333333333333333333333333333333333333 +6277 3333333333333333333333333333333333333333333333333333333 +6278 3333333333333333333333333333333333333333333333333333333 +6279 3333333333333333333333333333333333333333333333333333333 +6280 3333333333333333333333333333333333333333333333333333333 +6281 3333333333333333333333333333333333333333333333333333333 +6282 3333333333333333333333333333333333333333333333333333333 +6283 3333333333333333333333333333333333333333333333333333333 +6284 3333333333333333333333333333333333333333333333333333333 +6285 3333333333333333333333333333333333333333333333333333333 +6286 3333333333333333333333333333333333333333333333333333333 +6287 3333333333333333333333333333333333333333333333333333333 +6288 3333333333333333333333333333333333333333333333333333333 +6289 3333333333333333333333333333333333333333333333333333333 +6290 3333333333333333333333333333333333333333333333333333333 +6291 3333333333333333333333333333333333333333333333333333333 +6292 3333333333333333333333333333333333333333333333333333333 +6293 3333333333333333333333333333333333333333333333333333333 +6294 3333333333333333333333333333333333333333333333333333333 +6295 3333333333333333333333333333333333333333333333333333333 +6296 3333333333333333333333333333333333333333333333333333333 +6297 3333333333333333333333333333333333333333333333333333333 +6298 3333333333333333333333333333333333333333333333333333333 +6299 3333333333333333333333333333333333333333333333333333333 +6300 3333333333333333333333333333333333333333333333333333333 +6301 3333333333333333333333333333333333333333333333333333333 +6302 3333333333333333333333333333333333333333333333333333333 +6303 3333333333333333333333333333333333333333333333333333333 +6304 3333333333333333333333333333333333333333333333333333333 +6305 3333333333333333333333333333333333333333333333333333333 +6306 3333333333333333333333333333333333333333333333333333333 +6307 3333333333333333333333333333333333333333333333333333333 +6308 3333333333333333333333333333333333333333333333333333333 +6309 3333333333333333333333333333333333333333333333333333333 +6310 3333333333333333333333333333333333333333333333333333333 +6311 3333333333333333333333333333333333333333333333333333333 +6312 3333333333333333333333333333333333333333333333333333333 +6313 3333333333333333333333333333333333333333333333333333333 +6314 3333333333333333333333333333333333333333333333333333333 +6315 3333333333333333333333333333333333333333333333333333333 +6316 3333333333333333333333333333333333333333333333333333333 +6317 3333333333333333333333333333333333333333333333333333333 +6318 3333333333333333333333333333333333333333333333333333333 +6319 3333333333333333333333333333333333333333333333333333333 +6320 3333333333333333333333333333333333333333333333333333333 +6321 3333333333333333333333333333333333333333333333333333333 +6322 3333333333333333333333333333333333333333333333333333333 +6323 3333333333333333333333333333333333333333333333333333333 +6324 3333333333333333333333333333333333333333333333333333333 +6325 3333333333333333333333333333333333333333333333333333333 +6326 3333333333333333333333333333333333333333333333333333333 +6327 3333333333333333333333333333333333333333333333333333333 +6328 3333333333333333333333333333333333333333333333333333333 +6329 3333333333333333333333333333333333333333333333333333333 +6330 3333333333333333333333333333333333333333333333333333333 +6331 3333333333333333333333333333333333333333333333333333333 +6332 3333333333333333333333333333333333333333333333333333333 +6333 3333333333333333333333333333333333333333333333333333333 +6334 3333333333333333333333333333333333333333333333333333333 +6335 3333333333333333333333333333333333333333333333333333333 +6336 3333333333333333333333333333333333333333333333333333333 +6337 3333333333333333333333333333333333333333333333333333333 +6338 3333333333333333333333333333333333333333333333333333333 +6339 3333333333333333333333333333333333333333333333333333333 +6340 3333333333333333333333333333333333333333333333333333333 +6341 3333333333333333333333333333333333333333333333333333333 +6342 3333333333333333333333333333333333333333333333333333333 +6343 3333333333333333333333333333333333333333333333333333333 +6344 3333333333333333333333333333333333333333333333333333333 +6345 3333333333333333333333333333333333333333333333333333333 +6346 3333333333333333333333333333333333333333333333333333333 +6347 3333333333333333333333333333333333333333333333333333333 +6348 3333333333333333333333333333333333333333333333333333333 +6349 3333333333333333333333333333333333333333333333333333333 +6350 3333333333333333333333333333333333333333333333333333333 +6351 3333333333333333333333333333333333333333333333333333333 +6352 3333333333333333333333333333333333333333333333333333333 +6353 3333333333333333333333333333333333333333333333333333333 +6354 3333333333333333333333333333333333333333333333333333333 +6355 3333333333333333333333333333333333333333333333333333333 +6356 3333333333333333333333333333333333333333333333333333333 +6357 3333333333333333333333333333333333333333333333333333333 +6358 3333333333333333333333333333333333333333333333333333333 +6359 3333333333333333333333333333333333333333333333333333333 +6360 3333333333333333333333333333333333333333333333333333333 +6361 3333333333333333333333333333333333333333333333333333333 +6362 3333333333333333333333333333333333333333333333333333333 +6363 3333333333333333333333333333333333333333333333333333333 +6364 3333333333333333333333333333333333333333333333333333333 +6365 3333333333333333333333333333333333333333333333333333333 +6366 3333333333333333333333333333333333333333333333333333333 +6367 3333333333333333333333333333333333333333333333333333333 +6368 3333333333333333333333333333333333333333333333333333333 +6369 3333333333333333333333333333333333333333333333333333333 +6370 3333333333333333333333333333333333333333333333333333333 +6371 3333333333333333333333333333333333333333333333333333333 +6372 3333333333333333333333333333333333333333333333333333333 +6373 3333333333333333333333333333333333333333333333333333333 +6374 3333333333333333333333333333333333333333333333333333333 +6375 3333333333333333333333333333333333333333333333333333333 +6376 3333333333333333333333333333333333333333333333333333333 +6377 3333333333333333333333333333333333333333333333333333333 +6378 3333333333333333333333333333333333333333333333333333333 +6379 3333333333333333333333333333333333333333333333333333333 +6380 3333333333333333333333333333333333333333333333333333333 +6381 3333333333333333333333333333333333333333333333333333333 +6382 3333333333333333333333333333333333333333333333333333333 +6383 3333333333333333333333333333333333333333333333333333333 +6384 3333333333333333333333333333333333333333333333333333333 +6385 3333333333333333333333333333333333333333333333333333333 +6386 3333333333333333333333333333333333333333333333333333333 +6387 3333333333333333333333333333333333333333333333333333333 +6388 3333333333333333333333333333333333333333333333333333333 +6389 3333333333333333333333333333333333333333333333333333333 +6390 3333333333333333333333333333333333333333333333333333333 +6391 3333333333333333333333333333333333333333333333333333333 +6392 3333333333333333333333333333333333333333333333333333333 +6393 3333333333333333333333333333333333333333333333333333333 +6394 3333333333333333333333333333333333333333333333333333333 +6395 3333333333333333333333333333333333333333333333333333333 +6396 3333333333333333333333333333333333333333333333333333333 +6397 3333333333333333333333333333333333333333333333333333333 +6398 3333333333333333333333333333333333333333333333333333333 +6399 3333333333333333333333333333333333333333333333333333333 +6400 3333333333333333333333333333333333333333333333333333333 +6401 3333333333333333333333333333333333333333333333333333333 +6402 3333333333333333333333333333333333333333333333333333333 +6403 3333333333333333333333333333333333333333333333333333333 +6404 3333333333333333333333333333333333333333333333333333333 +6405 3333333333333333333333333333333333333333333333333333333 +6406 3333333333333333333333333333333333333333333333333333333 +6407 3333333333333333333333333333333333333333333333333333333 +6408 3333333333333333333333333333333333333333333333333333333 +6409 3333333333333333333333333333333333333333333333333333333 +6410 3333333333333333333333333333333333333333333333333333333 +6411 3333333333333333333333333333333333333333333333333333333 +6412 3333333333333333333333333333333333333333333333333333333 +6413 3333333333333333333333333333333333333333333333333333333 +6414 3333333333333333333333333333333333333333333333333333333 +6415 3333333333333333333333333333333333333333333333333333333 +6416 3333333333333333333333333333333333333333333333333333333 +6417 3333333333333333333333333333333333333333333333333333333 +6418 3333333333333333333333333333333333333333333333333333333 +6419 3333333333333333333333333333333333333333333333333333333 +6420 3333333333333333333333333333333333333333333333333333333 +6421 3333333333333333333333333333333333333333333333333333333 +6422 3333333333333333333333333333333333333333333333333333333 +6423 3333333333333333333333333333333333333333333333333333333 +6424 3333333333333333333333333333333333333333333333333333333 +6425 3333333333333333333333333333333333333333333333333333333 +6426 3333333333333333333333333333333333333333333333333333333 +6427 3333333333333333333333333333333333333333333333333333333 +6428 3333333333333333333333333333333333333333333333333333333 +6429 3333333333333333333333333333333333333333333333333333333 +6430 3333333333333333333333333333333333333333333333333333333 +6431 3333333333333333333333333333333333333333333333333333333 +6432 3333333333333333333333333333333333333333333333333333333 +6433 3333333333333333333333333333333333333333333333333333333 +6434 3333333333333333333333333333333333333333333333333333333 +6435 3333333333333333333333333333333333333333333333333333333 +6436 3333333333333333333333333333333333333333333333333333333 +6437 3333333333333333333333333333333333333333333333333333333 +6438 3333333333333333333333333333333333333333333333333333333 +6439 3333333333333333333333333333333333333333333333333333333 +6440 3333333333333333333333333333333333333333333333333333333 +6441 3333333333333333333333333333333333333333333333333333333 +6442 3333333333333333333333333333333333333333333333333333333 +6443 3333333333333333333333333333333333333333333333333333333 +6444 3333333333333333333333333333333333333333333333333333333 +6445 3333333333333333333333333333333333333333333333333333333 +6446 3333333333333333333333333333333333333333333333333333333 +6447 3333333333333333333333333333333333333333333333333333333 +6448 3333333333333333333333333333333333333333333333333333333 +6449 3333333333333333333333333333333333333333333333333333333 +6450 3333333333333333333333333333333333333333333333333333333 +6451 3333333333333333333333333333333333333333333333333333333 +6452 3333333333333333333333333333333333333333333333333333333 +6453 3333333333333333333333333333333333333333333333333333333 +6454 3333333333333333333333333333333333333333333333333333333 +6455 3333333333333333333333333333333333333333333333333333333 +6456 3333333333333333333333333333333333333333333333333333333 +6457 3333333333333333333333333333333333333333333333333333333 +6458 3333333333333333333333333333333333333333333333333333333 +6459 3333333333333333333333333333333333333333333333333333333 +6460 3333333333333333333333333333333333333333333333333333333 +6461 3333333333333333333333333333333333333333333333333333333 +6462 3333333333333333333333333333333333333333333333333333333 +6463 3333333333333333333333333333333333333333333333333333333 +6464 3333333333333333333333333333333333333333333333333333333 +6465 3333333333333333333333333333333333333333333333333333333 +6466 3333333333333333333333333333333333333333333333333333333 +6467 3333333333333333333333333333333333333333333333333333333 +6468 3333333333333333333333333333333333333333333333333333333 +6469 3333333333333333333333333333333333333333333333333333333 +6470 3333333333333333333333333333333333333333333333333333333 +6471 3333333333333333333333333333333333333333333333333333333 +6472 3333333333333333333333333333333333333333333333333333333 +6473 3333333333333333333333333333333333333333333333333333333 +6474 3333333333333333333333333333333333333333333333333333333 +6475 3333333333333333333333333333333333333333333333333333333 +6476 3333333333333333333333333333333333333333333333333333333 +6477 3333333333333333333333333333333333333333333333333333333 +6478 3333333333333333333333333333333333333333333333333333333 +6479 3333333333333333333333333333333333333333333333333333333 +6480 3333333333333333333333333333333333333333333333333333333 +6481 3333333333333333333333333333333333333333333333333333333 +6482 3333333333333333333333333333333333333333333333333333333 +6483 3333333333333333333333333333333333333333333333333333333 +6484 3333333333333333333333333333333333333333333333333333333 +6485 3333333333333333333333333333333333333333333333333333333 +6486 3333333333333333333333333333333333333333333333333333333 +6487 3333333333333333333333333333333333333333333333333333333 +6488 3333333333333333333333333333333333333333333333333333333 +6489 3333333333333333333333333333333333333333333333333333333 +6490 3333333333333333333333333333333333333333333333333333333 +6491 3333333333333333333333333333333333333333333333333333333 +6492 3333333333333333333333333333333333333333333333333333333 +6493 3333333333333333333333333333333333333333333333333333333 +6494 3333333333333333333333333333333333333333333333333333333 +6495 3333333333333333333333333333333333333333333333333333333 +6496 3333333333333333333333333333333333333333333333333333333 +6497 3333333333333333333333333333333333333333333333333333333 +6498 3333333333333333333333333333333333333333333333333333333 +6499 3333333333333333333333333333333333333333333333333333333 +6500 3333333333333333333333333333333333333333333333333333333 +6501 3333333333333333333333333333333333333333333333333333333 +6502 3333333333333333333333333333333333333333333333333333333 +6503 3333333333333333333333333333333333333333333333333333333 +6504 3333333333333333333333333333333333333333333333333333333 +6505 3333333333333333333333333333333333333333333333333333333 +6506 3333333333333333333333333333333333333333333333333333333 +6507 3333333333333333333333333333333333333333333333333333333 +6508 3333333333333333333333333333333333333333333333333333333 +6509 3333333333333333333333333333333333333333333333333333333 +6510 3333333333333333333333333333333333333333333333333333333 +6511 3333333333333333333333333333333333333333333333333333333 +6512 3333333333333333333333333333333333333333333333333333333 +6513 3333333333333333333333333333333333333333333333333333333 +6514 3333333333333333333333333333333333333333333333333333333 +6515 3333333333333333333333333333333333333333333333333333333 +6516 3333333333333333333333333333333333333333333333333333333 +6517 3333333333333333333333333333333333333333333333333333333 +6518 3333333333333333333333333333333333333333333333333333333 +6519 3333333333333333333333333333333333333333333333333333333 +6520 3333333333333333333333333333333333333333333333333333333 +6521 3333333333333333333333333333333333333333333333333333333 +6522 3333333333333333333333333333333333333333333333333333333 +6523 3333333333333333333333333333333333333333333333333333333 +6524 3333333333333333333333333333333333333333333333333333333 +6525 3333333333333333333333333333333333333333333333333333333 +6526 3333333333333333333333333333333333333333333333333333333 +6527 3333333333333333333333333333333333333333333333333333333 +6528 3333333333333333333333333333333333333333333333333333333 +6529 3333333333333333333333333333333333333333333333333333333 +6530 3333333333333333333333333333333333333333333333333333333 +6531 3333333333333333333333333333333333333333333333333333333 +6532 3333333333333333333333333333333333333333333333333333333 +6533 3333333333333333333333333333333333333333333333333333333 +6534 3333333333333333333333333333333333333333333333333333333 +6535 3333333333333333333333333333333333333333333333333333333 +6536 3333333333333333333333333333333333333333333333333333333 +6537 3333333333333333333333333333333333333333333333333333333 +6538 3333333333333333333333333333333333333333333333333333333 +6539 3333333333333333333333333333333333333333333333333333333 +6540 3333333333333333333333333333333333333333333333333333333 +6541 3333333333333333333333333333333333333333333333333333333 +6542 3333333333333333333333333333333333333333333333333333333 +6543 3333333333333333333333333333333333333333333333333333333 +6544 3333333333333333333333333333333333333333333333333333333 +6545 3333333333333333333333333333333333333333333333333333333 +6546 3333333333333333333333333333333333333333333333333333333 +6547 3333333333333333333333333333333333333333333333333333333 +6548 3333333333333333333333333333333333333333333333333333333 +6549 3333333333333333333333333333333333333333333333333333333 +6550 3333333333333333333333333333333333333333333333333333333 +6551 3333333333333333333333333333333333333333333333333333333 +6552 3333333333333333333333333333333333333333333333333333333 +6553 3333333333333333333333333333333333333333333333333333333 +6554 3333333333333333333333333333333333333333333333333333333 +6555 3333333333333333333333333333333333333333333333333333333 +6556 3333333333333333333333333333333333333333333333333333333 +6557 3333333333333333333333333333333333333333333333333333333 +6558 3333333333333333333333333333333333333333333333333333333 +6559 3333333333333333333333333333333333333333333333333333333 +6560 3333333333333333333333333333333333333333333333333333333 +6561 3333333333333333333333333333333333333333333333333333333 +6562 3333333333333333333333333333333333333333333333333333333 +6563 3333333333333333333333333333333333333333333333333333333 +6564 3333333333333333333333333333333333333333333333333333333 +6565 3333333333333333333333333333333333333333333333333333333 +6566 3333333333333333333333333333333333333333333333333333333 +6567 3333333333333333333333333333333333333333333333333333333 +6568 3333333333333333333333333333333333333333333333333333333 +6569 3333333333333333333333333333333333333333333333333333333 +6570 3333333333333333333333333333333333333333333333333333333 +6571 3333333333333333333333333333333333333333333333333333333 +6572 3333333333333333333333333333333333333333333333333333333 +6573 3333333333333333333333333333333333333333333333333333333 +6574 3333333333333333333333333333333333333333333333333333333 +6575 3333333333333333333333333333333333333333333333333333333 +6576 3333333333333333333333333333333333333333333333333333333 +6577 3333333333333333333333333333333333333333333333333333333 +6578 3333333333333333333333333333333333333333333333333333333 +6579 3333333333333333333333333333333333333333333333333333333 +6580 3333333333333333333333333333333333333333333333333333333 +6581 3333333333333333333333333333333333333333333333333333333 +6582 3333333333333333333333333333333333333333333333333333333 +6583 3333333333333333333333333333333333333333333333333333333 +6584 3333333333333333333333333333333333333333333333333333333 +6585 3333333333333333333333333333333333333333333333333333333 +6586 3333333333333333333333333333333333333333333333333333333 +6587 3333333333333333333333333333333333333333333333333333333 +6588 3333333333333333333333333333333333333333333333333333333 +6589 3333333333333333333333333333333333333333333333333333333 +6590 3333333333333333333333333333333333333333333333333333333 +6591 3333333333333333333333333333333333333333333333333333333 +6592 3333333333333333333333333333333333333333333333333333333 +6593 3333333333333333333333333333333333333333333333333333333 +6594 3333333333333333333333333333333333333333333333333333333 +6595 3333333333333333333333333333333333333333333333333333333 +6596 3333333333333333333333333333333333333333333333333333333 +6597 3333333333333333333333333333333333333333333333333333333 +6598 3333333333333333333333333333333333333333333333333333333 +6599 3333333333333333333333333333333333333333333333333333333 +6600 3333333333333333333333333333333333333333333333333333333 +6601 3333333333333333333333333333333333333333333333333333333 +6602 3333333333333333333333333333333333333333333333333333333 +6603 3333333333333333333333333333333333333333333333333333333 +6604 3333333333333333333333333333333333333333333333333333333 +6605 3333333333333333333333333333333333333333333333333333333 +6606 3333333333333333333333333333333333333333333333333333333 +6607 3333333333333333333333333333333333333333333333333333333 +6608 3333333333333333333333333333333333333333333333333333333 +6609 3333333333333333333333333333333333333333333333333333333 +6610 3333333333333333333333333333333333333333333333333333333 +6611 3333333333333333333333333333333333333333333333333333333 +6612 3333333333333333333333333333333333333333333333333333333 +6613 3333333333333333333333333333333333333333333333333333333 +6614 3333333333333333333333333333333333333333333333333333333 +6615 3333333333333333333333333333333333333333333333333333333 +6616 3333333333333333333333333333333333333333333333333333333 +6617 3333333333333333333333333333333333333333333333333333333 +6618 3333333333333333333333333333333333333333333333333333333 +6619 3333333333333333333333333333333333333333333333333333333 +6620 3333333333333333333333333333333333333333333333333333333 +6621 3333333333333333333333333333333333333333333333333333333 +6622 3333333333333333333333333333333333333333333333333333333 +6623 3333333333333333333333333333333333333333333333333333333 +6624 3333333333333333333333333333333333333333333333333333333 +6625 3333333333333333333333333333333333333333333333333333333 +6626 3333333333333333333333333333333333333333333333333333333 +6627 3333333333333333333333333333333333333333333333333333333 +6628 3333333333333333333333333333333333333333333333333333333 +6629 3333333333333333333333333333333333333333333333333333333 +6630 3333333333333333333333333333333333333333333333333333333 +6631 3333333333333333333333333333333333333333333333333333333 +6632 3333333333333333333333333333333333333333333333333333333 +6633 3333333333333333333333333333333333333333333333333333333 +6634 3333333333333333333333333333333333333333333333333333333 +6635 3333333333333333333333333333333333333333333333333333333 +6636 3333333333333333333333333333333333333333333333333333333 +6637 3333333333333333333333333333333333333333333333333333333 +6638 3333333333333333333333333333333333333333333333333333333 +6639 3333333333333333333333333333333333333333333333333333333 +6640 3333333333333333333333333333333333333333333333333333333 +6641 3333333333333333333333333333333333333333333333333333333 +6642 3333333333333333333333333333333333333333333333333333333 +6643 3333333333333333333333333333333333333333333333333333333 +6644 3333333333333333333333333333333333333333333333333333333 +6645 3333333333333333333333333333333333333333333333333333333 +6646 3333333333333333333333333333333333333333333333333333333 +6647 3333333333333333333333333333333333333333333333333333333 +6648 3333333333333333333333333333333333333333333333333333333 +6649 3333333333333333333333333333333333333333333333333333333 +6650 3333333333333333333333333333333333333333333333333333333 +6651 3333333333333333333333333333333333333333333333333333333 +6652 3333333333333333333333333333333333333333333333333333333 +6653 3333333333333333333333333333333333333333333333333333333 +6654 3333333333333333333333333333333333333333333333333333333 +6655 3333333333333333333333333333333333333333333333333333333 +6656 3333333333333333333333333333333333333333333333333333333 +6657 3333333333333333333333333333333333333333333333333333333 +6658 3333333333333333333333333333333333333333333333333333333 +6659 3333333333333333333333333333333333333333333333333333333 +6660 3333333333333333333333333333333333333333333333333333333 +6661 3333333333333333333333333333333333333333333333333333333 +6662 3333333333333333333333333333333333333333333333333333333 +6663 3333333333333333333333333333333333333333333333333333333 +6664 3333333333333333333333333333333333333333333333333333333 +6665 3333333333333333333333333333333333333333333333333333333 +6666 3333333333333333333333333333333333333333333333333333333 +6667 3333333333333333333333333333333333333333333333333333333 +6668 3333333333333333333333333333333333333333333333333333333 +6669 3333333333333333333333333333333333333333333333333333333 +6670 3333333333333333333333333333333333333333333333333333333 +6671 3333333333333333333333333333333333333333333333333333333 +6672 3333333333333333333333333333333333333333333333333333333 +6673 3333333333333333333333333333333333333333333333333333333 +6674 3333333333333333333333333333333333333333333333333333333 +6675 3333333333333333333333333333333333333333333333333333333 +6676 3333333333333333333333333333333333333333333333333333333 +6677 3333333333333333333333333333333333333333333333333333333 +6678 3333333333333333333333333333333333333333333333333333333 +6679 3333333333333333333333333333333333333333333333333333333 +6680 3333333333333333333333333333333333333333333333333333333 +6681 3333333333333333333333333333333333333333333333333333333 +6682 3333333333333333333333333333333333333333333333333333333 +6683 3333333333333333333333333333333333333333333333333333333 +6684 3333333333333333333333333333333333333333333333333333333 +6685 3333333333333333333333333333333333333333333333333333333 +6686 3333333333333333333333333333333333333333333333333333333 +6687 3333333333333333333333333333333333333333333333333333333 +6688 3333333333333333333333333333333333333333333333333333333 +6689 3333333333333333333333333333333333333333333333333333333 +6690 3333333333333333333333333333333333333333333333333333333 +6691 3333333333333333333333333333333333333333333333333333333 +6692 3333333333333333333333333333333333333333333333333333333 +6693 3333333333333333333333333333333333333333333333333333333 +6694 3333333333333333333333333333333333333333333333333333333 +6695 3333333333333333333333333333333333333333333333333333333 +6696 3333333333333333333333333333333333333333333333333333333 +6697 3333333333333333333333333333333333333333333333333333333 +6698 3333333333333333333333333333333333333333333333333333333 +6699 3333333333333333333333333333333333333333333333333333333 +6700 3333333333333333333333333333333333333333333333333333333 +6701 3333333333333333333333333333333333333333333333333333333 +6702 3333333333333333333333333333333333333333333333333333333 +6703 3333333333333333333333333333333333333333333333333333333 +6704 3333333333333333333333333333333333333333333333333333333 +6705 3333333333333333333333333333333333333333333333333333333 +6706 3333333333333333333333333333333333333333333333333333333 +6707 3333333333333333333333333333333333333333333333333333333 +6708 3333333333333333333333333333333333333333333333333333333 +6709 3333333333333333333333333333333333333333333333333333333 +6710 3333333333333333333333333333333333333333333333333333333 +6711 3333333333333333333333333333333333333333333333333333333 +6712 3333333333333333333333333333333333333333333333333333333 +6713 3333333333333333333333333333333333333333333333333333333 +6714 3333333333333333333333333333333333333333333333333333333 +6715 3333333333333333333333333333333333333333333333333333333 +6716 3333333333333333333333333333333333333333333333333333333 +6717 3333333333333333333333333333333333333333333333333333333 +6718 3333333333333333333333333333333333333333333333333333333 +6719 3333333333333333333333333333333333333333333333333333333 +6720 3333333333333333333333333333333333333333333333333333333 +6721 3333333333333333333333333333333333333333333333333333333 +6722 3333333333333333333333333333333333333333333333333333333 +6723 3333333333333333333333333333333333333333333333333333333 +6724 3333333333333333333333333333333333333333333333333333333 +6725 3333333333333333333333333333333333333333333333333333333 +6726 3333333333333333333333333333333333333333333333333333333 +6727 3333333333333333333333333333333333333333333333333333333 +6728 3333333333333333333333333333333333333333333333333333333 +6729 3333333333333333333333333333333333333333333333333333333 +6730 3333333333333333333333333333333333333333333333333333333 +6731 3333333333333333333333333333333333333333333333333333333 +6732 3333333333333333333333333333333333333333333333333333333 +6733 3333333333333333333333333333333333333333333333333333333 +6734 3333333333333333333333333333333333333333333333333333333 +6735 3333333333333333333333333333333333333333333333333333333 +6736 3333333333333333333333333333333333333333333333333333333 +6737 3333333333333333333333333333333333333333333333333333333 +6738 3333333333333333333333333333333333333333333333333333333 +6739 3333333333333333333333333333333333333333333333333333333 +6740 3333333333333333333333333333333333333333333333333333333 +6741 3333333333333333333333333333333333333333333333333333333 +6742 3333333333333333333333333333333333333333333333333333333 +6743 3333333333333333333333333333333333333333333333333333333 +6744 3333333333333333333333333333333333333333333333333333333 +6745 3333333333333333333333333333333333333333333333333333333 +6746 3333333333333333333333333333333333333333333333333333333 +6747 3333333333333333333333333333333333333333333333333333333 +6748 3333333333333333333333333333333333333333333333333333333 +6749 3333333333333333333333333333333333333333333333333333333 +6750 3333333333333333333333333333333333333333333333333333333 +6751 3333333333333333333333333333333333333333333333333333333 +6752 3333333333333333333333333333333333333333333333333333333 +6753 3333333333333333333333333333333333333333333333333333333 +6754 3333333333333333333333333333333333333333333333333333333 +6755 3333333333333333333333333333333333333333333333333333333 +6756 3333333333333333333333333333333333333333333333333333333 +6757 3333333333333333333333333333333333333333333333333333333 +6758 3333333333333333333333333333333333333333333333333333333 +6759 3333333333333333333333333333333333333333333333333333333 +6760 3333333333333333333333333333333333333333333333333333333 +6761 3333333333333333333333333333333333333333333333333333333 +6762 3333333333333333333333333333333333333333333333333333333 +6763 3333333333333333333333333333333333333333333333333333333 +6764 3333333333333333333333333333333333333333333333333333333 +6765 3333333333333333333333333333333333333333333333333333333 +6766 3333333333333333333333333333333333333333333333333333333 +6767 3333333333333333333333333333333333333333333333333333333 +6768 3333333333333333333333333333333333333333333333333333333 +6769 3333333333333333333333333333333333333333333333333333333 +6770 3333333333333333333333333333333333333333333333333333333 +6771 3333333333333333333333333333333333333333333333333333333 +6772 3333333333333333333333333333333333333333333333333333333 +6773 3333333333333333333333333333333333333333333333333333333 +6774 3333333333333333333333333333333333333333333333333333333 +6775 3333333333333333333333333333333333333333333333333333333 +6776 3333333333333333333333333333333333333333333333333333333 +6777 3333333333333333333333333333333333333333333333333333333 +6778 3333333333333333333333333333333333333333333333333333333 +6779 3333333333333333333333333333333333333333333333333333333 +6780 3333333333333333333333333333333333333333333333333333333 +6781 3333333333333333333333333333333333333333333333333333333 +6782 3333333333333333333333333333333333333333333333333333333 +6783 3333333333333333333333333333333333333333333333333333333 +6784 3333333333333333333333333333333333333333333333333333333 +6785 3333333333333333333333333333333333333333333333333333333 +6786 3333333333333333333333333333333333333333333333333333333 +6787 3333333333333333333333333333333333333333333333333333333 +6788 3333333333333333333333333333333333333333333333333333333 +6789 3333333333333333333333333333333333333333333333333333333 +6790 3333333333333333333333333333333333333333333333333333333 +6791 3333333333333333333333333333333333333333333333333333333 +6792 3333333333333333333333333333333333333333333333333333333 +6793 3333333333333333333333333333333333333333333333333333333 +6794 3333333333333333333333333333333333333333333333333333333 +6795 3333333333333333333333333333333333333333333333333333333 +6796 3333333333333333333333333333333333333333333333333333333 +6797 3333333333333333333333333333333333333333333333333333333 +6798 3333333333333333333333333333333333333333333333333333333 +6799 3333333333333333333333333333333333333333333333333333333 +6800 3333333333333333333333333333333333333333333333333333333 +6801 3333333333333333333333333333333333333333333333333333333 +6802 3333333333333333333333333333333333333333333333333333333 +6803 3333333333333333333333333333333333333333333333333333333 +6804 3333333333333333333333333333333333333333333333333333333 +6805 3333333333333333333333333333333333333333333333333333333 +6806 3333333333333333333333333333333333333333333333333333333 +6807 3333333333333333333333333333333333333333333333333333333 +6808 3333333333333333333333333333333333333333333333333333333 +6809 3333333333333333333333333333333333333333333333333333333 +6810 3333333333333333333333333333333333333333333333333333333 +6811 3333333333333333333333333333333333333333333333333333333 +6812 3333333333333333333333333333333333333333333333333333333 +6813 3333333333333333333333333333333333333333333333333333333 +6814 3333333333333333333333333333333333333333333333333333333 +6815 3333333333333333333333333333333333333333333333333333333 +6816 3333333333333333333333333333333333333333333333333333333 +6817 3333333333333333333333333333333333333333333333333333333 +6818 3333333333333333333333333333333333333333333333333333333 +6819 3333333333333333333333333333333333333333333333333333333 +6820 3333333333333333333333333333333333333333333333333333333 +6821 3333333333333333333333333333333333333333333333333333333 +6822 3333333333333333333333333333333333333333333333333333333 +6823 3333333333333333333333333333333333333333333333333333333 +6824 3333333333333333333333333333333333333333333333333333333 +6825 3333333333333333333333333333333333333333333333333333333 +6826 3333333333333333333333333333333333333333333333333333333 +6827 3333333333333333333333333333333333333333333333333333333 +6828 3333333333333333333333333333333333333333333333333333333 +6829 3333333333333333333333333333333333333333333333333333333 +6830 3333333333333333333333333333333333333333333333333333333 +6831 3333333333333333333333333333333333333333333333333333333 +6832 3333333333333333333333333333333333333333333333333333333 +6833 3333333333333333333333333333333333333333333333333333333 +6834 3333333333333333333333333333333333333333333333333333333 +6835 3333333333333333333333333333333333333333333333333333333 +6836 3333333333333333333333333333333333333333333333333333333 +6837 3333333333333333333333333333333333333333333333333333333 +6838 3333333333333333333333333333333333333333333333333333333 +6839 3333333333333333333333333333333333333333333333333333333 +6840 3333333333333333333333333333333333333333333333333333333 +6841 3333333333333333333333333333333333333333333333333333333 +6842 3333333333333333333333333333333333333333333333333333333 +6843 3333333333333333333333333333333333333333333333333333333 +6844 3333333333333333333333333333333333333333333333333333333 +6845 3333333333333333333333333333333333333333333333333333333 +6846 3333333333333333333333333333333333333333333333333333333 +6847 3333333333333333333333333333333333333333333333333333333 +6848 3333333333333333333333333333333333333333333333333333333 +6849 3333333333333333333333333333333333333333333333333333333 +6850 3333333333333333333333333333333333333333333333333333333 +6851 3333333333333333333333333333333333333333333333333333333 +6852 3333333333333333333333333333333333333333333333333333333 +6853 3333333333333333333333333333333333333333333333333333333 +6854 3333333333333333333333333333333333333333333333333333333 +6855 3333333333333333333333333333333333333333333333333333333 +6856 3333333333333333333333333333333333333333333333333333333 +6857 3333333333333333333333333333333333333333333333333333333 +6858 3333333333333333333333333333333333333333333333333333333 +6859 3333333333333333333333333333333333333333333333333333333 +6860 3333333333333333333333333333333333333333333333333333333 +6861 3333333333333333333333333333333333333333333333333333333 +6862 3333333333333333333333333333333333333333333333333333333 +6863 3333333333333333333333333333333333333333333333333333333 +6864 3333333333333333333333333333333333333333333333333333333 +6865 3333333333333333333333333333333333333333333333333333333 +6866 3333333333333333333333333333333333333333333333333333333 +6867 3333333333333333333333333333333333333333333333333333333 +6868 3333333333333333333333333333333333333333333333333333333 +6869 3333333333333333333333333333333333333333333333333333333 +6870 3333333333333333333333333333333333333333333333333333333 +6871 3333333333333333333333333333333333333333333333333333333 +6872 3333333333333333333333333333333333333333333333333333333 +6873 3333333333333333333333333333333333333333333333333333333 +6874 3333333333333333333333333333333333333333333333333333333 +6875 3333333333333333333333333333333333333333333333333333333 +6876 3333333333333333333333333333333333333333333333333333333 +6877 3333333333333333333333333333333333333333333333333333333 +6878 3333333333333333333333333333333333333333333333333333333 +6879 3333333333333333333333333333333333333333333333333333333 +6880 3333333333333333333333333333333333333333333333333333333 +6881 3333333333333333333333333333333333333333333333333333333 +6882 3333333333333333333333333333333333333333333333333333333 +6883 3333333333333333333333333333333333333333333333333333333 +6884 3333333333333333333333333333333333333333333333333333333 +6885 3333333333333333333333333333333333333333333333333333333 +6886 3333333333333333333333333333333333333333333333333333333 +6887 3333333333333333333333333333333333333333333333333333333 +6888 3333333333333333333333333333333333333333333333333333333 +6889 3333333333333333333333333333333333333333333333333333333 +6890 3333333333333333333333333333333333333333333333333333333 +6891 3333333333333333333333333333333333333333333333333333333 +6892 3333333333333333333333333333333333333333333333333333333 +6893 3333333333333333333333333333333333333333333333333333333 +6894 3333333333333333333333333333333333333333333333333333333 +6895 3333333333333333333333333333333333333333333333333333333 +6896 3333333333333333333333333333333333333333333333333333333 +6897 3333333333333333333333333333333333333333333333333333333 +6898 3333333333333333333333333333333333333333333333333333333 +6899 3333333333333333333333333333333333333333333333333333333 +6900 3333333333333333333333333333333333333333333333333333333 +6901 3333333333333333333333333333333333333333333333333333333 +6902 3333333333333333333333333333333333333333333333333333333 +6903 3333333333333333333333333333333333333333333333333333333 +6904 3333333333333333333333333333333333333333333333333333333 +6905 3333333333333333333333333333333333333333333333333333333 +6906 3333333333333333333333333333333333333333333333333333333 +6907 3333333333333333333333333333333333333333333333333333333 +6908 3333333333333333333333333333333333333333333333333333333 +6909 3333333333333333333333333333333333333333333333333333333 +6910 3333333333333333333333333333333333333333333333333333333 +6911 3333333333333333333333333333333333333333333333333333333 +6912 3333333333333333333333333333333333333333333333333333333 +6913 3333333333333333333333333333333333333333333333333333333 +6914 3333333333333333333333333333333333333333333333333333333 +6915 3333333333333333333333333333333333333333333333333333333 +6916 3333333333333333333333333333333333333333333333333333333 +6917 3333333333333333333333333333333333333333333333333333333 +6918 3333333333333333333333333333333333333333333333333333333 +6919 3333333333333333333333333333333333333333333333333333333 +6920 3333333333333333333333333333333333333333333333333333333 +6921 3333333333333333333333333333333333333333333333333333333 +6922 3333333333333333333333333333333333333333333333333333333 +6923 3333333333333333333333333333333333333333333333333333333 +6924 3333333333333333333333333333333333333333333333333333333 +6925 3333333333333333333333333333333333333333333333333333333 +6926 3333333333333333333333333333333333333333333333333333333 +6927 3333333333333333333333333333333333333333333333333333333 +6928 3333333333333333333333333333333333333333333333333333333 +6929 3333333333333333333333333333333333333333333333333333333 +6930 3333333333333333333333333333333333333333333333333333333 +6931 3333333333333333333333333333333333333333333333333333333 +6932 3333333333333333333333333333333333333333333333333333333 +6933 3333333333333333333333333333333333333333333333333333333 +6934 3333333333333333333333333333333333333333333333333333333 +6935 3333333333333333333333333333333333333333333333333333333 +6936 3333333333333333333333333333333333333333333333333333333 +6937 3333333333333333333333333333333333333333333333333333333 +6938 3333333333333333333333333333333333333333333333333333333 +6939 3333333333333333333333333333333333333333333333333333333 +6940 3333333333333333333333333333333333333333333333333333333 +6941 3333333333333333333333333333333333333333333333333333333 +6942 3333333333333333333333333333333333333333333333333333333 +6943 3333333333333333333333333333333333333333333333333333333 +6944 3333333333333333333333333333333333333333333333333333333 +6945 3333333333333333333333333333333333333333333333333333333 +6946 3333333333333333333333333333333333333333333333333333333 +6947 3333333333333333333333333333333333333333333333333333333 +6948 3333333333333333333333333333333333333333333333333333333 +6949 3333333333333333333333333333333333333333333333333333333 +6950 3333333333333333333333333333333333333333333333333333333 +6951 3333333333333333333333333333333333333333333333333333333 +6952 3333333333333333333333333333333333333333333333333333333 +6953 3333333333333333333333333333333333333333333333333333333 +6954 3333333333333333333333333333333333333333333333333333333 +6955 3333333333333333333333333333333333333333333333333333333 +6956 3333333333333333333333333333333333333333333333333333333 +6957 3333333333333333333333333333333333333333333333333333333 +6958 3333333333333333333333333333333333333333333333333333333 +6959 3333333333333333333333333333333333333333333333333333333 +6960 3333333333333333333333333333333333333333333333333333333 +6961 3333333333333333333333333333333333333333333333333333333 +6962 3333333333333333333333333333333333333333333333333333333 +6963 3333333333333333333333333333333333333333333333333333333 +6964 3333333333333333333333333333333333333333333333333333333 +6965 3333333333333333333333333333333333333333333333333333333 +6966 3333333333333333333333333333333333333333333333333333333 +6967 3333333333333333333333333333333333333333333333333333333 +6968 3333333333333333333333333333333333333333333333333333333 +6969 3333333333333333333333333333333333333333333333333333333 +6970 3333333333333333333333333333333333333333333333333333333 +6971 3333333333333333333333333333333333333333333333333333333 +6972 3333333333333333333333333333333333333333333333333333333 +6973 3333333333333333333333333333333333333333333333333333333 +6974 3333333333333333333333333333333333333333333333333333333 +6975 3333333333333333333333333333333333333333333333333333333 +6976 3333333333333333333333333333333333333333333333333333333 +6977 3333333333333333333333333333333333333333333333333333333 +6978 3333333333333333333333333333333333333333333333333333333 +6979 3333333333333333333333333333333333333333333333333333333 +6980 3333333333333333333333333333333333333333333333333333333 +6981 3333333333333333333333333333333333333333333333333333333 +6982 3333333333333333333333333333333333333333333333333333333 +6983 3333333333333333333333333333333333333333333333333333333 +6984 3333333333333333333333333333333333333333333333333333333 +6985 3333333333333333333333333333333333333333333333333333333 +6986 3333333333333333333333333333333333333333333333333333333 +6987 3333333333333333333333333333333333333333333333333333333 +6988 3333333333333333333333333333333333333333333333333333333 +6989 3333333333333333333333333333333333333333333333333333333 +6990 3333333333333333333333333333333333333333333333333333333 +6991 3333333333333333333333333333333333333333333333333333333 +6992 3333333333333333333333333333333333333333333333333333333 +6993 3333333333333333333333333333333333333333333333333333333 +6994 3333333333333333333333333333333333333333333333333333333 +6995 3333333333333333333333333333333333333333333333333333333 +6996 3333333333333333333333333333333333333333333333333333333 +6997 3333333333333333333333333333333333333333333333333333333 +6998 3333333333333333333333333333333333333333333333333333333 +6999 3333333333333333333333333333333333333333333333333333333 +7000 3333333333333333333333333333333333333333333333333333333 +7001 3333333333333333333333333333333333333333333333333333333 +7002 3333333333333333333333333333333333333333333333333333333 +7003 3333333333333333333333333333333333333333333333333333333 +7004 3333333333333333333333333333333333333333333333333333333 +7005 3333333333333333333333333333333333333333333333333333333 +7006 3333333333333333333333333333333333333333333333333333333 +7007 3333333333333333333333333333333333333333333333333333333 +7008 3333333333333333333333333333333333333333333333333333333 +7009 3333333333333333333333333333333333333333333333333333333 +7010 3333333333333333333333333333333333333333333333333333333 +7011 3333333333333333333333333333333333333333333333333333333 +7012 3333333333333333333333333333333333333333333333333333333 +7013 3333333333333333333333333333333333333333333333333333333 +7014 3333333333333333333333333333333333333333333333333333333 +7015 3333333333333333333333333333333333333333333333333333333 +7016 3333333333333333333333333333333333333333333333333333333 +7017 3333333333333333333333333333333333333333333333333333333 +7018 3333333333333333333333333333333333333333333333333333333 +7019 3333333333333333333333333333333333333333333333333333333 +7020 3333333333333333333333333333333333333333333333333333333 +7021 3333333333333333333333333333333333333333333333333333333 +7022 3333333333333333333333333333333333333333333333333333333 +7023 3333333333333333333333333333333333333333333333333333333 +7024 3333333333333333333333333333333333333333333333333333333 +7025 3333333333333333333333333333333333333333333333333333333 +7026 3333333333333333333333333333333333333333333333333333333 +7027 3333333333333333333333333333333333333333333333333333333 +7028 3333333333333333333333333333333333333333333333333333333 +7029 3333333333333333333333333333333333333333333333333333333 +7030 3333333333333333333333333333333333333333333333333333333 +7031 3333333333333333333333333333333333333333333333333333333 +7032 3333333333333333333333333333333333333333333333333333333 +7033 3333333333333333333333333333333333333333333333333333333 +7034 3333333333333333333333333333333333333333333333333333333 +7035 3333333333333333333333333333333333333333333333333333333 +7036 3333333333333333333333333333333333333333333333333333333 +7037 3333333333333333333333333333333333333333333333333333333 +7038 3333333333333333333333333333333333333333333333333333333 +7039 3333333333333333333333333333333333333333333333333333333 +7040 3333333333333333333333333333333333333333333333333333333 +7041 3333333333333333333333333333333333333333333333333333333 +7042 3333333333333333333333333333333333333333333333333333333 +7043 3333333333333333333333333333333333333333333333333333333 +7044 3333333333333333333333333333333333333333333333333333333 +7045 3333333333333333333333333333333333333333333333333333333 +7046 3333333333333333333333333333333333333333333333333333333 +7047 3333333333333333333333333333333333333333333333333333333 +7048 3333333333333333333333333333333333333333333333333333333 +7049 3333333333333333333333333333333333333333333333333333333 +7050 3333333333333333333333333333333333333333333333333333333 +7051 3333333333333333333333333333333333333333333333333333333 +7052 3333333333333333333333333333333333333333333333333333333 +7053 3333333333333333333333333333333333333333333333333333333 +7054 3333333333333333333333333333333333333333333333333333333 +7055 3333333333333333333333333333333333333333333333333333333 +7056 3333333333333333333333333333333333333333333333333333333 +7057 3333333333333333333333333333333333333333333333333333333 +7058 3333333333333333333333333333333333333333333333333333333 +7059 3333333333333333333333333333333333333333333333333333333 +7060 3333333333333333333333333333333333333333333333333333333 +7061 3333333333333333333333333333333333333333333333333333333 +7062 3333333333333333333333333333333333333333333333333333333 +7063 3333333333333333333333333333333333333333333333333333333 +7064 3333333333333333333333333333333333333333333333333333333 +7065 3333333333333333333333333333333333333333333333333333333 +7066 3333333333333333333333333333333333333333333333333333333 +7067 3333333333333333333333333333333333333333333333333333333 +7068 3333333333333333333333333333333333333333333333333333333 +7069 3333333333333333333333333333333333333333333333333333333 +7070 3333333333333333333333333333333333333333333333333333333 +7071 3333333333333333333333333333333333333333333333333333333 +7072 3333333333333333333333333333333333333333333333333333333 +7073 3333333333333333333333333333333333333333333333333333333 +7074 3333333333333333333333333333333333333333333333333333333 +7075 3333333333333333333333333333333333333333333333333333333 +7076 3333333333333333333333333333333333333333333333333333333 +7077 3333333333333333333333333333333333333333333333333333333 +7078 3333333333333333333333333333333333333333333333333333333 +7079 3333333333333333333333333333333333333333333333333333333 +7080 3333333333333333333333333333333333333333333333333333333 +7081 3333333333333333333333333333333333333333333333333333333 +7082 3333333333333333333333333333333333333333333333333333333 +7083 3333333333333333333333333333333333333333333333333333333 +7084 3333333333333333333333333333333333333333333333333333333 +7085 3333333333333333333333333333333333333333333333333333333 +7086 3333333333333333333333333333333333333333333333333333333 +7087 3333333333333333333333333333333333333333333333333333333 +7088 3333333333333333333333333333333333333333333333333333333 +7089 3333333333333333333333333333333333333333333333333333333 +7090 3333333333333333333333333333333333333333333333333333333 +7091 3333333333333333333333333333333333333333333333333333333 +7092 3333333333333333333333333333333333333333333333333333333 +7093 3333333333333333333333333333333333333333333333333333333 +7094 3333333333333333333333333333333333333333333333333333333 +7095 3333333333333333333333333333333333333333333333333333333 +7096 3333333333333333333333333333333333333333333333333333333 +7097 3333333333333333333333333333333333333333333333333333333 +7098 3333333333333333333333333333333333333333333333333333333 +7099 3333333333333333333333333333333333333333333333333333333 +7100 3333333333333333333333333333333333333333333333333333333 +7101 3333333333333333333333333333333333333333333333333333333 +7102 3333333333333333333333333333333333333333333333333333333 +7103 3333333333333333333333333333333333333333333333333333333 +7104 3333333333333333333333333333333333333333333333333333333 +7105 3333333333333333333333333333333333333333333333333333333 +7106 3333333333333333333333333333333333333333333333333333333 +7107 3333333333333333333333333333333333333333333333333333333 +7108 3333333333333333333333333333333333333333333333333333333 +7109 3333333333333333333333333333333333333333333333333333333 +7110 3333333333333333333333333333333333333333333333333333333 +7111 3333333333333333333333333333333333333333333333333333333 +7112 3333333333333333333333333333333333333333333333333333333 +7113 3333333333333333333333333333333333333333333333333333333 +7114 3333333333333333333333333333333333333333333333333333333 +7115 3333333333333333333333333333333333333333333333333333333 +7116 3333333333333333333333333333333333333333333333333333333 +7117 3333333333333333333333333333333333333333333333333333333 +7118 3333333333333333333333333333333333333333333333333333333 +7119 3333333333333333333333333333333333333333333333333333333 +7120 3333333333333333333333333333333333333333333333333333333 +7121 3333333333333333333333333333333333333333333333333333333 +7122 3333333333333333333333333333333333333333333333333333333 +7123 3333333333333333333333333333333333333333333333333333333 +7124 3333333333333333333333333333333333333333333333333333333 +7125 3333333333333333333333333333333333333333333333333333333 +7126 3333333333333333333333333333333333333333333333333333333 +7127 3333333333333333333333333333333333333333333333333333333 +7128 3333333333333333333333333333333333333333333333333333333 +7129 3333333333333333333333333333333333333333333333333333333 +7130 3333333333333333333333333333333333333333333333333333333 +7131 3333333333333333333333333333333333333333333333333333333 +7132 3333333333333333333333333333333333333333333333333333333 +7133 3333333333333333333333333333333333333333333333333333333 +7134 3333333333333333333333333333333333333333333333333333333 +7135 3333333333333333333333333333333333333333333333333333333 +7136 3333333333333333333333333333333333333333333333333333333 +7137 3333333333333333333333333333333333333333333333333333333 +7138 3333333333333333333333333333333333333333333333333333333 +7139 3333333333333333333333333333333333333333333333333333333 +7140 3333333333333333333333333333333333333333333333333333333 +7141 3333333333333333333333333333333333333333333333333333333 +7142 3333333333333333333333333333333333333333333333333333333 +7143 3333333333333333333333333333333333333333333333333333333 +7144 3333333333333333333333333333333333333333333333333333333 +7145 3333333333333333333333333333333333333333333333333333333 +7146 3333333333333333333333333333333333333333333333333333333 +7147 3333333333333333333333333333333333333333333333333333333 +7148 3333333333333333333333333333333333333333333333333333333 +7149 3333333333333333333333333333333333333333333333333333333 +7150 3333333333333333333333333333333333333333333333333333333 +7151 3333333333333333333333333333333333333333333333333333333 +7152 3333333333333333333333333333333333333333333333333333333 +7153 3333333333333333333333333333333333333333333333333333333 +7154 3333333333333333333333333333333333333333333333333333333 +7155 3333333333333333333333333333333333333333333333333333333 +7156 3333333333333333333333333333333333333333333333333333333 +7157 3333333333333333333333333333333333333333333333333333333 +7158 3333333333333333333333333333333333333333333333333333333 +7159 3333333333333333333333333333333333333333333333333333333 +7160 3333333333333333333333333333333333333333333333333333333 +7161 3333333333333333333333333333333333333333333333333333333 +7162 3333333333333333333333333333333333333333333333333333333 +7163 3333333333333333333333333333333333333333333333333333333 +7164 3333333333333333333333333333333333333333333333333333333 +7165 3333333333333333333333333333333333333333333333333333333 +7166 3333333333333333333333333333333333333333333333333333333 +7167 3333333333333333333333333333333333333333333333333333333 +7168 3333333333333333333333333333333333333333333333333333333 +7169 3333333333333333333333333333333333333333333333333333333 +7170 3333333333333333333333333333333333333333333333333333333 +7171 3333333333333333333333333333333333333333333333333333333 +7172 3333333333333333333333333333333333333333333333333333333 +7173 3333333333333333333333333333333333333333333333333333333 +7174 3333333333333333333333333333333333333333333333333333333 +7175 3333333333333333333333333333333333333333333333333333333 +7176 3333333333333333333333333333333333333333333333333333333 +7177 3333333333333333333333333333333333333333333333333333333 +7178 3333333333333333333333333333333333333333333333333333333 +7179 3333333333333333333333333333333333333333333333333333333 +7180 3333333333333333333333333333333333333333333333333333333 +7181 3333333333333333333333333333333333333333333333333333333 +7182 3333333333333333333333333333333333333333333333333333333 +7183 3333333333333333333333333333333333333333333333333333333 +7184 3333333333333333333333333333333333333333333333333333333 +7185 3333333333333333333333333333333333333333333333333333333 +7186 3333333333333333333333333333333333333333333333333333333 +7187 3333333333333333333333333333333333333333333333333333333 +7188 3333333333333333333333333333333333333333333333333333333 +7189 3333333333333333333333333333333333333333333333333333333 +7190 3333333333333333333333333333333333333333333333333333333 +7191 3333333333333333333333333333333333333333333333333333333 +7192 3333333333333333333333333333333333333333333333333333333 +7193 3333333333333333333333333333333333333333333333333333333 +7194 3333333333333333333333333333333333333333333333333333333 +7195 3333333333333333333333333333333333333333333333333333333 +7196 3333333333333333333333333333333333333333333333333333333 +7197 3333333333333333333333333333333333333333333333333333333 +7198 3333333333333333333333333333333333333333333333333333333 +7199 3333333333333333333333333333333333333333333333333333333 +7200 3333333333333333333333333333333333333333333333333333333 +7201 3333333333333333333333333333333333333333333333333333333 +7202 3333333333333333333333333333333333333333333333333333333 +7203 3333333333333333333333333333333333333333333333333333333 +7204 3333333333333333333333333333333333333333333333333333333 +7205 3333333333333333333333333333333333333333333333333333333 +7206 3333333333333333333333333333333333333333333333333333333 +7207 3333333333333333333333333333333333333333333333333333333 +7208 3333333333333333333333333333333333333333333333333333333 +7209 3333333333333333333333333333333333333333333333333333333 +7210 3333333333333333333333333333333333333333333333333333333 +7211 3333333333333333333333333333333333333333333333333333333 +7212 3333333333333333333333333333333333333333333333333333333 +7213 3333333333333333333333333333333333333333333333333333333 +7214 3333333333333333333333333333333333333333333333333333333 +7215 3333333333333333333333333333333333333333333333333333333 +7216 3333333333333333333333333333333333333333333333333333333 +7217 3333333333333333333333333333333333333333333333333333333 +7218 3333333333333333333333333333333333333333333333333333333 +7219 3333333333333333333333333333333333333333333333333333333 +7220 3333333333333333333333333333333333333333333333333333333 +7221 3333333333333333333333333333333333333333333333333333333 +7222 3333333333333333333333333333333333333333333333333333333 +7223 3333333333333333333333333333333333333333333333333333333 +7224 3333333333333333333333333333333333333333333333333333333 +7225 3333333333333333333333333333333333333333333333333333333 +7226 3333333333333333333333333333333333333333333333333333333 +7227 3333333333333333333333333333333333333333333333333333333 +7228 3333333333333333333333333333333333333333333333333333333 +7229 3333333333333333333333333333333333333333333333333333333 +7230 3333333333333333333333333333333333333333333333333333333 +7231 3333333333333333333333333333333333333333333333333333333 +7232 3333333333333333333333333333333333333333333333333333333 +7233 3333333333333333333333333333333333333333333333333333333 +7234 3333333333333333333333333333333333333333333333333333333 +7235 3333333333333333333333333333333333333333333333333333333 +7236 3333333333333333333333333333333333333333333333333333333 +7237 3333333333333333333333333333333333333333333333333333333 +7238 3333333333333333333333333333333333333333333333333333333 +7239 3333333333333333333333333333333333333333333333333333333 +7240 3333333333333333333333333333333333333333333333333333333 +7241 3333333333333333333333333333333333333333333333333333333 +7242 3333333333333333333333333333333333333333333333333333333 +7243 3333333333333333333333333333333333333333333333333333333 +7244 3333333333333333333333333333333333333333333333333333333 +7245 3333333333333333333333333333333333333333333333333333333 +7246 3333333333333333333333333333333333333333333333333333333 +7247 3333333333333333333333333333333333333333333333333333333 +7248 3333333333333333333333333333333333333333333333333333333 +7249 3333333333333333333333333333333333333333333333333333333 +7250 3333333333333333333333333333333333333333333333333333333 +7251 3333333333333333333333333333333333333333333333333333333 +7252 3333333333333333333333333333333333333333333333333333333 +7253 3333333333333333333333333333333333333333333333333333333 +7254 3333333333333333333333333333333333333333333333333333333 +7255 3333333333333333333333333333333333333333333333333333333 +7256 3333333333333333333333333333333333333333333333333333333 +7257 3333333333333333333333333333333333333333333333333333333 +7258 3333333333333333333333333333333333333333333333333333333 +7259 3333333333333333333333333333333333333333333333333333333 +7260 3333333333333333333333333333333333333333333333333333333 +7261 3333333333333333333333333333333333333333333333333333333 +7262 3333333333333333333333333333333333333333333333333333333 +7263 3333333333333333333333333333333333333333333333333333333 +7264 3333333333333333333333333333333333333333333333333333333 +7265 3333333333333333333333333333333333333333333333333333333 +7266 3333333333333333333333333333333333333333333333333333333 +7267 3333333333333333333333333333333333333333333333333333333 +7268 3333333333333333333333333333333333333333333333333333333 +7269 3333333333333333333333333333333333333333333333333333333 +7270 3333333333333333333333333333333333333333333333333333333 +7271 3333333333333333333333333333333333333333333333333333333 +7272 3333333333333333333333333333333333333333333333333333333 +7273 3333333333333333333333333333333333333333333333333333333 +7274 3333333333333333333333333333333333333333333333333333333 +7275 3333333333333333333333333333333333333333333333333333333 +7276 3333333333333333333333333333333333333333333333333333333 +7277 3333333333333333333333333333333333333333333333333333333 +7278 3333333333333333333333333333333333333333333333333333333 +7279 3333333333333333333333333333333333333333333333333333333 +7280 3333333333333333333333333333333333333333333333333333333 +7281 3333333333333333333333333333333333333333333333333333333 +7282 3333333333333333333333333333333333333333333333333333333 +7283 3333333333333333333333333333333333333333333333333333333 +7284 3333333333333333333333333333333333333333333333333333333 +7285 3333333333333333333333333333333333333333333333333333333 +7286 3333333333333333333333333333333333333333333333333333333 +7287 3333333333333333333333333333333333333333333333333333333 +7288 3333333333333333333333333333333333333333333333333333333 +7289 3333333333333333333333333333333333333333333333333333333 +7290 3333333333333333333333333333333333333333333333333333333 +7291 3333333333333333333333333333333333333333333333333333333 +7292 3333333333333333333333333333333333333333333333333333333 +7293 3333333333333333333333333333333333333333333333333333333 +7294 3333333333333333333333333333333333333333333333333333333 +7295 3333333333333333333333333333333333333333333333333333333 +7296 3333333333333333333333333333333333333333333333333333333 +7297 3333333333333333333333333333333333333333333333333333333 +7298 3333333333333333333333333333333333333333333333333333333 +7299 3333333333333333333333333333333333333333333333333333333 +7300 3333333333333333333333333333333333333333333333333333333 +7301 3333333333333333333333333333333333333333333333333333333 +7302 3333333333333333333333333333333333333333333333333333333 +7303 3333333333333333333333333333333333333333333333333333333 +7304 3333333333333333333333333333333333333333333333333333333 +7305 3333333333333333333333333333333333333333333333333333333 +7306 3333333333333333333333333333333333333333333333333333333 +7307 3333333333333333333333333333333333333333333333333333333 +7308 3333333333333333333333333333333333333333333333333333333 +7309 3333333333333333333333333333333333333333333333333333333 +7310 3333333333333333333333333333333333333333333333333333333 +7311 3333333333333333333333333333333333333333333333333333333 +7312 3333333333333333333333333333333333333333333333333333333 +7313 3333333333333333333333333333333333333333333333333333333 +7314 3333333333333333333333333333333333333333333333333333333 +7315 3333333333333333333333333333333333333333333333333333333 +7316 3333333333333333333333333333333333333333333333333333333 +7317 3333333333333333333333333333333333333333333333333333333 +7318 3333333333333333333333333333333333333333333333333333333 +7319 3333333333333333333333333333333333333333333333333333333 +7320 3333333333333333333333333333333333333333333333333333333 +7321 3333333333333333333333333333333333333333333333333333333 +7322 3333333333333333333333333333333333333333333333333333333 +7323 3333333333333333333333333333333333333333333333333333333 +7324 3333333333333333333333333333333333333333333333333333333 +7325 3333333333333333333333333333333333333333333333333333333 +7326 3333333333333333333333333333333333333333333333333333333 +7327 3333333333333333333333333333333333333333333333333333333 +7328 3333333333333333333333333333333333333333333333333333333 +7329 3333333333333333333333333333333333333333333333333333333 +7330 3333333333333333333333333333333333333333333333333333333 +7331 3333333333333333333333333333333333333333333333333333333 +7332 3333333333333333333333333333333333333333333333333333333 +7333 3333333333333333333333333333333333333333333333333333333 +7334 3333333333333333333333333333333333333333333333333333333 +7335 3333333333333333333333333333333333333333333333333333333 +7336 3333333333333333333333333333333333333333333333333333333 +7337 3333333333333333333333333333333333333333333333333333333 +7338 3333333333333333333333333333333333333333333333333333333 +7339 3333333333333333333333333333333333333333333333333333333 +7340 3333333333333333333333333333333333333333333333333333333 +7341 3333333333333333333333333333333333333333333333333333333 +7342 3333333333333333333333333333333333333333333333333333333 +7343 3333333333333333333333333333333333333333333333333333333 +7344 3333333333333333333333333333333333333333333333333333333 +7345 3333333333333333333333333333333333333333333333333333333 +7346 3333333333333333333333333333333333333333333333333333333 +7347 3333333333333333333333333333333333333333333333333333333 +7348 3333333333333333333333333333333333333333333333333333333 +7349 3333333333333333333333333333333333333333333333333333333 +7350 3333333333333333333333333333333333333333333333333333333 +7351 3333333333333333333333333333333333333333333333333333333 +7352 3333333333333333333333333333333333333333333333333333333 +7353 3333333333333333333333333333333333333333333333333333333 +7354 3333333333333333333333333333333333333333333333333333333 +7355 3333333333333333333333333333333333333333333333333333333 +7356 3333333333333333333333333333333333333333333333333333333 +7357 3333333333333333333333333333333333333333333333333333333 +7358 3333333333333333333333333333333333333333333333333333333 +7359 3333333333333333333333333333333333333333333333333333333 +7360 3333333333333333333333333333333333333333333333333333333 +7361 3333333333333333333333333333333333333333333333333333333 +7362 3333333333333333333333333333333333333333333333333333333 +7363 3333333333333333333333333333333333333333333333333333333 +7364 3333333333333333333333333333333333333333333333333333333 +7365 3333333333333333333333333333333333333333333333333333333 +7366 3333333333333333333333333333333333333333333333333333333 +7367 3333333333333333333333333333333333333333333333333333333 +7368 3333333333333333333333333333333333333333333333333333333 +7369 3333333333333333333333333333333333333333333333333333333 +7370 3333333333333333333333333333333333333333333333333333333 +7371 3333333333333333333333333333333333333333333333333333333 +7372 3333333333333333333333333333333333333333333333333333333 +7373 3333333333333333333333333333333333333333333333333333333 +7374 3333333333333333333333333333333333333333333333333333333 +7375 3333333333333333333333333333333333333333333333333333333 +7376 3333333333333333333333333333333333333333333333333333333 +7377 3333333333333333333333333333333333333333333333333333333 +7378 3333333333333333333333333333333333333333333333333333333 +7379 3333333333333333333333333333333333333333333333333333333 +7380 3333333333333333333333333333333333333333333333333333333 +7381 3333333333333333333333333333333333333333333333333333333 +7382 3333333333333333333333333333333333333333333333333333333 +7383 3333333333333333333333333333333333333333333333333333333 +7384 3333333333333333333333333333333333333333333333333333333 +7385 3333333333333333333333333333333333333333333333333333333 +7386 3333333333333333333333333333333333333333333333333333333 +7387 3333333333333333333333333333333333333333333333333333333 +7388 3333333333333333333333333333333333333333333333333333333 +7389 3333333333333333333333333333333333333333333333333333333 +7390 3333333333333333333333333333333333333333333333333333333 +7391 3333333333333333333333333333333333333333333333333333333 +7392 3333333333333333333333333333333333333333333333333333333 +7393 3333333333333333333333333333333333333333333333333333333 +7394 3333333333333333333333333333333333333333333333333333333 +7395 3333333333333333333333333333333333333333333333333333333 +7396 3333333333333333333333333333333333333333333333333333333 +7397 3333333333333333333333333333333333333333333333333333333 +7398 3333333333333333333333333333333333333333333333333333333 +7399 3333333333333333333333333333333333333333333333333333333 +7400 3333333333333333333333333333333333333333333333333333333 +7401 3333333333333333333333333333333333333333333333333333333 +7402 3333333333333333333333333333333333333333333333333333333 +7403 3333333333333333333333333333333333333333333333333333333 +7404 3333333333333333333333333333333333333333333333333333333 +7405 3333333333333333333333333333333333333333333333333333333 +7406 3333333333333333333333333333333333333333333333333333333 +7407 3333333333333333333333333333333333333333333333333333333 +7408 3333333333333333333333333333333333333333333333333333333 +7409 3333333333333333333333333333333333333333333333333333333 +7410 3333333333333333333333333333333333333333333333333333333 +7411 3333333333333333333333333333333333333333333333333333333 +7412 3333333333333333333333333333333333333333333333333333333 +7413 3333333333333333333333333333333333333333333333333333333 +7414 3333333333333333333333333333333333333333333333333333333 +7415 3333333333333333333333333333333333333333333333333333333 +7416 3333333333333333333333333333333333333333333333333333333 +7417 3333333333333333333333333333333333333333333333333333333 +7418 3333333333333333333333333333333333333333333333333333333 +7419 3333333333333333333333333333333333333333333333333333333 +7420 3333333333333333333333333333333333333333333333333333333 +7421 3333333333333333333333333333333333333333333333333333333 +7422 3333333333333333333333333333333333333333333333333333333 +7423 3333333333333333333333333333333333333333333333333333333 +7424 3333333333333333333333333333333333333333333333333333333 +7425 3333333333333333333333333333333333333333333333333333333 +7426 3333333333333333333333333333333333333333333333333333333 +7427 3333333333333333333333333333333333333333333333333333333 +7428 3333333333333333333333333333333333333333333333333333333 +7429 3333333333333333333333333333333333333333333333333333333 +7430 3333333333333333333333333333333333333333333333333333333 +7431 3333333333333333333333333333333333333333333333333333333 +7432 3333333333333333333333333333333333333333333333333333333 +7433 3333333333333333333333333333333333333333333333333333333 +7434 3333333333333333333333333333333333333333333333333333333 +7435 3333333333333333333333333333333333333333333333333333333 +7436 3333333333333333333333333333333333333333333333333333333 +7437 3333333333333333333333333333333333333333333333333333333 +7438 3333333333333333333333333333333333333333333333333333333 +7439 3333333333333333333333333333333333333333333333333333333 +7440 3333333333333333333333333333333333333333333333333333333 +7441 3333333333333333333333333333333333333333333333333333333 +7442 3333333333333333333333333333333333333333333333333333333 +7443 3333333333333333333333333333333333333333333333333333333 +7444 3333333333333333333333333333333333333333333333333333333 +7445 3333333333333333333333333333333333333333333333333333333 +7446 3333333333333333333333333333333333333333333333333333333 +7447 3333333333333333333333333333333333333333333333333333333 +7448 3333333333333333333333333333333333333333333333333333333 +7449 3333333333333333333333333333333333333333333333333333333 +7450 3333333333333333333333333333333333333333333333333333333 +7451 3333333333333333333333333333333333333333333333333333333 +7452 3333333333333333333333333333333333333333333333333333333 +7453 3333333333333333333333333333333333333333333333333333333 +7454 3333333333333333333333333333333333333333333333333333333 +7455 3333333333333333333333333333333333333333333333333333333 +7456 3333333333333333333333333333333333333333333333333333333 +7457 3333333333333333333333333333333333333333333333333333333 +7458 3333333333333333333333333333333333333333333333333333333 +7459 3333333333333333333333333333333333333333333333333333333 +7460 3333333333333333333333333333333333333333333333333333333 +7461 3333333333333333333333333333333333333333333333333333333 +7462 3333333333333333333333333333333333333333333333333333333 +7463 3333333333333333333333333333333333333333333333333333333 +7464 3333333333333333333333333333333333333333333333333333333 +7465 3333333333333333333333333333333333333333333333333333333 +7466 3333333333333333333333333333333333333333333333333333333 +7467 3333333333333333333333333333333333333333333333333333333 +7468 3333333333333333333333333333333333333333333333333333333 +7469 3333333333333333333333333333333333333333333333333333333 +7470 3333333333333333333333333333333333333333333333333333333 +7471 3333333333333333333333333333333333333333333333333333333 +7472 3333333333333333333333333333333333333333333333333333333 +7473 3333333333333333333333333333333333333333333333333333333 +7474 3333333333333333333333333333333333333333333333333333333 +7475 3333333333333333333333333333333333333333333333333333333 +7476 3333333333333333333333333333333333333333333333333333333 +7477 3333333333333333333333333333333333333333333333333333333 +7478 3333333333333333333333333333333333333333333333333333333 +7479 3333333333333333333333333333333333333333333333333333333 +7480 3333333333333333333333333333333333333333333333333333333 +7481 3333333333333333333333333333333333333333333333333333333 +7482 3333333333333333333333333333333333333333333333333333333 +7483 3333333333333333333333333333333333333333333333333333333 +7484 3333333333333333333333333333333333333333333333333333333 +7485 3333333333333333333333333333333333333333333333333333333 +7486 3333333333333333333333333333333333333333333333333333333 +7487 3333333333333333333333333333333333333333333333333333333 +7488 3333333333333333333333333333333333333333333333333333333 +7489 3333333333333333333333333333333333333333333333333333333 +7490 3333333333333333333333333333333333333333333333333333333 +7491 3333333333333333333333333333333333333333333333333333333 +7492 3333333333333333333333333333333333333333333333333333333 +7493 3333333333333333333333333333333333333333333333333333333 +7494 3333333333333333333333333333333333333333333333333333333 +7495 3333333333333333333333333333333333333333333333333333333 +7496 3333333333333333333333333333333333333333333333333333333 +7497 3333333333333333333333333333333333333333333333333333333 +7498 3333333333333333333333333333333333333333333333333333333 +7499 3333333333333333333333333333333333333333333333333333333 +7500 3333333333333333333333333333333333333333333333333333333 +7501 3333333333333333333333333333333333333333333333333333333 +7502 3333333333333333333333333333333333333333333333333333333 +7503 3333333333333333333333333333333333333333333333333333333 +7504 3333333333333333333333333333333333333333333333333333333 +7505 3333333333333333333333333333333333333333333333333333333 +7506 3333333333333333333333333333333333333333333333333333333 +7507 3333333333333333333333333333333333333333333333333333333 +7508 3333333333333333333333333333333333333333333333333333333 +7509 3333333333333333333333333333333333333333333333333333333 +7510 3333333333333333333333333333333333333333333333333333333 +7511 3333333333333333333333333333333333333333333333333333333 +7512 3333333333333333333333333333333333333333333333333333333 +7513 3333333333333333333333333333333333333333333333333333333 +7514 3333333333333333333333333333333333333333333333333333333 +7515 3333333333333333333333333333333333333333333333333333333 +7516 3333333333333333333333333333333333333333333333333333333 +7517 3333333333333333333333333333333333333333333333333333333 +7518 3333333333333333333333333333333333333333333333333333333 +7519 3333333333333333333333333333333333333333333333333333333 +7520 3333333333333333333333333333333333333333333333333333333 +7521 3333333333333333333333333333333333333333333333333333333 +7522 3333333333333333333333333333333333333333333333333333333 +7523 3333333333333333333333333333333333333333333333333333333 +7524 3333333333333333333333333333333333333333333333333333333 +7525 3333333333333333333333333333333333333333333333333333333 +7526 3333333333333333333333333333333333333333333333333333333 +7527 3333333333333333333333333333333333333333333333333333333 +7528 3333333333333333333333333333333333333333333333333333333 +7529 3333333333333333333333333333333333333333333333333333333 +7530 3333333333333333333333333333333333333333333333333333333 +7531 3333333333333333333333333333333333333333333333333333333 +7532 3333333333333333333333333333333333333333333333333333333 +7533 3333333333333333333333333333333333333333333333333333333 +7534 3333333333333333333333333333333333333333333333333333333 +7535 3333333333333333333333333333333333333333333333333333333 +7536 3333333333333333333333333333333333333333333333333333333 +7537 3333333333333333333333333333333333333333333333333333333 +7538 3333333333333333333333333333333333333333333333333333333 +7539 3333333333333333333333333333333333333333333333333333333 +7540 3333333333333333333333333333333333333333333333333333333 +7541 3333333333333333333333333333333333333333333333333333333 +7542 3333333333333333333333333333333333333333333333333333333 +7543 3333333333333333333333333333333333333333333333333333333 +7544 3333333333333333333333333333333333333333333333333333333 +7545 3333333333333333333333333333333333333333333333333333333 +7546 3333333333333333333333333333333333333333333333333333333 +7547 3333333333333333333333333333333333333333333333333333333 +7548 3333333333333333333333333333333333333333333333333333333 +7549 3333333333333333333333333333333333333333333333333333333 +7550 3333333333333333333333333333333333333333333333333333333 +7551 3333333333333333333333333333333333333333333333333333333 +7552 3333333333333333333333333333333333333333333333333333333 +7553 3333333333333333333333333333333333333333333333333333333 +7554 3333333333333333333333333333333333333333333333333333333 +7555 3333333333333333333333333333333333333333333333333333333 +7556 3333333333333333333333333333333333333333333333333333333 +7557 3333333333333333333333333333333333333333333333333333333 +7558 3333333333333333333333333333333333333333333333333333333 +7559 3333333333333333333333333333333333333333333333333333333 +7560 3333333333333333333333333333333333333333333333333333333 +7561 3333333333333333333333333333333333333333333333333333333 +7562 3333333333333333333333333333333333333333333333333333333 +7563 3333333333333333333333333333333333333333333333333333333 +7564 3333333333333333333333333333333333333333333333333333333 +7565 3333333333333333333333333333333333333333333333333333333 +7566 3333333333333333333333333333333333333333333333333333333 +7567 3333333333333333333333333333333333333333333333333333333 +7568 3333333333333333333333333333333333333333333333333333333 +7569 3333333333333333333333333333333333333333333333333333333 +7570 3333333333333333333333333333333333333333333333333333333 +7571 3333333333333333333333333333333333333333333333333333333 +7572 3333333333333333333333333333333333333333333333333333333 +7573 3333333333333333333333333333333333333333333333333333333 +7574 3333333333333333333333333333333333333333333333333333333 +7575 3333333333333333333333333333333333333333333333333333333 +7576 3333333333333333333333333333333333333333333333333333333 +7577 3333333333333333333333333333333333333333333333333333333 +7578 3333333333333333333333333333333333333333333333333333333 +7579 3333333333333333333333333333333333333333333333333333333 +7580 3333333333333333333333333333333333333333333333333333333 +7581 3333333333333333333333333333333333333333333333333333333 +7582 3333333333333333333333333333333333333333333333333333333 +7583 3333333333333333333333333333333333333333333333333333333 +7584 3333333333333333333333333333333333333333333333333333333 +7585 3333333333333333333333333333333333333333333333333333333 +7586 3333333333333333333333333333333333333333333333333333333 +7587 3333333333333333333333333333333333333333333333333333333 +7588 3333333333333333333333333333333333333333333333333333333 +7589 3333333333333333333333333333333333333333333333333333333 +7590 3333333333333333333333333333333333333333333333333333333 +7591 3333333333333333333333333333333333333333333333333333333 +7592 3333333333333333333333333333333333333333333333333333333 +7593 3333333333333333333333333333333333333333333333333333333 +7594 3333333333333333333333333333333333333333333333333333333 +7595 3333333333333333333333333333333333333333333333333333333 +7596 3333333333333333333333333333333333333333333333333333333 +7597 3333333333333333333333333333333333333333333333333333333 +7598 3333333333333333333333333333333333333333333333333333333 +7599 3333333333333333333333333333333333333333333333333333333 +7600 3333333333333333333333333333333333333333333333333333333 +7601 3333333333333333333333333333333333333333333333333333333 +7602 3333333333333333333333333333333333333333333333333333333 +7603 3333333333333333333333333333333333333333333333333333333 +7604 3333333333333333333333333333333333333333333333333333333 +7605 3333333333333333333333333333333333333333333333333333333 +7606 3333333333333333333333333333333333333333333333333333333 +7607 3333333333333333333333333333333333333333333333333333333 +7608 3333333333333333333333333333333333333333333333333333333 +7609 3333333333333333333333333333333333333333333333333333333 +7610 3333333333333333333333333333333333333333333333333333333 +7611 3333333333333333333333333333333333333333333333333333333 +7612 3333333333333333333333333333333333333333333333333333333 +7613 3333333333333333333333333333333333333333333333333333333 +7614 3333333333333333333333333333333333333333333333333333333 +7615 3333333333333333333333333333333333333333333333333333333 +7616 3333333333333333333333333333333333333333333333333333333 +7617 3333333333333333333333333333333333333333333333333333333 +7618 3333333333333333333333333333333333333333333333333333333 +7619 3333333333333333333333333333333333333333333333333333333 +7620 3333333333333333333333333333333333333333333333333333333 +7621 3333333333333333333333333333333333333333333333333333333 +7622 3333333333333333333333333333333333333333333333333333333 +7623 3333333333333333333333333333333333333333333333333333333 +7624 3333333333333333333333333333333333333333333333333333333 +7625 3333333333333333333333333333333333333333333333333333333 +7626 3333333333333333333333333333333333333333333333333333333 +7627 3333333333333333333333333333333333333333333333333333333 +7628 3333333333333333333333333333333333333333333333333333333 +7629 3333333333333333333333333333333333333333333333333333333 +7630 3333333333333333333333333333333333333333333333333333333 +7631 3333333333333333333333333333333333333333333333333333333 +7632 3333333333333333333333333333333333333333333333333333333 +7633 3333333333333333333333333333333333333333333333333333333 +7634 3333333333333333333333333333333333333333333333333333333 +7635 3333333333333333333333333333333333333333333333333333333 +7636 3333333333333333333333333333333333333333333333333333333 +7637 3333333333333333333333333333333333333333333333333333333 +7638 3333333333333333333333333333333333333333333333333333333 +7639 3333333333333333333333333333333333333333333333333333333 +7640 3333333333333333333333333333333333333333333333333333333 +7641 3333333333333333333333333333333333333333333333333333333 +7642 3333333333333333333333333333333333333333333333333333333 +7643 3333333333333333333333333333333333333333333333333333333 +7644 3333333333333333333333333333333333333333333333333333333 +7645 3333333333333333333333333333333333333333333333333333333 +7646 3333333333333333333333333333333333333333333333333333333 +7647 3333333333333333333333333333333333333333333333333333333 +7648 3333333333333333333333333333333333333333333333333333333 +7649 3333333333333333333333333333333333333333333333333333333 +7650 3333333333333333333333333333333333333333333333333333333 +7651 3333333333333333333333333333333333333333333333333333333 +7652 3333333333333333333333333333333333333333333333333333333 +7653 3333333333333333333333333333333333333333333333333333333 +7654 3333333333333333333333333333333333333333333333333333333 +7655 3333333333333333333333333333333333333333333333333333333 +7656 3333333333333333333333333333333333333333333333333333333 +7657 3333333333333333333333333333333333333333333333333333333 +7658 3333333333333333333333333333333333333333333333333333333 +7659 3333333333333333333333333333333333333333333333333333333 +7660 3333333333333333333333333333333333333333333333333333333 +7661 3333333333333333333333333333333333333333333333333333333 +7662 3333333333333333333333333333333333333333333333333333333 +7663 3333333333333333333333333333333333333333333333333333333 +7664 3333333333333333333333333333333333333333333333333333333 +7665 3333333333333333333333333333333333333333333333333333333 +7666 3333333333333333333333333333333333333333333333333333333 +7667 3333333333333333333333333333333333333333333333333333333 +7668 3333333333333333333333333333333333333333333333333333333 +7669 3333333333333333333333333333333333333333333333333333333 +7670 3333333333333333333333333333333333333333333333333333333 +7671 3333333333333333333333333333333333333333333333333333333 +7672 3333333333333333333333333333333333333333333333333333333 +7673 3333333333333333333333333333333333333333333333333333333 +7674 3333333333333333333333333333333333333333333333333333333 +7675 3333333333333333333333333333333333333333333333333333333 +7676 3333333333333333333333333333333333333333333333333333333 +7677 3333333333333333333333333333333333333333333333333333333 +7678 3333333333333333333333333333333333333333333333333333333 +7679 3333333333333333333333333333333333333333333333333333333 +7680 3333333333333333333333333333333333333333333333333333333 +7681 3333333333333333333333333333333333333333333333333333333 +7682 3333333333333333333333333333333333333333333333333333333 +7683 3333333333333333333333333333333333333333333333333333333 +7684 3333333333333333333333333333333333333333333333333333333 +7685 3333333333333333333333333333333333333333333333333333333 +7686 3333333333333333333333333333333333333333333333333333333 +7687 3333333333333333333333333333333333333333333333333333333 +7688 3333333333333333333333333333333333333333333333333333333 +7689 3333333333333333333333333333333333333333333333333333333 +7690 3333333333333333333333333333333333333333333333333333333 +7691 3333333333333333333333333333333333333333333333333333333 +7692 3333333333333333333333333333333333333333333333333333333 +7693 3333333333333333333333333333333333333333333333333333333 +7694 3333333333333333333333333333333333333333333333333333333 +7695 3333333333333333333333333333333333333333333333333333333 +7696 3333333333333333333333333333333333333333333333333333333 +7697 3333333333333333333333333333333333333333333333333333333 +7698 3333333333333333333333333333333333333333333333333333333 +7699 3333333333333333333333333333333333333333333333333333333 +7700 3333333333333333333333333333333333333333333333333333333 +7701 3333333333333333333333333333333333333333333333333333333 +7702 3333333333333333333333333333333333333333333333333333333 +7703 3333333333333333333333333333333333333333333333333333333 +7704 3333333333333333333333333333333333333333333333333333333 +7705 3333333333333333333333333333333333333333333333333333333 +7706 3333333333333333333333333333333333333333333333333333333 +7707 3333333333333333333333333333333333333333333333333333333 +7708 3333333333333333333333333333333333333333333333333333333 +7709 3333333333333333333333333333333333333333333333333333333 +7710 3333333333333333333333333333333333333333333333333333333 +7711 3333333333333333333333333333333333333333333333333333333 +7712 3333333333333333333333333333333333333333333333333333333 +7713 3333333333333333333333333333333333333333333333333333333 +7714 3333333333333333333333333333333333333333333333333333333 +7715 3333333333333333333333333333333333333333333333333333333 +7716 3333333333333333333333333333333333333333333333333333333 +7717 3333333333333333333333333333333333333333333333333333333 +7718 3333333333333333333333333333333333333333333333333333333 +7719 3333333333333333333333333333333333333333333333333333333 +7720 3333333333333333333333333333333333333333333333333333333 +7721 3333333333333333333333333333333333333333333333333333333 +7722 3333333333333333333333333333333333333333333333333333333 +7723 3333333333333333333333333333333333333333333333333333333 +7724 3333333333333333333333333333333333333333333333333333333 +7725 3333333333333333333333333333333333333333333333333333333 +7726 3333333333333333333333333333333333333333333333333333333 +7727 3333333333333333333333333333333333333333333333333333333 +7728 3333333333333333333333333333333333333333333333333333333 +7729 3333333333333333333333333333333333333333333333333333333 +7730 3333333333333333333333333333333333333333333333333333333 +7731 3333333333333333333333333333333333333333333333333333333 +7732 3333333333333333333333333333333333333333333333333333333 +7733 3333333333333333333333333333333333333333333333333333333 +7734 3333333333333333333333333333333333333333333333333333333 +7735 3333333333333333333333333333333333333333333333333333333 +7736 3333333333333333333333333333333333333333333333333333333 +7737 3333333333333333333333333333333333333333333333333333333 +7738 3333333333333333333333333333333333333333333333333333333 +7739 3333333333333333333333333333333333333333333333333333333 +7740 3333333333333333333333333333333333333333333333333333333 +7741 3333333333333333333333333333333333333333333333333333333 +7742 3333333333333333333333333333333333333333333333333333333 +7743 3333333333333333333333333333333333333333333333333333333 +7744 3333333333333333333333333333333333333333333333333333333 +7745 3333333333333333333333333333333333333333333333333333333 +7746 3333333333333333333333333333333333333333333333333333333 +7747 3333333333333333333333333333333333333333333333333333333 +7748 3333333333333333333333333333333333333333333333333333333 +7749 3333333333333333333333333333333333333333333333333333333 +7750 3333333333333333333333333333333333333333333333333333333 +7751 3333333333333333333333333333333333333333333333333333333 +7752 3333333333333333333333333333333333333333333333333333333 +7753 3333333333333333333333333333333333333333333333333333333 +7754 3333333333333333333333333333333333333333333333333333333 +7755 3333333333333333333333333333333333333333333333333333333 +7756 3333333333333333333333333333333333333333333333333333333 +7757 3333333333333333333333333333333333333333333333333333333 +7758 3333333333333333333333333333333333333333333333333333333 +7759 3333333333333333333333333333333333333333333333333333333 +7760 3333333333333333333333333333333333333333333333333333333 +7761 3333333333333333333333333333333333333333333333333333333 +7762 3333333333333333333333333333333333333333333333333333333 +7763 3333333333333333333333333333333333333333333333333333333 +7764 3333333333333333333333333333333333333333333333333333333 +7765 3333333333333333333333333333333333333333333333333333333 +7766 3333333333333333333333333333333333333333333333333333333 +7767 3333333333333333333333333333333333333333333333333333333 +7768 3333333333333333333333333333333333333333333333333333333 +7769 3333333333333333333333333333333333333333333333333333333 +7770 3333333333333333333333333333333333333333333333333333333 +7771 3333333333333333333333333333333333333333333333333333333 +7772 3333333333333333333333333333333333333333333333333333333 +7773 3333333333333333333333333333333333333333333333333333333 +7774 3333333333333333333333333333333333333333333333333333333 +7775 3333333333333333333333333333333333333333333333333333333 +7776 3333333333333333333333333333333333333333333333333333333 +7777 3333333333333333333333333333333333333333333333333333333 +7778 3333333333333333333333333333333333333333333333333333333 +7779 3333333333333333333333333333333333333333333333333333333 +7780 3333333333333333333333333333333333333333333333333333333 +7781 3333333333333333333333333333333333333333333333333333333 +7782 3333333333333333333333333333333333333333333333333333333 +7783 3333333333333333333333333333333333333333333333333333333 +7784 3333333333333333333333333333333333333333333333333333333 +7785 3333333333333333333333333333333333333333333333333333333 +7786 3333333333333333333333333333333333333333333333333333333 +7787 3333333333333333333333333333333333333333333333333333333 +7788 3333333333333333333333333333333333333333333333333333333 +7789 3333333333333333333333333333333333333333333333333333333 +7790 3333333333333333333333333333333333333333333333333333333 +7791 3333333333333333333333333333333333333333333333333333333 +7792 3333333333333333333333333333333333333333333333333333333 +7793 3333333333333333333333333333333333333333333333333333333 +7794 3333333333333333333333333333333333333333333333333333333 +7795 3333333333333333333333333333333333333333333333333333333 +7796 3333333333333333333333333333333333333333333333333333333 +7797 3333333333333333333333333333333333333333333333333333333 +7798 3333333333333333333333333333333333333333333333333333333 +7799 3333333333333333333333333333333333333333333333333333333 +7800 3333333333333333333333333333333333333333333333333333333 +7801 3333333333333333333333333333333333333333333333333333333 +7802 3333333333333333333333333333333333333333333333333333333 +7803 3333333333333333333333333333333333333333333333333333333 +7804 3333333333333333333333333333333333333333333333333333333 +7805 3333333333333333333333333333333333333333333333333333333 +7806 3333333333333333333333333333333333333333333333333333333 +7807 3333333333333333333333333333333333333333333333333333333 +7808 3333333333333333333333333333333333333333333333333333333 +7809 3333333333333333333333333333333333333333333333333333333 +7810 3333333333333333333333333333333333333333333333333333333 +7811 3333333333333333333333333333333333333333333333333333333 +7812 3333333333333333333333333333333333333333333333333333333 +7813 3333333333333333333333333333333333333333333333333333333 +7814 3333333333333333333333333333333333333333333333333333333 +7815 3333333333333333333333333333333333333333333333333333333 +7816 3333333333333333333333333333333333333333333333333333333 +7817 3333333333333333333333333333333333333333333333333333333 +7818 3333333333333333333333333333333333333333333333333333333 +7819 3333333333333333333333333333333333333333333333333333333 +7820 3333333333333333333333333333333333333333333333333333333 +7821 3333333333333333333333333333333333333333333333333333333 +7822 3333333333333333333333333333333333333333333333333333333 +7823 3333333333333333333333333333333333333333333333333333333 +7824 3333333333333333333333333333333333333333333333333333333 +7825 3333333333333333333333333333333333333333333333333333333 +7826 3333333333333333333333333333333333333333333333333333333 +7827 3333333333333333333333333333333333333333333333333333333 +7828 3333333333333333333333333333333333333333333333333333333 +7829 3333333333333333333333333333333333333333333333333333333 +7830 3333333333333333333333333333333333333333333333333333333 +7831 3333333333333333333333333333333333333333333333333333333 +7832 3333333333333333333333333333333333333333333333333333333 +7833 3333333333333333333333333333333333333333333333333333333 +7834 3333333333333333333333333333333333333333333333333333333 +7835 3333333333333333333333333333333333333333333333333333333 +7836 3333333333333333333333333333333333333333333333333333333 +7837 3333333333333333333333333333333333333333333333333333333 +7838 3333333333333333333333333333333333333333333333333333333 +7839 3333333333333333333333333333333333333333333333333333333 +7840 3333333333333333333333333333333333333333333333333333333 +7841 3333333333333333333333333333333333333333333333333333333 +7842 3333333333333333333333333333333333333333333333333333333 +7843 3333333333333333333333333333333333333333333333333333333 +7844 3333333333333333333333333333333333333333333333333333333 +7845 3333333333333333333333333333333333333333333333333333333 +7846 3333333333333333333333333333333333333333333333333333333 +7847 3333333333333333333333333333333333333333333333333333333 +7848 3333333333333333333333333333333333333333333333333333333 +7849 3333333333333333333333333333333333333333333333333333333 +7850 3333333333333333333333333333333333333333333333333333333 +7851 3333333333333333333333333333333333333333333333333333333 +7852 3333333333333333333333333333333333333333333333333333333 +7853 3333333333333333333333333333333333333333333333333333333 +7854 3333333333333333333333333333333333333333333333333333333 +7855 3333333333333333333333333333333333333333333333333333333 +7856 3333333333333333333333333333333333333333333333333333333 +7857 3333333333333333333333333333333333333333333333333333333 +7858 3333333333333333333333333333333333333333333333333333333 +7859 3333333333333333333333333333333333333333333333333333333 +7860 3333333333333333333333333333333333333333333333333333333 +7861 3333333333333333333333333333333333333333333333333333333 +7862 3333333333333333333333333333333333333333333333333333333 +7863 3333333333333333333333333333333333333333333333333333333 +7864 3333333333333333333333333333333333333333333333333333333 +7865 3333333333333333333333333333333333333333333333333333333 +7866 3333333333333333333333333333333333333333333333333333333 +7867 3333333333333333333333333333333333333333333333333333333 +7868 3333333333333333333333333333333333333333333333333333333 +7869 3333333333333333333333333333333333333333333333333333333 +7870 3333333333333333333333333333333333333333333333333333333 +7871 3333333333333333333333333333333333333333333333333333333 +7872 3333333333333333333333333333333333333333333333333333333 +7873 3333333333333333333333333333333333333333333333333333333 +7874 3333333333333333333333333333333333333333333333333333333 +7875 3333333333333333333333333333333333333333333333333333333 +7876 3333333333333333333333333333333333333333333333333333333 +7877 3333333333333333333333333333333333333333333333333333333 +7878 3333333333333333333333333333333333333333333333333333333 +7879 3333333333333333333333333333333333333333333333333333333 +7880 3333333333333333333333333333333333333333333333333333333 +7881 3333333333333333333333333333333333333333333333333333333 +7882 3333333333333333333333333333333333333333333333333333333 +7883 3333333333333333333333333333333333333333333333333333333 +7884 3333333333333333333333333333333333333333333333333333333 +7885 3333333333333333333333333333333333333333333333333333333 +7886 3333333333333333333333333333333333333333333333333333333 +7887 3333333333333333333333333333333333333333333333333333333 +7888 3333333333333333333333333333333333333333333333333333333 +7889 3333333333333333333333333333333333333333333333333333333 +7890 3333333333333333333333333333333333333333333333333333333 +7891 3333333333333333333333333333333333333333333333333333333 +7892 3333333333333333333333333333333333333333333333333333333 +7893 3333333333333333333333333333333333333333333333333333333 +7894 3333333333333333333333333333333333333333333333333333333 +7895 3333333333333333333333333333333333333333333333333333333 +7896 3333333333333333333333333333333333333333333333333333333 +7897 3333333333333333333333333333333333333333333333333333333 +7898 3333333333333333333333333333333333333333333333333333333 +7899 3333333333333333333333333333333333333333333333333333333 +7900 3333333333333333333333333333333333333333333333333333333 +7901 3333333333333333333333333333333333333333333333333333333 +7902 3333333333333333333333333333333333333333333333333333333 +7903 3333333333333333333333333333333333333333333333333333333 +7904 3333333333333333333333333333333333333333333333333333333 +7905 3333333333333333333333333333333333333333333333333333333 +7906 3333333333333333333333333333333333333333333333333333333 +7907 3333333333333333333333333333333333333333333333333333333 +7908 3333333333333333333333333333333333333333333333333333333 +7909 3333333333333333333333333333333333333333333333333333333 +7910 3333333333333333333333333333333333333333333333333333333 +7911 3333333333333333333333333333333333333333333333333333333 +7912 3333333333333333333333333333333333333333333333333333333 +7913 3333333333333333333333333333333333333333333333333333333 +7914 3333333333333333333333333333333333333333333333333333333 +7915 3333333333333333333333333333333333333333333333333333333 +7916 3333333333333333333333333333333333333333333333333333333 +7917 3333333333333333333333333333333333333333333333333333333 +7918 3333333333333333333333333333333333333333333333333333333 +7919 3333333333333333333333333333333333333333333333333333333 +7920 3333333333333333333333333333333333333333333333333333333 +7921 3333333333333333333333333333333333333333333333333333333 +7922 3333333333333333333333333333333333333333333333333333333 +7923 3333333333333333333333333333333333333333333333333333333 +7924 3333333333333333333333333333333333333333333333333333333 +7925 3333333333333333333333333333333333333333333333333333333 +7926 3333333333333333333333333333333333333333333333333333333 +7927 3333333333333333333333333333333333333333333333333333333 +7928 3333333333333333333333333333333333333333333333333333333 +7929 3333333333333333333333333333333333333333333333333333333 +7930 3333333333333333333333333333333333333333333333333333333 +7931 3333333333333333333333333333333333333333333333333333333 +7932 3333333333333333333333333333333333333333333333333333333 +7933 3333333333333333333333333333333333333333333333333333333 +7934 3333333333333333333333333333333333333333333333333333333 +7935 3333333333333333333333333333333333333333333333333333333 +7936 3333333333333333333333333333333333333333333333333333333 +7937 3333333333333333333333333333333333333333333333333333333 +7938 3333333333333333333333333333333333333333333333333333333 +7939 3333333333333333333333333333333333333333333333333333333 +7940 3333333333333333333333333333333333333333333333333333333 +7941 3333333333333333333333333333333333333333333333333333333 +7942 3333333333333333333333333333333333333333333333333333333 +7943 3333333333333333333333333333333333333333333333333333333 +7944 3333333333333333333333333333333333333333333333333333333 +7945 3333333333333333333333333333333333333333333333333333333 +7946 3333333333333333333333333333333333333333333333333333333 +7947 3333333333333333333333333333333333333333333333333333333 +7948 3333333333333333333333333333333333333333333333333333333 +7949 3333333333333333333333333333333333333333333333333333333 +7950 3333333333333333333333333333333333333333333333333333333 +7951 3333333333333333333333333333333333333333333333333333333 +7952 3333333333333333333333333333333333333333333333333333333 +7953 3333333333333333333333333333333333333333333333333333333 +7954 3333333333333333333333333333333333333333333333333333333 +7955 3333333333333333333333333333333333333333333333333333333 +7956 3333333333333333333333333333333333333333333333333333333 +7957 3333333333333333333333333333333333333333333333333333333 +7958 3333333333333333333333333333333333333333333333333333333 +7959 3333333333333333333333333333333333333333333333333333333 +7960 3333333333333333333333333333333333333333333333333333333 +7961 3333333333333333333333333333333333333333333333333333333 +7962 3333333333333333333333333333333333333333333333333333333 +7963 3333333333333333333333333333333333333333333333333333333 +7964 3333333333333333333333333333333333333333333333333333333 +7965 3333333333333333333333333333333333333333333333333333333 +7966 3333333333333333333333333333333333333333333333333333333 +7967 3333333333333333333333333333333333333333333333333333333 +7968 3333333333333333333333333333333333333333333333333333333 +7969 3333333333333333333333333333333333333333333333333333333 +7970 3333333333333333333333333333333333333333333333333333333 +7971 3333333333333333333333333333333333333333333333333333333 +7972 3333333333333333333333333333333333333333333333333333333 +7973 3333333333333333333333333333333333333333333333333333333 +7974 3333333333333333333333333333333333333333333333333333333 +7975 3333333333333333333333333333333333333333333333333333333 +7976 3333333333333333333333333333333333333333333333333333333 +7977 3333333333333333333333333333333333333333333333333333333 +7978 3333333333333333333333333333333333333333333333333333333 +7979 3333333333333333333333333333333333333333333333333333333 +7980 3333333333333333333333333333333333333333333333333333333 +7981 3333333333333333333333333333333333333333333333333333333 +7982 3333333333333333333333333333333333333333333333333333333 +7983 3333333333333333333333333333333333333333333333333333333 +7984 3333333333333333333333333333333333333333333333333333333 +7985 3333333333333333333333333333333333333333333333333333333 +7986 3333333333333333333333333333333333333333333333333333333 +7987 3333333333333333333333333333333333333333333333333333333 +7988 3333333333333333333333333333333333333333333333333333333 +7989 3333333333333333333333333333333333333333333333333333333 +7990 3333333333333333333333333333333333333333333333333333333 +7991 3333333333333333333333333333333333333333333333333333333 +7992 3333333333333333333333333333333333333333333333333333333 +7993 3333333333333333333333333333333333333333333333333333333 +7994 3333333333333333333333333333333333333333333333333333333 +7995 3333333333333333333333333333333333333333333333333333333 +7996 3333333333333333333333333333333333333333333333333333333 +7997 3333333333333333333333333333333333333333333333333333333 +7998 3333333333333333333333333333333333333333333333333333333 +7999 3333333333333333333333333333333333333333333333333333333 +8000 3333333333333333333333333333333333333333333333333333333 +8001 3333333333333333333333333333333333333333333333333333333 +8002 3333333333333333333333333333333333333333333333333333333 +8003 3333333333333333333333333333333333333333333333333333333 +8004 3333333333333333333333333333333333333333333333333333333 +8005 3333333333333333333333333333333333333333333333333333333 +8006 3333333333333333333333333333333333333333333333333333333 +8007 3333333333333333333333333333333333333333333333333333333 +8008 3333333333333333333333333333333333333333333333333333333 +8009 3333333333333333333333333333333333333333333333333333333 +8010 3333333333333333333333333333333333333333333333333333333 +8011 3333333333333333333333333333333333333333333333333333333 +8012 3333333333333333333333333333333333333333333333333333333 +8013 3333333333333333333333333333333333333333333333333333333 +8014 3333333333333333333333333333333333333333333333333333333 +8015 3333333333333333333333333333333333333333333333333333333 +8016 3333333333333333333333333333333333333333333333333333333 +8017 3333333333333333333333333333333333333333333333333333333 +8018 3333333333333333333333333333333333333333333333333333333 +8019 3333333333333333333333333333333333333333333333333333333 +8020 3333333333333333333333333333333333333333333333333333333 +8021 3333333333333333333333333333333333333333333333333333333 +8022 3333333333333333333333333333333333333333333333333333333 +8023 3333333333333333333333333333333333333333333333333333333 +8024 3333333333333333333333333333333333333333333333333333333 +8025 3333333333333333333333333333333333333333333333333333333 +8026 3333333333333333333333333333333333333333333333333333333 +8027 3333333333333333333333333333333333333333333333333333333 +8028 3333333333333333333333333333333333333333333333333333333 +8029 3333333333333333333333333333333333333333333333333333333 +8030 3333333333333333333333333333333333333333333333333333333 +8031 3333333333333333333333333333333333333333333333333333333 +8032 3333333333333333333333333333333333333333333333333333333 +8033 3333333333333333333333333333333333333333333333333333333 +8034 3333333333333333333333333333333333333333333333333333333 +8035 3333333333333333333333333333333333333333333333333333333 +8036 3333333333333333333333333333333333333333333333333333333 +8037 3333333333333333333333333333333333333333333333333333333 +8038 3333333333333333333333333333333333333333333333333333333 +8039 3333333333333333333333333333333333333333333333333333333 +8040 3333333333333333333333333333333333333333333333333333333 +8041 3333333333333333333333333333333333333333333333333333333 +8042 3333333333333333333333333333333333333333333333333333333 +8043 3333333333333333333333333333333333333333333333333333333 +8044 3333333333333333333333333333333333333333333333333333333 +8045 3333333333333333333333333333333333333333333333333333333 +8046 3333333333333333333333333333333333333333333333333333333 +8047 3333333333333333333333333333333333333333333333333333333 +8048 3333333333333333333333333333333333333333333333333333333 +8049 3333333333333333333333333333333333333333333333333333333 +8050 3333333333333333333333333333333333333333333333333333333 +8051 3333333333333333333333333333333333333333333333333333333 +8052 3333333333333333333333333333333333333333333333333333333 +8053 3333333333333333333333333333333333333333333333333333333 +8054 3333333333333333333333333333333333333333333333333333333 +8055 3333333333333333333333333333333333333333333333333333333 +8056 3333333333333333333333333333333333333333333333333333333 +8057 3333333333333333333333333333333333333333333333333333333 +8058 3333333333333333333333333333333333333333333333333333333 +8059 3333333333333333333333333333333333333333333333333333333 +8060 3333333333333333333333333333333333333333333333333333333 +8061 3333333333333333333333333333333333333333333333333333333 +8062 3333333333333333333333333333333333333333333333333333333 +8063 3333333333333333333333333333333333333333333333333333333 +8064 3333333333333333333333333333333333333333333333333333333 +8065 3333333333333333333333333333333333333333333333333333333 +8066 3333333333333333333333333333333333333333333333333333333 +8067 3333333333333333333333333333333333333333333333333333333 +8068 3333333333333333333333333333333333333333333333333333333 +8069 3333333333333333333333333333333333333333333333333333333 +8070 3333333333333333333333333333333333333333333333333333333 +8071 3333333333333333333333333333333333333333333333333333333 +8072 3333333333333333333333333333333333333333333333333333333 +8073 3333333333333333333333333333333333333333333333333333333 +8074 3333333333333333333333333333333333333333333333333333333 +8075 3333333333333333333333333333333333333333333333333333333 +8076 3333333333333333333333333333333333333333333333333333333 +8077 3333333333333333333333333333333333333333333333333333333 +8078 3333333333333333333333333333333333333333333333333333333 +8079 3333333333333333333333333333333333333333333333333333333 +8080 3333333333333333333333333333333333333333333333333333333 +8081 3333333333333333333333333333333333333333333333333333333 +8082 3333333333333333333333333333333333333333333333333333333 +8083 3333333333333333333333333333333333333333333333333333333 +8084 3333333333333333333333333333333333333333333333333333333 +8085 3333333333333333333333333333333333333333333333333333333 +8086 3333333333333333333333333333333333333333333333333333333 +8087 3333333333333333333333333333333333333333333333333333333 +8088 3333333333333333333333333333333333333333333333333333333 +8089 3333333333333333333333333333333333333333333333333333333 +8090 3333333333333333333333333333333333333333333333333333333 +8091 3333333333333333333333333333333333333333333333333333333 +8092 3333333333333333333333333333333333333333333333333333333 +8093 3333333333333333333333333333333333333333333333333333333 +8094 3333333333333333333333333333333333333333333333333333333 +8095 3333333333333333333333333333333333333333333333333333333 +8096 3333333333333333333333333333333333333333333333333333333 +8097 3333333333333333333333333333333333333333333333333333333 +8098 3333333333333333333333333333333333333333333333333333333 +8099 3333333333333333333333333333333333333333333333333333333 +8100 3333333333333333333333333333333333333333333333333333333 +8101 3333333333333333333333333333333333333333333333333333333 +8102 3333333333333333333333333333333333333333333333333333333 +8103 3333333333333333333333333333333333333333333333333333333 +8104 3333333333333333333333333333333333333333333333333333333 +8105 3333333333333333333333333333333333333333333333333333333 +8106 3333333333333333333333333333333333333333333333333333333 +8107 3333333333333333333333333333333333333333333333333333333 +8108 3333333333333333333333333333333333333333333333333333333 +8109 3333333333333333333333333333333333333333333333333333333 +8110 3333333333333333333333333333333333333333333333333333333 +8111 3333333333333333333333333333333333333333333333333333333 +8112 3333333333333333333333333333333333333333333333333333333 +8113 3333333333333333333333333333333333333333333333333333333 +8114 3333333333333333333333333333333333333333333333333333333 +8115 3333333333333333333333333333333333333333333333333333333 +8116 3333333333333333333333333333333333333333333333333333333 +8117 3333333333333333333333333333333333333333333333333333333 +8118 3333333333333333333333333333333333333333333333333333333 +8119 3333333333333333333333333333333333333333333333333333333 +8120 3333333333333333333333333333333333333333333333333333333 +8121 3333333333333333333333333333333333333333333333333333333 +8122 3333333333333333333333333333333333333333333333333333333 +8123 3333333333333333333333333333333333333333333333333333333 +8124 3333333333333333333333333333333333333333333333333333333 +8125 3333333333333333333333333333333333333333333333333333333 +8126 3333333333333333333333333333333333333333333333333333333 +8127 3333333333333333333333333333333333333333333333333333333 +8128 3333333333333333333333333333333333333333333333333333333 +8129 3333333333333333333333333333333333333333333333333333333 +8130 3333333333333333333333333333333333333333333333333333333 +8131 3333333333333333333333333333333333333333333333333333333 +8132 3333333333333333333333333333333333333333333333333333333 +8133 3333333333333333333333333333333333333333333333333333333 +8134 3333333333333333333333333333333333333333333333333333333 +8135 3333333333333333333333333333333333333333333333333333333 +8136 3333333333333333333333333333333333333333333333333333333 +8137 3333333333333333333333333333333333333333333333333333333 +8138 3333333333333333333333333333333333333333333333333333333 +8139 3333333333333333333333333333333333333333333333333333333 +8140 3333333333333333333333333333333333333333333333333333333 +8141 3333333333333333333333333333333333333333333333333333333 +8142 3333333333333333333333333333333333333333333333333333333 +8143 3333333333333333333333333333333333333333333333333333333 +8144 3333333333333333333333333333333333333333333333333333333 +8145 3333333333333333333333333333333333333333333333333333333 +8146 3333333333333333333333333333333333333333333333333333333 +8147 3333333333333333333333333333333333333333333333333333333 +8148 3333333333333333333333333333333333333333333333333333333 +8149 3333333333333333333333333333333333333333333333333333333 +8150 3333333333333333333333333333333333333333333333333333333 +8151 3333333333333333333333333333333333333333333333333333333 +8152 3333333333333333333333333333333333333333333333333333333 +8153 3333333333333333333333333333333333333333333333333333333 +8154 3333333333333333333333333333333333333333333333333333333 +8155 3333333333333333333333333333333333333333333333333333333 +8156 3333333333333333333333333333333333333333333333333333333 +8157 3333333333333333333333333333333333333333333333333333333 +8158 3333333333333333333333333333333333333333333333333333333 +8159 3333333333333333333333333333333333333333333333333333333 +8160 3333333333333333333333333333333333333333333333333333333 +8161 3333333333333333333333333333333333333333333333333333333 +8162 3333333333333333333333333333333333333333333333333333333 +8163 3333333333333333333333333333333333333333333333333333333 +8164 3333333333333333333333333333333333333333333333333333333 +8165 3333333333333333333333333333333333333333333333333333333 +8166 3333333333333333333333333333333333333333333333333333333 +8167 3333333333333333333333333333333333333333333333333333333 +8168 3333333333333333333333333333333333333333333333333333333 +8169 3333333333333333333333333333333333333333333333333333333 +8170 3333333333333333333333333333333333333333333333333333333 +8171 3333333333333333333333333333333333333333333333333333333 +8172 3333333333333333333333333333333333333333333333333333333 +8173 3333333333333333333333333333333333333333333333333333333 +8174 3333333333333333333333333333333333333333333333333333333 +8175 3333333333333333333333333333333333333333333333333333333 +8176 3333333333333333333333333333333333333333333333333333333 +8177 3333333333333333333333333333333333333333333333333333333 +8178 3333333333333333333333333333333333333333333333333333333 +8179 3333333333333333333333333333333333333333333333333333333 +8180 3333333333333333333333333333333333333333333333333333333 +8181 3333333333333333333333333333333333333333333333333333333 +8182 3333333333333333333333333333333333333333333333333333333 +8183 3333333333333333333333333333333333333333333333333333333 +8184 3333333333333333333333333333333333333333333333333333333 +8185 3333333333333333333333333333333333333333333333333333333 +8186 3333333333333333333333333333333333333333333333333333333 +8187 3333333333333333333333333333333333333333333333333333333 +8188 3333333333333333333333333333333333333333333333333333333 +8189 3333333333333333333333333333333333333333333333333333333 +8190 3333333333333333333333333333333333333333333333333333333 +8191 3333333333333333333333333333333333333333333333333333333 +8192 3333333333333333333333333333333333333333333333333333333 +8193 3333333333333333333333333333333333333333333333333333333 +8194 3333333333333333333333333333333333333333333333333333333 +8195 3333333333333333333333333333333333333333333333333333333 +8196 3333333333333333333333333333333333333333333333333333333 +8197 3333333333333333333333333333333333333333333333333333333 +8198 3333333333333333333333333333333333333333333333333333333 +8199 3333333333333333333333333333333333333333333333333333333 +8200 3333333333333333333333333333333333333333333333333333333 +8201 3333333333333333333333333333333333333333333333333333333 +8202 3333333333333333333333333333333333333333333333333333333 +8203 3333333333333333333333333333333333333333333333333333333 +8204 3333333333333333333333333333333333333333333333333333333 +8205 3333333333333333333333333333333333333333333333333333333 +8206 3333333333333333333333333333333333333333333333333333333 +8207 3333333333333333333333333333333333333333333333333333333 +8208 3333333333333333333333333333333333333333333333333333333 +8209 3333333333333333333333333333333333333333333333333333333 +8210 3333333333333333333333333333333333333333333333333333333 +8211 3333333333333333333333333333333333333333333333333333333 +8212 3333333333333333333333333333333333333333333333333333333 +8213 3333333333333333333333333333333333333333333333333333333 +8214 3333333333333333333333333333333333333333333333333333333 +8215 3333333333333333333333333333333333333333333333333333333 +8216 3333333333333333333333333333333333333333333333333333333 +8217 3333333333333333333333333333333333333333333333333333333 +8218 3333333333333333333333333333333333333333333333333333333 +8219 3333333333333333333333333333333333333333333333333333333 +8220 3333333333333333333333333333333333333333333333333333333 +8221 3333333333333333333333333333333333333333333333333333333 +8222 3333333333333333333333333333333333333333333333333333333 +8223 3333333333333333333333333333333333333333333333333333333 +8224 3333333333333333333333333333333333333333333333333333333 +8225 3333333333333333333333333333333333333333333333333333333 +8226 3333333333333333333333333333333333333333333333333333333 +8227 3333333333333333333333333333333333333333333333333333333 +8228 3333333333333333333333333333333333333333333333333333333 +8229 3333333333333333333333333333333333333333333333333333333 +8230 3333333333333333333333333333333333333333333333333333333 +8231 3333333333333333333333333333333333333333333333333333333 +8232 3333333333333333333333333333333333333333333333333333333 +8233 3333333333333333333333333333333333333333333333333333333 +8234 3333333333333333333333333333333333333333333333333333333 +8235 3333333333333333333333333333333333333333333333333333333 +8236 3333333333333333333333333333333333333333333333333333333 +8237 3333333333333333333333333333333333333333333333333333333 +8238 3333333333333333333333333333333333333333333333333333333 +8239 3333333333333333333333333333333333333333333333333333333 +8240 3333333333333333333333333333333333333333333333333333333 +8241 3333333333333333333333333333333333333333333333333333333 +8242 3333333333333333333333333333333333333333333333333333333 +8243 3333333333333333333333333333333333333333333333333333333 +8244 3333333333333333333333333333333333333333333333333333333 +8245 3333333333333333333333333333333333333333333333333333333 +8246 3333333333333333333333333333333333333333333333333333333 +8247 3333333333333333333333333333333333333333333333333333333 +8248 3333333333333333333333333333333333333333333333333333333 +8249 3333333333333333333333333333333333333333333333333333333 +8250 3333333333333333333333333333333333333333333333333333333 +8251 3333333333333333333333333333333333333333333333333333333 +8252 3333333333333333333333333333333333333333333333333333333 +8253 3333333333333333333333333333333333333333333333333333333 +8254 3333333333333333333333333333333333333333333333333333333 +8255 3333333333333333333333333333333333333333333333333333333 +8256 3333333333333333333333333333333333333333333333333333333 +8257 3333333333333333333333333333333333333333333333333333333 +8258 3333333333333333333333333333333333333333333333333333333 +8259 3333333333333333333333333333333333333333333333333333333 +8260 3333333333333333333333333333333333333333333333333333333 +8261 3333333333333333333333333333333333333333333333333333333 +8262 3333333333333333333333333333333333333333333333333333333 +8263 3333333333333333333333333333333333333333333333333333333 +8264 3333333333333333333333333333333333333333333333333333333 +8265 3333333333333333333333333333333333333333333333333333333 +8266 3333333333333333333333333333333333333333333333333333333 +8267 3333333333333333333333333333333333333333333333333333333 +8268 3333333333333333333333333333333333333333333333333333333 +8269 3333333333333333333333333333333333333333333333333333333 +8270 3333333333333333333333333333333333333333333333333333333 +8271 3333333333333333333333333333333333333333333333333333333 +8272 3333333333333333333333333333333333333333333333333333333 +8273 3333333333333333333333333333333333333333333333333333333 +8274 3333333333333333333333333333333333333333333333333333333 +8275 3333333333333333333333333333333333333333333333333333333 +8276 3333333333333333333333333333333333333333333333333333333 +8277 3333333333333333333333333333333333333333333333333333333 +8278 3333333333333333333333333333333333333333333333333333333 +8279 3333333333333333333333333333333333333333333333333333333 +8280 3333333333333333333333333333333333333333333333333333333 +8281 3333333333333333333333333333333333333333333333333333333 +8282 3333333333333333333333333333333333333333333333333333333 +8283 3333333333333333333333333333333333333333333333333333333 +8284 3333333333333333333333333333333333333333333333333333333 +8285 3333333333333333333333333333333333333333333333333333333 +8286 3333333333333333333333333333333333333333333333333333333 +8287 3333333333333333333333333333333333333333333333333333333 +8288 3333333333333333333333333333333333333333333333333333333 +8289 3333333333333333333333333333333333333333333333333333333 +8290 3333333333333333333333333333333333333333333333333333333 +8291 3333333333333333333333333333333333333333333333333333333 +8292 3333333333333333333333333333333333333333333333333333333 +8293 3333333333333333333333333333333333333333333333333333333 +8294 3333333333333333333333333333333333333333333333333333333 +8295 3333333333333333333333333333333333333333333333333333333 +8296 3333333333333333333333333333333333333333333333333333333 +8297 3333333333333333333333333333333333333333333333333333333 +8298 3333333333333333333333333333333333333333333333333333333 +8299 3333333333333333333333333333333333333333333333333333333 +8300 3333333333333333333333333333333333333333333333333333333 +8301 3333333333333333333333333333333333333333333333333333333 +8302 3333333333333333333333333333333333333333333333333333333 +8303 3333333333333333333333333333333333333333333333333333333 +8304 3333333333333333333333333333333333333333333333333333333 +8305 3333333333333333333333333333333333333333333333333333333 +8306 3333333333333333333333333333333333333333333333333333333 +8307 3333333333333333333333333333333333333333333333333333333 +8308 3333333333333333333333333333333333333333333333333333333 +8309 3333333333333333333333333333333333333333333333333333333 +8310 3333333333333333333333333333333333333333333333333333333 +8311 3333333333333333333333333333333333333333333333333333333 +8312 3333333333333333333333333333333333333333333333333333333 +8313 3333333333333333333333333333333333333333333333333333333 +8314 3333333333333333333333333333333333333333333333333333333 +8315 3333333333333333333333333333333333333333333333333333333 +8316 3333333333333333333333333333333333333333333333333333333 +8317 3333333333333333333333333333333333333333333333333333333 +8318 3333333333333333333333333333333333333333333333333333333 +8319 3333333333333333333333333333333333333333333333333333333 +8320 3333333333333333333333333333333333333333333333333333333 +8321 3333333333333333333333333333333333333333333333333333333 +8322 3333333333333333333333333333333333333333333333333333333 +8323 3333333333333333333333333333333333333333333333333333333 +8324 3333333333333333333333333333333333333333333333333333333 +8325 3333333333333333333333333333333333333333333333333333333 +8326 3333333333333333333333333333333333333333333333333333333 +8327 3333333333333333333333333333333333333333333333333333333 +8328 3333333333333333333333333333333333333333333333333333333 +8329 3333333333333333333333333333333333333333333333333333333 +8330 3333333333333333333333333333333333333333333333333333333 +8331 3333333333333333333333333333333333333333333333333333333 +8332 3333333333333333333333333333333333333333333333333333333 +8333 3333333333333333333333333333333333333333333333333333333 +8334 3333333333333333333333333333333333333333333333333333333 +8335 3333333333333333333333333333333333333333333333333333333 +8336 3333333333333333333333333333333333333333333333333333333 +8337 3333333333333333333333333333333333333333333333333333333 +8338 3333333333333333333333333333333333333333333333333333333 +8339 3333333333333333333333333333333333333333333333333333333 +8340 3333333333333333333333333333333333333333333333333333333 +8341 3333333333333333333333333333333333333333333333333333333 +8342 3333333333333333333333333333333333333333333333333333333 +8343 3333333333333333333333333333333333333333333333333333333 +8344 3333333333333333333333333333333333333333333333333333333 +8345 3333333333333333333333333333333333333333333333333333333 +8346 3333333333333333333333333333333333333333333333333333333 +8347 3333333333333333333333333333333333333333333333333333333 +8348 3333333333333333333333333333333333333333333333333333333 +8349 3333333333333333333333333333333333333333333333333333333 +8350 3333333333333333333333333333333333333333333333333333333 +8351 3333333333333333333333333333333333333333333333333333333 +8352 3333333333333333333333333333333333333333333333333333333 +8353 3333333333333333333333333333333333333333333333333333333 +8354 3333333333333333333333333333333333333333333333333333333 +8355 3333333333333333333333333333333333333333333333333333333 +8356 3333333333333333333333333333333333333333333333333333333 +8357 3333333333333333333333333333333333333333333333333333333 +8358 3333333333333333333333333333333333333333333333333333333 +8359 3333333333333333333333333333333333333333333333333333333 +8360 3333333333333333333333333333333333333333333333333333333 +8361 3333333333333333333333333333333333333333333333333333333 +8362 3333333333333333333333333333333333333333333333333333333 +8363 3333333333333333333333333333333333333333333333333333333 +8364 3333333333333333333333333333333333333333333333333333333 +8365 3333333333333333333333333333333333333333333333333333333 +8366 3333333333333333333333333333333333333333333333333333333 +8367 3333333333333333333333333333333333333333333333333333333 +8368 3333333333333333333333333333333333333333333333333333333 +8369 3333333333333333333333333333333333333333333333333333333 +8370 3333333333333333333333333333333333333333333333333333333 +8371 3333333333333333333333333333333333333333333333333333333 +8372 3333333333333333333333333333333333333333333333333333333 +8373 3333333333333333333333333333333333333333333333333333333 +8374 3333333333333333333333333333333333333333333333333333333 +8375 3333333333333333333333333333333333333333333333333333333 +8376 3333333333333333333333333333333333333333333333333333333 +8377 3333333333333333333333333333333333333333333333333333333 +8378 3333333333333333333333333333333333333333333333333333333 +8379 3333333333333333333333333333333333333333333333333333333 +8380 3333333333333333333333333333333333333333333333333333333 +8381 3333333333333333333333333333333333333333333333333333333 +8382 3333333333333333333333333333333333333333333333333333333 +8383 3333333333333333333333333333333333333333333333333333333 +8384 3333333333333333333333333333333333333333333333333333333 +8385 3333333333333333333333333333333333333333333333333333333 +8386 3333333333333333333333333333333333333333333333333333333 +8387 3333333333333333333333333333333333333333333333333333333 +8388 3333333333333333333333333333333333333333333333333333333 +8389 3333333333333333333333333333333333333333333333333333333 +8390 3333333333333333333333333333333333333333333333333333333 +8391 3333333333333333333333333333333333333333333333333333333 +8392 3333333333333333333333333333333333333333333333333333333 +8393 3333333333333333333333333333333333333333333333333333333 +8394 3333333333333333333333333333333333333333333333333333333 +8395 3333333333333333333333333333333333333333333333333333333 +8396 3333333333333333333333333333333333333333333333333333333 +8397 3333333333333333333333333333333333333333333333333333333 +8398 3333333333333333333333333333333333333333333333333333333 +8399 3333333333333333333333333333333333333333333333333333333 +8400 3333333333333333333333333333333333333333333333333333333 +8401 3333333333333333333333333333333333333333333333333333333 +8402 3333333333333333333333333333333333333333333333333333333 +8403 3333333333333333333333333333333333333333333333333333333 +8404 3333333333333333333333333333333333333333333333333333333 +8405 3333333333333333333333333333333333333333333333333333333 +8406 3333333333333333333333333333333333333333333333333333333 +8407 3333333333333333333333333333333333333333333333333333333 +8408 3333333333333333333333333333333333333333333333333333333 +8409 3333333333333333333333333333333333333333333333333333333 +8410 3333333333333333333333333333333333333333333333333333333 +8411 3333333333333333333333333333333333333333333333333333333 +8412 3333333333333333333333333333333333333333333333333333333 +8413 3333333333333333333333333333333333333333333333333333333 +8414 3333333333333333333333333333333333333333333333333333333 +8415 3333333333333333333333333333333333333333333333333333333 +8416 3333333333333333333333333333333333333333333333333333333 +8417 3333333333333333333333333333333333333333333333333333333 +8418 3333333333333333333333333333333333333333333333333333333 +8419 3333333333333333333333333333333333333333333333333333333 +8420 3333333333333333333333333333333333333333333333333333333 +8421 3333333333333333333333333333333333333333333333333333333 +8422 3333333333333333333333333333333333333333333333333333333 +8423 3333333333333333333333333333333333333333333333333333333 +8424 3333333333333333333333333333333333333333333333333333333 +8425 3333333333333333333333333333333333333333333333333333333 +8426 3333333333333333333333333333333333333333333333333333333 +8427 3333333333333333333333333333333333333333333333333333333 +8428 3333333333333333333333333333333333333333333333333333333 +8429 3333333333333333333333333333333333333333333333333333333 +8430 3333333333333333333333333333333333333333333333333333333 +8431 3333333333333333333333333333333333333333333333333333333 +8432 3333333333333333333333333333333333333333333333333333333 +8433 3333333333333333333333333333333333333333333333333333333 +8434 3333333333333333333333333333333333333333333333333333333 +8435 3333333333333333333333333333333333333333333333333333333 +8436 3333333333333333333333333333333333333333333333333333333 +8437 3333333333333333333333333333333333333333333333333333333 +8438 3333333333333333333333333333333333333333333333333333333 +8439 3333333333333333333333333333333333333333333333333333333 +8440 3333333333333333333333333333333333333333333333333333333 +8441 3333333333333333333333333333333333333333333333333333333 +8442 3333333333333333333333333333333333333333333333333333333 +8443 3333333333333333333333333333333333333333333333333333333 +8444 3333333333333333333333333333333333333333333333333333333 +8445 3333333333333333333333333333333333333333333333333333333 +8446 3333333333333333333333333333333333333333333333333333333 +8447 3333333333333333333333333333333333333333333333333333333 +8448 3333333333333333333333333333333333333333333333333333333 +8449 3333333333333333333333333333333333333333333333333333333 +8450 3333333333333333333333333333333333333333333333333333333 +8451 3333333333333333333333333333333333333333333333333333333 +8452 3333333333333333333333333333333333333333333333333333333 +8453 3333333333333333333333333333333333333333333333333333333 +8454 3333333333333333333333333333333333333333333333333333333 +8455 3333333333333333333333333333333333333333333333333333333 +8456 3333333333333333333333333333333333333333333333333333333 +8457 3333333333333333333333333333333333333333333333333333333 +8458 3333333333333333333333333333333333333333333333333333333 +8459 3333333333333333333333333333333333333333333333333333333 +8460 3333333333333333333333333333333333333333333333333333333 +8461 3333333333333333333333333333333333333333333333333333333 +8462 3333333333333333333333333333333333333333333333333333333 +8463 3333333333333333333333333333333333333333333333333333333 +8464 3333333333333333333333333333333333333333333333333333333 +8465 3333333333333333333333333333333333333333333333333333333 +8466 3333333333333333333333333333333333333333333333333333333 +8467 3333333333333333333333333333333333333333333333333333333 +8468 3333333333333333333333333333333333333333333333333333333 +8469 3333333333333333333333333333333333333333333333333333333 +8470 3333333333333333333333333333333333333333333333333333333 +8471 3333333333333333333333333333333333333333333333333333333 +8472 3333333333333333333333333333333333333333333333333333333 +8473 3333333333333333333333333333333333333333333333333333333 +8474 3333333333333333333333333333333333333333333333333333333 +8475 3333333333333333333333333333333333333333333333333333333 +8476 3333333333333333333333333333333333333333333333333333333 +8477 3333333333333333333333333333333333333333333333333333333 +8478 3333333333333333333333333333333333333333333333333333333 +8479 3333333333333333333333333333333333333333333333333333333 +8480 3333333333333333333333333333333333333333333333333333333 +8481 3333333333333333333333333333333333333333333333333333333 +8482 3333333333333333333333333333333333333333333333333333333 +8483 3333333333333333333333333333333333333333333333333333333 +8484 3333333333333333333333333333333333333333333333333333333 +8485 3333333333333333333333333333333333333333333333333333333 +8486 3333333333333333333333333333333333333333333333333333333 +8487 3333333333333333333333333333333333333333333333333333333 +8488 3333333333333333333333333333333333333333333333333333333 +8489 3333333333333333333333333333333333333333333333333333333 +8490 3333333333333333333333333333333333333333333333333333333 +8491 3333333333333333333333333333333333333333333333333333333 +8492 3333333333333333333333333333333333333333333333333333333 +8493 3333333333333333333333333333333333333333333333333333333 +8494 3333333333333333333333333333333333333333333333333333333 +8495 3333333333333333333333333333333333333333333333333333333 +8496 3333333333333333333333333333333333333333333333333333333 +8497 3333333333333333333333333333333333333333333333333333333 +8498 3333333333333333333333333333333333333333333333333333333 +8499 3333333333333333333333333333333333333333333333333333333 +8500 3333333333333333333333333333333333333333333333333333333 +8501 3333333333333333333333333333333333333333333333333333333 +8502 3333333333333333333333333333333333333333333333333333333 +8503 3333333333333333333333333333333333333333333333333333333 +8504 3333333333333333333333333333333333333333333333333333333 +8505 3333333333333333333333333333333333333333333333333333333 +8506 3333333333333333333333333333333333333333333333333333333 +8507 3333333333333333333333333333333333333333333333333333333 +8508 3333333333333333333333333333333333333333333333333333333 +8509 3333333333333333333333333333333333333333333333333333333 +8510 3333333333333333333333333333333333333333333333333333333 +8511 3333333333333333333333333333333333333333333333333333333 +8512 3333333333333333333333333333333333333333333333333333333 +8513 3333333333333333333333333333333333333333333333333333333 +8514 3333333333333333333333333333333333333333333333333333333 +8515 3333333333333333333333333333333333333333333333333333333 +8516 3333333333333333333333333333333333333333333333333333333 +8517 3333333333333333333333333333333333333333333333333333333 +8518 3333333333333333333333333333333333333333333333333333333 +8519 3333333333333333333333333333333333333333333333333333333 +8520 3333333333333333333333333333333333333333333333333333333 +8521 3333333333333333333333333333333333333333333333333333333 +8522 3333333333333333333333333333333333333333333333333333333 +8523 3333333333333333333333333333333333333333333333333333333 +8524 3333333333333333333333333333333333333333333333333333333 +8525 3333333333333333333333333333333333333333333333333333333 +8526 3333333333333333333333333333333333333333333333333333333 +8527 3333333333333333333333333333333333333333333333333333333 +8528 3333333333333333333333333333333333333333333333333333333 +8529 3333333333333333333333333333333333333333333333333333333 +8530 3333333333333333333333333333333333333333333333333333333 +8531 3333333333333333333333333333333333333333333333333333333 +8532 3333333333333333333333333333333333333333333333333333333 +8533 3333333333333333333333333333333333333333333333333333333 +8534 3333333333333333333333333333333333333333333333333333333 +8535 3333333333333333333333333333333333333333333333333333333 +8536 3333333333333333333333333333333333333333333333333333333 +8537 3333333333333333333333333333333333333333333333333333333 +8538 3333333333333333333333333333333333333333333333333333333 +8539 3333333333333333333333333333333333333333333333333333333 +8540 3333333333333333333333333333333333333333333333333333333 +8541 3333333333333333333333333333333333333333333333333333333 +8542 3333333333333333333333333333333333333333333333333333333 +8543 3333333333333333333333333333333333333333333333333333333 +8544 3333333333333333333333333333333333333333333333333333333 +8545 3333333333333333333333333333333333333333333333333333333 +8546 3333333333333333333333333333333333333333333333333333333 +8547 3333333333333333333333333333333333333333333333333333333 +8548 3333333333333333333333333333333333333333333333333333333 +8549 3333333333333333333333333333333333333333333333333333333 +8550 3333333333333333333333333333333333333333333333333333333 +8551 3333333333333333333333333333333333333333333333333333333 +8552 3333333333333333333333333333333333333333333333333333333 +8553 3333333333333333333333333333333333333333333333333333333 +8554 3333333333333333333333333333333333333333333333333333333 +8555 3333333333333333333333333333333333333333333333333333333 +8556 3333333333333333333333333333333333333333333333333333333 +8557 3333333333333333333333333333333333333333333333333333333 +8558 3333333333333333333333333333333333333333333333333333333 +8559 3333333333333333333333333333333333333333333333333333333 +8560 3333333333333333333333333333333333333333333333333333333 +8561 3333333333333333333333333333333333333333333333333333333 +8562 3333333333333333333333333333333333333333333333333333333 +8563 3333333333333333333333333333333333333333333333333333333 +8564 3333333333333333333333333333333333333333333333333333333 +8565 3333333333333333333333333333333333333333333333333333333 +8566 3333333333333333333333333333333333333333333333333333333 +8567 3333333333333333333333333333333333333333333333333333333 +8568 3333333333333333333333333333333333333333333333333333333 +8569 3333333333333333333333333333333333333333333333333333333 +8570 3333333333333333333333333333333333333333333333333333333 +8571 3333333333333333333333333333333333333333333333333333333 +8572 3333333333333333333333333333333333333333333333333333333 +8573 3333333333333333333333333333333333333333333333333333333 +8574 3333333333333333333333333333333333333333333333333333333 +8575 3333333333333333333333333333333333333333333333333333333 +8576 3333333333333333333333333333333333333333333333333333333 +8577 3333333333333333333333333333333333333333333333333333333 +8578 3333333333333333333333333333333333333333333333333333333 +8579 3333333333333333333333333333333333333333333333333333333 +8580 3333333333333333333333333333333333333333333333333333333 +8581 3333333333333333333333333333333333333333333333333333333 +8582 3333333333333333333333333333333333333333333333333333333 +8583 3333333333333333333333333333333333333333333333333333333 +8584 3333333333333333333333333333333333333333333333333333333 +8585 3333333333333333333333333333333333333333333333333333333 +8586 3333333333333333333333333333333333333333333333333333333 +8587 3333333333333333333333333333333333333333333333333333333 +8588 3333333333333333333333333333333333333333333333333333333 +8589 3333333333333333333333333333333333333333333333333333333 +8590 3333333333333333333333333333333333333333333333333333333 +8591 3333333333333333333333333333333333333333333333333333333 +8592 3333333333333333333333333333333333333333333333333333333 +8593 3333333333333333333333333333333333333333333333333333333 +8594 3333333333333333333333333333333333333333333333333333333 +8595 3333333333333333333333333333333333333333333333333333333 +8596 3333333333333333333333333333333333333333333333333333333 +8597 3333333333333333333333333333333333333333333333333333333 +8598 3333333333333333333333333333333333333333333333333333333 +8599 3333333333333333333333333333333333333333333333333333333 +8600 3333333333333333333333333333333333333333333333333333333 +8601 3333333333333333333333333333333333333333333333333333333 +8602 3333333333333333333333333333333333333333333333333333333 +8603 3333333333333333333333333333333333333333333333333333333 +8604 3333333333333333333333333333333333333333333333333333333 +8605 3333333333333333333333333333333333333333333333333333333 +8606 3333333333333333333333333333333333333333333333333333333 +8607 3333333333333333333333333333333333333333333333333333333 +8608 3333333333333333333333333333333333333333333333333333333 +8609 3333333333333333333333333333333333333333333333333333333 +8610 3333333333333333333333333333333333333333333333333333333 +8611 3333333333333333333333333333333333333333333333333333333 +8612 3333333333333333333333333333333333333333333333333333333 +8613 3333333333333333333333333333333333333333333333333333333 +8614 3333333333333333333333333333333333333333333333333333333 +8615 3333333333333333333333333333333333333333333333333333333 +8616 3333333333333333333333333333333333333333333333333333333 +8617 3333333333333333333333333333333333333333333333333333333 +8618 3333333333333333333333333333333333333333333333333333333 +8619 3333333333333333333333333333333333333333333333333333333 +8620 3333333333333333333333333333333333333333333333333333333 +8621 3333333333333333333333333333333333333333333333333333333 +8622 3333333333333333333333333333333333333333333333333333333 +8623 3333333333333333333333333333333333333333333333333333333 +8624 3333333333333333333333333333333333333333333333333333333 +8625 3333333333333333333333333333333333333333333333333333333 +8626 3333333333333333333333333333333333333333333333333333333 +8627 3333333333333333333333333333333333333333333333333333333 +8628 3333333333333333333333333333333333333333333333333333333 +8629 3333333333333333333333333333333333333333333333333333333 +8630 3333333333333333333333333333333333333333333333333333333 +8631 3333333333333333333333333333333333333333333333333333333 +8632 3333333333333333333333333333333333333333333333333333333 +8633 3333333333333333333333333333333333333333333333333333333 +8634 3333333333333333333333333333333333333333333333333333333 +8635 3333333333333333333333333333333333333333333333333333333 +8636 3333333333333333333333333333333333333333333333333333333 +8637 3333333333333333333333333333333333333333333333333333333 +8638 3333333333333333333333333333333333333333333333333333333 +8639 3333333333333333333333333333333333333333333333333333333 +8640 3333333333333333333333333333333333333333333333333333333 +8641 3333333333333333333333333333333333333333333333333333333 +8642 3333333333333333333333333333333333333333333333333333333 +8643 3333333333333333333333333333333333333333333333333333333 +8644 3333333333333333333333333333333333333333333333333333333 +8645 3333333333333333333333333333333333333333333333333333333 +8646 3333333333333333333333333333333333333333333333333333333 +8647 3333333333333333333333333333333333333333333333333333333 +8648 3333333333333333333333333333333333333333333333333333333 +8649 3333333333333333333333333333333333333333333333333333333 +8650 3333333333333333333333333333333333333333333333333333333 +8651 3333333333333333333333333333333333333333333333333333333 +8652 3333333333333333333333333333333333333333333333333333333 +8653 3333333333333333333333333333333333333333333333333333333 +8654 3333333333333333333333333333333333333333333333333333333 +8655 3333333333333333333333333333333333333333333333333333333 +8656 3333333333333333333333333333333333333333333333333333333 +8657 3333333333333333333333333333333333333333333333333333333 +8658 3333333333333333333333333333333333333333333333333333333 +8659 3333333333333333333333333333333333333333333333333333333 +8660 3333333333333333333333333333333333333333333333333333333 +8661 3333333333333333333333333333333333333333333333333333333 +8662 3333333333333333333333333333333333333333333333333333333 +8663 3333333333333333333333333333333333333333333333333333333 +8664 3333333333333333333333333333333333333333333333333333333 +8665 3333333333333333333333333333333333333333333333333333333 +8666 3333333333333333333333333333333333333333333333333333333 +8667 3333333333333333333333333333333333333333333333333333333 +8668 3333333333333333333333333333333333333333333333333333333 +8669 3333333333333333333333333333333333333333333333333333333 +8670 3333333333333333333333333333333333333333333333333333333 +8671 3333333333333333333333333333333333333333333333333333333 +8672 3333333333333333333333333333333333333333333333333333333 +8673 3333333333333333333333333333333333333333333333333333333 +8674 3333333333333333333333333333333333333333333333333333333 +8675 3333333333333333333333333333333333333333333333333333333 +8676 3333333333333333333333333333333333333333333333333333333 +8677 3333333333333333333333333333333333333333333333333333333 +8678 3333333333333333333333333333333333333333333333333333333 +8679 3333333333333333333333333333333333333333333333333333333 +8680 3333333333333333333333333333333333333333333333333333333 +8681 3333333333333333333333333333333333333333333333333333333 +8682 3333333333333333333333333333333333333333333333333333333 +8683 3333333333333333333333333333333333333333333333333333333 +8684 3333333333333333333333333333333333333333333333333333333 +8685 3333333333333333333333333333333333333333333333333333333 +8686 3333333333333333333333333333333333333333333333333333333 +8687 3333333333333333333333333333333333333333333333333333333 +8688 3333333333333333333333333333333333333333333333333333333 +8689 3333333333333333333333333333333333333333333333333333333 +8690 3333333333333333333333333333333333333333333333333333333 +8691 3333333333333333333333333333333333333333333333333333333 +8692 3333333333333333333333333333333333333333333333333333333 +8693 3333333333333333333333333333333333333333333333333333333 +8694 3333333333333333333333333333333333333333333333333333333 +8695 3333333333333333333333333333333333333333333333333333333 +8696 3333333333333333333333333333333333333333333333333333333 +8697 3333333333333333333333333333333333333333333333333333333 +8698 3333333333333333333333333333333333333333333333333333333 +8699 3333333333333333333333333333333333333333333333333333333 +8700 3333333333333333333333333333333333333333333333333333333 +8701 3333333333333333333333333333333333333333333333333333333 +8702 3333333333333333333333333333333333333333333333333333333 +8703 3333333333333333333333333333333333333333333333333333333 +8704 3333333333333333333333333333333333333333333333333333333 +8705 3333333333333333333333333333333333333333333333333333333 +8706 3333333333333333333333333333333333333333333333333333333 +8707 3333333333333333333333333333333333333333333333333333333 +8708 3333333333333333333333333333333333333333333333333333333 +8709 3333333333333333333333333333333333333333333333333333333 +8710 3333333333333333333333333333333333333333333333333333333 +8711 3333333333333333333333333333333333333333333333333333333 +8712 3333333333333333333333333333333333333333333333333333333 +8713 3333333333333333333333333333333333333333333333333333333 +8714 3333333333333333333333333333333333333333333333333333333 +8715 3333333333333333333333333333333333333333333333333333333 +8716 3333333333333333333333333333333333333333333333333333333 +8717 3333333333333333333333333333333333333333333333333333333 +8718 3333333333333333333333333333333333333333333333333333333 +8719 3333333333333333333333333333333333333333333333333333333 +8720 3333333333333333333333333333333333333333333333333333333 +8721 3333333333333333333333333333333333333333333333333333333 +8722 3333333333333333333333333333333333333333333333333333333 +8723 3333333333333333333333333333333333333333333333333333333 +8724 3333333333333333333333333333333333333333333333333333333 +8725 3333333333333333333333333333333333333333333333333333333 +8726 3333333333333333333333333333333333333333333333333333333 +8727 3333333333333333333333333333333333333333333333333333333 +8728 3333333333333333333333333333333333333333333333333333333 +8729 3333333333333333333333333333333333333333333333333333333 +8730 3333333333333333333333333333333333333333333333333333333 +8731 3333333333333333333333333333333333333333333333333333333 +8732 3333333333333333333333333333333333333333333333333333333 +8733 3333333333333333333333333333333333333333333333333333333 +8734 3333333333333333333333333333333333333333333333333333333 +8735 3333333333333333333333333333333333333333333333333333333 +8736 3333333333333333333333333333333333333333333333333333333 +8737 3333333333333333333333333333333333333333333333333333333 +8738 3333333333333333333333333333333333333333333333333333333 +8739 3333333333333333333333333333333333333333333333333333333 +8740 3333333333333333333333333333333333333333333333333333333 +8741 3333333333333333333333333333333333333333333333333333333 +8742 3333333333333333333333333333333333333333333333333333333 +8743 3333333333333333333333333333333333333333333333333333333 +8744 3333333333333333333333333333333333333333333333333333333 +8745 3333333333333333333333333333333333333333333333333333333 +8746 3333333333333333333333333333333333333333333333333333333 +8747 3333333333333333333333333333333333333333333333333333333 +8748 3333333333333333333333333333333333333333333333333333333 +8749 3333333333333333333333333333333333333333333333333333333 +8750 3333333333333333333333333333333333333333333333333333333 +8751 3333333333333333333333333333333333333333333333333333333 +8752 3333333333333333333333333333333333333333333333333333333 +8753 3333333333333333333333333333333333333333333333333333333 +8754 3333333333333333333333333333333333333333333333333333333 +8755 3333333333333333333333333333333333333333333333333333333 +8756 3333333333333333333333333333333333333333333333333333333 +8757 3333333333333333333333333333333333333333333333333333333 +8758 3333333333333333333333333333333333333333333333333333333 +8759 3333333333333333333333333333333333333333333333333333333 +8760 3333333333333333333333333333333333333333333333333333333 +8761 3333333333333333333333333333333333333333333333333333333 +8762 3333333333333333333333333333333333333333333333333333333 +8763 3333333333333333333333333333333333333333333333333333333 +8764 3333333333333333333333333333333333333333333333333333333 +8765 3333333333333333333333333333333333333333333333333333333 +8766 3333333333333333333333333333333333333333333333333333333 +8767 3333333333333333333333333333333333333333333333333333333 +8768 3333333333333333333333333333333333333333333333333333333 +8769 3333333333333333333333333333333333333333333333333333333 +8770 3333333333333333333333333333333333333333333333333333333 +8771 3333333333333333333333333333333333333333333333333333333 +8772 3333333333333333333333333333333333333333333333333333333 +8773 3333333333333333333333333333333333333333333333333333333 +8774 3333333333333333333333333333333333333333333333333333333 +8775 3333333333333333333333333333333333333333333333333333333 +8776 3333333333333333333333333333333333333333333333333333333 +8777 3333333333333333333333333333333333333333333333333333333 +8778 3333333333333333333333333333333333333333333333333333333 +8779 3333333333333333333333333333333333333333333333333333333 +8780 3333333333333333333333333333333333333333333333333333333 +8781 3333333333333333333333333333333333333333333333333333333 +8782 3333333333333333333333333333333333333333333333333333333 +8783 3333333333333333333333333333333333333333333333333333333 +8784 3333333333333333333333333333333333333333333333333333333 +8785 3333333333333333333333333333333333333333333333333333333 +8786 3333333333333333333333333333333333333333333333333333333 +8787 3333333333333333333333333333333333333333333333333333333 +8788 3333333333333333333333333333333333333333333333333333333 +8789 3333333333333333333333333333333333333333333333333333333 +8790 3333333333333333333333333333333333333333333333333333333 +8791 3333333333333333333333333333333333333333333333333333333 +8792 3333333333333333333333333333333333333333333333333333333 +8793 3333333333333333333333333333333333333333333333333333333 +8794 3333333333333333333333333333333333333333333333333333333 +8795 3333333333333333333333333333333333333333333333333333333 +8796 3333333333333333333333333333333333333333333333333333333 +8797 3333333333333333333333333333333333333333333333333333333 +8798 3333333333333333333333333333333333333333333333333333333 +8799 3333333333333333333333333333333333333333333333333333333 +8800 3333333333333333333333333333333333333333333333333333333 +8801 3333333333333333333333333333333333333333333333333333333 +8802 3333333333333333333333333333333333333333333333333333333 +8803 3333333333333333333333333333333333333333333333333333333 +8804 3333333333333333333333333333333333333333333333333333333 +8805 3333333333333333333333333333333333333333333333333333333 +8806 3333333333333333333333333333333333333333333333333333333 +8807 3333333333333333333333333333333333333333333333333333333 +8808 3333333333333333333333333333333333333333333333333333333 +8809 3333333333333333333333333333333333333333333333333333333 +8810 3333333333333333333333333333333333333333333333333333333 +8811 3333333333333333333333333333333333333333333333333333333 +8812 3333333333333333333333333333333333333333333333333333333 +8813 3333333333333333333333333333333333333333333333333333333 +8814 3333333333333333333333333333333333333333333333333333333 +8815 3333333333333333333333333333333333333333333333333333333 +8816 3333333333333333333333333333333333333333333333333333333 +8817 3333333333333333333333333333333333333333333333333333333 +8818 3333333333333333333333333333333333333333333333333333333 +8819 3333333333333333333333333333333333333333333333333333333 +8820 3333333333333333333333333333333333333333333333333333333 +8821 3333333333333333333333333333333333333333333333333333333 +8822 3333333333333333333333333333333333333333333333333333333 +8823 3333333333333333333333333333333333333333333333333333333 +8824 3333333333333333333333333333333333333333333333333333333 +8825 3333333333333333333333333333333333333333333333333333333 +8826 3333333333333333333333333333333333333333333333333333333 +8827 3333333333333333333333333333333333333333333333333333333 +8828 3333333333333333333333333333333333333333333333333333333 +8829 3333333333333333333333333333333333333333333333333333333 +8830 3333333333333333333333333333333333333333333333333333333 +8831 3333333333333333333333333333333333333333333333333333333 +8832 3333333333333333333333333333333333333333333333333333333 +8833 3333333333333333333333333333333333333333333333333333333 +8834 3333333333333333333333333333333333333333333333333333333 +8835 3333333333333333333333333333333333333333333333333333333 +8836 3333333333333333333333333333333333333333333333333333333 +8837 3333333333333333333333333333333333333333333333333333333 +8838 3333333333333333333333333333333333333333333333333333333 +8839 3333333333333333333333333333333333333333333333333333333 +8840 3333333333333333333333333333333333333333333333333333333 +8841 3333333333333333333333333333333333333333333333333333333 +8842 3333333333333333333333333333333333333333333333333333333 +8843 3333333333333333333333333333333333333333333333333333333 +8844 3333333333333333333333333333333333333333333333333333333 +8845 3333333333333333333333333333333333333333333333333333333 +8846 3333333333333333333333333333333333333333333333333333333 +8847 3333333333333333333333333333333333333333333333333333333 +8848 3333333333333333333333333333333333333333333333333333333 +8849 3333333333333333333333333333333333333333333333333333333 +8850 3333333333333333333333333333333333333333333333333333333 +8851 3333333333333333333333333333333333333333333333333333333 +8852 3333333333333333333333333333333333333333333333333333333 +8853 3333333333333333333333333333333333333333333333333333333 +8854 3333333333333333333333333333333333333333333333333333333 +8855 3333333333333333333333333333333333333333333333333333333 +8856 3333333333333333333333333333333333333333333333333333333 +8857 3333333333333333333333333333333333333333333333333333333 +8858 3333333333333333333333333333333333333333333333333333333 +8859 3333333333333333333333333333333333333333333333333333333 +8860 3333333333333333333333333333333333333333333333333333333 +8861 3333333333333333333333333333333333333333333333333333333 +8862 3333333333333333333333333333333333333333333333333333333 +8863 3333333333333333333333333333333333333333333333333333333 +8864 3333333333333333333333333333333333333333333333333333333 +8865 3333333333333333333333333333333333333333333333333333333 +8866 3333333333333333333333333333333333333333333333333333333 +8867 3333333333333333333333333333333333333333333333333333333 +8868 3333333333333333333333333333333333333333333333333333333 +8869 3333333333333333333333333333333333333333333333333333333 +8870 3333333333333333333333333333333333333333333333333333333 +8871 3333333333333333333333333333333333333333333333333333333 +8872 3333333333333333333333333333333333333333333333333333333 +8873 3333333333333333333333333333333333333333333333333333333 +8874 3333333333333333333333333333333333333333333333333333333 +8875 3333333333333333333333333333333333333333333333333333333 +8876 3333333333333333333333333333333333333333333333333333333 +8877 3333333333333333333333333333333333333333333333333333333 +8878 3333333333333333333333333333333333333333333333333333333 +8879 3333333333333333333333333333333333333333333333333333333 +8880 3333333333333333333333333333333333333333333333333333333 +8881 3333333333333333333333333333333333333333333333333333333 +8882 3333333333333333333333333333333333333333333333333333333 +8883 3333333333333333333333333333333333333333333333333333333 +8884 3333333333333333333333333333333333333333333333333333333 +8885 3333333333333333333333333333333333333333333333333333333 +8886 3333333333333333333333333333333333333333333333333333333 +8887 3333333333333333333333333333333333333333333333333333333 +8888 3333333333333333333333333333333333333333333333333333333 +8889 3333333333333333333333333333333333333333333333333333333 +8890 3333333333333333333333333333333333333333333333333333333 +8891 3333333333333333333333333333333333333333333333333333333 +8892 3333333333333333333333333333333333333333333333333333333 +8893 3333333333333333333333333333333333333333333333333333333 +8894 3333333333333333333333333333333333333333333333333333333 +8895 3333333333333333333333333333333333333333333333333333333 +8896 3333333333333333333333333333333333333333333333333333333 +8897 3333333333333333333333333333333333333333333333333333333 +8898 3333333333333333333333333333333333333333333333333333333 +8899 3333333333333333333333333333333333333333333333333333333 +8900 3333333333333333333333333333333333333333333333333333333 +8901 3333333333333333333333333333333333333333333333333333333 +8902 3333333333333333333333333333333333333333333333333333333 +8903 3333333333333333333333333333333333333333333333333333333 +8904 3333333333333333333333333333333333333333333333333333333 +8905 3333333333333333333333333333333333333333333333333333333 +8906 3333333333333333333333333333333333333333333333333333333 +8907 3333333333333333333333333333333333333333333333333333333 +8908 3333333333333333333333333333333333333333333333333333333 +8909 3333333333333333333333333333333333333333333333333333333 +8910 3333333333333333333333333333333333333333333333333333333 +8911 3333333333333333333333333333333333333333333333333333333 +8912 3333333333333333333333333333333333333333333333333333333 +8913 3333333333333333333333333333333333333333333333333333333 +8914 3333333333333333333333333333333333333333333333333333333 +8915 3333333333333333333333333333333333333333333333333333333 +8916 3333333333333333333333333333333333333333333333333333333 +8917 3333333333333333333333333333333333333333333333333333333 +8918 3333333333333333333333333333333333333333333333333333333 +8919 3333333333333333333333333333333333333333333333333333333 +8920 3333333333333333333333333333333333333333333333333333333 +8921 3333333333333333333333333333333333333333333333333333333 +8922 3333333333333333333333333333333333333333333333333333333 +8923 3333333333333333333333333333333333333333333333333333333 +8924 3333333333333333333333333333333333333333333333333333333 +8925 3333333333333333333333333333333333333333333333333333333 +8926 3333333333333333333333333333333333333333333333333333333 +8927 3333333333333333333333333333333333333333333333333333333 +8928 3333333333333333333333333333333333333333333333333333333 +8929 3333333333333333333333333333333333333333333333333333333 +8930 3333333333333333333333333333333333333333333333333333333 +8931 3333333333333333333333333333333333333333333333333333333 +8932 3333333333333333333333333333333333333333333333333333333 +8933 3333333333333333333333333333333333333333333333333333333 +8934 3333333333333333333333333333333333333333333333333333333 +8935 3333333333333333333333333333333333333333333333333333333 +8936 3333333333333333333333333333333333333333333333333333333 +8937 3333333333333333333333333333333333333333333333333333333 +8938 3333333333333333333333333333333333333333333333333333333 +8939 3333333333333333333333333333333333333333333333333333333 +8940 3333333333333333333333333333333333333333333333333333333 +8941 3333333333333333333333333333333333333333333333333333333 +8942 3333333333333333333333333333333333333333333333333333333 +8943 3333333333333333333333333333333333333333333333333333333 +8944 3333333333333333333333333333333333333333333333333333333 +8945 3333333333333333333333333333333333333333333333333333333 +8946 3333333333333333333333333333333333333333333333333333333 +8947 3333333333333333333333333333333333333333333333333333333 +8948 3333333333333333333333333333333333333333333333333333333 +8949 3333333333333333333333333333333333333333333333333333333 +8950 3333333333333333333333333333333333333333333333333333333 +8951 3333333333333333333333333333333333333333333333333333333 +8952 3333333333333333333333333333333333333333333333333333333 +8953 3333333333333333333333333333333333333333333333333333333 +8954 3333333333333333333333333333333333333333333333333333333 +8955 3333333333333333333333333333333333333333333333333333333 +8956 3333333333333333333333333333333333333333333333333333333 +8957 3333333333333333333333333333333333333333333333333333333 +8958 3333333333333333333333333333333333333333333333333333333 +8959 3333333333333333333333333333333333333333333333333333333 +8960 3333333333333333333333333333333333333333333333333333333 +8961 3333333333333333333333333333333333333333333333333333333 +8962 3333333333333333333333333333333333333333333333333333333 +8963 3333333333333333333333333333333333333333333333333333333 +8964 3333333333333333333333333333333333333333333333333333333 +8965 3333333333333333333333333333333333333333333333333333333 +8966 3333333333333333333333333333333333333333333333333333333 +8967 3333333333333333333333333333333333333333333333333333333 +8968 3333333333333333333333333333333333333333333333333333333 +8969 3333333333333333333333333333333333333333333333333333333 +8970 3333333333333333333333333333333333333333333333333333333 +8971 3333333333333333333333333333333333333333333333333333333 +8972 3333333333333333333333333333333333333333333333333333333 +8973 3333333333333333333333333333333333333333333333333333333 +8974 3333333333333333333333333333333333333333333333333333333 +8975 3333333333333333333333333333333333333333333333333333333 +8976 3333333333333333333333333333333333333333333333333333333 +8977 3333333333333333333333333333333333333333333333333333333 +8978 3333333333333333333333333333333333333333333333333333333 +8979 3333333333333333333333333333333333333333333333333333333 +8980 3333333333333333333333333333333333333333333333333333333 +8981 3333333333333333333333333333333333333333333333333333333 +8982 3333333333333333333333333333333333333333333333333333333 +8983 3333333333333333333333333333333333333333333333333333333 +8984 3333333333333333333333333333333333333333333333333333333 +8985 3333333333333333333333333333333333333333333333333333333 +8986 3333333333333333333333333333333333333333333333333333333 +8987 3333333333333333333333333333333333333333333333333333333 +8988 3333333333333333333333333333333333333333333333333333333 +8989 3333333333333333333333333333333333333333333333333333333 +8990 3333333333333333333333333333333333333333333333333333333 +8991 3333333333333333333333333333333333333333333333333333333 +8992 3333333333333333333333333333333333333333333333333333333 +8993 3333333333333333333333333333333333333333333333333333333 +8994 3333333333333333333333333333333333333333333333333333333 +8995 3333333333333333333333333333333333333333333333333333333 +8996 3333333333333333333333333333333333333333333333333333333 +8997 3333333333333333333333333333333333333333333333333333333 +8998 3333333333333333333333333333333333333333333333333333333 +8999 3333333333333333333333333333333333333333333333333333333 +9000 3333333333333333333333333333333333333333333333333333333 +9001 3333333333333333333333333333333333333333333333333333333 +9002 3333333333333333333333333333333333333333333333333333333 +9003 3333333333333333333333333333333333333333333333333333333 +9004 3333333333333333333333333333333333333333333333333333333 +9005 3333333333333333333333333333333333333333333333333333333 +9006 3333333333333333333333333333333333333333333333333333333 +9007 3333333333333333333333333333333333333333333333333333333 +9008 3333333333333333333333333333333333333333333333333333333 +9009 3333333333333333333333333333333333333333333333333333333 +9010 3333333333333333333333333333333333333333333333333333333 +9011 3333333333333333333333333333333333333333333333333333333 +9012 3333333333333333333333333333333333333333333333333333333 +9013 3333333333333333333333333333333333333333333333333333333 +9014 3333333333333333333333333333333333333333333333333333333 +9015 3333333333333333333333333333333333333333333333333333333 +9016 3333333333333333333333333333333333333333333333333333333 +9017 3333333333333333333333333333333333333333333333333333333 +9018 3333333333333333333333333333333333333333333333333333333 +9019 3333333333333333333333333333333333333333333333333333333 +9020 3333333333333333333333333333333333333333333333333333333 +9021 3333333333333333333333333333333333333333333333333333333 +9022 3333333333333333333333333333333333333333333333333333333 +9023 3333333333333333333333333333333333333333333333333333333 +9024 3333333333333333333333333333333333333333333333333333333 +9025 3333333333333333333333333333333333333333333333333333333 +9026 3333333333333333333333333333333333333333333333333333333 +9027 3333333333333333333333333333333333333333333333333333333 +9028 3333333333333333333333333333333333333333333333333333333 +9029 3333333333333333333333333333333333333333333333333333333 +9030 3333333333333333333333333333333333333333333333333333333 +9031 3333333333333333333333333333333333333333333333333333333 +9032 3333333333333333333333333333333333333333333333333333333 +9033 3333333333333333333333333333333333333333333333333333333 +9034 3333333333333333333333333333333333333333333333333333333 +9035 3333333333333333333333333333333333333333333333333333333 +9036 3333333333333333333333333333333333333333333333333333333 +9037 3333333333333333333333333333333333333333333333333333333 +9038 3333333333333333333333333333333333333333333333333333333 +9039 3333333333333333333333333333333333333333333333333333333 +9040 3333333333333333333333333333333333333333333333333333333 +9041 3333333333333333333333333333333333333333333333333333333 +9042 3333333333333333333333333333333333333333333333333333333 +9043 3333333333333333333333333333333333333333333333333333333 +9044 3333333333333333333333333333333333333333333333333333333 +9045 3333333333333333333333333333333333333333333333333333333 +9046 3333333333333333333333333333333333333333333333333333333 +9047 3333333333333333333333333333333333333333333333333333333 +9048 3333333333333333333333333333333333333333333333333333333 +9049 3333333333333333333333333333333333333333333333333333333 +9050 3333333333333333333333333333333333333333333333333333333 +9051 3333333333333333333333333333333333333333333333333333333 +9052 3333333333333333333333333333333333333333333333333333333 +9053 3333333333333333333333333333333333333333333333333333333 +9054 3333333333333333333333333333333333333333333333333333333 +9055 3333333333333333333333333333333333333333333333333333333 +9056 3333333333333333333333333333333333333333333333333333333 +9057 3333333333333333333333333333333333333333333333333333333 +9058 3333333333333333333333333333333333333333333333333333333 +9059 3333333333333333333333333333333333333333333333333333333 +9060 3333333333333333333333333333333333333333333333333333333 +9061 3333333333333333333333333333333333333333333333333333333 +9062 3333333333333333333333333333333333333333333333333333333 +9063 3333333333333333333333333333333333333333333333333333333 +9064 3333333333333333333333333333333333333333333333333333333 +9065 3333333333333333333333333333333333333333333333333333333 +9066 3333333333333333333333333333333333333333333333333333333 +9067 3333333333333333333333333333333333333333333333333333333 +9068 3333333333333333333333333333333333333333333333333333333 +9069 3333333333333333333333333333333333333333333333333333333 +9070 3333333333333333333333333333333333333333333333333333333 +9071 3333333333333333333333333333333333333333333333333333333 +9072 3333333333333333333333333333333333333333333333333333333 +9073 3333333333333333333333333333333333333333333333333333333 +9074 3333333333333333333333333333333333333333333333333333333 +9075 3333333333333333333333333333333333333333333333333333333 +9076 3333333333333333333333333333333333333333333333333333333 +9077 3333333333333333333333333333333333333333333333333333333 +9078 3333333333333333333333333333333333333333333333333333333 +9079 3333333333333333333333333333333333333333333333333333333 +9080 3333333333333333333333333333333333333333333333333333333 +9081 3333333333333333333333333333333333333333333333333333333 +9082 3333333333333333333333333333333333333333333333333333333 +9083 3333333333333333333333333333333333333333333333333333333 +9084 3333333333333333333333333333333333333333333333333333333 +9085 3333333333333333333333333333333333333333333333333333333 +9086 3333333333333333333333333333333333333333333333333333333 +9087 3333333333333333333333333333333333333333333333333333333 +9088 3333333333333333333333333333333333333333333333333333333 +9089 3333333333333333333333333333333333333333333333333333333 +9090 3333333333333333333333333333333333333333333333333333333 +9091 3333333333333333333333333333333333333333333333333333333 +9092 3333333333333333333333333333333333333333333333333333333 +9093 3333333333333333333333333333333333333333333333333333333 +9094 3333333333333333333333333333333333333333333333333333333 +9095 3333333333333333333333333333333333333333333333333333333 +9096 3333333333333333333333333333333333333333333333333333333 +9097 3333333333333333333333333333333333333333333333333333333 +9098 3333333333333333333333333333333333333333333333333333333 +9099 3333333333333333333333333333333333333333333333333333333 +9100 3333333333333333333333333333333333333333333333333333333 +9101 3333333333333333333333333333333333333333333333333333333 +9102 3333333333333333333333333333333333333333333333333333333 +9103 3333333333333333333333333333333333333333333333333333333 +9104 3333333333333333333333333333333333333333333333333333333 +9105 3333333333333333333333333333333333333333333333333333333 +9106 3333333333333333333333333333333333333333333333333333333 +9107 3333333333333333333333333333333333333333333333333333333 +9108 3333333333333333333333333333333333333333333333333333333 +9109 3333333333333333333333333333333333333333333333333333333 +9110 3333333333333333333333333333333333333333333333333333333 +9111 3333333333333333333333333333333333333333333333333333333 +9112 3333333333333333333333333333333333333333333333333333333 +9113 3333333333333333333333333333333333333333333333333333333 +9114 3333333333333333333333333333333333333333333333333333333 +9115 3333333333333333333333333333333333333333333333333333333 +9116 3333333333333333333333333333333333333333333333333333333 +9117 3333333333333333333333333333333333333333333333333333333 +9118 3333333333333333333333333333333333333333333333333333333 +9119 3333333333333333333333333333333333333333333333333333333 +9120 3333333333333333333333333333333333333333333333333333333 +9121 3333333333333333333333333333333333333333333333333333333 +9122 3333333333333333333333333333333333333333333333333333333 +9123 3333333333333333333333333333333333333333333333333333333 +9124 3333333333333333333333333333333333333333333333333333333 +9125 3333333333333333333333333333333333333333333333333333333 +9126 3333333333333333333333333333333333333333333333333333333 +9127 3333333333333333333333333333333333333333333333333333333 +9128 3333333333333333333333333333333333333333333333333333333 +9129 3333333333333333333333333333333333333333333333333333333 +9130 3333333333333333333333333333333333333333333333333333333 +9131 3333333333333333333333333333333333333333333333333333333 +9132 3333333333333333333333333333333333333333333333333333333 +9133 3333333333333333333333333333333333333333333333333333333 +9134 3333333333333333333333333333333333333333333333333333333 +9135 3333333333333333333333333333333333333333333333333333333 +9136 3333333333333333333333333333333333333333333333333333333 +9137 3333333333333333333333333333333333333333333333333333333 +9138 3333333333333333333333333333333333333333333333333333333 +9139 3333333333333333333333333333333333333333333333333333333 +9140 3333333333333333333333333333333333333333333333333333333 +9141 3333333333333333333333333333333333333333333333333333333 +9142 3333333333333333333333333333333333333333333333333333333 +9143 3333333333333333333333333333333333333333333333333333333 +9144 3333333333333333333333333333333333333333333333333333333 +9145 3333333333333333333333333333333333333333333333333333333 +9146 3333333333333333333333333333333333333333333333333333333 +9147 3333333333333333333333333333333333333333333333333333333 +9148 3333333333333333333333333333333333333333333333333333333 +9149 3333333333333333333333333333333333333333333333333333333 +9150 3333333333333333333333333333333333333333333333333333333 +9151 3333333333333333333333333333333333333333333333333333333 +9152 3333333333333333333333333333333333333333333333333333333 +9153 3333333333333333333333333333333333333333333333333333333 +9154 3333333333333333333333333333333333333333333333333333333 +9155 3333333333333333333333333333333333333333333333333333333 +9156 3333333333333333333333333333333333333333333333333333333 +9157 3333333333333333333333333333333333333333333333333333333 +9158 3333333333333333333333333333333333333333333333333333333 +9159 3333333333333333333333333333333333333333333333333333333 +9160 3333333333333333333333333333333333333333333333333333333 +9161 3333333333333333333333333333333333333333333333333333333 +9162 3333333333333333333333333333333333333333333333333333333 +9163 3333333333333333333333333333333333333333333333333333333 +9164 3333333333333333333333333333333333333333333333333333333 +9165 3333333333333333333333333333333333333333333333333333333 +9166 3333333333333333333333333333333333333333333333333333333 +9167 3333333333333333333333333333333333333333333333333333333 +9168 3333333333333333333333333333333333333333333333333333333 +9169 3333333333333333333333333333333333333333333333333333333 +9170 3333333333333333333333333333333333333333333333333333333 +9171 3333333333333333333333333333333333333333333333333333333 +9172 3333333333333333333333333333333333333333333333333333333 +9173 3333333333333333333333333333333333333333333333333333333 +9174 3333333333333333333333333333333333333333333333333333333 +9175 3333333333333333333333333333333333333333333333333333333 +9176 3333333333333333333333333333333333333333333333333333333 +9177 3333333333333333333333333333333333333333333333333333333 +9178 3333333333333333333333333333333333333333333333333333333 +9179 3333333333333333333333333333333333333333333333333333333 +9180 3333333333333333333333333333333333333333333333333333333 +9181 3333333333333333333333333333333333333333333333333333333 +9182 3333333333333333333333333333333333333333333333333333333 +9183 3333333333333333333333333333333333333333333333333333333 +9184 3333333333333333333333333333333333333333333333333333333 +9185 3333333333333333333333333333333333333333333333333333333 +9186 3333333333333333333333333333333333333333333333333333333 +9187 3333333333333333333333333333333333333333333333333333333 +9188 3333333333333333333333333333333333333333333333333333333 +9189 3333333333333333333333333333333333333333333333333333333 +9190 3333333333333333333333333333333333333333333333333333333 +9191 3333333333333333333333333333333333333333333333333333333 +9192 3333333333333333333333333333333333333333333333333333333 +9193 3333333333333333333333333333333333333333333333333333333 +9194 3333333333333333333333333333333333333333333333333333333 +9195 3333333333333333333333333333333333333333333333333333333 +9196 3333333333333333333333333333333333333333333333333333333 +9197 3333333333333333333333333333333333333333333333333333333 +9198 3333333333333333333333333333333333333333333333333333333 +9199 3333333333333333333333333333333333333333333333333333333 +9200 3333333333333333333333333333333333333333333333333333333 +9201 3333333333333333333333333333333333333333333333333333333 +9202 3333333333333333333333333333333333333333333333333333333 +9203 3333333333333333333333333333333333333333333333333333333 +9204 3333333333333333333333333333333333333333333333333333333 +9205 3333333333333333333333333333333333333333333333333333333 +9206 3333333333333333333333333333333333333333333333333333333 +9207 3333333333333333333333333333333333333333333333333333333 +9208 3333333333333333333333333333333333333333333333333333333 +9209 3333333333333333333333333333333333333333333333333333333 +9210 3333333333333333333333333333333333333333333333333333333 +9211 3333333333333333333333333333333333333333333333333333333 +9212 3333333333333333333333333333333333333333333333333333333 +9213 3333333333333333333333333333333333333333333333333333333 +9214 3333333333333333333333333333333333333333333333333333333 +9215 3333333333333333333333333333333333333333333333333333333 +9216 3333333333333333333333333333333333333333333333333333333 +9217 3333333333333333333333333333333333333333333333333333333 +9218 3333333333333333333333333333333333333333333333333333333 +9219 3333333333333333333333333333333333333333333333333333333 +9220 3333333333333333333333333333333333333333333333333333333 +9221 3333333333333333333333333333333333333333333333333333333 +9222 3333333333333333333333333333333333333333333333333333333 +9223 3333333333333333333333333333333333333333333333333333333 +9224 3333333333333333333333333333333333333333333333333333333 +9225 3333333333333333333333333333333333333333333333333333333 +9226 3333333333333333333333333333333333333333333333333333333 +9227 3333333333333333333333333333333333333333333333333333333 +9228 3333333333333333333333333333333333333333333333333333333 +9229 3333333333333333333333333333333333333333333333333333333 +9230 3333333333333333333333333333333333333333333333333333333 +9231 3333333333333333333333333333333333333333333333333333333 +9232 3333333333333333333333333333333333333333333333333333333 +9233 3333333333333333333333333333333333333333333333333333333 +9234 3333333333333333333333333333333333333333333333333333333 +9235 3333333333333333333333333333333333333333333333333333333 +9236 3333333333333333333333333333333333333333333333333333333 +9237 3333333333333333333333333333333333333333333333333333333 +9238 3333333333333333333333333333333333333333333333333333333 +9239 3333333333333333333333333333333333333333333333333333333 +9240 3333333333333333333333333333333333333333333333333333333 +9241 3333333333333333333333333333333333333333333333333333333 +9242 3333333333333333333333333333333333333333333333333333333 +9243 3333333333333333333333333333333333333333333333333333333 +9244 3333333333333333333333333333333333333333333333333333333 +9245 3333333333333333333333333333333333333333333333333333333 +9246 3333333333333333333333333333333333333333333333333333333 +9247 3333333333333333333333333333333333333333333333333333333 +9248 3333333333333333333333333333333333333333333333333333333 +9249 3333333333333333333333333333333333333333333333333333333 +9250 3333333333333333333333333333333333333333333333333333333 +9251 3333333333333333333333333333333333333333333333333333333 +9252 3333333333333333333333333333333333333333333333333333333 +9253 3333333333333333333333333333333333333333333333333333333 +9254 3333333333333333333333333333333333333333333333333333333 +9255 3333333333333333333333333333333333333333333333333333333 +9256 3333333333333333333333333333333333333333333333333333333 +9257 3333333333333333333333333333333333333333333333333333333 +9258 3333333333333333333333333333333333333333333333333333333 +9259 3333333333333333333333333333333333333333333333333333333 +9260 3333333333333333333333333333333333333333333333333333333 +9261 3333333333333333333333333333333333333333333333333333333 +9262 3333333333333333333333333333333333333333333333333333333 +9263 3333333333333333333333333333333333333333333333333333333 +9264 3333333333333333333333333333333333333333333333333333333 +9265 3333333333333333333333333333333333333333333333333333333 +9266 3333333333333333333333333333333333333333333333333333333 +9267 3333333333333333333333333333333333333333333333333333333 +9268 3333333333333333333333333333333333333333333333333333333 +9269 3333333333333333333333333333333333333333333333333333333 +9270 3333333333333333333333333333333333333333333333333333333 +9271 3333333333333333333333333333333333333333333333333333333 +9272 3333333333333333333333333333333333333333333333333333333 +9273 3333333333333333333333333333333333333333333333333333333 +9274 3333333333333333333333333333333333333333333333333333333 +9275 3333333333333333333333333333333333333333333333333333333 +9276 3333333333333333333333333333333333333333333333333333333 +9277 3333333333333333333333333333333333333333333333333333333 +9278 3333333333333333333333333333333333333333333333333333333 +9279 3333333333333333333333333333333333333333333333333333333 +9280 3333333333333333333333333333333333333333333333333333333 +9281 3333333333333333333333333333333333333333333333333333333 +9282 3333333333333333333333333333333333333333333333333333333 +9283 3333333333333333333333333333333333333333333333333333333 +9284 3333333333333333333333333333333333333333333333333333333 +9285 3333333333333333333333333333333333333333333333333333333 +9286 3333333333333333333333333333333333333333333333333333333 +9287 3333333333333333333333333333333333333333333333333333333 +9288 3333333333333333333333333333333333333333333333333333333 +9289 3333333333333333333333333333333333333333333333333333333 +9290 3333333333333333333333333333333333333333333333333333333 +9291 3333333333333333333333333333333333333333333333333333333 +9292 3333333333333333333333333333333333333333333333333333333 +9293 3333333333333333333333333333333333333333333333333333333 +9294 3333333333333333333333333333333333333333333333333333333 +9295 3333333333333333333333333333333333333333333333333333333 +9296 3333333333333333333333333333333333333333333333333333333 +9297 3333333333333333333333333333333333333333333333333333333 +9298 3333333333333333333333333333333333333333333333333333333 +9299 3333333333333333333333333333333333333333333333333333333 +9300 3333333333333333333333333333333333333333333333333333333 +9301 3333333333333333333333333333333333333333333333333333333 +9302 3333333333333333333333333333333333333333333333333333333 +9303 3333333333333333333333333333333333333333333333333333333 +9304 3333333333333333333333333333333333333333333333333333333 +9305 3333333333333333333333333333333333333333333333333333333 +9306 3333333333333333333333333333333333333333333333333333333 +9307 3333333333333333333333333333333333333333333333333333333 +9308 3333333333333333333333333333333333333333333333333333333 +9309 3333333333333333333333333333333333333333333333333333333 +9310 3333333333333333333333333333333333333333333333333333333 +9311 3333333333333333333333333333333333333333333333333333333 +9312 3333333333333333333333333333333333333333333333333333333 +9313 3333333333333333333333333333333333333333333333333333333 +9314 3333333333333333333333333333333333333333333333333333333 +9315 3333333333333333333333333333333333333333333333333333333 +9316 3333333333333333333333333333333333333333333333333333333 +9317 3333333333333333333333333333333333333333333333333333333 +9318 3333333333333333333333333333333333333333333333333333333 +9319 3333333333333333333333333333333333333333333333333333333 +9320 3333333333333333333333333333333333333333333333333333333 +9321 3333333333333333333333333333333333333333333333333333333 +9322 3333333333333333333333333333333333333333333333333333333 +9323 3333333333333333333333333333333333333333333333333333333 +9324 3333333333333333333333333333333333333333333333333333333 +9325 3333333333333333333333333333333333333333333333333333333 +9326 3333333333333333333333333333333333333333333333333333333 +9327 3333333333333333333333333333333333333333333333333333333 +9328 3333333333333333333333333333333333333333333333333333333 +9329 3333333333333333333333333333333333333333333333333333333 +9330 3333333333333333333333333333333333333333333333333333333 +9331 3333333333333333333333333333333333333333333333333333333 +9332 3333333333333333333333333333333333333333333333333333333 +9333 3333333333333333333333333333333333333333333333333333333 +9334 3333333333333333333333333333333333333333333333333333333 +9335 3333333333333333333333333333333333333333333333333333333 +9336 3333333333333333333333333333333333333333333333333333333 +9337 3333333333333333333333333333333333333333333333333333333 +9338 3333333333333333333333333333333333333333333333333333333 +9339 3333333333333333333333333333333333333333333333333333333 +9340 3333333333333333333333333333333333333333333333333333333 +9341 3333333333333333333333333333333333333333333333333333333 +9342 3333333333333333333333333333333333333333333333333333333 +9343 3333333333333333333333333333333333333333333333333333333 +9344 3333333333333333333333333333333333333333333333333333333 +9345 3333333333333333333333333333333333333333333333333333333 +9346 3333333333333333333333333333333333333333333333333333333 +9347 3333333333333333333333333333333333333333333333333333333 +9348 3333333333333333333333333333333333333333333333333333333 +9349 3333333333333333333333333333333333333333333333333333333 +9350 3333333333333333333333333333333333333333333333333333333 +9351 3333333333333333333333333333333333333333333333333333333 +9352 3333333333333333333333333333333333333333333333333333333 +9353 3333333333333333333333333333333333333333333333333333333 +9354 3333333333333333333333333333333333333333333333333333333 +9355 3333333333333333333333333333333333333333333333333333333 +9356 3333333333333333333333333333333333333333333333333333333 +9357 3333333333333333333333333333333333333333333333333333333 +9358 3333333333333333333333333333333333333333333333333333333 +9359 3333333333333333333333333333333333333333333333333333333 +9360 3333333333333333333333333333333333333333333333333333333 +9361 3333333333333333333333333333333333333333333333333333333 +9362 3333333333333333333333333333333333333333333333333333333 +9363 3333333333333333333333333333333333333333333333333333333 +9364 3333333333333333333333333333333333333333333333333333333 +9365 3333333333333333333333333333333333333333333333333333333 +9366 3333333333333333333333333333333333333333333333333333333 +9367 3333333333333333333333333333333333333333333333333333333 +9368 3333333333333333333333333333333333333333333333333333333 +9369 3333333333333333333333333333333333333333333333333333333 +9370 3333333333333333333333333333333333333333333333333333333 +9371 3333333333333333333333333333333333333333333333333333333 +9372 3333333333333333333333333333333333333333333333333333333 +9373 3333333333333333333333333333333333333333333333333333333 +9374 3333333333333333333333333333333333333333333333333333333 +9375 3333333333333333333333333333333333333333333333333333333 +9376 3333333333333333333333333333333333333333333333333333333 +9377 3333333333333333333333333333333333333333333333333333333 +9378 3333333333333333333333333333333333333333333333333333333 +9379 3333333333333333333333333333333333333333333333333333333 +9380 3333333333333333333333333333333333333333333333333333333 +9381 3333333333333333333333333333333333333333333333333333333 +9382 3333333333333333333333333333333333333333333333333333333 +9383 3333333333333333333333333333333333333333333333333333333 +9384 3333333333333333333333333333333333333333333333333333333 +9385 3333333333333333333333333333333333333333333333333333333 +9386 3333333333333333333333333333333333333333333333333333333 +9387 3333333333333333333333333333333333333333333333333333333 +9388 3333333333333333333333333333333333333333333333333333333 +9389 3333333333333333333333333333333333333333333333333333333 +9390 3333333333333333333333333333333333333333333333333333333 +9391 3333333333333333333333333333333333333333333333333333333 +9392 3333333333333333333333333333333333333333333333333333333 +9393 3333333333333333333333333333333333333333333333333333333 +9394 3333333333333333333333333333333333333333333333333333333 +9395 3333333333333333333333333333333333333333333333333333333 +9396 3333333333333333333333333333333333333333333333333333333 +9397 3333333333333333333333333333333333333333333333333333333 +9398 3333333333333333333333333333333333333333333333333333333 +9399 3333333333333333333333333333333333333333333333333333333 +9400 3333333333333333333333333333333333333333333333333333333 +9401 3333333333333333333333333333333333333333333333333333333 +9402 3333333333333333333333333333333333333333333333333333333 +9403 3333333333333333333333333333333333333333333333333333333 +9404 3333333333333333333333333333333333333333333333333333333 +9405 3333333333333333333333333333333333333333333333333333333 +9406 3333333333333333333333333333333333333333333333333333333 +9407 3333333333333333333333333333333333333333333333333333333 +9408 3333333333333333333333333333333333333333333333333333333 +9409 3333333333333333333333333333333333333333333333333333333 +9410 3333333333333333333333333333333333333333333333333333333 +9411 3333333333333333333333333333333333333333333333333333333 +9412 3333333333333333333333333333333333333333333333333333333 +9413 3333333333333333333333333333333333333333333333333333333 +9414 3333333333333333333333333333333333333333333333333333333 +9415 3333333333333333333333333333333333333333333333333333333 +9416 3333333333333333333333333333333333333333333333333333333 +9417 3333333333333333333333333333333333333333333333333333333 +9418 3333333333333333333333333333333333333333333333333333333 +9419 3333333333333333333333333333333333333333333333333333333 +9420 3333333333333333333333333333333333333333333333333333333 +9421 3333333333333333333333333333333333333333333333333333333 +9422 3333333333333333333333333333333333333333333333333333333 +9423 3333333333333333333333333333333333333333333333333333333 +9424 3333333333333333333333333333333333333333333333333333333 +9425 3333333333333333333333333333333333333333333333333333333 +9426 3333333333333333333333333333333333333333333333333333333 +9427 3333333333333333333333333333333333333333333333333333333 +9428 3333333333333333333333333333333333333333333333333333333 +9429 3333333333333333333333333333333333333333333333333333333 +9430 3333333333333333333333333333333333333333333333333333333 +9431 3333333333333333333333333333333333333333333333333333333 +9432 3333333333333333333333333333333333333333333333333333333 +9433 3333333333333333333333333333333333333333333333333333333 +9434 3333333333333333333333333333333333333333333333333333333 +9435 3333333333333333333333333333333333333333333333333333333 +9436 3333333333333333333333333333333333333333333333333333333 +9437 3333333333333333333333333333333333333333333333333333333 +9438 3333333333333333333333333333333333333333333333333333333 +9439 3333333333333333333333333333333333333333333333333333333 +9440 3333333333333333333333333333333333333333333333333333333 +9441 3333333333333333333333333333333333333333333333333333333 +9442 3333333333333333333333333333333333333333333333333333333 +9443 3333333333333333333333333333333333333333333333333333333 +9444 3333333333333333333333333333333333333333333333333333333 +9445 3333333333333333333333333333333333333333333333333333333 +9446 3333333333333333333333333333333333333333333333333333333 +9447 3333333333333333333333333333333333333333333333333333333 +9448 3333333333333333333333333333333333333333333333333333333 +9449 3333333333333333333333333333333333333333333333333333333 +9450 3333333333333333333333333333333333333333333333333333333 +9451 3333333333333333333333333333333333333333333333333333333 +9452 3333333333333333333333333333333333333333333333333333333 +9453 3333333333333333333333333333333333333333333333333333333 +9454 3333333333333333333333333333333333333333333333333333333 +9455 3333333333333333333333333333333333333333333333333333333 +9456 3333333333333333333333333333333333333333333333333333333 +9457 3333333333333333333333333333333333333333333333333333333 +9458 3333333333333333333333333333333333333333333333333333333 +9459 3333333333333333333333333333333333333333333333333333333 +9460 3333333333333333333333333333333333333333333333333333333 +9461 3333333333333333333333333333333333333333333333333333333 +9462 3333333333333333333333333333333333333333333333333333333 +9463 3333333333333333333333333333333333333333333333333333333 +9464 3333333333333333333333333333333333333333333333333333333 +9465 3333333333333333333333333333333333333333333333333333333 +9466 3333333333333333333333333333333333333333333333333333333 +9467 3333333333333333333333333333333333333333333333333333333 +9468 3333333333333333333333333333333333333333333333333333333 +9469 3333333333333333333333333333333333333333333333333333333 +9470 3333333333333333333333333333333333333333333333333333333 +9471 3333333333333333333333333333333333333333333333333333333 +9472 3333333333333333333333333333333333333333333333333333333 +9473 3333333333333333333333333333333333333333333333333333333 +9474 3333333333333333333333333333333333333333333333333333333 +9475 3333333333333333333333333333333333333333333333333333333 +9476 3333333333333333333333333333333333333333333333333333333 +9477 3333333333333333333333333333333333333333333333333333333 +9478 3333333333333333333333333333333333333333333333333333333 +9479 3333333333333333333333333333333333333333333333333333333 +9480 3333333333333333333333333333333333333333333333333333333 +9481 3333333333333333333333333333333333333333333333333333333 +9482 3333333333333333333333333333333333333333333333333333333 +9483 3333333333333333333333333333333333333333333333333333333 +9484 3333333333333333333333333333333333333333333333333333333 +9485 3333333333333333333333333333333333333333333333333333333 +9486 3333333333333333333333333333333333333333333333333333333 +9487 3333333333333333333333333333333333333333333333333333333 +9488 3333333333333333333333333333333333333333333333333333333 +9489 3333333333333333333333333333333333333333333333333333333 +9490 3333333333333333333333333333333333333333333333333333333 +9491 3333333333333333333333333333333333333333333333333333333 +9492 3333333333333333333333333333333333333333333333333333333 +9493 3333333333333333333333333333333333333333333333333333333 +9494 3333333333333333333333333333333333333333333333333333333 +9495 3333333333333333333333333333333333333333333333333333333 +9496 3333333333333333333333333333333333333333333333333333333 +9497 3333333333333333333333333333333333333333333333333333333 +9498 3333333333333333333333333333333333333333333333333333333 +9499 3333333333333333333333333333333333333333333333333333333 +9500 3333333333333333333333333333333333333333333333333333333 +9501 3333333333333333333333333333333333333333333333333333333 +9502 3333333333333333333333333333333333333333333333333333333 +9503 3333333333333333333333333333333333333333333333333333333 +9504 3333333333333333333333333333333333333333333333333333333 +9505 3333333333333333333333333333333333333333333333333333333 +9506 3333333333333333333333333333333333333333333333333333333 +9507 3333333333333333333333333333333333333333333333333333333 +9508 3333333333333333333333333333333333333333333333333333333 +9509 3333333333333333333333333333333333333333333333333333333 +9510 3333333333333333333333333333333333333333333333333333333 +9511 3333333333333333333333333333333333333333333333333333333 +9512 3333333333333333333333333333333333333333333333333333333 +9513 3333333333333333333333333333333333333333333333333333333 +9514 3333333333333333333333333333333333333333333333333333333 +9515 3333333333333333333333333333333333333333333333333333333 +9516 3333333333333333333333333333333333333333333333333333333 +9517 3333333333333333333333333333333333333333333333333333333 +9518 3333333333333333333333333333333333333333333333333333333 +9519 3333333333333333333333333333333333333333333333333333333 +9520 3333333333333333333333333333333333333333333333333333333 +9521 3333333333333333333333333333333333333333333333333333333 +9522 3333333333333333333333333333333333333333333333333333333 +9523 3333333333333333333333333333333333333333333333333333333 +9524 3333333333333333333333333333333333333333333333333333333 +9525 3333333333333333333333333333333333333333333333333333333 +9526 3333333333333333333333333333333333333333333333333333333 +9527 3333333333333333333333333333333333333333333333333333333 +9528 3333333333333333333333333333333333333333333333333333333 +9529 3333333333333333333333333333333333333333333333333333333 +9530 3333333333333333333333333333333333333333333333333333333 +9531 3333333333333333333333333333333333333333333333333333333 +9532 3333333333333333333333333333333333333333333333333333333 +9533 3333333333333333333333333333333333333333333333333333333 +9534 3333333333333333333333333333333333333333333333333333333 +9535 3333333333333333333333333333333333333333333333333333333 +9536 3333333333333333333333333333333333333333333333333333333 +9537 3333333333333333333333333333333333333333333333333333333 +9538 3333333333333333333333333333333333333333333333333333333 +9539 3333333333333333333333333333333333333333333333333333333 +9540 3333333333333333333333333333333333333333333333333333333 +9541 3333333333333333333333333333333333333333333333333333333 +9542 3333333333333333333333333333333333333333333333333333333 +9543 3333333333333333333333333333333333333333333333333333333 +9544 3333333333333333333333333333333333333333333333333333333 +9545 3333333333333333333333333333333333333333333333333333333 +9546 3333333333333333333333333333333333333333333333333333333 +9547 3333333333333333333333333333333333333333333333333333333 +9548 3333333333333333333333333333333333333333333333333333333 +9549 3333333333333333333333333333333333333333333333333333333 +9550 3333333333333333333333333333333333333333333333333333333 +9551 3333333333333333333333333333333333333333333333333333333 +9552 3333333333333333333333333333333333333333333333333333333 +9553 3333333333333333333333333333333333333333333333333333333 +9554 3333333333333333333333333333333333333333333333333333333 +9555 3333333333333333333333333333333333333333333333333333333 +9556 3333333333333333333333333333333333333333333333333333333 +9557 3333333333333333333333333333333333333333333333333333333 +9558 3333333333333333333333333333333333333333333333333333333 +9559 3333333333333333333333333333333333333333333333333333333 +9560 3333333333333333333333333333333333333333333333333333333 +9561 3333333333333333333333333333333333333333333333333333333 +9562 3333333333333333333333333333333333333333333333333333333 +9563 3333333333333333333333333333333333333333333333333333333 +9564 3333333333333333333333333333333333333333333333333333333 +9565 3333333333333333333333333333333333333333333333333333333 +9566 3333333333333333333333333333333333333333333333333333333 +9567 3333333333333333333333333333333333333333333333333333333 +9568 3333333333333333333333333333333333333333333333333333333 +9569 3333333333333333333333333333333333333333333333333333333 +9570 3333333333333333333333333333333333333333333333333333333 +9571 3333333333333333333333333333333333333333333333333333333 +9572 3333333333333333333333333333333333333333333333333333333 +9573 3333333333333333333333333333333333333333333333333333333 +9574 3333333333333333333333333333333333333333333333333333333 +9575 3333333333333333333333333333333333333333333333333333333 +9576 3333333333333333333333333333333333333333333333333333333 +9577 3333333333333333333333333333333333333333333333333333333 +9578 3333333333333333333333333333333333333333333333333333333 +9579 3333333333333333333333333333333333333333333333333333333 +9580 3333333333333333333333333333333333333333333333333333333 +9581 3333333333333333333333333333333333333333333333333333333 +9582 3333333333333333333333333333333333333333333333333333333 +9583 3333333333333333333333333333333333333333333333333333333 +9584 3333333333333333333333333333333333333333333333333333333 +9585 3333333333333333333333333333333333333333333333333333333 +9586 3333333333333333333333333333333333333333333333333333333 +9587 3333333333333333333333333333333333333333333333333333333 +9588 3333333333333333333333333333333333333333333333333333333 +9589 3333333333333333333333333333333333333333333333333333333 +9590 3333333333333333333333333333333333333333333333333333333 +9591 3333333333333333333333333333333333333333333333333333333 +9592 3333333333333333333333333333333333333333333333333333333 +9593 3333333333333333333333333333333333333333333333333333333 +9594 3333333333333333333333333333333333333333333333333333333 +9595 3333333333333333333333333333333333333333333333333333333 +9596 3333333333333333333333333333333333333333333333333333333 +9597 3333333333333333333333333333333333333333333333333333333 +9598 3333333333333333333333333333333333333333333333333333333 +9599 3333333333333333333333333333333333333333333333333333333 +9600 3333333333333333333333333333333333333333333333333333333 +9601 3333333333333333333333333333333333333333333333333333333 +9602 3333333333333333333333333333333333333333333333333333333 +9603 3333333333333333333333333333333333333333333333333333333 +9604 3333333333333333333333333333333333333333333333333333333 +9605 3333333333333333333333333333333333333333333333333333333 +9606 3333333333333333333333333333333333333333333333333333333 +9607 3333333333333333333333333333333333333333333333333333333 +9608 3333333333333333333333333333333333333333333333333333333 +9609 3333333333333333333333333333333333333333333333333333333 +9610 3333333333333333333333333333333333333333333333333333333 +9611 3333333333333333333333333333333333333333333333333333333 +9612 3333333333333333333333333333333333333333333333333333333 +9613 3333333333333333333333333333333333333333333333333333333 +9614 3333333333333333333333333333333333333333333333333333333 +9615 3333333333333333333333333333333333333333333333333333333 +9616 3333333333333333333333333333333333333333333333333333333 +9617 3333333333333333333333333333333333333333333333333333333 +9618 3333333333333333333333333333333333333333333333333333333 +9619 3333333333333333333333333333333333333333333333333333333 +9620 3333333333333333333333333333333333333333333333333333333 +9621 3333333333333333333333333333333333333333333333333333333 +9622 3333333333333333333333333333333333333333333333333333333 +9623 3333333333333333333333333333333333333333333333333333333 +9624 3333333333333333333333333333333333333333333333333333333 +9625 3333333333333333333333333333333333333333333333333333333 +9626 3333333333333333333333333333333333333333333333333333333 +9627 3333333333333333333333333333333333333333333333333333333 +9628 3333333333333333333333333333333333333333333333333333333 +9629 3333333333333333333333333333333333333333333333333333333 +9630 3333333333333333333333333333333333333333333333333333333 +9631 3333333333333333333333333333333333333333333333333333333 +9632 3333333333333333333333333333333333333333333333333333333 +9633 3333333333333333333333333333333333333333333333333333333 +9634 3333333333333333333333333333333333333333333333333333333 +9635 3333333333333333333333333333333333333333333333333333333 +9636 3333333333333333333333333333333333333333333333333333333 +9637 3333333333333333333333333333333333333333333333333333333 +9638 3333333333333333333333333333333333333333333333333333333 +9639 3333333333333333333333333333333333333333333333333333333 +9640 3333333333333333333333333333333333333333333333333333333 +9641 3333333333333333333333333333333333333333333333333333333 +9642 3333333333333333333333333333333333333333333333333333333 +9643 3333333333333333333333333333333333333333333333333333333 +9644 3333333333333333333333333333333333333333333333333333333 +9645 3333333333333333333333333333333333333333333333333333333 +9646 3333333333333333333333333333333333333333333333333333333 +9647 3333333333333333333333333333333333333333333333333333333 +9648 3333333333333333333333333333333333333333333333333333333 +9649 3333333333333333333333333333333333333333333333333333333 +9650 3333333333333333333333333333333333333333333333333333333 +9651 3333333333333333333333333333333333333333333333333333333 +9652 3333333333333333333333333333333333333333333333333333333 +9653 3333333333333333333333333333333333333333333333333333333 +9654 3333333333333333333333333333333333333333333333333333333 +9655 3333333333333333333333333333333333333333333333333333333 +9656 3333333333333333333333333333333333333333333333333333333 +9657 3333333333333333333333333333333333333333333333333333333 +9658 3333333333333333333333333333333333333333333333333333333 +9659 3333333333333333333333333333333333333333333333333333333 +9660 3333333333333333333333333333333333333333333333333333333 +9661 3333333333333333333333333333333333333333333333333333333 +9662 3333333333333333333333333333333333333333333333333333333 +9663 3333333333333333333333333333333333333333333333333333333 +9664 3333333333333333333333333333333333333333333333333333333 +9665 3333333333333333333333333333333333333333333333333333333 +9666 3333333333333333333333333333333333333333333333333333333 +9667 3333333333333333333333333333333333333333333333333333333 +9668 3333333333333333333333333333333333333333333333333333333 +9669 3333333333333333333333333333333333333333333333333333333 +9670 3333333333333333333333333333333333333333333333333333333 +9671 3333333333333333333333333333333333333333333333333333333 +9672 3333333333333333333333333333333333333333333333333333333 +9673 3333333333333333333333333333333333333333333333333333333 +9674 3333333333333333333333333333333333333333333333333333333 +9675 3333333333333333333333333333333333333333333333333333333 +9676 3333333333333333333333333333333333333333333333333333333 +9677 3333333333333333333333333333333333333333333333333333333 +9678 3333333333333333333333333333333333333333333333333333333 +9679 3333333333333333333333333333333333333333333333333333333 +9680 3333333333333333333333333333333333333333333333333333333 +9681 3333333333333333333333333333333333333333333333333333333 +9682 3333333333333333333333333333333333333333333333333333333 +9683 3333333333333333333333333333333333333333333333333333333 +9684 3333333333333333333333333333333333333333333333333333333 +9685 3333333333333333333333333333333333333333333333333333333 +9686 3333333333333333333333333333333333333333333333333333333 +9687 3333333333333333333333333333333333333333333333333333333 +9688 3333333333333333333333333333333333333333333333333333333 +9689 3333333333333333333333333333333333333333333333333333333 +9690 3333333333333333333333333333333333333333333333333333333 +9691 3333333333333333333333333333333333333333333333333333333 +9692 3333333333333333333333333333333333333333333333333333333 +9693 3333333333333333333333333333333333333333333333333333333 +9694 3333333333333333333333333333333333333333333333333333333 +9695 3333333333333333333333333333333333333333333333333333333 +9696 3333333333333333333333333333333333333333333333333333333 +9697 3333333333333333333333333333333333333333333333333333333 +9698 3333333333333333333333333333333333333333333333333333333 +9699 3333333333333333333333333333333333333333333333333333333 +9700 3333333333333333333333333333333333333333333333333333333 +9701 3333333333333333333333333333333333333333333333333333333 +9702 3333333333333333333333333333333333333333333333333333333 +9703 3333333333333333333333333333333333333333333333333333333 +9704 3333333333333333333333333333333333333333333333333333333 +9705 3333333333333333333333333333333333333333333333333333333 +9706 3333333333333333333333333333333333333333333333333333333 +9707 3333333333333333333333333333333333333333333333333333333 +9708 3333333333333333333333333333333333333333333333333333333 +9709 3333333333333333333333333333333333333333333333333333333 +9710 3333333333333333333333333333333333333333333333333333333 +9711 3333333333333333333333333333333333333333333333333333333 +9712 3333333333333333333333333333333333333333333333333333333 +9713 3333333333333333333333333333333333333333333333333333333 +9714 3333333333333333333333333333333333333333333333333333333 +9715 3333333333333333333333333333333333333333333333333333333 +9716 3333333333333333333333333333333333333333333333333333333 +9717 3333333333333333333333333333333333333333333333333333333 +9718 3333333333333333333333333333333333333333333333333333333 +9719 3333333333333333333333333333333333333333333333333333333 +9720 3333333333333333333333333333333333333333333333333333333 +9721 3333333333333333333333333333333333333333333333333333333 +9722 3333333333333333333333333333333333333333333333333333333 +9723 3333333333333333333333333333333333333333333333333333333 +9724 3333333333333333333333333333333333333333333333333333333 +9725 3333333333333333333333333333333333333333333333333333333 +9726 3333333333333333333333333333333333333333333333333333333 +9727 3333333333333333333333333333333333333333333333333333333 +9728 3333333333333333333333333333333333333333333333333333333 +9729 3333333333333333333333333333333333333333333333333333333 +9730 3333333333333333333333333333333333333333333333333333333 +9731 3333333333333333333333333333333333333333333333333333333 +9732 3333333333333333333333333333333333333333333333333333333 +9733 3333333333333333333333333333333333333333333333333333333 +9734 3333333333333333333333333333333333333333333333333333333 +9735 3333333333333333333333333333333333333333333333333333333 +9736 3333333333333333333333333333333333333333333333333333333 +9737 3333333333333333333333333333333333333333333333333333333 +9738 3333333333333333333333333333333333333333333333333333333 +9739 3333333333333333333333333333333333333333333333333333333 +9740 3333333333333333333333333333333333333333333333333333333 +9741 3333333333333333333333333333333333333333333333333333333 +9742 3333333333333333333333333333333333333333333333333333333 +9743 3333333333333333333333333333333333333333333333333333333 +9744 3333333333333333333333333333333333333333333333333333333 +9745 3333333333333333333333333333333333333333333333333333333 +9746 3333333333333333333333333333333333333333333333333333333 +9747 3333333333333333333333333333333333333333333333333333333 +9748 3333333333333333333333333333333333333333333333333333333 +9749 3333333333333333333333333333333333333333333333333333333 +9750 3333333333333333333333333333333333333333333333333333333 +9751 3333333333333333333333333333333333333333333333333333333 +9752 3333333333333333333333333333333333333333333333333333333 +9753 3333333333333333333333333333333333333333333333333333333 +9754 3333333333333333333333333333333333333333333333333333333 +9755 3333333333333333333333333333333333333333333333333333333 +9756 3333333333333333333333333333333333333333333333333333333 +9757 3333333333333333333333333333333333333333333333333333333 +9758 3333333333333333333333333333333333333333333333333333333 +9759 3333333333333333333333333333333333333333333333333333333 +9760 3333333333333333333333333333333333333333333333333333333 +9761 3333333333333333333333333333333333333333333333333333333 +9762 3333333333333333333333333333333333333333333333333333333 +9763 3333333333333333333333333333333333333333333333333333333 +9764 3333333333333333333333333333333333333333333333333333333 +9765 3333333333333333333333333333333333333333333333333333333 +9766 3333333333333333333333333333333333333333333333333333333 +9767 3333333333333333333333333333333333333333333333333333333 +9768 3333333333333333333333333333333333333333333333333333333 +9769 3333333333333333333333333333333333333333333333333333333 +9770 3333333333333333333333333333333333333333333333333333333 +9771 3333333333333333333333333333333333333333333333333333333 +9772 3333333333333333333333333333333333333333333333333333333 +9773 3333333333333333333333333333333333333333333333333333333 +9774 3333333333333333333333333333333333333333333333333333333 +9775 3333333333333333333333333333333333333333333333333333333 +9776 3333333333333333333333333333333333333333333333333333333 +9777 3333333333333333333333333333333333333333333333333333333 +9778 3333333333333333333333333333333333333333333333333333333 +9779 3333333333333333333333333333333333333333333333333333333 +9780 3333333333333333333333333333333333333333333333333333333 +9781 3333333333333333333333333333333333333333333333333333333 +9782 3333333333333333333333333333333333333333333333333333333 +9783 3333333333333333333333333333333333333333333333333333333 +9784 3333333333333333333333333333333333333333333333333333333 +9785 3333333333333333333333333333333333333333333333333333333 +9786 3333333333333333333333333333333333333333333333333333333 +9787 3333333333333333333333333333333333333333333333333333333 +9788 3333333333333333333333333333333333333333333333333333333 +9789 3333333333333333333333333333333333333333333333333333333 +9790 3333333333333333333333333333333333333333333333333333333 +9791 3333333333333333333333333333333333333333333333333333333 +9792 3333333333333333333333333333333333333333333333333333333 +9793 3333333333333333333333333333333333333333333333333333333 +9794 3333333333333333333333333333333333333333333333333333333 +9795 3333333333333333333333333333333333333333333333333333333 +9796 3333333333333333333333333333333333333333333333333333333 +9797 3333333333333333333333333333333333333333333333333333333 +9798 3333333333333333333333333333333333333333333333333333333 +9799 3333333333333333333333333333333333333333333333333333333 +9800 3333333333333333333333333333333333333333333333333333333 +9801 3333333333333333333333333333333333333333333333333333333 +9802 3333333333333333333333333333333333333333333333333333333 +9803 3333333333333333333333333333333333333333333333333333333 +9804 3333333333333333333333333333333333333333333333333333333 +9805 3333333333333333333333333333333333333333333333333333333 +9806 3333333333333333333333333333333333333333333333333333333 +9807 3333333333333333333333333333333333333333333333333333333 +9808 3333333333333333333333333333333333333333333333333333333 +9809 3333333333333333333333333333333333333333333333333333333 +9810 3333333333333333333333333333333333333333333333333333333 +9811 3333333333333333333333333333333333333333333333333333333 +9812 3333333333333333333333333333333333333333333333333333333 +9813 3333333333333333333333333333333333333333333333333333333 +9814 3333333333333333333333333333333333333333333333333333333 +9815 3333333333333333333333333333333333333333333333333333333 +9816 3333333333333333333333333333333333333333333333333333333 +9817 3333333333333333333333333333333333333333333333333333333 +9818 3333333333333333333333333333333333333333333333333333333 +9819 3333333333333333333333333333333333333333333333333333333 +9820 3333333333333333333333333333333333333333333333333333333 +9821 3333333333333333333333333333333333333333333333333333333 +9822 3333333333333333333333333333333333333333333333333333333 +9823 3333333333333333333333333333333333333333333333333333333 +9824 3333333333333333333333333333333333333333333333333333333 +9825 3333333333333333333333333333333333333333333333333333333 +9826 3333333333333333333333333333333333333333333333333333333 +9827 3333333333333333333333333333333333333333333333333333333 +9828 3333333333333333333333333333333333333333333333333333333 +9829 3333333333333333333333333333333333333333333333333333333 +9830 3333333333333333333333333333333333333333333333333333333 +9831 3333333333333333333333333333333333333333333333333333333 +9832 3333333333333333333333333333333333333333333333333333333 +9833 3333333333333333333333333333333333333333333333333333333 +9834 3333333333333333333333333333333333333333333333333333333 +9835 3333333333333333333333333333333333333333333333333333333 +9836 3333333333333333333333333333333333333333333333333333333 +9837 3333333333333333333333333333333333333333333333333333333 +9838 3333333333333333333333333333333333333333333333333333333 +9839 3333333333333333333333333333333333333333333333333333333 +9840 3333333333333333333333333333333333333333333333333333333 +9841 3333333333333333333333333333333333333333333333333333333 +9842 3333333333333333333333333333333333333333333333333333333 +9843 3333333333333333333333333333333333333333333333333333333 +9844 3333333333333333333333333333333333333333333333333333333 +9845 3333333333333333333333333333333333333333333333333333333 +9846 3333333333333333333333333333333333333333333333333333333 +9847 3333333333333333333333333333333333333333333333333333333 +9848 3333333333333333333333333333333333333333333333333333333 +9849 3333333333333333333333333333333333333333333333333333333 +9850 3333333333333333333333333333333333333333333333333333333 +9851 3333333333333333333333333333333333333333333333333333333 +9852 3333333333333333333333333333333333333333333333333333333 +9853 3333333333333333333333333333333333333333333333333333333 +9854 3333333333333333333333333333333333333333333333333333333 +9855 3333333333333333333333333333333333333333333333333333333 +9856 3333333333333333333333333333333333333333333333333333333 +9857 3333333333333333333333333333333333333333333333333333333 +9858 3333333333333333333333333333333333333333333333333333333 +9859 3333333333333333333333333333333333333333333333333333333 +9860 3333333333333333333333333333333333333333333333333333333 +9861 3333333333333333333333333333333333333333333333333333333 +9862 3333333333333333333333333333333333333333333333333333333 +9863 3333333333333333333333333333333333333333333333333333333 +9864 3333333333333333333333333333333333333333333333333333333 +9865 3333333333333333333333333333333333333333333333333333333 +9866 3333333333333333333333333333333333333333333333333333333 +9867 3333333333333333333333333333333333333333333333333333333 +9868 3333333333333333333333333333333333333333333333333333333 +9869 3333333333333333333333333333333333333333333333333333333 +9870 3333333333333333333333333333333333333333333333333333333 +9871 3333333333333333333333333333333333333333333333333333333 +9872 3333333333333333333333333333333333333333333333333333333 +9873 3333333333333333333333333333333333333333333333333333333 +9874 3333333333333333333333333333333333333333333333333333333 +9875 3333333333333333333333333333333333333333333333333333333 +9876 3333333333333333333333333333333333333333333333333333333 +9877 3333333333333333333333333333333333333333333333333333333 +9878 3333333333333333333333333333333333333333333333333333333 +9879 3333333333333333333333333333333333333333333333333333333 +9880 3333333333333333333333333333333333333333333333333333333 +9881 3333333333333333333333333333333333333333333333333333333 +9882 3333333333333333333333333333333333333333333333333333333 +9883 3333333333333333333333333333333333333333333333333333333 +9884 3333333333333333333333333333333333333333333333333333333 +9885 3333333333333333333333333333333333333333333333333333333 +9886 3333333333333333333333333333333333333333333333333333333 +9887 3333333333333333333333333333333333333333333333333333333 +9888 3333333333333333333333333333333333333333333333333333333 +9889 3333333333333333333333333333333333333333333333333333333 +9890 3333333333333333333333333333333333333333333333333333333 +9891 3333333333333333333333333333333333333333333333333333333 +9892 3333333333333333333333333333333333333333333333333333333 +9893 3333333333333333333333333333333333333333333333333333333 +9894 3333333333333333333333333333333333333333333333333333333 +9895 3333333333333333333333333333333333333333333333333333333 +9896 3333333333333333333333333333333333333333333333333333333 +9897 3333333333333333333333333333333333333333333333333333333 +9898 3333333333333333333333333333333333333333333333333333333 +9899 3333333333333333333333333333333333333333333333333333333 +9900 3333333333333333333333333333333333333333333333333333333 +9901 3333333333333333333333333333333333333333333333333333333 +9902 3333333333333333333333333333333333333333333333333333333 +9903 3333333333333333333333333333333333333333333333333333333 +9904 3333333333333333333333333333333333333333333333333333333 +9905 3333333333333333333333333333333333333333333333333333333 +9906 3333333333333333333333333333333333333333333333333333333 +9907 3333333333333333333333333333333333333333333333333333333 +9908 3333333333333333333333333333333333333333333333333333333 +9909 3333333333333333333333333333333333333333333333333333333 +9910 3333333333333333333333333333333333333333333333333333333 +9911 3333333333333333333333333333333333333333333333333333333 +9912 3333333333333333333333333333333333333333333333333333333 +9913 3333333333333333333333333333333333333333333333333333333 +9914 3333333333333333333333333333333333333333333333333333333 +9915 3333333333333333333333333333333333333333333333333333333 +9916 3333333333333333333333333333333333333333333333333333333 +9917 3333333333333333333333333333333333333333333333333333333 +9918 3333333333333333333333333333333333333333333333333333333 +9919 3333333333333333333333333333333333333333333333333333333 +9920 3333333333333333333333333333333333333333333333333333333 +9921 3333333333333333333333333333333333333333333333333333333 +9922 3333333333333333333333333333333333333333333333333333333 +9923 3333333333333333333333333333333333333333333333333333333 +9924 3333333333333333333333333333333333333333333333333333333 +9925 3333333333333333333333333333333333333333333333333333333 +9926 3333333333333333333333333333333333333333333333333333333 +9927 3333333333333333333333333333333333333333333333333333333 +9928 3333333333333333333333333333333333333333333333333333333 +9929 3333333333333333333333333333333333333333333333333333333 +9930 3333333333333333333333333333333333333333333333333333333 +9931 3333333333333333333333333333333333333333333333333333333 +9932 3333333333333333333333333333333333333333333333333333333 +9933 3333333333333333333333333333333333333333333333333333333 +9934 3333333333333333333333333333333333333333333333333333333 +9935 3333333333333333333333333333333333333333333333333333333 +9936 3333333333333333333333333333333333333333333333333333333 +9937 3333333333333333333333333333333333333333333333333333333 +9938 3333333333333333333333333333333333333333333333333333333 +9939 3333333333333333333333333333333333333333333333333333333 +9940 3333333333333333333333333333333333333333333333333333333 +9941 3333333333333333333333333333333333333333333333333333333 +9942 3333333333333333333333333333333333333333333333333333333 +9943 3333333333333333333333333333333333333333333333333333333 +9944 3333333333333333333333333333333333333333333333333333333 +9945 3333333333333333333333333333333333333333333333333333333 +9946 3333333333333333333333333333333333333333333333333333333 +9947 3333333333333333333333333333333333333333333333333333333 +9948 3333333333333333333333333333333333333333333333333333333 +9949 3333333333333333333333333333333333333333333333333333333 +9950 3333333333333333333333333333333333333333333333333333333 +9951 3333333333333333333333333333333333333333333333333333333 +9952 3333333333333333333333333333333333333333333333333333333 +9953 3333333333333333333333333333333333333333333333333333333 +9954 3333333333333333333333333333333333333333333333333333333 +9955 3333333333333333333333333333333333333333333333333333333 +9956 3333333333333333333333333333333333333333333333333333333 +9957 3333333333333333333333333333333333333333333333333333333 +9958 3333333333333333333333333333333333333333333333333333333 +9959 3333333333333333333333333333333333333333333333333333333 +9960 3333333333333333333333333333333333333333333333333333333 +9961 3333333333333333333333333333333333333333333333333333333 +9962 3333333333333333333333333333333333333333333333333333333 +9963 3333333333333333333333333333333333333333333333333333333 +9964 3333333333333333333333333333333333333333333333333333333 +9965 3333333333333333333333333333333333333333333333333333333 +9966 3333333333333333333333333333333333333333333333333333333 +9967 3333333333333333333333333333333333333333333333333333333 +9968 3333333333333333333333333333333333333333333333333333333 +9969 3333333333333333333333333333333333333333333333333333333 +9970 3333333333333333333333333333333333333333333333333333333 +9971 3333333333333333333333333333333333333333333333333333333 +9972 3333333333333333333333333333333333333333333333333333333 +9973 3333333333333333333333333333333333333333333333333333333 +9974 3333333333333333333333333333333333333333333333333333333 +9975 3333333333333333333333333333333333333333333333333333333 +9976 3333333333333333333333333333333333333333333333333333333 +9977 3333333333333333333333333333333333333333333333333333333 +9978 3333333333333333333333333333333333333333333333333333333 +9979 3333333333333333333333333333333333333333333333333333333 +9980 3333333333333333333333333333333333333333333333333333333 +9981 3333333333333333333333333333333333333333333333333333333 +9982 3333333333333333333333333333333333333333333333333333333 +9983 3333333333333333333333333333333333333333333333333333333 +9984 3333333333333333333333333333333333333333333333333333333 +9985 3333333333333333333333333333333333333333333333333333333 +9986 3333333333333333333333333333333333333333333333333333333 +9987 3333333333333333333333333333333333333333333333333333333 +9988 3333333333333333333333333333333333333333333333333333333 +9989 3333333333333333333333333333333333333333333333333333333 +9990 3333333333333333333333333333333333333333333333333333333 +9991 3333333333333333333333333333333333333333333333333333333 +9992 3333333333333333333333333333333333333333333333333333333 +9993 3333333333333333333333333333333333333333333333333333333 +9994 3333333333333333333333333333333333333333333333333333333 +9995 3333333333333333333333333333333333333333333333333333333 +9996 3333333333333333333333333333333333333333333333333333333 +9997 3333333333333333333333333333333333333333333333333333333 +9998 3333333333333333333333333333333333333333333333333333333 +9999 3333333333333333333333333333333333333333333333333333333 diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/data.txt.gz b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/data.txt.gz new file mode 100644 index 00000000000..42b9da0307c Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/data.txt.gz differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/demo.css b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/demo.css new file mode 100644 index 00000000000..f2b91d3365d --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/demo.css @@ -0,0 +1,83 @@ +body +{ + font-family: Arial, Verdana, Helvetica, sans-serif; +} + +.topnav +{ + overflow: hidden; + padding: 10px; + border: 1px solid #f6815c; + border-radius: 10px; + text-align: right; +} + +.menu +{ + margin-left: 3em; +} + +.content +{ + padding: 10px; +} + +.footer +{ + padding: 10px; + border-radius: 10px; + border: 1px solid #f6815c; +} + +.test +{ + background-color: #0099cc; + color: white; + padding: 10px 15px; + border: none; + font-size: 12pt; + border-radius: 10px; + box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2), 0 3px 10px 0 rgba(0,0,0,0.19); +} + +.test:hover +{ + background-color: #f6815c; + color: white; +} + +A:link +{ + color: #0099cc; + text-decoration: none; + font-weight: normal; + font-size: 11pt; + font-family:sans-serif; +} + +A:visited +{ + color: #0099cc; + text-decoration: none; + font-weight: normal; + font-size: 11pt; + font-family:sans-serif; +} + +A:hover +{ + color: #ff6600; + text-decoration: none; + font-weight: normal; + font-size: 11pt; + font-family:sans-serif; +} + +A:active +{ + color: #0099cc; + text-decoration: none; + font-weight: normal; + font-size: 11pt; + font-family:sans-serif; +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/error404.html b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/error404.html new file mode 100644 index 00000000000..4aea1975753 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/error404.html @@ -0,0 +1,4 @@ + +

Not Found ERROR

+custom 404 page + \ No newline at end of file diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/favicon.ico b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/favicon.ico new file mode 100644 index 00000000000..ea9e174b48b Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/favicon.ico differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/index.html b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/index.html new file mode 100644 index 00000000000..517c05c4b6c --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/index.html @@ -0,0 +1,72 @@ + + + Powered By Jetty + + + + + + + +
+
+ Demo Web Application Only - Do NOT Deploy in Production +
+

Eclipse Jetty Demo Webapp

+

+ This is a demo webapp for the Eclipse Jetty HTTP Server and Servlet Container. It was added into your $JETTY_BASE/webapps directory. +

+ +

Jetty Tests:

+ + + + + +
+ + + +
+
+ + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/jakarta.websocket/index.html b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/jakarta.websocket/index.html new file mode 100644 index 00000000000..1efeb3aa7b6 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/jakarta.websocket/index.html @@ -0,0 +1,112 @@ + + + WebSocket Chat + + + +
+
+
+ Username:  +
+ +
+ + +

+This is a demonstration of the Jetty's support for jakarta.websocket server sockets. +

+ + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/jetty.websocket/index.html b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/jetty.websocket/index.html new file mode 100644 index 00000000000..e02981ae110 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/jetty.websocket/index.html @@ -0,0 +1,112 @@ + + + WebSocket Chat + + + +
+
+
+ Username:  +
+ +
+ + +

+This is a demonstration of the Jetty websocket server. +

+ + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/logon.html b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/logon.html new file mode 100644 index 00000000000..6bededa576d --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/logon.html @@ -0,0 +1,20 @@ + +

FORM Authentication demo

+
+ + + + + + + + + + + + +
Username:
Password:
+ +
+
+ \ No newline at end of file diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/logonError.html b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/logonError.html new file mode 100644 index 00000000000..66a83869061 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/logonError.html @@ -0,0 +1,4 @@ + +

Authentication ERROR

+Username, password or role incorrect. + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/remote.html b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/remote.html new file mode 100644 index 00000000000..705a6953fe1 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/remote.html @@ -0,0 +1,35 @@ + + + Jetty Demo + + + + + + + +
+

Welcome to Jetty 11 - REMOTE ACCESS!!

+

+ This is a demo webapp for the Eclipse Jetty HTTP Server and Servlet Container. +

+

+ This test context serves several demo filters and servlets that are not safe for deployment on the internet, since (by design) they contain cross domain scripting vulnerabilities and reveal private information. This page is displayed because you have accessed this context from a non local IP address. +

+

+ You can disable the remote address checking by editing demo-base/webapps/demo-jetty.d/demo-jetty-override-web.xml, uncommenting the declaration of the TestFilter, and changing the "remote" init parameter to "true". +

+
+ + + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/rewrite/index.html b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/rewrite/index.html new file mode 100644 index 00000000000..ed308429b1a --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/rewrite/index.html @@ -0,0 +1,13 @@ + + + + + +RewriteHandler + + +

Rewrite not enabled

+

The rewrite handler is currently not enabled. To enable this demo, start Jetty with:

+java -jar start.jar --module=rewrite + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/rewrite/info.html b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/rewrite/info.html new file mode 100644 index 00000000000..2720d3bd0a1 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/rewrite/info.html @@ -0,0 +1,59 @@ + + + + + + +RewriteHandler + + + + +
+

Links to test the RewriteHandler

+

All examples below were configured using etc/jetty-rewrite.xml.

+ +

Internal URI rewrite

+
+
Rewrite "../some/old/context" to "../rewritten/newcontext"
+
This demo shows how the entire request URI can be internally rewritten to point to another context, using simple text matching
+ +
Rewrite "../rewrite/for/beginning" to "../rewritten/beginning"
+
This demo shows how the beginning of the request URI can be rewritten, while keeping the ending section
+ +
Rewrite "bar/foo" to "foo/bar" using regex
+
This demo shows how sections of the request URI can be rearranged. It uses regex to parse out each section, and then return them in reverse order
+ +
Rewrite the beginning, and reverse the path sections
+
This demo shows how rewrite patterns can be chained.
+ +
Rewrite "bar/foo" to "foo/bar", full dump view
+
This demo rewrites "bar/foo" to "foo/bar" the same as earlier, but shows a full dump of the request
+ +

Redirect

+
Redirect "../redirect/this" to "../redirected/this"
+
This demo redirects the request in a manner visible to the user agent, instead of doing an internal rewrite.
+ +

Cookie

+
All pages
+
This demo rule sets a "visited" cookie for each page you visit. The second time you go to any of the links above, you will see an additional line, "Previously visited: yes".
+ +

Response Code

+
Return a 400 error status
+
This demo shows how to modify the response code of a page to an error, based on its URL
+
+
+ + + + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/small_powered_by.gif b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/small_powered_by.gif new file mode 100644 index 00000000000..c5dd44319f0 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp/small_powered_by.gif differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/test/java/org/eclipse/jetty/ee11/ChatServletTest.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/test/java/org/eclipse/jetty/ee11/ChatServletTest.java new file mode 100644 index 00000000000..d1e21e70137 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/test/java/org/eclipse/jetty/ee11/ChatServletTest.java @@ -0,0 +1,88 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11; + +import org.eclipse.jetty.ee11.servlet.ServletContextHandler; +import org.eclipse.jetty.ee11.servlet.ServletHolder; +import org.eclipse.jetty.server.LocalConnector; +import org.eclipse.jetty.server.Server; +import org.example.ChatServlet; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +public class ChatServletTest +{ + private Server server; + private LocalConnector connector; + + @BeforeEach + public void setUp() throws Exception + { + server = new Server(); + connector = new LocalConnector(server); + server.addConnector(connector); + ServletContextHandler context = new ServletContextHandler("/"); + server.setHandler(context); + ServletHolder dispatch = context.addServlet(ChatServlet.class, "/chat/*"); + dispatch.setInitParameter("asyncTimeout", "500"); + server.start(); + } + + @AfterEach + public void tearDown() throws Exception + { + server.stop(); + } + + @Test + public void testLogin() throws Exception + { + assertResponse("user=test&join=true&message=has%20joined!", "{\"from\":\"test\",\"chat\":\"has joined!\"}"); + } + + @Test + public void testChat() throws Exception + { + assertResponse("user=test&join=true&message=has%20joined!", "{\"from\":\"test\",\"chat\":\"has joined!\"}"); + String response = connector.getResponse(createRequestString("user=test&message=message")); + assertThat(response.contains("{"), is(false)); // make sure we didn't get a json body + } + + @Test + public void testPoll() throws Exception + { + assertResponse("user=test", "{action:\"poll\"}"); + } + + private void assertResponse(String requestBody, String expectedResponse) throws Exception + { + String response = connector.getResponse(createRequestString(requestBody)); + assertThat(response.contains(expectedResponse), is(true)); + } + + private String createRequestString(String body) + { + return "POST /chat/ HTTP/1.1\r\n" + + "Host: tester\r\n" + + "Content-length: " + body.length() + "\r\n" + + "Content-type: application/x-www-form-urlencoded\r\n" + + "Connection: close\r\n" + + "\r\n" + + body; + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/test/java/org/eclipse/jetty/ee11/DispatchServletTest.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/test/java/org/eclipse/jetty/ee11/DispatchServletTest.java new file mode 100644 index 00000000000..595ad056907 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/test/java/org/eclipse/jetty/ee11/DispatchServletTest.java @@ -0,0 +1,148 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11; + +import org.eclipse.jetty.ee11.servlet.DefaultServlet; +import org.eclipse.jetty.ee11.servlet.ServletContextHandler; +import org.eclipse.jetty.ee11.servlet.ServletHolder; +import org.eclipse.jetty.server.LocalConnector; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.component.LifeCycle; +import org.example.DispatchServlet; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Simple tests against DispatchServlet. + */ +public class DispatchServletTest +{ + private Server server; + private LocalConnector connector; + private ServletContextHandler context; + + @BeforeEach + public void setUp() throws Exception + { + server = new Server(); + connector = new LocalConnector(server); + server.addConnector(connector); + context = new ServletContextHandler("/tests"); + server.setHandler(context); + server.start(); + } + + @AfterEach + public void tearDown() + { + LifeCycle.stop(server); + } + + /** + * As filed in JETTY-978. + * + * Security problems in demo dispatch servlet. + * + *
+ *

+ * The dispatcher servlet (org.example.DispatchServlet) is prone to a Denial of + * Service vulnerability. + *

+ *

+ * This example servlet is meant to be used as a resources dispatcher, + * however a malicious aggressor may abuse this functionality in order to + * cause a recursive inclusion. In details, it is possible to abuse the + * method org.example.DispatchServlet.doGet(DispatchServlet.java:203) forcing + * the application to recursively include the "Dispatch" servlet. + *

+ *

+ * Dispatch org.example.DispatchServlet 1 Dispatch /dispatch/* As a result, it + * is possible to trigger a "java.lang.StackOverflowError" and consequently + * an internal server error (500). + *

+ *

+ * Multiple requests may easily affect the availability of the servlet + * container. Since this attack can cause the server to consume resources in + * a non-linear relationship to the size of inputs, it should be considered + * as a server flaw. + *

+ *

+ * The vulnerability seems confined to the example servlet and it does not + * afflict the Jetty's core." + *

+ *
+ */ + @Test + public void testSelfRefForwardDenialOfService() throws Exception + { + ServletHolder dispatch = context.addServlet(DispatchServlet.class, "/dispatch/*"); + context.addServlet(DefaultServlet.class, "/"); + + String request = "GET /tests/dispatch/includeN/" + dispatch.getName() + " HTTP/1.1\n" + + "Host: tester\n" + + "Connection: close\n" + + "\n"; + String response = connector.getResponse(request); + + String msg = "Response code on SelfRefDoS"; + + assertFalse(response.startsWith("HTTP/1.1 500 "), msg + " should not be code 500."); + assertTrue(response.startsWith("HTTP/1.1 403 "), msg + " should return error code 403 (Forbidden)"); + } + + @Test + public void testSelfRefDeep() throws Exception + { + context.addServlet(DispatchServlet.class, "/dispatch/*"); + context.addServlet(DefaultServlet.class, "/"); + + String[] selfRefs = + {"/dispatch/forward", "/dispatch/includeS", "/dispatch/includeW", "/dispatch/includeN"}; + + /* + * Number of nested dispatch requests. 220 is a good value, as it won't + * trigger an Error 413 response (Entity too large). Anything larger + * than 220 will trigger a 413 response. + */ + int nestedDepth = 220; + + for (String selfRef : selfRefs) + { + String request = "GET /tests" + + selfRef.repeat(nestedDepth) + + "/ HTTP/1.1\n" + + "Host: tester\n" + + "Connection: close\n" + + "\n"; + String response = connector.getResponse(request); + + StringBuilder msg = new StringBuilder(); + msg.append("Response code on nested \"").append(selfRef).append("\""); + msg.append(" (depth:").append(nestedDepth).append(")"); + + assertFalse(response.startsWith("HTTP/1.1 413 "), + msg + " should not be code 413 (Request Entity Too Large)," + + "the nestedDepth in the TestCase is too large (reduce it)"); + + assertFalse(response.startsWith("HTTP/1.1 500 "), msg + " should not be code 500."); + assertThat(response, Matchers.startsWith("HTTP/1.1 403 ")); + } + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/test/java/org/eclipse/jetty/ee11/TestServer.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/test/java/org/eclipse/jetty/ee11/TestServer.java new file mode 100644 index 00000000000..e8b82492cb0 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/test/java/org/eclipse/jetty/ee11/TestServer.java @@ -0,0 +1,177 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11; + +import java.io.FileNotFoundException; +import java.lang.management.ManagementFactory; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.eclipse.jetty.ee11.webapp.Configurations; +import org.eclipse.jetty.ee11.webapp.MetaInfConfiguration; +import org.eclipse.jetty.ee11.webapp.WebAppContext; +import org.eclipse.jetty.jmx.MBeanContainer; +import org.eclipse.jetty.security.HashLoginService; +import org.eclipse.jetty.server.CustomRequestLog; +import org.eclipse.jetty.server.ForwardedRequestCustomizer; +import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.HttpConnectionFactory; +import org.eclipse.jetty.server.SecureRequestCustomizer; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.server.handler.ContextHandlerCollection; +import org.eclipse.jetty.server.handler.ResourceHandler; +import org.eclipse.jetty.session.DefaultSessionCache; +import org.eclipse.jetty.session.FileSessionDataStore; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.util.resource.ResourceFactory; +import org.eclipse.jetty.util.thread.QueuedThreadPool; +import org.junit.jupiter.api.Disabled; + +@Disabled("Not a test case") +public class TestServer +{ + public static void main(String[] args) throws Exception + { + Path webappProjectRoot = MavenTestingUtils.getBasePath(); + + // Setup Threadpool + QueuedThreadPool threadPool = new QueuedThreadPool(); + threadPool.setMaxThreads(100); + + // Setup server + Server server = new Server(threadPool); + Configurations.setServerDefault(server); + server.manage(threadPool); + + // Setup JMX + MBeanContainer mbContainer = new MBeanContainer(ManagementFactory.getPlatformMBeanServer()); + server.addBean(mbContainer); + + // Common HTTP configuration + HttpConfiguration config = new HttpConfiguration(); + config.setSecurePort(8443); + config.addCustomizer(new ForwardedRequestCustomizer()); + config.addCustomizer(new SecureRequestCustomizer()); + config.setSendDateHeader(true); + config.setSendServerVersion(true); + + // Http Connector + HttpConnectionFactory http = new HttpConnectionFactory(config); + ServerConnector httpConnector = new ServerConnector(server, http); + httpConnector.setPort(8080); + httpConnector.setIdleTimeout(30000); + server.addConnector(httpConnector); + + // Handlers + ContextHandlerCollection contexts = new ContextHandlerCollection(); + + // Add restart handler to test the ability to save sessions and restart + /* TODO: figure out how to do this + RestartHandler restart = new RestartHandler(); + restart.setHandler(handlers); + server.setHandler(restart); + */ + + // Setup context + HashLoginService login = new HashLoginService(); + login.setName("Test Realm"); + Path realmPropPath = webappProjectRoot.resolve("jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/test/resources/test-realm.properties"); + if (!Files.exists(realmPropPath)) + throw new FileNotFoundException(realmPropPath.toString()); + Resource realmResource = ResourceFactory.of(server).newResource(realmPropPath); + login.setConfig(realmResource); + server.addBean(login); + + Path logPath = Files.createTempFile("jetty-yyyy_mm_dd", "log"); + CustomRequestLog requestLog = new CustomRequestLog(logPath.toString()); + server.setRequestLog(requestLog); + + server.setStopAtShutdown(true); + + WebAppContext webapp = new WebAppContext(); + webapp.setContextPath("/test"); + webapp.setParentLoaderPriority(true); + Path webappBase = webappProjectRoot.resolve("jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/main/webapp"); + if (!Files.exists(webappBase)) + throw new FileNotFoundException(webappBase.toString()); + webapp.setBaseResource(webapp.getResourceFactory().newResource(webappBase)); + webapp.setAttribute(MetaInfConfiguration.CONTAINER_JAR_PATTERN, + ".*/test-jetty-webapp/target/classes.*$|" + + ".*/jakarta.servlet.api-[^/]*\\.jar$|.*/jakarta.servlet.jsp.jstl-.*\\.jar$|.*/org.apache.taglibs.taglibs-standard.*\\.jar$" + ); + + webapp.setAttribute("testAttribute", "testValue"); + Path sessionDir = Files.createTempDirectory("sessions"); + DefaultSessionCache ss = new DefaultSessionCache(webapp.getSessionHandler()); + FileSessionDataStore sds = new FileSessionDataStore(); + ss.setSessionDataStore(sds); + sds.setStoreDir(sessionDir.toFile()); + webapp.getSessionHandler().setSessionCache(ss); + + contexts.addHandler(webapp); + + ContextHandler srcroot = new ContextHandler(); + Path srcRootPath = webappProjectRoot.resolve("jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src"); + if (!Files.exists(srcRootPath)) + throw new FileNotFoundException(srcRootPath.toString()); + srcroot.setBaseResource(ResourceFactory.of(server).newResource(srcRootPath)); + srcroot.setHandler(new ResourceHandler()); + srcroot.setContextPath("/src"); + contexts.addHandler(srcroot); + + server.setHandler(contexts); + server.start(); + server.dumpStdErr(); + + server.join(); + } + + //TODO how to restart server? + /* + private static class RestartHandler extends HandlerWrapper + { + + @Override + public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException + { + super.handle(target, baseRequest, request, response); + if (Boolean.valueOf(request.getParameter("restart"))) + { + final Server server = getServer(); + + new Thread() + { + @Override + public void run() + { + try + { + Thread.sleep(100); + server.stop(); + Thread.sleep(100); + server.start(); + } + catch (Exception e) + { + LOG.warn("Unable to restart server", e); + } + } + }.start(); + } + } + }*/ +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/test/resources/jetty-logging.properties b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/test/resources/jetty-logging.properties new file mode 100644 index 00000000000..28e85c0b13d --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/test/resources/jetty-logging.properties @@ -0,0 +1,3 @@ +# Jetty Logging using jetty-slf4j-impl +org.example.LEVEL=INFO +# org.eclipse.jetty.ee11.annotations.LEVEL=DEBUG diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/test/resources/test-realm.properties b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/test/resources/test-realm.properties new file mode 100644 index 00000000000..9d9bc368493 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jetty-webapp/src/test/resources/test-realm.properties @@ -0,0 +1,20 @@ +# +# This file defines users passwords and roles for a HashUserRealm +# +# The format is +# : [, ...] +# +# Passwords may be clear text, obfuscated or checksummed. The class +# org.eclipse.util.Password should be used to generate obfuscated +# passwords or password checksums +# +# If DIGEST Authentication is used, the password must be in a recoverable +# format, either plain text or OBF:. +# +jetty:MD5:164c88b302622e17050af52c89945d44,user +admin:CRYPT:adpexzg3FUZAk,server-administrator,content-administrator,admin,user +other:OBF:1xmk1w261u9r1w1c1xmq,user +plain:plain,user +user:password,user +# This entry is for digest auth. The credential is a MD5 hash of username:realmname:password +digest:MD5:6e120743ad67abfbc385bc2bb754e297,user diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jndi-webapp/pom.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jndi-webapp/pom.xml new file mode 100644 index 00000000000..1e42670f66d --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jndi-webapp/pom.xml @@ -0,0 +1,81 @@ + + + 4.0.0 + + org.eclipse.jetty.ee11.demos + jetty-ee11-demos + 12.1.0-SNAPSHOT + + jetty-ee11-demo-jndi-webapp + war + EE11 :: Demo :: JNDI WebApp + + ${project.groupId}.jndi + + + + jakarta.servlet + jakarta.servlet-api + provided + + + jakarta.transaction + jakarta.transaction-api + provided + + + org.eclipse.jetty.ee11.demos + jetty-ee11-demo-mock-resources + provided + + + + + + + org.eclipse.jetty.ee11 + jetty-ee11-maven-plugin + ${project.version} + + src/main/templates/plugin-context.xml + + src/main/webapp + src/main/webapp/WEB-INF/web.xml + /test-jndi + + + + + org.eclipse.jetty.ee11 + jetty-ee11-jndi + ${project.version} + + + org.eclipse.jetty.ee11.demos + jetty-ee11-demo-mock-resources + ${project.version} + + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + + copy-dependencies + + package + + jakarta.transaction-api,ee11-demo-mock-resources + ${project.build.directory}/lib/jndi + + + + + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jndi-webapp/src/main/config/modules/demo.d/ee11-demo-jndi.properties b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jndi-webapp/src/main/config/modules/demo.d/ee11-demo-jndi.properties new file mode 100644 index 00000000000..5c25b5bb012 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jndi-webapp/src/main/config/modules/demo.d/ee11-demo-jndi.properties @@ -0,0 +1 @@ +environment: ee11 diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jndi-webapp/src/main/config/modules/demo.d/ee11-demo-jndi.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jndi-webapp/src/main/config/modules/demo.d/ee11-demo-jndi.xml new file mode 100644 index 00000000000..562fb161826 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jndi-webapp/src/main/config/modules/demo.d/ee11-demo-jndi.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + /ee11-test-jndi + /ee11-demo-jndi.war + + true + false + true + + + + + + + woggle + 4000 + false + + + + + + + + wiggle + 100 + true + + + + + + + + mail/Session + + + CHANGE-ME + CHANGE-ME + + + false + CHANGE-ME + CHANGE-ME + false + + + + + + + + + + + + jdbc/mydatasource + + + + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jndi-webapp/src/main/config/modules/ee11-demo-jndi.mod b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jndi-webapp/src/main/config/modules/ee11-demo-jndi.mod new file mode 100644 index 00000000000..a5a2da6957e --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jndi-webapp/src/main/config/modules/ee11-demo-jndi.mod @@ -0,0 +1,29 @@ +# DO NOT EDIT THIS FILE - See: https://eclipse.dev/jetty/documentation/ + +[description] +Demo JNDI Resources Webapp + +[environment] +ee11 + +[tags] +demo +webapp + +[depends] +ee11-deploy +ext +jdbc +ee11-plus +ee11-jndi +ee11-demo-mock-resources + +[files] +basehome:modules/demo.d/ee11-demo-jndi.xml|webapps/ee11-demo-jndi.xml +maven://org.eclipse.jetty.ee11.demos/jetty-ee11-demo-jndi-webapp/${jetty.version}/war|webapps/ee11-demo-jndi.war +maven://jakarta.mail/jakarta.mail-api/@jakarta.mail.api.version@/jar|lib/ee11/jakarta.mail-api-@jakarta.mail.api.version@.jar +maven://jakarta.activation/jakarta.activation-api/@jakarta.activation.api.version@/jar|lib/ee11/jakarta.activation-api-@jakarta.activation.api.version@.jar + +[lib] +lib/ee11/jakarta.mail-api-@jakarta.mail.api.version@.jar +lib/ee11/jakarta.activation-api-@jakarta.activation.api.version@.jar \ No newline at end of file diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jndi-webapp/src/main/java/org/example/JNDITest.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jndi-webapp/src/main/java/org/example/JNDITest.java new file mode 100644 index 00000000000..8c6bcce5ee3 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jndi-webapp/src/main/java/org/example/JNDITest.java @@ -0,0 +1,136 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.example; + +import java.io.IOException; +import javax.naming.InitialContext; +import javax.sql.DataSource; + +import jakarta.servlet.ServletConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.transaction.UserTransaction; + +/** + * JNDITest + * + * Use JNDI from within Jetty. + * + * Also, use servlet spec 2.5 resource injection and lifecycle callbacks from within the web.xml + * to set up some of the JNDI resources. + * + */ +public class JNDITest extends HttpServlet +{ + private DataSource myDS; + private Double wiggle; + private Integer woggle; + private Double gargle; + private String svr; + + private String resourceNameMappingInjectionResult; + private String envEntryOverrideResult; + private String postConstructResult = "PostConstruct method called: FALSE"; + private String preDestroyResult = "PreDestroy method called: NOT YET"; + private String envEntryGlobalScopeResult; + private String envEntryWebAppScopeResult; + private String userTransactionResult; + private String svrResult; + + public void setMyDatasource(DataSource ds) + { + myDS = ds; + } + + private void postConstruct() + { + resourceNameMappingInjectionResult = "Injection of resource to locally mapped name (java:comp/env/mydatasource as java:comp/env/mydatasource1): " + (myDS != null ? "PASS" : "FAIL"); + envEntryOverrideResult = "Override of EnvEntry in jetty-env.xml (java:comp/env/wiggle): " + (wiggle == 55.0 ? "PASS" : "FAIL(expected 55.0, got " + wiggle + ")") + ""; + postConstructResult = "PostConstruct method called: PASS"; + } + + private void preDestroy() + { + preDestroyResult = "PreDestroy method called: PASS"; + } + + @Override + public void init(ServletConfig config) throws ServletException + { + super.init(config); + try + { + InitialContext ic = new InitialContext(); + woggle = (Integer)ic.lookup("java:comp/env/woggle"); + envEntryGlobalScopeResult = "EnvEntry defined in context xml lookup result (java:comp/env/woggle): " + (woggle == 4000 ? "PASS" : "FAIL(expected 4000, got " + woggle + ")") + ""; + gargle = (Double)ic.lookup("java:comp/env/gargle"); + svr = (String)ic.lookup("java:comp/env/svr"); + svrResult = "Ref to Server in jetty-env.xml result: " + (svr != null ? "PASS" : "FAIL") + ""; + + envEntryWebAppScopeResult = "EnvEntry defined in jetty-env.xml lookup result (java:comp/env/gargle): " + (gargle == 100.0 ? "PASS" : "FAIL(expected 100, got " + gargle + ")") + ""; + UserTransaction utx = (UserTransaction)ic.lookup("java:comp/UserTransaction"); + userTransactionResult = "UserTransaction lookup result (java:comp/UserTransaction): " + (utx != null ? "PASS" : "FAIL") + ""; + } + catch (Exception e) + { + throw new ServletException(e); + } + } + + @Override + public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + doGet(request, response); + } + + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + try + { + response.setContentType("text/html"); + ServletOutputStream out = response.getOutputStream(); + out.println(""); + out.println(""); + out.println("

Jetty JNDI Tests

"); + out.println(""); + + out.println("

Injection and JNDI Lookup Results

"); + out.println("

" + resourceNameMappingInjectionResult + "

"); + out.println("

" + envEntryOverrideResult + "

"); + out.println("

" + postConstructResult + "

"); + out.println("

" + preDestroyResult + "

"); + out.println("

" + envEntryGlobalScopeResult + "

"); + out.println("

" + envEntryWebAppScopeResult + "

"); + out.println("

" + svrResult + "

"); + out.println("

" + userTransactionResult + "

"); + + out.println(""); + out.println(""); + out.flush(); + } + catch (Exception e) + { + throw new ServletException(e); + } + } + + @Override + public void destroy() + { + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jndi-webapp/src/main/templates/plugin-context.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jndi-webapp/src/main/templates/plugin-context.xml new file mode 100644 index 00000000000..fc2469d5cce --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jndi-webapp/src/main/templates/plugin-context.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + woggle + 4000 + false + + + + + + + + wiggle + 100 + true + + + + + + + + mail/Session + + + CHANGE-ME + CHANGE-ME + + + false + CHANGE-ME + CHANGE-ME + false + + + + + + + + + + + + jdbc/mydatasource + + + + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jndi-webapp/src/main/webapp/WEB-INF/jetty-env.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jndi-webapp/src/main/webapp/WEB-INF/jetty-env.xml new file mode 100644 index 00000000000..af92cf43668 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jndi-webapp/src/main/webapp/WEB-INF/jetty-env.xml @@ -0,0 +1,45 @@ + + + + + + + + + gargle + 100 + true + + + + + + wiggle + 55.0 + true + + + + + + jdbc/mydatasource1 + jdbc/mydatasource + + + + + + svr + + + + + + + + true + + + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jndi-webapp/src/main/webapp/WEB-INF/jetty-web.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jndi-webapp/src/main/webapp/WEB-INF/jetty-web.xml new file mode 100644 index 00000000000..99eaf71a9b3 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jndi-webapp/src/main/webapp/WEB-INF/jetty-web.xml @@ -0,0 +1,8 @@ + + + + + + The test-jndi webapp is deployed. DO NOT USE IN PRODUCTION! + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jndi-webapp/src/main/webapp/WEB-INF/web.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jndi-webapp/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000000..d94a8c8be3b --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jndi-webapp/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,54 @@ + + + + EE11 Demo JNDI WebApp + + + JNDITest + org.example.JNDITest + 1 + + + + JNDITest + /test/* + + + + wiggle + java.lang.Double + 99.99 + + org.example.JNDITest + wiggle + + + + + jdbc/mydatasource1 + javax.sql.DataSource + Container + + org.example.JNDITest + myDatasource + + + + + org.example.JNDITest + postConstruct + + + + org.example.JNDITest + preDestroy + + + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jndi-webapp/src/main/webapp/demo.css b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jndi-webapp/src/main/webapp/demo.css new file mode 100644 index 00000000000..f2b91d3365d --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jndi-webapp/src/main/webapp/demo.css @@ -0,0 +1,83 @@ +body +{ + font-family: Arial, Verdana, Helvetica, sans-serif; +} + +.topnav +{ + overflow: hidden; + padding: 10px; + border: 1px solid #f6815c; + border-radius: 10px; + text-align: right; +} + +.menu +{ + margin-left: 3em; +} + +.content +{ + padding: 10px; +} + +.footer +{ + padding: 10px; + border-radius: 10px; + border: 1px solid #f6815c; +} + +.test +{ + background-color: #0099cc; + color: white; + padding: 10px 15px; + border: none; + font-size: 12pt; + border-radius: 10px; + box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2), 0 3px 10px 0 rgba(0,0,0,0.19); +} + +.test:hover +{ + background-color: #f6815c; + color: white; +} + +A:link +{ + color: #0099cc; + text-decoration: none; + font-weight: normal; + font-size: 11pt; + font-family:sans-serif; +} + +A:visited +{ + color: #0099cc; + text-decoration: none; + font-weight: normal; + font-size: 11pt; + font-family:sans-serif; +} + +A:hover +{ + color: #ff6600; + text-decoration: none; + font-weight: normal; + font-size: 11pt; + font-family:sans-serif; +} + +A:active +{ + color: #0099cc; + text-decoration: none; + font-weight: normal; + font-size: 11pt; + font-family:sans-serif; +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jndi-webapp/src/main/webapp/index.html b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jndi-webapp/src/main/webapp/index.html new file mode 100644 index 00000000000..3ad3cbc983c --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jndi-webapp/src/main/webapp/index.html @@ -0,0 +1,56 @@ + + + JNDI Demo WebApp + + + + + + + +
+
+ Demo Web Application Only - Do NOT Deploy in Production +
+

Eclipse Jetty JNDI Demo Webapp

+

+ This is a demo webapp for the Eclipse Jetty HTTP Server and Servlet Container. It was added into your $JETTY_BASE/webapps directory. +

+ +

+ It shows how to configure and lookup resources such as javax.sql.DataSource, a JTA transaction manager and a java.mail.Session in JNDI. +

+ +

Preparation

+

To use JNDI in a base jetty instance enable the jndi module: +

+     $ cd $JETTY_BASE
+     $ java -jar $JETTY_HOME/start.jar --add-module=jndi
+         
+

+

+ The jetty demo module pre-enables JNDI along with some mock resources used by this demo. +

+ +

Execution

+

+ Click Test to check the runtime lookup of the JNDI resources. +

+
+ +
+
+ + + + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jndi-webapp/src/main/webapp/small_powered_by.gif b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jndi-webapp/src/main/webapp/small_powered_by.gif new file mode 100644 index 00000000000..c5dd44319f0 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jndi-webapp/src/main/webapp/small_powered_by.gif differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jndi-webapp/src/main/webapp/stylesheet.css b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jndi-webapp/src/main/webapp/stylesheet.css new file mode 100644 index 00000000000..4ecc2cb4ae1 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jndi-webapp/src/main/webapp/stylesheet.css @@ -0,0 +1,7 @@ +body {color: #2E2E2E; font-family:sans-serif; font-size:90%;} +h1 {font-variant: small-caps; font-size:130%; letter-spacing: 0.1em;} +h2 {font-variant: small-caps; font-size:100%; letter-spacing: 0.1em;} +h3 {font-size:100%; letter-spacing: 0.1em;} + +span.pass { color: green; } +span.fail { color:red; } diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/pom.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/pom.xml new file mode 100644 index 00000000000..77a1417f907 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/pom.xml @@ -0,0 +1,125 @@ + + + + 4.0.0 + + org.eclipse.jetty.ee11.demos + jetty-ee11-demos + 12.1.0-SNAPSHOT + + jetty-ee11-demo-jsp-webapp + war + EE11 :: Demo :: JSP WebApp + + + ${project.groupId}.jsp + + + + + jakarta.servlet + jakarta.servlet-api + provided + + + jakarta.servlet.jsp + jakarta.servlet.jsp-api + provided + + + jakarta.servlet.jsp.jstl + jakarta.servlet.jsp.jstl-api + provided + + + + + + + org.apache.felix + maven-bundle-plugin + true + + + war + + + jakarta.servlet.jsp.*;version="[3,4)",org.eclipse.jetty.*;version="[$(version;===;${parsedVersion.osgiVersion}),$(version;==+;${parsedVersion.osgiVersion}))",* + !org.example.* + /ee11-demo-jsp + .,WEB-INF/classes + ee11 + + + + + maven-assembly-plugin + + + web-bundle-assembly + + single + + package + + + src/main/assembly/web-bundle.xml + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + + + + maven-war-plugin + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + + + + + precompile-jsp + + + + org.apache.maven.plugins + maven-war-plugin + + ${basedir}/target/web.xml + + + + org.eclipse.jetty + jetty-jspc-maven-plugin + ${project.version} + + + jspc + + jspc + + + + + + + + + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/assembly/web-bundle.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/assembly/web-bundle.xml new file mode 100644 index 00000000000..803a7455f19 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/assembly/web-bundle.xml @@ -0,0 +1,20 @@ + + + webbundle + + jar + + false + + + ${basedir}/${project.build.directory}/${project.build.finalName}/ + + + **/*.* + + + WEB-INF/lib/** + + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/config/modules/ee11-demo-jsp.mod b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/config/modules/ee11-demo-jsp.mod new file mode 100644 index 00000000000..36f70700315 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/config/modules/ee11-demo-jsp.mod @@ -0,0 +1,17 @@ +[description] +Demo Simple JSP Webapp + +[environment] +ee11 + +[tags] +demo +webapp + +[depends] +ee11-jsp +ee11-jstl +ee11-deploy + +[files] +maven://org.eclipse.jetty.ee11.demos/jetty-ee11-demo-jsp-webapp/${jetty.version}/war|webapps/ee11-demo-jsp.war diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/java/org/example/Counter.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/java/org/example/Counter.java new file mode 100644 index 00000000000..3589a0762cf --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/java/org/example/Counter.java @@ -0,0 +1,38 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.example; + +@SuppressWarnings("serial") +public class Counter implements java.io.Serializable +{ + int counter = 0; + String last; + + public int getCount() + { + counter++; + return counter; + } + + public void setLast(String uri) + { + last = uri; + } + + public String getLast() + { + return last; + } +} + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/java/org/example/Date2Tag.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/java/org/example/Date2Tag.java new file mode 100644 index 00000000000..dcb8b7da4eb --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/java/org/example/Date2Tag.java @@ -0,0 +1,51 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.example; + +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.StringTokenizer; + +import jakarta.servlet.jsp.JspContext; +import jakarta.servlet.jsp.JspException; +import jakarta.servlet.jsp.tagext.JspFragment; +import jakarta.servlet.jsp.tagext.SimpleTagSupport; + +public class Date2Tag extends SimpleTagSupport +{ + String format; + + public void setFormat(String value) + { + this.format = value; + } + + @Override + public void doTag() throws JspException, IOException + { + String formatted = + new SimpleDateFormat("long".equals(format) ? "EEE 'the' d:MMM:yyyy" : "d:MM:yy") + .format(new Date()); + StringTokenizer tok = new StringTokenizer(formatted, ":"); + JspContext context = getJspContext(); + context.setAttribute("day", tok.nextToken()); + context.setAttribute("month", tok.nextToken()); + context.setAttribute("year", tok.nextToken()); + + JspFragment fragment = getJspBody(); + fragment.invoke(null); + } +} + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/java/org/example/DateTag.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/java/org/example/DateTag.java new file mode 100644 index 00000000000..8b13d2bdcc2 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/java/org/example/DateTag.java @@ -0,0 +1,102 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.example; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; + +import jakarta.servlet.jsp.JspException; +import jakarta.servlet.jsp.JspTagException; +import jakarta.servlet.jsp.PageContext; +import jakarta.servlet.jsp.tagext.BodyContent; +import jakarta.servlet.jsp.tagext.BodyTagSupport; +import jakarta.servlet.jsp.tagext.Tag; + +@SuppressWarnings("serial") +public class DateTag extends BodyTagSupport +{ + Tag parent; + BodyContent body; + String tz = "GMT"; + + @Override + public void setParent(Tag parent) + { + this.parent = parent; + } + + @Override + public Tag getParent() + { + return parent; + } + + @Override + public void setBodyContent(BodyContent content) + { + body = content; + } + + @Override + public void setPageContext(PageContext pageContext) + { + } + + public void setTz(String value) + { + tz = value; + } + + @Override + public int doStartTag() throws JspException + { + return EVAL_BODY_BUFFERED; + } + + @Override + public int doEndTag() throws JspException + { + return EVAL_PAGE; + } + + @Override + public void doInitBody() throws JspException + { + } + + @Override + public int doAfterBody() throws JspException + { + try + { + SimpleDateFormat format = new SimpleDateFormat(body.getString()); + format.setTimeZone(TimeZone.getTimeZone(tz)); + body.getEnclosingWriter().write(format.format(new Date())); + return SKIP_BODY; + } + catch (Exception ex) + { + ex.printStackTrace(); + throw new JspTagException(ex.toString()); + } + } + + @Override + public void release() + { + body = null; + } +} + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/java/org/example/TagListener.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/java/org/example/TagListener.java new file mode 100644 index 00000000000..dad793fde37 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/java/org/example/TagListener.java @@ -0,0 +1,133 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.example; + +import jakarta.servlet.ServletContextAttributeEvent; +import jakarta.servlet.ServletContextAttributeListener; +import jakarta.servlet.ServletContextEvent; +import jakarta.servlet.ServletContextListener; +import jakarta.servlet.ServletRequestAttributeEvent; +import jakarta.servlet.ServletRequestAttributeListener; +import jakarta.servlet.ServletRequestEvent; +import jakarta.servlet.ServletRequestListener; +import jakarta.servlet.http.HttpSessionActivationListener; +import jakarta.servlet.http.HttpSessionAttributeListener; +import jakarta.servlet.http.HttpSessionBindingEvent; +import jakarta.servlet.http.HttpSessionEvent; +import jakarta.servlet.http.HttpSessionListener; + +public class TagListener implements HttpSessionListener, HttpSessionAttributeListener, HttpSessionActivationListener, ServletContextListener, ServletContextAttributeListener, ServletRequestListener, ServletRequestAttributeListener +{ + @Override + public void attributeAdded(HttpSessionBindingEvent se) + { + //System.err.println("tagListener: attributedAdded "+se); + } + + @Override + public void attributeAdded(ServletContextAttributeEvent scab) + { + //System.err.println("tagListener: attributeAdded "+scab); + } + + @Override + public void attributeAdded(ServletRequestAttributeEvent srae) + { + //System.err.println("tagListener: attributeAdded "+srae); + } + + @Override + public void attributeRemoved(HttpSessionBindingEvent se) + { + //System.err.println("tagListener: attributeRemoved "+se); + } + + @Override + public void attributeRemoved(ServletContextAttributeEvent scab) + { + //System.err.println("tagListener: attributeRemoved "+scab); + } + + @Override + public void attributeRemoved(ServletRequestAttributeEvent srae) + { + //System.err.println("tagListener: attributeRemoved "+srae); + } + + @Override + public void attributeReplaced(HttpSessionBindingEvent se) + { + //System.err.println("tagListener: attributeReplaced "+se); + } + + @Override + public void attributeReplaced(ServletContextAttributeEvent scab) + { + //System.err.println("tagListener: attributeReplaced "+scab); + } + + @Override + public void attributeReplaced(ServletRequestAttributeEvent srae) + { + //System.err.println("tagListener: attributeReplaced "+srae); + } + + @Override + public void contextDestroyed(ServletContextEvent sce) + { + //System.err.println("tagListener: contextDestroyed "+sce); + } + + @Override + public void contextInitialized(ServletContextEvent sce) + { + //System.err.println("tagListener: contextInitialized "+sce); + } + + @Override + public void requestDestroyed(ServletRequestEvent sre) + { + //System.err.println("tagListener: requestDestroyed "+sre); + } + + @Override + public void requestInitialized(ServletRequestEvent sre) + { + //System.err.println("tagListener: requestInitialized "+sre); + } + + @Override + public void sessionCreated(HttpSessionEvent se) + { + //System.err.println("tagListener: sessionCreated "+se); + } + + @Override + public void sessionDestroyed(HttpSessionEvent se) + { + //System.err.println("tagListener: sessionDestroyed "+se); + } + + @Override + public void sessionDidActivate(HttpSessionEvent se) + { + //System.err.println("tagListener: sessionDidActivate "+se); + } + + @Override + public void sessionWillPassivate(HttpSessionEvent se) + { + //System.err.println("tagListener: sessionWillPassivate "+se); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/WEB-INF/acme-taglib.tld b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/WEB-INF/acme-taglib.tld new file mode 100644 index 00000000000..6ccbd1764bc --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/WEB-INF/acme-taglib.tld @@ -0,0 +1,28 @@ + + + + taglib example + + 1.0 + acme + http://www.acme.com/taglib + + + org.example.TagListener + + + + Display Date + date + org.example.DateTag + tagdependent + + tz + false + + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/WEB-INF/acme-taglib2.tld b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/WEB-INF/acme-taglib2.tld new file mode 100644 index 00000000000..64e29a87c12 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/WEB-INF/acme-taglib2.tld @@ -0,0 +1,37 @@ + + + + Acme JSP2 tags + + 1.0 + acme2 + http://www.acme.com/taglib2 + + + Simple Date formatting + date2 + org.example.Date2Tag + scriptless + + Day of the Month + day + + + Month of the Year + month + + + Year + year + + + format + true + true + + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/WEB-INF/jetty-web.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/WEB-INF/jetty-web.xml new file mode 100644 index 00000000000..3ac6fde1969 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/WEB-INF/jetty-web.xml @@ -0,0 +1,8 @@ + + + + + + The demo-jsp webapp is deployed. DO NOT USE IN PRODUCTION! + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/WEB-INF/tags/panel.tag b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/WEB-INF/tags/panel.tag new file mode 100644 index 00000000000..798b80e7630 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/WEB-INF/tags/panel.tag @@ -0,0 +1,17 @@ +<%-- + Copyright (c) 2002 The Apache Software Foundation. + All rights reserved. +--%> +<%@ attribute name="color" %> +<%@ attribute name="bgcolor" %> +<%@ attribute name="title" %> + + + + + + + +
${title}
+ +
diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/WEB-INF/web.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000000..1698b5e34a4 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,18 @@ + + + + EE11 Demo JSP WebApp + + + foo.jsp + /foo/foo.jsp + + + foo.jsp + /foo/ + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/bean1.jsp b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/bean1.jsp new file mode 100644 index 00000000000..adb8eb1f79e --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/bean1.jsp @@ -0,0 +1,15 @@ + +<%@ page session="true"%> + + + +

JSP1.2 Beans: 1

+ +Counter accessed times.
+Counter last accessed by
+ + +Goto bean2.jsp + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/bean2.jsp b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/bean2.jsp new file mode 100644 index 00000000000..0598739e177 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/bean2.jsp @@ -0,0 +1,15 @@ + +<%@ page session="true"%> + + + +

JSP1.2 Beans: 2

+ +Counter accessed times.
+Counter last accessed by
+ + +Goto bean1.jsp + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/demo.css b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/demo.css new file mode 100644 index 00000000000..6cc26f40ece --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/demo.css @@ -0,0 +1,84 @@ +body +{ + font-family: Arial, Verdana, Helvetica, sans-serif; +} + +.topnav +{ + overflow: hidden; + padding: 10px; + border: 1px solid #f6815c; + border-radius: 10px; + text-align: right; +} + +.menu +{ + margin-left: 3em; +} + +.content +{ + padding: 10px; +} + +.footer +{ + padding: 10px; + border-radius: 10px; + border: 1px solid #f6815c; +} + +.test +{ + background-color: #0099cc; + color: white; + padding: 10px 15px; + border: none; + font-size: 12pt; + border-radius: 10px; + box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2), 0 3px 10px 0 rgba(0,0,0,0.19); +} + +.test:hover +{ + background-color: #f6815c; + color: white; +} + + +A:link +{ + color: #0099cc; + text-decoration: none; + font-weight: normal; + font-size: 11pt; + font-family:sans-serif; +} + +A:visited +{ + color: #0099cc; + text-decoration: none; + font-weight: normal; + font-size: 11pt; + font-family:sans-serif; +} + +A:hover +{ + color: #ff6600; + text-decoration: none; + font-weight: normal; + font-size: 11pt; + font-family:sans-serif; +} + +A:active +{ + color: #0099cc; + text-decoration: none; + font-weight: normal; + font-size: 11pt; + font-family:sans-serif; +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/dump.jsp b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/dump.jsp new file mode 100644 index 00000000000..b0cb8f131c9 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/dump.jsp @@ -0,0 +1,24 @@ + +<%@ page import="java.util.Enumeration" %> + +

JSP Dump

+ + + + + + + +<% + Enumeration e =request.getParameterNames(); + while(e.hasMoreElements()) + { + String name = (String)e.nextElement(); +%> + + + +<% } %> + +
Protocol:<%= request.getProtocol() %>
Request URI:<%= request.getRequestURI() %>
ServletPath:<%= request.getServletPath() %>
PathInfo:<%= request.getPathInfo() %>
getParameter("<%= name %>")<%= request.getParameter(name) %>
+ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/expr.jsp b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/expr.jsp new file mode 100644 index 00000000000..e0b25e20203 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/expr.jsp @@ -0,0 +1,23 @@ + +

JSP2.0 Expressions

+ + + + + + + + + + + + + + + + + + + +
ExpressionResult
\${param["A"]}${param["A"]} 
\${header["host"]}${header["host"]}
\${header["user-agent"]}${header["user-agent"]}
\${1+1}${1+1}
\${param["A"] * 2}${param["A"] * 2} 
+ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/foo/foo.jsp b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/foo/foo.jsp new file mode 100644 index 00000000000..7ec8955932d --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/foo/foo.jsp @@ -0,0 +1,15 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> + + + + +

FOO Example

+
+

A trivial FOO example +


+ + +
+
+ + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/index.jsp b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/index.jsp new file mode 100644 index 00000000000..06d1093b053 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/index.jsp @@ -0,0 +1,45 @@ +<%@ page import="java.time.format.DateTimeFormatter" %> +<%@ page import="java.time.LocalDate" %> +<%@ page contentType="text/html; charset=UTF-8" %> + + + + + + + + + +
+
+ Demo Web Application Only - Do NOT Deploy in Production +
+

Eclipse Jetty JSP Demo Webapp

+

+ This is a demo webapp for the Eclipse Jetty HTTP Server and Servlet Container. It was added into your $JETTY_BASE/webapps directory. +

+ +

JSP Examples on <%= DateTimeFormatter.ofPattern("d MMMM yyyy").format(LocalDate.now()) %>

+ +
+ + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/jstl.jsp b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/jstl.jsp new file mode 100644 index 00000000000..9fa7b57e96c --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/jstl.jsp @@ -0,0 +1,15 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> + + + + +

JSTL Example

+
+

A trivial jstl example +


+ + +
+
+ + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/small_powered_by.gif b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/small_powered_by.gif new file mode 100644 index 00000000000..c5dd44319f0 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/small_powered_by.gif differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/tag.jsp b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/tag.jsp new file mode 100644 index 00000000000..069d8c67b17 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/tag.jsp @@ -0,0 +1,16 @@ + + + +<%@ taglib uri="http://www.acme.com/taglib" prefix="acme" %> + +<acme:date tz="GMT">EEE, dd/MMM/yyyy HH:mm:ss ZZZ</acme:date> +==> +EEE, dd/MMM/yyyy HH:mm:ss ZZZ +
+<acme:date tz="EST">EEE, dd-MMM-yyyy HH:mm:ss ZZZ</acme:date> +==> +EEE, dd-MMM-yyyy HH:mm:ss ZZZ +
+ + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/tag2.jsp b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/tag2.jsp new file mode 100644 index 00000000000..8071927562a --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/tag2.jsp @@ -0,0 +1,19 @@ + + + +<%@ taglib uri="http://www.acme.com/taglib2" prefix="acme" %> + + + On ${day} of ${month} in the year ${year} + + +
+ + + ${day} - ${month} - ${year} + + +
+ + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/tagfile.jsp b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/tagfile.jsp new file mode 100644 index 00000000000..67299f0229c --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-jsp-webapp/src/main/webapp/tagfile.jsp @@ -0,0 +1,37 @@ +<%@ taglib prefix="acme" tagdir="/WEB-INF/tags" %> + + + + +

JSP 2.0 Tag File Example

+
+

Panel tag created from JSP fragment file in WEB-INF/tags +


+ + + + + + +
+ + First panel.
+
+
+ + Second panel.
+ Second panel.
+ Second panel.
+ Second panel.
+
+
+ + Third panel.
+ + A panel in a panel. + + Third panel.
+
+
+ + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-mock-resources/pom.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-mock-resources/pom.xml new file mode 100644 index 00000000000..31e3b7fd406 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-mock-resources/pom.xml @@ -0,0 +1,49 @@ + + + 4.0.0 + + org.eclipse.jetty.ee11.demos + jetty-ee11-demos + 12.1.0-SNAPSHOT + + jetty-ee11-demo-mock-resources + jar + EE11 :: Demo :: Mock Resources + + ${project.groupId}.mocks + + + + jakarta.mail + jakarta.mail-api + provided + + + jakarta.servlet + jakarta.servlet-api + provided + + + jakarta.transaction + jakarta.transaction-api + provided + + + + + + org.apache.felix + maven-bundle-plugin + + + org.eclipse.jetty.ee11.demos.demo-mock-resources + Mock resources used for testing + org.example;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}" + javax.naming, javax.naming.spi, javax.sql, jakarta.transaction;version="2.0.0", org.eclipse.jetty.jndi + <_nouses>true + + + + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-mock-resources/src/main/config/modules/ee11-demo-mock-resources.mod b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-mock-resources/src/main/config/modules/ee11-demo-mock-resources.mod new file mode 100644 index 00000000000..f1d48da7d51 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-mock-resources/src/main/config/modules/ee11-demo-mock-resources.mod @@ -0,0 +1,20 @@ +# DO NOT EDIT THIS FILE - See: https://eclipse.dev/jetty/documentation/ + +[description] +Download and install some Demo Mock Resources + +[environment] +ee11 + +[tags] +demo + +[depends] +jdbc +ee11-annotations + +[lib] +lib/ee11/ee11-demo-mock-resources-${jetty.version}.jar + +[files] +maven://org.eclipse.jetty.ee11.demos/jetty-ee11-demo-mock-resources/${jetty.version}/jar|lib/ee11/ee11-demo-mock-resources-${jetty.version}.jar diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-mock-resources/src/main/java/org/example/MockDataSource.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-mock-resources/src/main/java/org/example/MockDataSource.java new file mode 100644 index 00000000000..d5710d1e34e --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-mock-resources/src/main/java/org/example/MockDataSource.java @@ -0,0 +1,83 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.example; + +import java.io.PrintWriter; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.logging.Logger; +import javax.sql.DataSource; + +/** + * MockDataSource + */ +public class MockDataSource implements DataSource +{ + + /** + * NOTE: JDK7+ new feature + */ + @Override + public Logger getParentLogger() + { + return null; + } + + @Override + public Connection getConnection() throws SQLException + { + return null; + } + + @Override + public Connection getConnection(String username, String password) + throws SQLException + { + return null; + } + + @Override + public PrintWriter getLogWriter() throws SQLException + { + return null; + } + + @Override + public int getLoginTimeout() throws SQLException + { + return 0; + } + + @Override + public void setLogWriter(PrintWriter out) throws SQLException + { + } + + @Override + public void setLoginTimeout(int seconds) throws SQLException + { + } + + @Override + public boolean isWrapperFor(Class iface) throws SQLException + { + return false; + } + + @Override + public T unwrap(Class iface) throws SQLException + { + return null; + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-mock-resources/src/main/java/org/example/MockTransport.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-mock-resources/src/main/java/org/example/MockTransport.java new file mode 100644 index 00000000000..1bde6c2bd1a --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-mock-resources/src/main/java/org/example/MockTransport.java @@ -0,0 +1,41 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.example; + +import jakarta.mail.Address; +import jakarta.mail.Message; +import jakarta.mail.MessagingException; +import jakarta.mail.Session; +import jakarta.mail.Transport; +import jakarta.mail.URLName; + +/** + * MockTransport + */ +public class MockTransport extends Transport +{ + /** + * + */ + public MockTransport(Session session, URLName urlname) + { + super(session, urlname); + } + + @Override + public void sendMessage(Message arg0, Address[] arg1) throws MessagingException + { + System.err.println("Sending message"); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-mock-resources/src/main/java/org/example/MockUserTransaction.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-mock-resources/src/main/java/org/example/MockUserTransaction.java new file mode 100644 index 00000000000..7adb59a7b86 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-mock-resources/src/main/java/org/example/MockUserTransaction.java @@ -0,0 +1,69 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.example; + +import javax.naming.Reference; + +import jakarta.transaction.HeuristicMixedException; +import jakarta.transaction.HeuristicRollbackException; +import jakarta.transaction.NotSupportedException; +import jakarta.transaction.RollbackException; +import jakarta.transaction.SystemException; +import jakarta.transaction.UserTransaction; + +/** + * MockUserTransaction + */ +public class MockUserTransaction extends Reference implements UserTransaction +{ + + public MockUserTransaction() + { + super("org.example.MockUserTransaction", "org.example.MockUserTransactionFactory", null); + } + + @Override + public void begin() throws NotSupportedException, SystemException + { + } + + @Override + public void commit() throws HeuristicMixedException, + HeuristicRollbackException, IllegalStateException, + RollbackException, SecurityException, SystemException + { + } + + @Override + public int getStatus() throws SystemException + { + return 0; + } + + @Override + public void rollback() throws IllegalStateException, SecurityException, + SystemException + { + } + + @Override + public void setRollbackOnly() throws IllegalStateException, SystemException + { + } + + @Override + public void setTransactionTimeout(int arg0) throws SystemException + { + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-mock-resources/src/main/java/org/example/MockUserTransactionFactory.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-mock-resources/src/main/java/org/example/MockUserTransactionFactory.java new file mode 100644 index 00000000000..0d7a1bdbf9f --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-mock-resources/src/main/java/org/example/MockUserTransactionFactory.java @@ -0,0 +1,41 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.example; + +import java.util.Hashtable; +import javax.naming.Context; +import javax.naming.Name; +import javax.naming.spi.ObjectFactory; + +public class MockUserTransactionFactory implements ObjectFactory +{ + /** + * @param obj The possibly null object containing location or reference + * information that can be used in creating an object. + * @param name The name of this object relative to {@code nameCtx}, + * or null if no name is specified. + * @param nameCtx The context relative to which the {@code name} + * parameter is specified, or null if {@code name} is + * relative to the default initial context. + * @param environment The possibly null environment that is used in + * creating the object. + * @return + * @throws Exception + */ + @Override + public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable environment) throws Exception + { + return new MockUserTransaction(); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-mock-resources/src/main/resources/META-INF/javaxmail.providers b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-mock-resources/src/main/resources/META-INF/javaxmail.providers new file mode 100644 index 00000000000..f787ee3cb04 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-mock-resources/src/main/resources/META-INF/javaxmail.providers @@ -0,0 +1 @@ + protocol=smtp; type=transport; class=org.example.MockTransport; vendor=Acme Tests; diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-proxy-webapp/pom.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-proxy-webapp/pom.xml new file mode 100644 index 00000000000..1c09104ce9d --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-proxy-webapp/pom.xml @@ -0,0 +1,91 @@ + + + 4.0.0 + + org.eclipse.jetty.ee11.demos + jetty-ee11-demos + 12.1.0-SNAPSHOT + + jetty-ee11-demo-proxy-webapp + war + EE11 :: Demo :: Proxy WebApp + + ${project.groupId}.proxy + + + + org.eclipse.jetty + jetty-client + + + org.eclipse.jetty + jetty-slf4j-impl + compile + + + org.eclipse.jetty.ee11 + jetty-ee11-proxy + + + org.slf4j + slf4j-api + + + jakarta.servlet + jakarta.servlet-api + provided + + + jakarta.servlet.jsp + jakarta.servlet.jsp-api + provided + + + jakarta.servlet.jsp.jstl + jakarta.servlet.jsp.jstl-api + provided + + + org.eclipse.jetty + jetty-server + provided + + + org.eclipse.jetty + jetty-alpn-java-server + test + + + org.eclipse.jetty + jetty-jmx + test + + + org.eclipse.jetty.ee11 + jetty-ee11-annotations + test + + + org.eclipse.jetty.ee11 + jetty-ee11-webapp + test + + + org.eclipse.jetty.http2 + jetty-http2-server + test + + + + + + maven-war-plugin + + + src/main/webapp/META-INF/MANIFEST.MF + + + + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-proxy-webapp/src/main/config/modules/ee11-demo-proxy.mod b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-proxy-webapp/src/main/config/modules/ee11-demo-proxy.mod new file mode 100644 index 00000000000..1156edf0abc --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-proxy-webapp/src/main/config/modules/ee11-demo-proxy.mod @@ -0,0 +1,17 @@ +# DO NOT EDIT THIS FILE - See: https://eclipse.dev/jetty/documentation/ + +[description] +Demo Proxy Webapp + +[environment] +ee11 + +[tags] +demo +webapp + +[depends] +ee11-deploy + +[files] +maven://org.eclipse.jetty.ee11.demos/jetty-ee11-demo-proxy-webapp/${jetty.version}/war|webapps/ee11-demo-proxy.war diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-proxy-webapp/src/main/webapp/META-INF/MANIFEST.MF b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-proxy-webapp/src/main/webapp/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..d46c0166acb --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-proxy-webapp/src/main/webapp/META-INF/MANIFEST.MF @@ -0,0 +1,21 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: TestIt +Bundle-SymbolicName: TestIt +Bundle-Version: 1.0.0.qualifier +Bundle-Activator: testit.Activator +Import-Package: jakarta.servlet;version="2.6", + jakarta.servlet.http;version="2.6", + jakarta.servlet.jsp, + jakarta.servlet.jsp.tagext +Require-Bundle: org.eclipse.jetty.client, + org.eclipse.jetty.proxy, + org.eclipse.jetty.http, + org.eclipse.jetty.io, + org.eclipse.jetty.util +Bundle-ClassPath: WEB-INF/classes +Bundle-RequiredExecutionEnvironment: JavaSE-1.7 +Bundle-ActivationPolicy: lazy +Web-ContextPath: / +Class-Path: + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-proxy-webapp/src/main/webapp/WEB-INF/jetty-web.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-proxy-webapp/src/main/webapp/WEB-INF/jetty-web.xml new file mode 100644 index 00000000000..61e9cfe2557 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-proxy-webapp/src/main/webapp/WEB-INF/jetty-web.xml @@ -0,0 +1,9 @@ + + + + + + The test-proxy webapp is deployed. DO NOT USE IN PRODUCTION! + + /proxy + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-proxy-webapp/src/main/webapp/WEB-INF/web.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-proxy-webapp/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000000..bb2f1fe5a5a --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-proxy-webapp/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,31 @@ + + + + EE11 Demo Proxy WebApp + + + JavadocTransparentProxy + org.eclipse.jetty.ee11.proxy.ProxyServlet$Transparent + + proxyTo + https://eclipse.dev/jetty/javadoc/jetty-12/index.html?overview-summary.html + + + hostHeader + www.eclipse.org + + 1 + true + + + + JavadocTransparentProxy + /current/* + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-proxy-webapp/src/test/java/org/eclipse/jetty/ee11/demos/ProxyWebAppTest.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-proxy-webapp/src/test/java/org/eclipse/jetty/ee11/demos/ProxyWebAppTest.java new file mode 100644 index 00000000000..bb094c92a6b --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-proxy-webapp/src/test/java/org/eclipse/jetty/ee11/demos/ProxyWebAppTest.java @@ -0,0 +1,91 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + +import org.eclipse.jetty.client.ContentResponse; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.ee11.webapp.WebAppContext; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.util.component.LifeCycle; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsStringIgnoringCase; +import static org.hamcrest.Matchers.is; + +/** + * Test the configuration found in WEB-INF/web.xml for purposes of the demo-base + */ +public class ProxyWebAppTest +{ + private Server server; + private HttpClient client; + + @BeforeEach + public void setup() throws Exception + { + server = new Server(); + + ServerConnector connector = new ServerConnector(server); + connector.setPort(0); + server.addConnector(connector); + + WebAppContext webapp = new WebAppContext(); + // This is a pieced together WebApp. + // We don't have a valid WEB-INF/lib to rely on at this point. + // So, open up server classes here, for purposes of this testcase. + webapp.getHiddenClassMatcher().add("-org.eclipse.jetty.ee11.proxy."); + webapp.setWar(MavenTestingUtils.getProjectDirPath("src/main/webapp").toString()); + webapp.setExtraClasspath(MavenTestingUtils.getTargetPath().resolve("test-classes").toString()); + server.setHandler(webapp); + + server.start(); + + client = new HttpClient(); + client.start(); + } + + @AfterEach + public void teardown() + { + LifeCycle.stop(client); + LifeCycle.stop(server); + } + + @Test + @Tag("external") + public void testProxyRequest() throws InterruptedException, ExecutionException, TimeoutException + { + ContentResponse response = client.newRequest(server.getURI().resolve("/proxy/current/")) + .followRedirects(false) + .send(); + + // Expecting a 200 OK (not a 302 redirect or other error) + // If we got an error here, that means our configuration in web.xml is bad / out of date. + // Such as the redirect from the eclipse website, we want all of the requests to go through + // this proxy configuration, not redirected to the actual website. + assertThat("response status", response.getStatus(), is(HttpStatus.OK_200)); + // Expecting a Javadoc / APIDoc response - look for something unique for APIdoc. + assertThat("response", response.getContentAsString(), containsStringIgnoringCase("javadoc")); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-proxy-webapp/src/test/resources/jetty-logging.properties b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-proxy-webapp/src/test/resources/jetty-logging.properties new file mode 100644 index 00000000000..bf725104bbd --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-proxy-webapp/src/test/resources/jetty-logging.properties @@ -0,0 +1,5 @@ +org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog +#org.eclipse.jetty.LEVEL=WARN +#org.eclipse.jetty.client.LEVEL=DEBUG +#org.eclipse.jetty.http.LEVEL=DEBUG +#org.eclipse.jetty.proxy.LEVEL=DEBUG diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-simple-webapp/pom.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-simple-webapp/pom.xml new file mode 100644 index 00000000000..df99d1f2c94 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-simple-webapp/pom.xml @@ -0,0 +1,26 @@ + + + + 4.0.0 + + org.eclipse.jetty.ee11.demos + jetty-ee11-demos + 12.1.0-SNAPSHOT + + jetty-ee11-demo-simple-webapp + war + EE11 :: Demo :: Simple WebApp + + + ${project.groupId}.simple + + + + + jakarta.servlet + jakarta.servlet-api + provided + + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-simple-webapp/src/main/config/modules/ee11-demo-simple.mod b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-simple-webapp/src/main/config/modules/ee11-demo-simple.mod new file mode 100644 index 00000000000..952e4dc2c63 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-simple-webapp/src/main/config/modules/ee11-demo-simple.mod @@ -0,0 +1,15 @@ +[description] +Demo Simple Webapp + +[environment] +ee11 + +[tags] +demo +webapp + +[depends] +ee11-deploy + +[files] +maven://org.eclipse.jetty.ee11.demos/jetty-ee11-demo-simple-webapp/${jetty.version}/war|webapps/ee11-demo-simple.war diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-simple-webapp/src/main/java/org/eclipse/jetty/ee11/demo/simple/HelloWorldServlet.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-simple-webapp/src/main/java/org/eclipse/jetty/ee11/demo/simple/HelloWorldServlet.java new file mode 100644 index 00000000000..3dabaa3cef5 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-simple-webapp/src/main/java/org/eclipse/jetty/ee11/demo/simple/HelloWorldServlet.java @@ -0,0 +1,32 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demo.simple; + +import java.io.IOException; + +import jakarta.servlet.Servlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +public class HelloWorldServlet extends HttpServlet +{ + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException + { + resp.getOutputStream().println("HelloWorld from Servlet-" + + HttpServlet.class.getPackage().getSpecificationVersion() + + " with " + Servlet.class); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-simple-webapp/src/main/webapp/WEB-INF/web.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-simple-webapp/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000000..e8011ec27e3 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-simple-webapp/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,24 @@ + + + + EE11 Demo Simple WebApp + + + + icon + image/vnd.microsoft.icon + + + + hello + org.eclipse.jetty.ee11.demo.simple.HelloWorldServlet + + + hello + /hello/* + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-simple-webapp/src/main/webapp/index.html b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-simple-webapp/src/main/webapp/index.html new file mode 100644 index 00000000000..3f07f11dccd --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-simple-webapp/src/main/webapp/index.html @@ -0,0 +1,6 @@ + + + +

Hello World EE11!

+ + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-simple-webapp/src/main/webapp/jetty.icon b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-simple-webapp/src/main/webapp/jetty.icon new file mode 100644 index 00000000000..54e2e610433 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-simple-webapp/src/main/webapp/jetty.icon differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-simple-webapp/src/main/webapp/jetty.png b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-simple-webapp/src/main/webapp/jetty.png new file mode 100644 index 00000000000..d579fffddfe Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-simple-webapp/src/main/webapp/jetty.png differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-simple-webapp/src/main/webapp/jetty.webp b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-simple-webapp/src/main/webapp/jetty.webp new file mode 100644 index 00000000000..2d1bfea3ef7 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-simple-webapp/src/main/webapp/jetty.webp differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-container-initializer/pom.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-container-initializer/pom.xml new file mode 100644 index 00000000000..45e3fb99c2b --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-container-initializer/pom.xml @@ -0,0 +1,42 @@ + + + 4.0.0 + + org.eclipse.jetty.ee11.demos + jetty-ee11-demos + 12.1.0-SNAPSHOT + ../../pom.xml + + jetty-ee11-demo-container-initializer + jar + EE11 :: Demo :: Servlet Spec :: ServletContainerInitializer Jar + + ${project.groupId}.sci + + + + jakarta.servlet + jakarta.servlet-api + provided + + + + + + org.apache.felix + maven-bundle-plugin + true + + + org.eclipse.jetty.ee11.demos.demo-servlet-container-initializer;singleton:=true + A bundle containing a ServletContainerInitializer for testing + osgi.extender; filter:="(osgi.extender=osgi.serviceloader.registrar)" + osgi.serviceloader; osgi.serviceloader=jakarta.servlet.ServletContainerInitializer + org.example.initializer;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}" + <_nouses>true + + + + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-container-initializer/src/main/java/org/example/initializer/Foo.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-container-initializer/src/main/java/org/example/initializer/Foo.java new file mode 100644 index 00000000000..43583aae1e9 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-container-initializer/src/main/java/org/example/initializer/Foo.java @@ -0,0 +1,27 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.example.initializer; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD}) +public @interface Foo +{ + int value(); +} + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-container-initializer/src/main/java/org/example/initializer/FooInitializer.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-container-initializer/src/main/java/org/example/initializer/FooInitializer.java new file mode 100644 index 00000000000..74f5dc47bf9 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-container-initializer/src/main/java/org/example/initializer/FooInitializer.java @@ -0,0 +1,96 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.example.initializer; + +import java.util.ArrayList; +import java.util.Set; + +import jakarta.servlet.ServletContainerInitializer; +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletContextEvent; +import jakarta.servlet.ServletContextListener; +import jakarta.servlet.ServletRegistration; +import jakarta.servlet.annotation.HandlesTypes; + +@HandlesTypes({jakarta.servlet.Servlet.class, Foo.class}) +public class FooInitializer implements ServletContainerInitializer +{ + public static class BarListener implements ServletContextListener + { + + @Override + public void contextInitialized(ServletContextEvent sce) + { + throw new IllegalStateException("BAR LISTENER CALLED!"); + } + + @Override + public void contextDestroyed(ServletContextEvent sce) + { + + } + } + + public static class FooListener implements ServletContextListener + { + + @Override + public void contextInitialized(ServletContextEvent sce) + { + if (sce.getServletContext().getAttribute("org.example.AnnotationTest.listenerTest") != null) + throw new IllegalStateException("FooListener already initialized"); + + //Can add a ServletContextListener from a ServletContainerInitializer + sce.getServletContext().setAttribute("org.example.AnnotationTest.listenerTest", Boolean.TRUE); + + //Can't add a ServletContextListener from a ServletContextListener + try + { + sce.getServletContext().addListener(new BarListener()); + sce.getServletContext().setAttribute("org.example.AnnotationTest.listenerRegoTest", Boolean.FALSE); + } + catch (UnsupportedOperationException e) + { + sce.getServletContext().setAttribute("org.example.AnnotationTest.listenerRegoTest", Boolean.TRUE); + } + catch (Exception e) + { + sce.getServletContext().setAttribute("org.example.AnnotationTest.listenerRegoTest", Boolean.FALSE); + } + } + + @Override + public void contextDestroyed(ServletContextEvent sce) + { + + } + } + + @Override + public void onStartup(Set> classes, ServletContext context) + { + if (context.getAttribute("org.example.Foo") != null) + throw new IllegalStateException("FooInitializer on Startup already called"); + + context.setAttribute("org.example.Foo", new ArrayList<>(classes)); + ServletRegistration.Dynamic reg = context.addServlet("AnnotationTest", "org.example.test.AnnotationTest"); + context.setAttribute("org.example.AnnotationTest.complete", (reg == null)); + context.addListener(new FooListener()); + + //test adding jsp file dynamically + ServletRegistration.Dynamic jspFile = context.addJspFile("dynamic.jsp", "/dynamic.jsp"); + context.setAttribute("org.example.jsp.file", (jspFile != null)); + jspFile.addMapping("/dynamicjsp/*"); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-container-initializer/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-container-initializer/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer new file mode 100644 index 00000000000..c072ba5d1a5 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-container-initializer/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer @@ -0,0 +1 @@ +org.example.initializer.FooInitializer diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/pom.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/pom.xml new file mode 100644 index 00000000000..423e4cf24d1 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/pom.xml @@ -0,0 +1,257 @@ + + + 4.0.0 + + org.eclipse.jetty.ee11.demos + jetty-ee11-demos + 12.1.0-SNAPSHOT + ../../pom.xml + + jetty-ee11-demo-spec-webapp + war + EE11 :: Demo :: Servlet Spec :: WebApp + + ${project.groupId}.spec.webapp + + + + + org.eclipse.jetty.ee11.demos + jetty-ee11-demo-container-initializer + + + org.eclipse.jetty.ee11.demos + jetty-ee11-demo-web-fragment + + + jakarta.annotation + jakarta.annotation-api + provided + + + jakarta.servlet + jakarta.servlet-api + provided + + + jakarta.transaction + jakarta.transaction-api + provided + + + org.eclipse.jetty + jetty-client + test + + + org.eclipse.jetty + jetty-slf4j-impl + test + + + org.eclipse.jetty.ee11 + jetty-ee11-annotations + test + + + org.eclipse.jetty.ee11 + jetty-ee11-webapp + test + + + org.eclipse.jetty.ee11.demos + jetty-ee11-demo-mock-resources + test + + + + + + + org.eclipse.jetty.ee11 + jetty-ee11-maven-plugin + ${project.version} + + 10 + src/main/templates/plugin-context.xml + + src/main/webapp + src/main/webapp/WEB-INF/web.xml + /test-spec + .*/jakarta.servlet-api-[^/]*\.jar$ + true + ${basedir}/src/main/webapp/WEB-INF/jetty-env.xml + + + + Test Realm + src/etc/realm.properties + + + + + + org.eclipse.jetty.ee11 + jetty-ee11-jndi + ${project.version} + + + org.eclipse.jetty.ee11.demos + jetty-ee11-demo-mock-resources + ${project.version} + + + + + + + + + org.apache.felix + maven-bundle-plugin + true + + + war + + + Test Webapp for Servlet 6.0 Features + jakarta.transaction*;version="2.0.0", jakarta.servlet*;version="[6,7)", org.eclipse.jetty*;version="[$(version;===;${parsedVersion.osgiVersion}),$(version;==+;${parsedVersion.osgiVersion}))", org.eclipse.jetty.webapp;version="[$(version;===;${parsedVersion.osgiVersion}),$(version;==+;${parsedVersion.osgiVersion}))";resolution:="optional", org.eclipse.jetty.plus.jndi;version="[$(version;===;${parsedVersion.osgiVersion}),$(version;==+;${parsedVersion.osgiVersion}))";resolution:="optional", org.example;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}", * + <_nouses> + org.example.test;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}";-noimport:=true + /ee11-demo-spec + .,WEB-INF/classes,WEB-INF/lib + META-INF/plugin-context.xml + ee11 + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + web-bundle-assembly + + single + + package + + + src/main/assembly/web-bundle.xml + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + unpack-jetty-ee11-demo-container-initializer + + copy + + process-test-resources + + + + org.eclipse.jetty.ee11.demos + jetty-ee11-demo-container-initializer + jar + false + ${project.build.directory}/ + jetty-ee11-demo-container-initializer.jar + + + true + + + + unpack-jetty-ee11-demo-web-fragment + + copy + + process-test-resources + + + + org.eclipse.jetty.ee11.demos + jetty-ee11-demo-web-fragment + jar + false + ${project.build.directory}/ + jetty-ee11-demo-web-fragment.jar + + + true + + + + + copy + + package + + + + org.eclipse.jetty.ee11.demos + jetty-ee11-demo-mock-resources + ${project.version} + jar + ** + true + ${project.build.directory}/lib/jndi + + + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + enforce-java + + enforce + + + + + + org.eclipse.jetty:jetty-util + + + + + + + + + + maven-war-plugin + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + target + + plugin-context.xml + + META-INF + + + + + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/etc/realm.properties b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/etc/realm.properties new file mode 100644 index 00000000000..9d88b852b7f --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/etc/realm.properties @@ -0,0 +1,21 @@ +# +# This file defines users passwords and roles for a HashUserRealm +# +# The format is +# : [, ...] +# +# Passwords may be clear text, obfuscated or checksummed. The class +# org.eclipse.util.Password should be used to generate obfuscated +# passwords or password checksums +# +# If DIGEST Authentication is used, the password must be in a recoverable +# format, either plain text or OBF:. +# +jetty: MD5:164c88b302622e17050af52c89945d44,user +admin: CRYPT:adpexzg3FUZAk,server-administrator,content-administrator,admin,user +other: OBF:1xmk1w261u9r1w1c1xmq,user +plain: plain,user +user: password,user + +# This entry is for digest auth. The credential is a MD5 hash of username:realmname:password +digest: MD5:6e120743ad67abfbc385bc2bb754e297,user diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/assembly/web-bundle.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/assembly/web-bundle.xml new file mode 100644 index 00000000000..226f074f420 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/assembly/web-bundle.xml @@ -0,0 +1,25 @@ + + webbundle + + jar + + false + + + ${basedir}/${project.build.directory}/${project.build.finalName}/ + + + **/*.* + + + + ${basedir}/src/main/templates + META-INF + + **/plugin-context.xml + + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/config/modules/demo.d/ee11-demo-spec.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/config/modules/demo.d/ee11-demo-spec.xml new file mode 100644 index 00000000000..4145cae3085 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/config/modules/demo.d/ee11-demo-spec.xml @@ -0,0 +1,34 @@ + + + + + /ee11-test-spec + /ee11-demo-spec.war + + true + + + + + + + + + + + + + maxAmount + 100 + true + + + + + jdbc/mydatasource + + + + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/config/modules/ee11-demo-spec.mod b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/config/modules/ee11-demo-spec.mod new file mode 100644 index 00000000000..de60cc8acab --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/config/modules/ee11-demo-spec.mod @@ -0,0 +1,24 @@ +# DO NOT EDIT THIS FILE - See: https://eclipse.dev/jetty/documentation/ + +[description] +Download and deploy the Test Spec webapp demo. + +[environment] +ee11 + +[tags] +demo +webapp + +[depends] +deploy +jdbc +ee11-jsp +ee11-annotations +ext +demo-realm +ee11-demo-mock-resources + +[files] +basehome:modules/demo.d/ee11-demo-spec.xml|webapps/ee11-demo-spec.xml +maven://org.eclipse.jetty.ee11.demos/jetty-ee11-demo-spec-webapp/${jetty.version}/war|webapps/ee11-demo-spec.war diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/java/org/example/test/AnnotatedListener.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/java/org/example/test/AnnotatedListener.java new file mode 100644 index 00000000000..8a6b128c835 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/java/org/example/test/AnnotatedListener.java @@ -0,0 +1,175 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.example.test; + +import java.util.logging.Logger; + +import jakarta.annotation.Resource; +import jakarta.servlet.ServletContextAttributeEvent; +import jakarta.servlet.ServletContextAttributeListener; +import jakarta.servlet.ServletContextEvent; +import jakarta.servlet.ServletContextListener; +import jakarta.servlet.ServletRequestAttributeEvent; +import jakarta.servlet.ServletRequestAttributeListener; +import jakarta.servlet.ServletRequestEvent; +import jakarta.servlet.ServletRequestListener; +import jakarta.servlet.annotation.WebListener; +import jakarta.servlet.http.HttpSessionActivationListener; +import jakarta.servlet.http.HttpSessionAttributeListener; +import jakarta.servlet.http.HttpSessionBindingEvent; +import jakarta.servlet.http.HttpSessionEvent; +import jakarta.servlet.http.HttpSessionListener; + +@WebListener +public class AnnotatedListener implements HttpSessionListener, + HttpSessionAttributeListener, + HttpSessionActivationListener, + ServletContextListener, + ServletContextAttributeListener, + ServletRequestListener, + ServletRequestAttributeListener +{ + private static final Logger LOG = Logger.getLogger(AnnotatedListener.class.getName()); + + @Resource(mappedName = "maxAmount") + private Double maxAmount; + + @Override + public void attributeAdded(HttpSessionBindingEvent se) + { + LOG.fine("attributedAdded " + se); + } + + @Override + public void attributeAdded(ServletContextAttributeEvent scae) + { + LOG.fine("attributeAdded " + scae); + } + + @Override + public void attributeAdded(ServletRequestAttributeEvent srae) + { + LOG.fine("attributeAdded " + srae); + } + + @Override + public void attributeRemoved(HttpSessionBindingEvent se) + { + LOG.fine("attributeRemoved " + se); + } + + @Override + public void attributeRemoved(ServletContextAttributeEvent scab) + { + LOG.fine("attributeRemoved " + scab); + } + + @Override + public void attributeRemoved(ServletRequestAttributeEvent srae) + { + LOG.fine("attributeRemoved " + srae); + } + + @Override + public void attributeReplaced(HttpSessionBindingEvent se) + { + LOG.fine("attributeReplaced " + se); + } + + @Override + public void attributeReplaced(ServletContextAttributeEvent scab) + { + LOG.fine("attributeReplaced " + scab); + } + + @Override + public void attributeReplaced(ServletRequestAttributeEvent srae) + { + LOG.fine("attributeReplaced " + srae); + } + + @Override + public void contextDestroyed(ServletContextEvent sce) + { + LOG.fine("contextDestroyed " + sce); + } + + @Override + public void contextInitialized(ServletContextEvent sce) + { + if (sce.getServletContext().getAttribute("org.example.AnnotationTest.sclInjectWebListenerTest") != null) + throw new IllegalStateException("AnnotatedListener already initialized"); + + sce.getServletContext().setAttribute("org.example.AnnotationTest.sclInjectWebListenerTest", maxAmount != null); + + boolean setSessionTimeout; + try + { + sce.getServletContext().setSessionTimeout(180); + setSessionTimeout = true; + } + catch (Exception e) + { + setSessionTimeout = false; + } + sce.getServletContext().setAttribute("org.example.AnnotationTest.sclSetSessionTimeout", setSessionTimeout); + + boolean getSessionTimeout; + try + { + getSessionTimeout = (sce.getServletContext().getSessionTimeout() == 180); + } + catch (Exception e) + { + getSessionTimeout = false; + } + sce.getServletContext().setAttribute("org.example.AnnotationTest.sclGetSessionTimeout", getSessionTimeout); + } + + @Override + public void requestDestroyed(ServletRequestEvent sre) + { + LOG.fine("requestDestroyed " + sre); + } + + @Override + public void requestInitialized(ServletRequestEvent sre) + { + LOG.fine("requestInitialized " + sre); + } + + @Override + public void sessionCreated(HttpSessionEvent se) + { + LOG.fine("sessionCreated " + se); + } + + @Override + public void sessionDestroyed(HttpSessionEvent se) + { + LOG.fine("sessionDestroyed " + se); + } + + @Override + public void sessionDidActivate(HttpSessionEvent se) + { + LOG.fine("sessionDidActivate " + se); + } + + @Override + public void sessionWillPassivate(HttpSessionEvent se) + { + LOG.fine("sessionWillPassivate " + se); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/java/org/example/test/AnnotationTest.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/java/org/example/test/AnnotationTest.java new file mode 100644 index 00000000000..25e8b3666dd --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/java/org/example/test/AnnotationTest.java @@ -0,0 +1,349 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.example.test; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import javax.naming.InitialContext; +import javax.sql.DataSource; + +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; +import jakarta.annotation.Resource; +import jakarta.annotation.security.DeclareRoles; +import jakarta.annotation.security.RunAs; +import jakarta.servlet.ServletConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.annotation.WebInitParam; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.transaction.UserTransaction; + +/** + * AnnotationTest + * + * Use Annotations from within Jetty. + * + * Also, use servlet spec 2.5 resource injection and lifecycle callbacks from within the web.xml + * to set up some of the JNDI resources. + */ + +@RunAs("special") +@WebServlet(urlPatterns = {"/", "/test/*"}, name = "AnnotationTest", initParams = { + @WebInitParam(name = "fromAnnotation", value = "xyz") +}) +@DeclareRoles({"user", "client"}) +public class AnnotationTest extends HttpServlet +{ + static List __HandlesTypes; + private String postConstructResult = ""; + private String dsResult = ""; + private String envResult = ""; + private String envLookupResult = ""; + private String envResult2 = ""; + private String envLookupResult2 = ""; + private String envResult3 = ""; + private String envLookupResult3 = ""; + private String dsLookupResult = ""; + private String txResult = ""; + private String txLookupResult = ""; + private DataSource myDS; + private ServletConfig config; + + @Resource(mappedName = "UserTransaction") + private UserTransaction myUserTransaction; + + @Resource(mappedName = "maxAmount") + private Double maxAmount; + + @Resource(name = "someAmount") + private Double minAmount; + + @Resource + private Double avgAmount; + + @Resource(mappedName = "jdbc/mydatasource") + public void setMyDatasource(DataSource ds) + { + myDS = ds; + } + + @PostConstruct + private void myPostConstructMethod() + { + postConstructResult = "PASS"; + try + { + dsResult = (myDS == null ? "FAIL" : "myDS=" + myDS.toString() + ""); + } + catch (Exception e) + { + dsResult = "FAIL: " + e; + } + + envResult = (maxAmount == null ? "FAIL
" : "maxAmount=" + maxAmount.toString() + ""); + + try + { + InitialContext ic = new InitialContext(); + envLookupResult = "java:comp/env/org.example.test.AnnotationTest/maxAmount=" + ic.lookup("java:comp/env/org.example.test.AnnotationTest/maxAmount"); + } + catch (Exception e) + { + envLookupResult = "FAIL: " + e; + } + + envResult2 = (minAmount == null ? "FAIL" : "minAmount=" + minAmount.toString() + ""); + try + { + InitialContext ic = new InitialContext(); + envLookupResult2 = "java:comp/env/someAmount=" + ic.lookup("java:comp/env/someAmount"); + } + catch (Exception e) + { + envLookupResult2 = "FAIL: " + e; + } + envResult3 = (minAmount == null ? "FAIL" : "avgAmount=" + avgAmount.toString() + ""); + try + { + InitialContext ic = new InitialContext(); + envLookupResult3 = "java:comp/env/org.example.test.AnnotationTest/avgAmount=" + ic.lookup("java:comp/env/org.example.test.AnnotationTest/avgAmount"); + } + catch (Exception e) + { + envLookupResult3 = "FAIL: " + e; + } + + try + { + InitialContext ic = new InitialContext(); + dsLookupResult = "java:comp/env/org.example.test.AnnotationTest/myDatasource=" + ic.lookup("java:comp/env/org.example.test.AnnotationTest/myDatasource"); + } + catch (Exception e) + { + dsLookupResult = "FAIL: " + e; + } + + txResult = (myUserTransaction == null ? "FAIL" : "myUserTransaction=" + myUserTransaction + ""); + try + { + InitialContext ic = new InitialContext(); + txLookupResult = "java:comp/env/org.example.test.AnnotationTest/myUserTransaction=" + ic.lookup("java:comp/env/org.example.test.AnnotationTest/myUserTransaction"); + } + catch (Exception e) + { + txLookupResult = "FAIL: " + e; + } + } + + @PreDestroy + private void myPreDestroyMethod() + { + } + + @Override + public void init(ServletConfig config) throws ServletException + { + super.init(config); + this.config = config; + } + + @Override + public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + doGet(request, response); + } + + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + try + { + response.setContentType("text/html"); + ServletOutputStream out = response.getOutputStream(); + out.println(""); + out.println(""); + out.println(""); + out.println("

Results

"); + + out.println("

Context Defaults

"); + out.println("

default-context-path: " + + (request.getServletContext().getAttribute("default-context-path") != null ? "PASS" : "FAIL") + + "

"); + out.println("

request-character-encoding: " + + ("utf-8".equals(request.getServletContext().getAttribute("request-character-encoding")) ? "PASS" : "FAIL") + + "

"); + out.println("

response-character-encoding: " + + ("utf-8".equals(request.getServletContext().getAttribute("response-character-encoding")) ? "PASS" : "FAIL") + + "

"); + + out.println("

Init Params from Annotation

"); + out.println("
");
+            out.println("initParams={@WebInitParam(name=\"fromAnnotation\", value=\"xyz\")}");
+            out.println("
"); + out.println("

Result: " + ("xyz".equals(config.getInitParameter("fromAnnotation")) ? "PASS" : "FAIL") + "

"); + + out.println("

Init Params from web-fragment

"); + out.println("
");
+            out.println("extra1=123, extra2=345");
+            out.println("
"); + boolean fragInitParamResult = "123".equals(config.getInitParameter("extra1")) && "345".equals(config.getInitParameter("extra2")); + out.println("

Result: " + (fragInitParamResult ? "PASS" : "FAIL") + "

"); + + __HandlesTypes = Arrays.asList("jakarta.servlet.GenericServlet", + "jakarta.servlet.http.HttpServlet", + "org.example.test.AsyncListenerServlet", + "org.example.test.ClassLoaderServlet", + "org.example.test.AnnotationTest", + "org.example.test.RoleAnnotationTest", + "org.example.test.MultiPartTest", + "org.example.fragment.FragmentServlet", + "org.example.test.TestListener", + "org.example.test.SecuredServlet", + "org.example.test.Bar"); + out.println("

@ContainerInitializer

"); + out.println("
");
+            out.println("@HandlesTypes({jakarta.servlet.Servlet.class, Foo.class})");
+            out.println("
"); + out.print("

Result: "); + List classes = (List)config.getServletContext().getAttribute("org.example.Foo"); + List classNames = new ArrayList(); + if (classes != null) + { + for (Class c : classes) + { + classNames.add(c.getName()); + out.print(c.getName() + " "); + } + + if (classNames.size() != __HandlesTypes.size()) + out.println("
FAIL"); + else if (!classNames.containsAll(__HandlesTypes)) + out.println("
FAIL"); + else + out.println("
PASS"); + } + else + out.print("
FAIL (No such attribute org.example.Foo)"); + out.println("

"); + + out.println("

Complete Servlet Registration

"); + Boolean complete = (Boolean)config.getServletContext().getAttribute("org.example.AnnotationTest.complete"); + out.println("

Result: " + (complete.booleanValue() ? "PASS" : "FAIL") + "

"); + + out.println("

ServletContextListener Programmatic Registration from ServletContainerInitializer

"); + Boolean programmaticListener = (Boolean)config.getServletContext().getAttribute("org.example.AnnotationTest.listenerTest"); + out.println("

Result: " + (programmaticListener.booleanValue() ? "PASS" : "FAIL") + "

"); + + out.println("

ServletContextListener Programmatic Registration Prevented from ServletContextListener

"); + Boolean programmaticListenerPrevention = (Boolean)config.getServletContext().getAttribute("org.example.AnnotationTest.listenerRegoTest"); + out.println("

Result: " + (programmaticListenerPrevention.booleanValue() ? "PASS" : "FAIL") + "

"); + + out.println("

ServletContextListener Registration Prevented from ServletContextListener

"); + Boolean webListenerPrevention = (Boolean)config.getServletContext().getAttribute("org.example.AnnotationTest.sclFromSclRegoTest"); + out.println("

Result: " + (webListenerPrevention.booleanValue() ? "PASS" : "FAIL") + "

"); + + out.println("

Add Jsp File Registration

"); + complete = (Boolean)config.getServletContext().getAttribute("org.example.jsp.file"); + out.println("

Result: " + (complete.booleanValue() ? "PASS" : "FAIL") + "

"); + + out.println("

ServletContextListener In web.xml Injected

"); + Boolean listenerInject = (Boolean)config.getServletContext().getAttribute("org.example.AnnotationTest.sclInjectTest"); + out.println("

Result: " + (listenerInject.booleanValue() ? "PASS" : "FAIL") + "

"); + + out.println("

ServletContextListener as @WebListener Injected

"); + Boolean annotatedListenerInject = (Boolean)config.getServletContext().getAttribute("org.example.AnnotationTest.sclInjectWebListenerTest"); + out.println("

Result: " + (annotatedListenerInject.booleanValue() ? "PASS" : "FAIL") + "

"); + + out.println("

ServletContextListener as @WebListener Get/Set Session Timeout

"); + out.println("

getSessionTimeout Result: " + + ((Boolean)config.getServletContext().getAttribute("org.example.AnnotationTest.sclGetSessionTimeout") ? "PASS" : "FAIL") + "

"); + out.println("

setSessionTimeout Result: " + + ((Boolean)config.getServletContext().getAttribute("org.example.AnnotationTest.sclSetSessionTimeout") ? "PASS" : "FAIL") + "

"); + + out.println("

Programmatic Listener Injected

"); + Boolean programListenerInject = (Boolean)config.getServletContext().getAttribute("org.example.AnnotationTest.programListenerInjectTest"); + out.println("

Result: " + (programListenerInject.booleanValue() ? "PASS" : "FAIL") + "

"); + + out.println("

Invalid Type for Listener Detection

"); + Boolean badListener = (Boolean)config.getServletContext().getAttribute("org.example.AnnotationTest.invalidListenerRegoTest"); + out.println("

Result: " + (badListener.booleanValue() ? "PASS" : "FAIL") + "

"); + + out.println("

@PostConstruct Callback

"); + out.println("
");
+            out.println("@PostConstruct");
+            out.println("private void myPostConstructMethod ()");
+            out.println("{}");
+            out.println("
"); + out.println("

Result: " + postConstructResult + "

"); + + out.println("

@Resource Injection for DataSource

"); + out.println("
");
+            out.println("@Resource(mappedName=\"jdbc/mydatasource\");");
+            out.println("public void setMyDatasource(DataSource ds)");
+            out.println("{");
+            out.println("myDS=ds;");
+            out.println("}");
+            out.println("
"); + out.println("

Result: " + dsResult + ""); + out.println("
JNDI Lookup Result: " + dsLookupResult + "

"); + + out.println("

@Resource Injection for env-entry

"); + out.println("
");
+            out.println("@Resource(mappedName=\"maxAmount\")");
+            out.println("private Double maxAmount;");
+            out.println("@Resource(name=\"minAmount\")");
+            out.println("private Double minAmount;");
+            out.println("
"); + if (maxAmount == null) + out.println("

Result: " + envResult + ": FAIL"); + else + out.println("

Result: " + envResult + ": " + (maxAmount.compareTo(55D) == 0 ? " PASS" : " FAIL") + ""); + out.println("
JNDI Lookup Result: " + envLookupResult + ""); + + if (minAmount == null) + out.println("

Result: " + envResult2 + ": FAIL"); + else + out.println("
Result: " + envResult2 + ": " + (minAmount.compareTo(0.99D) == 0 ? " PASS" : " FAIL") + ""); + out.println("
JNDI Lookup Result: " + envLookupResult2 + ""); + + if (avgAmount == null) + out.println("

Result: " + envResult3 + ": FAIL"); + else + out.println("
Result: " + envResult3 + ": " + (avgAmount.compareTo(1.25D) == 0 ? " PASS" : " FAIL") + ""); + out.println("
JNDI Lookup Result: " + envLookupResult3 + "

"); + + out.println("

@Resource Injection for UserTransaction

"); + out.println("
");
+            out.println("@Resource(mappedName=\"UserTransaction\")");
+            out.println("private UserTransaction myUserTransaction;");
+            out.println("
"); + out.println("

Result: " + txResult + ""); + out.println("
JNDI Lookup Result: " + txLookupResult + "

"); + + out.println(""); + out.println(""); + out.flush(); + } + catch (Exception e) + { + throw new ServletException(e); + } + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/java/org/example/test/AsyncListenerServlet.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/java/org/example/test/AsyncListenerServlet.java new file mode 100644 index 00000000000..c84f7b0d5a2 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/java/org/example/test/AsyncListenerServlet.java @@ -0,0 +1,114 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.example.test; + +import java.io.IOException; +import java.io.PrintWriter; + +import jakarta.annotation.PostConstruct; +import jakarta.annotation.Resource; +import jakarta.servlet.AsyncContext; +import jakarta.servlet.AsyncEvent; +import jakarta.servlet.AsyncListener; +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +@WebServlet(urlPatterns = "/asy/*", asyncSupported = true) +public class AsyncListenerServlet extends HttpServlet +{ + public static class MyAsyncListener implements AsyncListener + { + @Resource(mappedName = "maxAmount") + private Double maxAmount; + + boolean postConstructCalled = false; + boolean resourceInjected = false; + + @PostConstruct + public void postConstruct() + { + postConstructCalled = true; + resourceInjected = (maxAmount != null); + } + + public boolean isPostConstructCalled() + { + return postConstructCalled; + } + + public boolean isResourceInjected() + { + return resourceInjected; + } + + @Override + public void onComplete(AsyncEvent event) throws IOException + { + } + + @Override + public void onTimeout(AsyncEvent event) throws IOException + { + } + + @Override + public void onError(AsyncEvent event) throws IOException + { + } + + @Override + public void onStartAsync(AsyncEvent event) throws IOException + { + } + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException + { + AsyncContext asyncContext = req.startAsync(); + MyAsyncListener listener = asyncContext.createListener(MyAsyncListener.class); + + PrintWriter writer = resp.getWriter(); + writer.println(""); + writer.println(""); + writer.println(""); + writer.println("

AsyncListener

"); + writer.println("
");
+        writer.println("

@PostConstruct Callback

"); + writer.println("
");
+        writer.println("@PostConstruct");
+        writer.println("private void postConstruct ()");
+        writer.println("{}");
+        writer.println("
"); + writer.println("
Result: " + (listener.isPostConstructCalled() ? "PASS" : "FAIL") + ""); + + writer.println("

@Resource Injection for env-entry

"); + writer.println("
");
+        writer.println("@Resource(mappedName=\"maxAmount\")");
+        writer.println("private Double maxAmount;");
+        writer.println("
"); + writer.println("
Result: " + (listener.isResourceInjected() ? " PASS" : " FAIL") + ""); + + writer.println(""); + writer.println(""); + writer.flush(); + writer.close(); + + asyncContext.complete(); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/java/org/example/test/Bar.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/java/org/example/test/Bar.java new file mode 100644 index 00000000000..172f5ad1fae --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/java/org/example/test/Bar.java @@ -0,0 +1,22 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.example.test; + +public class Bar +{ + @org.example.initializer.Foo(2) + public void someMethod() + { + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/java/org/example/test/ClassLoaderServlet.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/java/org/example/test/ClassLoaderServlet.java new file mode 100644 index 00000000000..9580653e288 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/java/org/example/test/ClassLoaderServlet.java @@ -0,0 +1,125 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.example.test; + +import java.io.PrintWriter; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.security.CodeSource; +import java.security.ProtectionDomain; + +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +@WebServlet(urlPatterns = "/classloader") +public class ClassLoaderServlet extends HttpServlet +{ + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException + { + try + { + PrintWriter writer = resp.getWriter(); + writer.println(""); + writer.println(""); + writer.println(""); + writer.println("

ClassLoader Isolation Test

"); + + // TODO uncomment the following once 9.4.19 is released with a fix for #3726 + /* + Class webappIO = IO.class; + URI webappURI = getLocationOfClass(webappIO); + String webappVersion = webappIO.getPackage().getImplementationVersion(); + Class serverIO = req.getServletContext().getClass().getClassLoader().loadClass("org.eclipse.jetty.util.IO"); + URI serverURI = getLocationOfClass(serverIO); + String serverVersion = serverIO.getPackage().getImplementationVersion(); + + writer.printf("

Webapp loaded org.eclipse.jetty.util.IO(%s) from %s%n",webappVersion,webappURI); + writer.printf("
Server loaded org.eclipse.jetty.util.IO(%s) from %s%n",serverVersion, serverURI); + if (webappVersion.equals(serverVersion)) + writer.println("
Version Result: FAIL"); + else + writer.println("
Version Result: PASS"); + if (webappURI.equals(serverURI)) + writer.println("
URI Result: FAIL

"); + else + writer.println("
URI Result: PASS

"); + */ + + writer.println(""); + writer.println(""); + writer.flush(); + writer.close(); + } + catch (Exception e) + { + throw new ServletException(e); + } + } + + public static URI getLocationOfClass(Class clazz) + { + try + { + ProtectionDomain domain = clazz.getProtectionDomain(); + if (domain != null) + { + CodeSource source = domain.getCodeSource(); + if (source != null) + { + URL location = source.getLocation(); + + if (location != null) + return location.toURI(); + } + } + + String resourceName = clazz.getName().replace('.', '/') + ".class"; + ClassLoader loader = clazz.getClassLoader(); + URL url = (loader == null ? ClassLoader.getSystemClassLoader() : loader).getResource(resourceName); + if (url != null) + { + return getJarSource(url.toURI()); + } + } + catch (URISyntaxException e) + { + throw new RuntimeException(e); + } + return null; + } + + public static URI getJarSource(URI uri) + { + try + { + if (!"jar".equals(uri.getScheme())) + return uri; + // Get SSP (retaining encoded form) + String s = uri.getRawSchemeSpecificPart(); + int bangSlash = s.indexOf("!/"); + if (bangSlash >= 0) + s = s.substring(0, bangSlash); + return new URI(s); + } + catch (URISyntaxException e) + { + throw new IllegalArgumentException(e); + } + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/java/org/example/test/MultiPartTest.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/java/org/example/test/MultiPartTest.java new file mode 100644 index 00000000000..1ede0103299 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/java/org/example/test/MultiPartTest.java @@ -0,0 +1,165 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.example.test; + +import java.io.IOException; +import java.util.Collection; + +import jakarta.servlet.ServletConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.annotation.MultipartConfig; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.Part; + +/** + * MultiPartTest + * + * Test Servlet 3.0 MultiPart Mime handling. + */ + +@MultipartConfig(location = "foo/bar", maxFileSize = 10240, maxRequestSize = -1, fileSizeThreshold = 2048) +public class MultiPartTest extends HttpServlet +{ + private ServletConfig config; + + @Override + public void init(ServletConfig config) throws ServletException + { + super.init(config); + this.config = config; + } + + @Override + public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + try + { + response.setContentType("text/html"); + ServletOutputStream out = response.getOutputStream(); + out.println(""); + out.println(""); + out.println(""); + out.println("

Results

"); + out.println("

"); + + Collection parts = request.getParts(); + out.println("Parts: " + parts.size() + "
"); + for (Part p : parts) + { + out.println("
PartName: " + sanitizeXmlString(p.getName())); + out.println("
Size: " + p.getSize()); + String contentType = p.getContentType(); + out.println("
ContentType: " + contentType); + } + out.println(""); + out.println(""); + out.flush(); + } + catch (ServletException e) + { + throw e; + } + catch (Exception e) + { + throw new ServletException(e); + } + } + + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + try + { + response.setContentType("text/html"); + ServletOutputStream out = response.getOutputStream(); + out.println(""); + out.println(""); + out.println("

Use a POST Instead

"); + out.println(""); + out.println(""); + out.flush(); + } + catch (Exception e) + { + throw new ServletException(e); + } + } + + public static String sanitizeXmlString(String html) + { + if (html == null) + return null; + + int i = 0; + + // Are there any characters that need sanitizing? + loop: + for (; i < html.length(); i++) + { + char c = html.charAt(i); + switch (c) + { + case '&': + case '<': + case '>': + case '\'': + case '"': + break loop; + default: + if (Character.isISOControl(c) && !Character.isWhitespace(c)) + break loop; + } + } + // No characters need sanitizing, so return original string + if (i == html.length()) + return html; + + // Create builder with OK content so far + StringBuilder out = new StringBuilder(html.length() * 4 / 3); + out.append(html, 0, i); + + // sanitize remaining content + for (; i < html.length(); i++) + { + char c = html.charAt(i); + switch (c) + { + case '&': + out.append("&"); + break; + case '<': + out.append("<"); + break; + case '>': + out.append(">"); + break; + case '\'': + out.append("'"); + break; + case '"': + out.append("""); + break; + default: + if (Character.isISOControl(c) && !Character.isWhitespace(c)) + out.append('?'); + else + out.append(c); + } + } + return out.toString(); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/java/org/example/test/RoleAnnotationTest.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/java/org/example/test/RoleAnnotationTest.java new file mode 100644 index 00000000000..d4c01483129 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/java/org/example/test/RoleAnnotationTest.java @@ -0,0 +1,84 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.example.test; + +import java.io.IOException; + +import jakarta.annotation.security.DeclareRoles; +import jakarta.servlet.ServletConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +/** + * RoleAnnotationTest + * + * Use DeclareRolesAnnotations from within Jetty. + */ +@DeclareRoles({"server-administrator", "user"}) +public class RoleAnnotationTest extends HttpServlet +{ + private ServletConfig _config; + + @Override + public void init(ServletConfig config) throws ServletException + { + super.init(config); + _config = config; + } + + @Override + public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + doGet(request, response); + } + + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + try + { + response.setContentType("text/html"); + ServletOutputStream out = response.getOutputStream(); + out.println(""); + out.println(""); + out.println("

Jetty DeclareRoles Annotation Results

"); + out.println(""); + + out.println("

Roles

"); + boolean result = request.isUserInRole("other"); + out.println("
Result: isUserInRole(\"other\")=" + result + ":" + (result == false ? " PASS" : " FAIL") + ""); + + result = request.isUserInRole("manager"); + out.println("
Result: isUserInRole(\"manager\")=" + result + ":" + (result ? " PASS" : " FAIL") + ""); + result = request.isUserInRole("user"); + out.println("
Result: isUserInRole(\"user\")=" + result + ":" + (result ? " PASS" : " FAIL") + ""); + String context = _config.getServletContext().getContextPath(); + if (!context.endsWith("/")) + context += "/"; + + out.println("

Logout

"); + + out.println(""); + out.println(""); + out.flush(); + } + catch (Exception e) + { + throw new ServletException(e); + } + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/java/org/example/test/SecuredServlet.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/java/org/example/test/SecuredServlet.java new file mode 100644 index 00000000000..be251d97ad9 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/java/org/example/test/SecuredServlet.java @@ -0,0 +1,53 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.example.test; + +import java.io.IOException; +import java.io.PrintWriter; + +import jakarta.servlet.annotation.HttpConstraint; +import jakarta.servlet.annotation.ServletSecurity; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +@WebServlet(urlPatterns = "/sec/*") +@ServletSecurity(@HttpConstraint(rolesAllowed = "admin")) +public class SecuredServlet extends HttpServlet +{ + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) + throws IOException + { + PrintWriter writer = resp.getWriter(); + writer.println(""); + writer.println(""); + writer.println(""); + writer.println("

@ServletSecurity

"); + writer.println("
");
+        writer.println("@ServletSecurity");
+        writer.println("public class SecuredServlet");
+        writer.println("
"); + writer.println("

Result: PASS

"); + String context = getServletConfig().getServletContext().getContextPath(); + if (!context.endsWith("/")) + context += "/"; + writer.println("

Logout

"); + writer.println(""); + writer.println(""); + writer.flush(); + writer.close(); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/java/org/example/test/TestListener.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/java/org/example/test/TestListener.java new file mode 100644 index 00000000000..76419ed62e8 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/java/org/example/test/TestListener.java @@ -0,0 +1,221 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.example.test; + +import java.util.EventListener; +import java.util.logging.Logger; + +import jakarta.annotation.Resource; +import jakarta.servlet.ServletContextAttributeEvent; +import jakarta.servlet.ServletContextAttributeListener; +import jakarta.servlet.ServletContextEvent; +import jakarta.servlet.ServletContextListener; +import jakarta.servlet.ServletRequestAttributeEvent; +import jakarta.servlet.ServletRequestAttributeListener; +import jakarta.servlet.ServletRequestEvent; +import jakarta.servlet.ServletRequestListener; +import jakarta.servlet.annotation.WebListener; +import jakarta.servlet.http.HttpSessionActivationListener; +import jakarta.servlet.http.HttpSessionAttributeListener; +import jakarta.servlet.http.HttpSessionBindingEvent; +import jakarta.servlet.http.HttpSessionEvent; +import jakarta.servlet.http.HttpSessionIdListener; +import jakarta.servlet.http.HttpSessionListener; + +@org.example.initializer.Foo(1) +@WebListener +public class TestListener implements HttpSessionListener, + HttpSessionAttributeListener, + HttpSessionActivationListener, + ServletContextListener, + ServletContextAttributeListener, + ServletRequestListener, + ServletRequestAttributeListener +{ + private static final Logger LOG = Logger.getLogger(TestListener.class.getName()); + @Resource(mappedName = "maxAmount") + private Double maxAmount; + + @Override + public void attributeAdded(HttpSessionBindingEvent se) + { + LOG.fine("attributeAdded " + se); + } + + @Override + public void attributeAdded(ServletContextAttributeEvent scab) + { + LOG.fine("attributeAdded " + scab); + } + + @Override + public void attributeAdded(ServletRequestAttributeEvent srae) + { + LOG.fine("attributeAdded " + srae); + } + + @Override + public void attributeRemoved(HttpSessionBindingEvent se) + { + LOG.fine("attributeRemoved " + se); + } + + @Override + public void attributeRemoved(ServletContextAttributeEvent scab) + { + LOG.fine("attributeRemoved " + scab); + } + + @Override + public void attributeRemoved(ServletRequestAttributeEvent srae) + { + LOG.fine("attributeRemoved " + srae); + } + + @Override + public void attributeReplaced(HttpSessionBindingEvent se) + { + LOG.fine("attributeReplaced " + se); + } + + @Override + public void attributeReplaced(ServletContextAttributeEvent scab) + { + LOG.fine("attributeReplaced " + scab); + } + + @Override + public void attributeReplaced(ServletRequestAttributeEvent srae) + { + LOG.fine("attributeReplaced " + srae); + } + + @Override + public void contextDestroyed(ServletContextEvent sce) + { + LOG.fine("contextDestroyed " + sce); + } + + @Override + public void contextInitialized(ServletContextEvent sce) + { + if (sce.getServletContext().getAttribute("org.example.AnnotationTest.sclInjectTest") != null) + throw new IllegalStateException("TestListener already initialized"); + + sce.getServletContext().setAttribute("org.example.AnnotationTest.sclInjectTest", maxAmount != null); + + // Can't add a ServletContextListener from a ServletContextListener even if it is declared in web.xml + try + { + sce.getServletContext().addListener(new NaughtyServletContextListener()); + sce.getServletContext().setAttribute("org.example.AnnotationTest.sclFromSclRegoTest", Boolean.FALSE); + } + catch (IllegalArgumentException e) + { + sce.getServletContext().setAttribute("org.example.AnnotationTest.sclFromSclRegoTest", Boolean.TRUE); + } + catch (Exception e) + { + sce.getServletContext().setAttribute("org.example.AnnotationTest.sclFromSclRegoTest", Boolean.FALSE); + } + + // Can't add an EventListener not part of the specified list for addListener() + try + { + sce.getServletContext().addListener(new InvalidListener()); + sce.getServletContext().setAttribute("org.example.AnnotationTest.invalidListenerRegoTest", Boolean.FALSE); + } + catch (IllegalArgumentException e) + { + sce.getServletContext().setAttribute("org.example.AnnotationTest.invalidListenerRegoTest", Boolean.TRUE); + } + catch (Exception e) + { + sce.getServletContext().setAttribute("org.example.AnnotationTest.invalidListenerRegoTest", Boolean.FALSE); + } + + // Programmatically add a listener and make sure its injected + try + { + ValidListener l = sce.getServletContext().createListener(ValidListener.class); + sce.getServletContext().setAttribute("org.example.AnnotationTest.programListenerInjectTest", l != null && l.maxAmount != null); + } + catch (Exception e) + { + sce.getServletContext().setAttribute("org.example.AnnotationTest.programListenerInjectTest", Boolean.FALSE); + } + } + + @Override + public void requestDestroyed(ServletRequestEvent sre) + { + LOG.fine("requestDestroyed " + sre); + } + + @Override + public void requestInitialized(ServletRequestEvent sre) + { + LOG.fine("requestInitialized " + sre); + } + + @Override + public void sessionCreated(HttpSessionEvent se) + { + LOG.fine("sessionCreated " + se); + } + + @Override + public void sessionDestroyed(HttpSessionEvent se) + { + LOG.fine("sessionDestroyed " + se); + } + + public static class NaughtyServletContextListener implements ServletContextListener + { + + @Override + public void contextDestroyed(ServletContextEvent sce) + { + throw new IllegalStateException("Should not call NaughtServletContextListener.contextDestroyed"); + } + + @Override + public void contextInitialized(ServletContextEvent sce) + { + throw new IllegalStateException("Should not call NaughtServletContextListener.contextInitialized"); + } + } + + public static class InvalidListener implements EventListener + { + public InvalidListener() + { + } + } + + public static class ValidListener implements HttpSessionIdListener + { + @Resource(mappedName = "maxAmount") + private Double maxAmount; + + public ValidListener() + { + } + + @Override + public void sessionIdChanged(HttpSessionEvent event, String oldSessionId) + { + } + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/templates/plugin-context.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/templates/plugin-context.xml new file mode 100644 index 00000000000..cd3e83783c7 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/templates/plugin-context.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + maxAmount + 100 + true + + + + + jdbc/mydatasource + + + + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/webapp/WEB-INF/jetty-env.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/webapp/WEB-INF/jetty-env.xml new file mode 100644 index 00000000000..784328c8405 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/webapp/WEB-INF/jetty-env.xml @@ -0,0 +1,17 @@ + + + + + + + + + + maxAmount + 55.0 + true + + + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/webapp/WEB-INF/jetty-web.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/webapp/WEB-INF/jetty-web.xml new file mode 100644 index 00000000000..0f2a65bfbb1 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/webapp/WEB-INF/jetty-web.xml @@ -0,0 +1,17 @@ + + + + + + + The test-spec webapp is deployed. DO NOT USE IN PRODUCTION! + + + + + org.eclipse.jetty + + WEB-INF/lib/jetty-util.jar logging used! + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/webapp/WEB-INF/web.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000000..1bf9ff3ba2b --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,106 @@ + + + + EE11 Demo Spec WebApp + + /test-spec + utf-8 + utf-8 + + + org.example.test.TestListener + + + + AnnotationTest + 1 + + + + AnnotationTest + /test/* + + + + RoleAnnotationTest + org.example.test.RoleAnnotationTest + 1 + + manager + server-administrator + + + + + RoleAnnotationTest + /role/* + + + + Multi + org.example.test.MultiPartTest + 2 + + + + Multi + /multi/* + + + + org.example.test.AnnotationTest/avgAmount + java.lang.Double + 1.25 + + + + someAmount + java.lang.Double + 0.99 + + + + + Admin Role + /role/* + + + admin + + + + + admin + + + + server-administrator + + + + + + FORM + Test Realm + + + /login.html + + + /authfail.html + + + + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/webapp/authfail.html b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/webapp/authfail.html new file mode 100644 index 00000000000..914a42fa284 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/webapp/authfail.html @@ -0,0 +1,10 @@ + + + Authentication Failure + + + +

Authentication Failure

+

Sorry, either your login or password were incorrect, please try again.

+ + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/webapp/demo.css b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/webapp/demo.css new file mode 100644 index 00000000000..f2b91d3365d --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/webapp/demo.css @@ -0,0 +1,83 @@ +body +{ + font-family: Arial, Verdana, Helvetica, sans-serif; +} + +.topnav +{ + overflow: hidden; + padding: 10px; + border: 1px solid #f6815c; + border-radius: 10px; + text-align: right; +} + +.menu +{ + margin-left: 3em; +} + +.content +{ + padding: 10px; +} + +.footer +{ + padding: 10px; + border-radius: 10px; + border: 1px solid #f6815c; +} + +.test +{ + background-color: #0099cc; + color: white; + padding: 10px 15px; + border: none; + font-size: 12pt; + border-radius: 10px; + box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2), 0 3px 10px 0 rgba(0,0,0,0.19); +} + +.test:hover +{ + background-color: #f6815c; + color: white; +} + +A:link +{ + color: #0099cc; + text-decoration: none; + font-weight: normal; + font-size: 11pt; + font-family:sans-serif; +} + +A:visited +{ + color: #0099cc; + text-decoration: none; + font-weight: normal; + font-size: 11pt; + font-family:sans-serif; +} + +A:hover +{ + color: #ff6600; + text-decoration: none; + font-weight: normal; + font-size: 11pt; + font-family:sans-serif; +} + +A:active +{ + color: #0099cc; + text-decoration: none; + font-weight: normal; + font-size: 11pt; + font-family:sans-serif; +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/webapp/dynamic.jsp b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/webapp/dynamic.jsp new file mode 100644 index 00000000000..cea8a15c635 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/webapp/dynamic.jsp @@ -0,0 +1,13 @@ +<%@ page contentType="text/html; charset=UTF-8" %> + + + Dynamic + + + + +

Programmatically Added Jsp File

+Success. + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/webapp/index.html b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/webapp/index.html new file mode 100644 index 00000000000..38e63d2a877 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/webapp/index.html @@ -0,0 +1,86 @@ + + + Demo Specification WebApp + + + + + + + +
+
+ Demo Web Application Only - Do NOT Deploy in Production +
+ +

Servlet 6.0 Demo WebApp

+

This example tests some aspects of the servlet specification:

+
    +
  • context defaults
  • +
  • servlet annotations
  • +
  • web-fragments
  • +
  • servlet container initializers
  • +
  • multi-part upload support
  • +
+ +

Test Defaults, Annotations, Fragments and Initializers

+
+ +
+ +

Test Dynamically Added JSP File

+

Click the link to test accessing a programmatically added jsp file:

+ Dynamically added jsp + +

Test Static Content from Fragment

+

Click the link to test accessing static content from a fragment's META-INF/resources:

+ Static resource from a fragment + +

Test Servlet from Fragment

+

Click the link to test accessing a servlet added from a fragment's web-fragment.xml:

+ Servlet added by web-fragment.xml + +

Test DeclaresRoles

+

Login as user admin with password admin when prompted after clicking the button below to test @DeclareRoles annotation:

+
+ +
+ +

Test Servlet Security

+

Login as user admin with password admin when prompted after clicking the button below to test @ServletSecurity annotation:

+
+ +
+ +

Test Servlet Multipart Mime

+ Test the annotation: +
+    @MultipartConfig(location="foo/bar", maxFileSize=10240, maxRequestSize=-1, fileSizeThreshold=2048)
+    
+
+ File to upload: + +
+ +

AsyncListener Resource Injection

+

Click the following link to test that jakarta.servlet.AsyncListeners are injectable:

+
+ +
+ +

Test ClassPath Isolation

+

Click the link to test classpath isolation of system and server classes:

+ ClassPathServlet +
+ + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/webapp/login.html b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/webapp/login.html new file mode 100644 index 00000000000..8e838e560e2 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/webapp/login.html @@ -0,0 +1,19 @@ + + + + Annotation Test + + + +

Enter your username and password to login

+ Enter login=admin and password=admin in order to authenticate successfully +
+ Login: +

+ Password: +

+ +

+

+ + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/webapp/logout.jsp b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/webapp/logout.jsp new file mode 100644 index 00000000000..efec5569f4a --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/webapp/logout.jsp @@ -0,0 +1,19 @@ +<%@ page contentType="text/html; charset=UTF-8" %> +<%@ page import="jakarta.servlet.http.HttpSession"%> + + + Logout + + + +<% + HttpSession s = request.getSession(false); + s.invalidate(); + %> +

Logout

+ +

You are now logged out.

+ Home + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/webapp/small_powered_by.gif b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/webapp/small_powered_by.gif new file mode 100644 index 00000000000..c5dd44319f0 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/webapp/small_powered_by.gif differ diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/webapp/stylesheet.css b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/webapp/stylesheet.css new file mode 100644 index 00000000000..f90463dd2c9 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/main/webapp/stylesheet.css @@ -0,0 +1,7 @@ +body {color: #2E2E2E; font-family:sans-serif; font-size:90%;} +h1 {font-variant: small-caps; font-size:130%; letter-spacing: 0.1em;} +h2 {font-variant: small-caps; font-size:100%; letter-spacing: 0.1em; margin-top:2em;} +h3 {font-size:100%; letter-spacing: 0.1em;} + +span.pass { color: green; } +span.fail { color:red; } diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/test/java/org/eclipse/jetty/ee11/demos/SpecWebAppTest.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/test/java/org/eclipse/jetty/ee11/demos/SpecWebAppTest.java new file mode 100644 index 00000000000..63e3df2dacb --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/test/java/org/eclipse/jetty/ee11/demos/SpecWebAppTest.java @@ -0,0 +1,164 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.demos; + +import java.io.IOException; +import java.net.URI; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + +import org.eclipse.jetty.client.ContentResponse; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.ee11.webapp.WebAppContext; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.security.HashLoginService; +import org.eclipse.jetty.security.SecurityHandler; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.toolchain.test.FS; +import org.eclipse.jetty.toolchain.test.MavenPaths; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; +import org.eclipse.jetty.util.IO; +import org.eclipse.jetty.util.component.LifeCycle; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.util.resource.ResourceFactory; +import org.example.MockDataSource; +import org.example.MockUserTransaction; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.junit.jupiter.api.Assertions.fail; + +@ExtendWith(WorkDirExtension.class) +public class SpecWebAppTest +{ + private Server server; + private HttpClient client; + + @BeforeEach + public void setup(WorkDir workDir) throws Exception + { + server = new Server(); + + ServerConnector connector = new ServerConnector(server); + connector.setPort(0); + server.addConnector(connector); + + Path webappDir = prepareWebAppDir(workDir); + + WebAppContext webapp = new WebAppContext(); + ResourceFactory resourceFactory = ResourceFactory.of(webapp); + webapp.setContextPath("/"); + webapp.setWarResource(resourceFactory.newResource(webappDir)); + webapp.setAttribute( + "org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern", + ".*/jakarta.servlet-api-[^/]*\\.jar$|.*/[^/]*taglibs.*\\.jar$"); + + HashLoginService hashLoginService = new HashLoginService(); + hashLoginService.setName("Test Realm"); + Path realmFile = MavenPaths.findTestResourceFile("ee11-demo-realm.properties"); + Resource realmResource = ResourceFactory.of(server).newResource(realmFile); + hashLoginService.setConfig(realmResource); + SecurityHandler securityHandler = webapp.getSecurityHandler(); + securityHandler.setLoginService(hashLoginService); + + new org.eclipse.jetty.plus.jndi.Resource(webapp, "jdbc/mydatasource", new MockDataSource()); + new org.eclipse.jetty.ee11.plus.jndi.Transaction("ee11", new MockUserTransaction()); + + server.setHandler(webapp); + server.start(); + + client = new HttpClient(); + client.start(); + } + + private Path prepareWebAppDir(WorkDir workDir) throws IOException + { + Path webappDir = workDir.getEmptyPathDir(); + Path srcWebapp = MavenPaths.projectBase().resolve("src/main/webapp"); + IO.copyDir(srcWebapp, webappDir); + + Path webappClassesDir = webappDir.resolve("WEB-INF/classes"); + FS.ensureDirExists(webappClassesDir); + Path classesDir = MavenPaths.projectBase().resolve("target/classes"); + IO.copyDir(classesDir, webappClassesDir); + + Path libDir = webappDir.resolve("WEB-INF/lib"); + FS.ensureDirExists(libDir); + copyDependency("jetty-ee11-demo-container-initializer", libDir); + copyDependency("jetty-ee11-demo-web-fragment", libDir); + + return webappDir; + } + + private void copyDependency(String depName, Path libDir) throws IOException + { + // sinply use copy:dependency from maven... + Path targetDir = MavenPaths.projectBase().resolve("target"); + Path jarFile = targetDir.resolve(depName + ".jar"); + if (Files.exists(jarFile)) + { + Files.copy(jarFile, libDir.resolve(depName + ".jar")); + return; + } + + Path depPath = MavenPaths.projectBase().resolve("../" + depName).normalize(); + if (!Files.isDirectory(depPath)) + fail("Dependency not found: " + depPath); + Path outputJar = libDir.resolve(depName + ".jar"); + + Map env = new HashMap<>(); + env.put("create", "true"); + URI uri = URI.create("jar:" + outputJar.toUri().toASCIIString()); + try (FileSystem fs = FileSystems.newFileSystem(uri, env)) + { + Path root = fs.getPath("/"); + IO.copyDir(depPath.resolve("target/classes"), root); + IO.copyDir(depPath.resolve("src/main/resources"), root, StandardCopyOption.REPLACE_EXISTING); + } + } + + @AfterEach + public void teardown() + { + LifeCycle.stop(client); + LifeCycle.stop(server); + } + + @Test + public void testNoFailures() throws InterruptedException, ExecutionException, TimeoutException + { + ContentResponse response = client.newRequest(server.getURI().resolve("/test/")) + .followRedirects(false) + .send(); + + assertThat("response status", response.getStatus(), is(HttpStatus.OK_200)); + // Look for 0 entries that fail. + assertThat("response", response.getContentAsString(), not(containsString(">FAIL<"))); + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/test/jetty-plugin-env.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/test/jetty-plugin-env.xml new file mode 100644 index 00000000000..684076980c0 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/test/jetty-plugin-env.xml @@ -0,0 +1,44 @@ + + + + + + + + + + maxAmount + 55.0 + true + + + + + + + + + + + + + + ee11 + + + + + + + + + + jdbc/mydatasource + + + + + + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/test/resources/ee11-demo-realm.properties b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/test/resources/ee11-demo-realm.properties new file mode 100644 index 00000000000..9d88b852b7f --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/test/resources/ee11-demo-realm.properties @@ -0,0 +1,21 @@ +# +# This file defines users passwords and roles for a HashUserRealm +# +# The format is +# : [, ...] +# +# Passwords may be clear text, obfuscated or checksummed. The class +# org.eclipse.util.Password should be used to generate obfuscated +# passwords or password checksums +# +# If DIGEST Authentication is used, the password must be in a recoverable +# format, either plain text or OBF:. +# +jetty: MD5:164c88b302622e17050af52c89945d44,user +admin: CRYPT:adpexzg3FUZAk,server-administrator,content-administrator,admin,user +other: OBF:1xmk1w261u9r1w1c1xmq,user +plain: plain,user +user: password,user + +# This entry is for digest auth. The credential is a MD5 hash of username:realmname:password +digest: MD5:6e120743ad67abfbc385bc2bb754e297,user diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/test/resources/jetty-logging.properties b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/test/resources/jetty-logging.properties new file mode 100644 index 00000000000..e4dca768ef9 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-spec-webapp/src/test/resources/jetty-logging.properties @@ -0,0 +1,11 @@ +## Jetty Logging using jetty-slf4j-impl +org.eclipse.jetty.LEVEL=INFO +#org.eclipse.jetty.STACKS=true +#org.eclipse.jetty.ee11.annotations.LEVEL=DEBUG +#org.eclipse.jetty.STACKS=false +#org.eclipse.jetty.io.LEVEL=DEBUG +#org.eclipse.jetty.io.ssl.LEVEL=DEBUG +#org.eclipse.jetty.server.LEVEL=DEBUG +#org.eclipse.jetty.ee11.servlets.LEVEL=DEBUG +#org.eclipse.jetty.alpn.LEVEL=DEBUG +#org.eclipse.jetty.jmx.LEVEL=DEBUG diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-web-fragment/pom.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-web-fragment/pom.xml new file mode 100644 index 00000000000..29dc6829a95 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-web-fragment/pom.xml @@ -0,0 +1,25 @@ + + + 4.0.0 + + org.eclipse.jetty.ee11.demos + jetty-ee11-demos + 12.1.0-SNAPSHOT + ../../pom.xml + + jetty-ee11-demo-web-fragment + jar + + EE11 :: Demo :: Servlet Spec :: Fragment Jar + + + ${project.groupId}.spec.fragment + + + + + jakarta.servlet + jakarta.servlet-api + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-web-fragment/src/main/java/org/example/fragment/FragmentServlet.java b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-web-fragment/src/main/java/org/example/fragment/FragmentServlet.java new file mode 100644 index 00000000000..899daa6a1b8 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-web-fragment/src/main/java/org/example/fragment/FragmentServlet.java @@ -0,0 +1,67 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.example.fragment; + +import java.io.IOException; + +import jakarta.servlet.ServletConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +/** + * FragmentServlet + * + * A web fragment jar. + */ + +public class FragmentServlet extends HttpServlet +{ + private ServletConfig config; + + @Override + public void init(ServletConfig config) throws ServletException + { + super.init(config); + this.config = config; + } + + @Override + public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + doGet(request, response); + } + + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + try + { + response.setContentType("text/html"); + ServletOutputStream out = response.getOutputStream(); + out.println(""); + out.println("

Jetty Fragment Servlet

"); + out.println(""); + out.println(""); + out.println(""); + out.flush(); + } + catch (Exception e) + { + throw new ServletException(e); + } + } +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-web-fragment/src/main/resources/META-INF/resources/fragmentA/index.html b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-web-fragment/src/main/resources/META-INF/resources/fragmentA/index.html new file mode 100644 index 00000000000..02303e909ae --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-web-fragment/src/main/resources/META-INF/resources/fragmentA/index.html @@ -0,0 +1,8 @@ +

Welcome to a Fragment

+ +

+ This index.html file was included in a fragment's META-INF/resources directory. +

+ +Now hit a servlet added by a fragment + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-web-fragment/src/main/resources/META-INF/web-fragment.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-web-fragment/src/main/resources/META-INF/web-fragment.xml new file mode 100644 index 00000000000..24c67ec7510 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/jetty-ee11-demo-web-fragment/src/main/resources/META-INF/web-fragment.xml @@ -0,0 +1,42 @@ + + + + + FragmentA + + + + + + + + + AnnotationTest + org.example.test.AnnotationTest + + extra1 + 123 + + + extra2 + 345 + + + + + Fragment + org.example.fragment.FragmentServlet + + + + Fragment + /fragment/* + + + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/pom.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/pom.xml new file mode 100644 index 00000000000..21d41eaac72 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-spec/pom.xml @@ -0,0 +1,18 @@ + + + 4.0.0 + + org.eclipse.jetty.ee11.demos + jetty-ee11-demos + 12.1.0-SNAPSHOT + + jetty-ee11-demo-spec + pom + EE11 :: Demo :: Servlet Spec + + + jetty-ee11-demo-container-initializer + jetty-ee11-demo-spec-webapp + jetty-ee11-demo-web-fragment + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-template/pom.xml b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-template/pom.xml new file mode 100644 index 00000000000..c0528890908 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-template/pom.xml @@ -0,0 +1,28 @@ + + + + 4.0.0 + + org.eclipse.jetty.ee11.demos + jetty-ee11-demos + 12.1.0-SNAPSHOT + + jetty-ee11-demo-template + jar + EE11 :: Demo :: Template + + + + + org.apache.maven.plugins + maven-jar-plugin + + + index.html + small_powered_by.gif + + + + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-template/src/main/resources/demo.css b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-template/src/main/resources/demo.css new file mode 100644 index 00000000000..f2b91d3365d --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-template/src/main/resources/demo.css @@ -0,0 +1,83 @@ +body +{ + font-family: Arial, Verdana, Helvetica, sans-serif; +} + +.topnav +{ + overflow: hidden; + padding: 10px; + border: 1px solid #f6815c; + border-radius: 10px; + text-align: right; +} + +.menu +{ + margin-left: 3em; +} + +.content +{ + padding: 10px; +} + +.footer +{ + padding: 10px; + border-radius: 10px; + border: 1px solid #f6815c; +} + +.test +{ + background-color: #0099cc; + color: white; + padding: 10px 15px; + border: none; + font-size: 12pt; + border-radius: 10px; + box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2), 0 3px 10px 0 rgba(0,0,0,0.19); +} + +.test:hover +{ + background-color: #f6815c; + color: white; +} + +A:link +{ + color: #0099cc; + text-decoration: none; + font-weight: normal; + font-size: 11pt; + font-family:sans-serif; +} + +A:visited +{ + color: #0099cc; + text-decoration: none; + font-weight: normal; + font-size: 11pt; + font-family:sans-serif; +} + +A:hover +{ + color: #ff6600; + text-decoration: none; + font-weight: normal; + font-size: 11pt; + font-family:sans-serif; +} + +A:active +{ + color: #0099cc; + text-decoration: none; + font-weight: normal; + font-size: 11pt; + font-family:sans-serif; +} diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-template/src/main/resources/index.html b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-template/src/main/resources/index.html new file mode 100644 index 00000000000..ad8caaebb4b --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-template/src/main/resources/index.html @@ -0,0 +1,35 @@ + + + + XXX + + + + + + + +
+
+ Demo Web Application Only - Do NOT Deploy in Production +
+ + +

Jetty XXX Demo Webapp

+ + + +
+ + + + diff --git a/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-template/src/main/resources/small_powered_by.gif b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-template/src/main/resources/small_powered_by.gif new file mode 100644 index 00000000000..c5dd44319f0 Binary files /dev/null and b/jetty-ee11/jetty-ee11-demos/jetty-ee11-demo-template/src/main/resources/small_powered_by.gif differ diff --git a/jetty-ee11/jetty-ee11-demos/pom.xml b/jetty-ee11/jetty-ee11-demos/pom.xml new file mode 100644 index 00000000000..1c7333f9e04 --- /dev/null +++ b/jetty-ee11/jetty-ee11-demos/pom.xml @@ -0,0 +1,45 @@ + + + + 4.0.0 + + org.eclipse.jetty.ee11 + jetty-ee11 + 12.1.0-SNAPSHOT + + org.eclipse.jetty.ee11.demos + jetty-ee11-demos + pom + EE11 :: Demos + + + jetty-ee11-demo-async-rest + jetty-ee11-demo-embedded + jetty-ee11-demo-jaas-webapp + jetty-ee11-demo-jetty-webapp + jetty-ee11-demo-jndi-webapp + jetty-ee11-demo-jsp-webapp + jetty-ee11-demo-mock-resources + jetty-ee11-demo-proxy-webapp + jetty-ee11-demo-simple-webapp + jetty-ee11-demo-spec + jetty-ee11-demo-template + + + + true + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + bogus.* + + + + + diff --git a/jetty-ee11/jetty-ee11-examples/pom.xml b/jetty-ee11/jetty-ee11-examples/pom.xml new file mode 100644 index 00000000000..c574e89a10f --- /dev/null +++ b/jetty-ee11/jetty-ee11-examples/pom.xml @@ -0,0 +1,36 @@ + + + + 4.0.0 + + org.eclipse.jetty.ee11 + jetty-ee11 + 12.1.0-SNAPSHOT + + jetty-ee11-examples + EE11 :: Examples + + + true + + + + + org.eclipse.jetty + jetty-http + + + org.eclipse.jetty + jetty-server + + + org.eclipse.jetty.ee11 + jetty-ee11-servlet + ${project.version} + + + org.hamcrest + hamcrest + + + diff --git a/jetty-ee11/jetty-ee11-examples/src/test/java/org/acme/MyServlet.java b/jetty-ee11/jetty-ee11-examples/src/test/java/org/acme/MyServlet.java new file mode 100644 index 00000000000..57fa5d2e5f4 --- /dev/null +++ b/jetty-ee11/jetty-ee11-examples/src/test/java/org/acme/MyServlet.java @@ -0,0 +1,30 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.acme; + +import java.io.IOException; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +public class MyServlet extends HttpServlet +{ + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException + { + resp.getWriter().println("the servlet says Hello World"); + } +} diff --git a/jetty-ee11/jetty-ee11-examples/src/test/java/org/eclipse/jetty/examples/Jetty12Example.java b/jetty-ee11/jetty-ee11-examples/src/test/java/org/eclipse/jetty/examples/Jetty12Example.java new file mode 100644 index 00000000000..c9592c1a6fa --- /dev/null +++ b/jetty-ee11/jetty-ee11-examples/src/test/java/org/eclipse/jetty/examples/Jetty12Example.java @@ -0,0 +1,78 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.examples; + +import java.net.URL; +import java.net.URLClassLoader; + +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.MimeTypes; +import org.eclipse.jetty.io.Content; +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.util.Callback; + +public class Jetty12Example +{ + public static void main(String[] args) throws Exception + { + org.eclipse.jetty.server.Server server = new org.eclipse.jetty.server.Server(); + + org.eclipse.jetty.server.ServerConnector connector = new org.eclipse.jetty.server.ServerConnector(server); + server.addConnector(connector); + + // Declare server handler collection + org.eclipse.jetty.server.handler.ContextHandlerCollection contexts = + new org.eclipse.jetty.server.handler.ContextHandlerCollection(); + server.setHandler(contexts); + + // Add an embedded jetty-12 context + org.eclipse.jetty.server.handler.ContextHandler embedded = + new org.eclipse.jetty.server.handler.ContextHandler("/embedded"); + embedded.setHandler(new Handler.Abstract.NonBlocking() + { + @Override + public boolean handle(org.eclipse.jetty.server.Request request, + org.eclipse.jetty.server.Response response, + Callback callback) throws Exception + { + response.setStatus(200); + response.getHeaders().put(HttpHeader.CONTENT_TYPE, MimeTypes.Type.TEXT_PLAIN_UTF_8.asString()); + Content.Sink.write(response, true, "the handler says Hello World", callback); + return true; + } + }); + contexts.addHandler(embedded); + + // Add an EE11 ServletContext + URLClassLoader ee11Loader = new URLClassLoader( + new URL[] {new URL("file:lib/ee11/servlet-api-6.0.jar"), + new URL("file:lib/ee11/jetty-ee11-servlet.jar")}, + Jetty12Example.class.getClassLoader()); + org.eclipse.jetty.server.handler.ContextHandler ee11Context = (org.eclipse.jetty.server.handler.ContextHandler) + ee11Loader.loadClass("org.eclipse.jetty.ee11.servlet.ServletContextHandler") + .getDeclaredConstructor().newInstance(); + org.eclipse.jetty.server.Handler ee11Servlet = (org.eclipse.jetty.server.Handler) + ee11Loader.loadClass("org.eclipse.jetty.ee11.servlet.ServletHandler") + .getDeclaredConstructor().newInstance(); + ee11Context.setHandler(ee11Servlet); + contexts.addHandler(ee11Context); + ee11Servlet.getClass().getMethod("addServletWithMapping", String.class, String.class) + .invoke(ee11Servlet, "org.acme.MyServlet", "/"); + + + server.start(); + server.dumpStdErr(); + server.join(); + } +} diff --git a/jetty-ee11/jetty-ee11-fcgi-proxy/pom.xml b/jetty-ee11/jetty-ee11-fcgi-proxy/pom.xml new file mode 100644 index 00000000000..5c024c15826 --- /dev/null +++ b/jetty-ee11/jetty-ee11-fcgi-proxy/pom.xml @@ -0,0 +1,48 @@ + + + + 4.0.0 + + org.eclipse.jetty.ee11 + jetty-ee11 + 12.1.0-SNAPSHOT + + jetty-ee11-fcgi-proxy + EE11 :: FCGI Proxy + + + ${project.groupId}.fcgi.proxy + + + + + org.eclipse.jetty + jetty-server + + + org.eclipse.jetty.ee11 + jetty-ee11-proxy + + + org.eclipse.jetty.fcgi + jetty-fcgi-client + + + jakarta.servlet + jakarta.servlet-api + provided + + + org.eclipse.jetty + jetty-slf4j-impl + test + + + + org.eclipse.jetty.ee11 + jetty-ee11-servlet + test + + + + diff --git a/jetty-ee11/jetty-ee11-fcgi-proxy/src/main/config/modules/ee11-fcgi-proxy.mod b/jetty-ee11/jetty-ee11-fcgi-proxy/src/main/config/modules/ee11-fcgi-proxy.mod new file mode 100644 index 00000000000..637c514a191 --- /dev/null +++ b/jetty-ee11/jetty-ee11-fcgi-proxy/src/main/config/modules/ee11-fcgi-proxy.mod @@ -0,0 +1,16 @@ +[description] +Enables support for EE11 FastCGI proxying. + +[environment] +ee11 + +[tags] +fcgi +proxy + +[depends] +fcgi + +[lib] +lib/jetty-ee11-fcgi-proxy-${jetty.version}.jar +lib/jetty-ee11-proxy-${jetty.version}.jar diff --git a/jetty-ee11/jetty-ee11-fcgi-proxy/src/main/java/module-info.java b/jetty-ee11/jetty-ee11-fcgi-proxy/src/main/java/module-info.java new file mode 100644 index 00000000000..785070a66ae --- /dev/null +++ b/jetty-ee11/jetty-ee11-fcgi-proxy/src/main/java/module-info.java @@ -0,0 +1,23 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +module org.eclipse.jetty.ee11.fcgi.proxy +{ + requires transitive org.eclipse.jetty.ee11.proxy; + requires transitive org.eclipse.jetty.fcgi.client; + requires transitive org.eclipse.jetty.server; + + requires static jakarta.servlet; + + exports org.eclipse.jetty.ee11.fcgi.proxy; +} diff --git a/jetty-ee11/jetty-ee11-fcgi-proxy/src/main/java/org/eclipse/jetty/ee11/fcgi/proxy/FastCGIProxyServlet.java b/jetty-ee11/jetty-ee11-fcgi-proxy/src/main/java/org/eclipse/jetty/ee11/fcgi/proxy/FastCGIProxyServlet.java new file mode 100644 index 00000000000..98c5f9cd2a0 --- /dev/null +++ b/jetty-ee11/jetty-ee11-fcgi-proxy/src/main/java/org/eclipse/jetty/ee11/fcgi/proxy/FastCGIProxyServlet.java @@ -0,0 +1,306 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.fcgi.proxy; + +import java.net.URI; +import java.nio.file.Path; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.TreeMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import jakarta.servlet.RequestDispatcher; +import jakarta.servlet.ServletConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.Request; +import org.eclipse.jetty.ee11.proxy.AsyncProxyServlet; +import org.eclipse.jetty.fcgi.FCGI; +import org.eclipse.jetty.fcgi.client.transport.HttpClientTransportOverFCGI; +import org.eclipse.jetty.http.HttpField; +import org.eclipse.jetty.http.HttpFields; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.HttpScheme; +import org.eclipse.jetty.io.ClientConnector; +import org.eclipse.jetty.io.Transport; +import org.eclipse.jetty.util.ProcessorUtils; +import org.eclipse.jetty.util.URIUtil; + +/** + * Specific implementation of {@link org.eclipse.jetty.ee11.proxy.AsyncProxyServlet.Transparent} for FastCGI. + *

+ * This servlet accepts an HTTP request and transforms it into a FastCGI request + * that is sent to the FastCGI server specified in the {@code proxyTo} + * init-param. + *

+ * This servlet accepts these additional {@code init-param}s: + *

    + *
  • {@code scriptRoot}, mandatory, that must be set to the directory where + * the application that must be served via FastCGI is installed and corresponds to + * the FastCGI DOCUMENT_ROOT parameter
  • + *
  • {@code scriptPattern}, optional, defaults to {@code (.+?\.php)}, + * that specifies a regular expression with at least 1 and at most 2 groups that specify + * respectively: + *
      + *
    • the FastCGI SCRIPT_NAME parameter
    • + *
    • the FastCGI PATH_INFO parameter
    • + *
  • + *
  • {@code fastCGI.HTTPS}, optional, defaults to false, that specifies whether + * to force the FastCGI {@code HTTPS} parameter to the value {@code on}
  • + *
  • {@code fastCGI.envNames}, optional, a comma separated list of environment variable + * names read via {@link System#getenv(String)} that are forwarded as FastCGI parameters.
  • + *
  • {@code unixDomainPath}, optional, that specifies the Unix-Domain path the FastCGI + * server listens to.
  • + *
+ * + * @see TryFilesFilter + */ +public class FastCGIProxyServlet extends AsyncProxyServlet.Transparent +{ + public static final String SCRIPT_ROOT_INIT_PARAM = "scriptRoot"; + public static final String SCRIPT_PATTERN_INIT_PARAM = "scriptPattern"; + public static final String ORIGINAL_URI_ATTRIBUTE_INIT_PARAM = "originalURIAttribute"; + public static final String ORIGINAL_QUERY_ATTRIBUTE_INIT_PARAM = "originalQueryAttribute"; + public static final String FASTCGI_HTTPS_INIT_PARAM = "fastCGI.HTTPS"; + public static final String FASTCGI_ENV_NAMES_INIT_PARAM = "fastCGI.envNames"; + + private static final String REMOTE_ADDR_ATTRIBUTE = FastCGIProxyServlet.class.getName() + ".remoteAddr"; + private static final String REMOTE_PORT_ATTRIBUTE = FastCGIProxyServlet.class.getName() + ".remotePort"; + private static final String SERVER_NAME_ATTRIBUTE = FastCGIProxyServlet.class.getName() + ".serverName"; + private static final String SERVER_ADDR_ATTRIBUTE = FastCGIProxyServlet.class.getName() + ".serverAddr"; + private static final String SERVER_PORT_ATTRIBUTE = FastCGIProxyServlet.class.getName() + ".serverPort"; + private static final String SCHEME_ATTRIBUTE = FastCGIProxyServlet.class.getName() + ".scheme"; + private static final String REQUEST_URI_ATTRIBUTE = FastCGIProxyServlet.class.getName() + ".requestURI"; + private static final String REQUEST_QUERY_ATTRIBUTE = FastCGIProxyServlet.class.getName() + ".requestQuery"; + + private Pattern scriptPattern; + private String originalURIAttribute; + private String originalQueryAttribute; + private boolean fcgiHTTPS; + private Set fcgiEnvNames; + private Path unixDomainPath; + + @Override + public void init() throws ServletException + { + super.init(); + + String value = getInitParameter(SCRIPT_PATTERN_INIT_PARAM); + if (value == null) + value = "(.+?\\.php)"; + scriptPattern = Pattern.compile(value); + + originalURIAttribute = getInitParameter(ORIGINAL_URI_ATTRIBUTE_INIT_PARAM); + originalQueryAttribute = getInitParameter(ORIGINAL_QUERY_ATTRIBUTE_INIT_PARAM); + + fcgiHTTPS = Boolean.parseBoolean(getInitParameter(FASTCGI_HTTPS_INIT_PARAM)); + + fcgiEnvNames = Collections.emptySet(); + String envNames = getInitParameter(FASTCGI_ENV_NAMES_INIT_PARAM); + if (envNames != null) + { + fcgiEnvNames = Stream.of(envNames.split(",")) + .map(String::trim) + .collect(Collectors.toSet()); + } + + String path = getInitParameter("unixDomainPath"); + if (path != null) + unixDomainPath = Path.of(path); + } + + @Override + protected HttpClient newHttpClient() + { + ServletConfig config = getServletConfig(); + String scriptRoot = config.getInitParameter(SCRIPT_ROOT_INIT_PARAM); + if (scriptRoot == null) + throw new IllegalArgumentException("Mandatory parameter '" + SCRIPT_ROOT_INIT_PARAM + "' not configured"); + + int selectors = Math.max(1, ProcessorUtils.availableProcessors() / 2); + String value = config.getInitParameter("selectors"); + if (value != null) + selectors = Integer.parseInt(value); + ClientConnector connector = new ClientConnector(); + connector.setSelectors(selectors); + return new HttpClient(new ProxyHttpClientTransportOverFCGI(connector, scriptRoot)); + } + + @Override + protected void sendProxyRequest(HttpServletRequest request, HttpServletResponse proxyResponse, Request proxyRequest) + { + proxyRequest.attribute(REMOTE_ADDR_ATTRIBUTE, request.getRemoteAddr()); + proxyRequest.attribute(REMOTE_PORT_ATTRIBUTE, String.valueOf(request.getRemotePort())); + proxyRequest.attribute(SERVER_NAME_ATTRIBUTE, request.getServerName()); + proxyRequest.attribute(SERVER_ADDR_ATTRIBUTE, request.getLocalAddr()); + proxyRequest.attribute(SERVER_PORT_ATTRIBUTE, String.valueOf(request.getLocalPort())); + proxyRequest.attribute(SCHEME_ATTRIBUTE, request.getScheme()); + + // Has the original URI been rewritten ? + String originalURI = null; + String originalQuery = null; + if (originalURIAttribute != null) + originalURI = (String)request.getAttribute(originalURIAttribute); + if (originalURI != null && originalQueryAttribute != null) + { + originalQuery = (String)request.getAttribute(originalQueryAttribute); + if (originalQuery != null) + originalURI += "?" + originalQuery; + } + + if (originalURI == null) + { + // If we are forwarded or included, retain the original request URI. + String originalPath = (String)request.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI); + originalQuery = (String)request.getAttribute(RequestDispatcher.FORWARD_QUERY_STRING); + if (originalPath == null) + { + originalPath = (String)request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI); + originalQuery = (String)request.getAttribute(RequestDispatcher.INCLUDE_QUERY_STRING); + } + if (originalPath != null) + { + originalURI = originalPath; + if (originalQuery != null) + originalURI += "?" + originalQuery; + } + } + + if (originalURI != null) + proxyRequest.attribute(REQUEST_URI_ATTRIBUTE, originalURI); + if (originalQuery != null) + proxyRequest.attribute(REQUEST_QUERY_ATTRIBUTE, originalQuery); + + // If the Host header is missing, add it. + if (!proxyRequest.getHeaders().contains(HttpHeader.HOST)) + { + String server = request.getServerName(); + int port = request.getServerPort(); + if (port != URIUtil.getDefaultPortForScheme(request.getScheme())) + server += ":" + port; + String host = server; + proxyRequest.headers(headers -> headers + .put(HttpHeader.HOST, host) + .put(HttpHeader.X_FORWARDED_HOST, host)); + } + + // PHP does not like multiple Cookie headers, coalesce into one. + List cookies = proxyRequest.getHeaders().getValuesList(HttpHeader.COOKIE); + if (cookies.size() > 1) + { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < cookies.size(); ++i) + { + if (i > 0) + builder.append("; "); + String cookie = cookies.get(i); + builder.append(cookie); + } + proxyRequest.headers(headers -> headers.put(HttpHeader.COOKIE, builder.toString())); + } + + Path unixDomain = unixDomainPath; + if (unixDomain != null) + proxyRequest.transport(new Transport.TCPUnix(unixDomain)); + + super.sendProxyRequest(request, proxyResponse, proxyRequest); + } + + protected void customizeFastCGIHeaders(Request proxyRequest, HttpFields.Mutable fastCGIHeaders) + { + for (String envName : fcgiEnvNames) + { + String envValue = System.getenv(envName); + if (envValue != null) + fastCGIHeaders.put(envName, envValue); + } + + fastCGIHeaders.remove("HTTP_PROXY"); + + fastCGIHeaders.put(FCGI.Headers.REMOTE_ADDR, (String)proxyRequest.getAttributes().get(REMOTE_ADDR_ATTRIBUTE)); + fastCGIHeaders.put(FCGI.Headers.REMOTE_PORT, (String)proxyRequest.getAttributes().get(REMOTE_PORT_ATTRIBUTE)); + fastCGIHeaders.put(FCGI.Headers.SERVER_NAME, (String)proxyRequest.getAttributes().get(SERVER_NAME_ATTRIBUTE)); + fastCGIHeaders.put(FCGI.Headers.SERVER_ADDR, (String)proxyRequest.getAttributes().get(SERVER_ADDR_ATTRIBUTE)); + fastCGIHeaders.put(FCGI.Headers.SERVER_PORT, (String)proxyRequest.getAttributes().get(SERVER_PORT_ATTRIBUTE)); + + if (fcgiHTTPS || HttpScheme.HTTPS.is((String)proxyRequest.getAttributes().get(SCHEME_ATTRIBUTE))) + fastCGIHeaders.put(FCGI.Headers.HTTPS, "on"); + + URI proxyRequestURI = proxyRequest.getURI(); + String rawPath = proxyRequestURI == null ? proxyRequest.getPath() : proxyRequestURI.getRawPath(); + String rawQuery = proxyRequestURI == null ? null : proxyRequestURI.getRawQuery(); + + String requestURI = (String)proxyRequest.getAttributes().get(REQUEST_URI_ATTRIBUTE); + if (requestURI == null) + { + requestURI = rawPath; + if (rawQuery != null) + requestURI += "?" + rawQuery; + } + fastCGIHeaders.put(FCGI.Headers.REQUEST_URI, requestURI); + + String requestQuery = (String)proxyRequest.getAttributes().get(REQUEST_QUERY_ATTRIBUTE); + if (requestQuery != null) + fastCGIHeaders.put(FCGI.Headers.QUERY_STRING, requestQuery); + + String scriptName = rawPath; + Matcher matcher = scriptPattern.matcher(rawPath); + if (matcher.matches()) + { + // Expect at least one group in the regular expression. + scriptName = matcher.group(1); + + // If there is a second group, map it to PATH_INFO. + if (matcher.groupCount() > 1) + fastCGIHeaders.put(FCGI.Headers.PATH_INFO, matcher.group(2)); + } + fastCGIHeaders.put(FCGI.Headers.SCRIPT_NAME, scriptName); + + String root = fastCGIHeaders.get(FCGI.Headers.DOCUMENT_ROOT); + fastCGIHeaders.put(FCGI.Headers.SCRIPT_FILENAME, root + scriptName); + } + + private class ProxyHttpClientTransportOverFCGI extends HttpClientTransportOverFCGI + { + private ProxyHttpClientTransportOverFCGI(ClientConnector connector, String scriptRoot) + { + super(connector, scriptRoot); + } + + @Override + public void customize(Request request, HttpFields.Mutable fastCGIHeaders) + { + super.customize(request, fastCGIHeaders); + customizeFastCGIHeaders(request, fastCGIHeaders); + if (_log.isDebugEnabled()) + { + TreeMap fcgi = new TreeMap<>(); + for (HttpField field : fastCGIHeaders) + { + fcgi.put(field.getName(), field.getValue()); + } + String eol = System.lineSeparator(); + _log.debug("FastCGI variables {}{}", eol, fcgi.entrySet().stream() + .map(entry -> String.format("%s: %s", entry.getKey(), entry.getValue())) + .collect(Collectors.joining(eol))); + } + } + } +} diff --git a/jetty-ee11/jetty-ee11-fcgi-proxy/src/main/java/org/eclipse/jetty/ee11/fcgi/proxy/TryFilesFilter.java b/jetty-ee11/jetty-ee11-fcgi-proxy/src/main/java/org/eclipse/jetty/ee11/fcgi/proxy/TryFilesFilter.java new file mode 100644 index 00000000000..f8f38a6af33 --- /dev/null +++ b/jetty-ee11/jetty-ee11-fcgi-proxy/src/main/java/org/eclipse/jetty/ee11/fcgi/proxy/TryFilesFilter.java @@ -0,0 +1,138 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.fcgi.proxy; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import jakarta.servlet.Filter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.FilterConfig; +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.eclipse.jetty.util.StringUtil; + +/** + * Inspired by nginx's try_files functionality. + *

+ * This filter accepts the {@code files} init-param as a list of space-separated + * file URIs. The special token {@code $path} represents the current request URL's + * path (the portion after the context path). + *

+ * Typical example of how this filter can be configured is the following: + *

+ * <filter>
+ *     <filter-name>try_files</filter-name>
+ *     <filter-class>org.eclipse.jetty.fcgi.server.proxy.TryFilesFilter</filter-class>
+ *     <init-param>
+ *         <param-name>files</param-name>
+ *         <param-value>/maintenance.html $path /index.php?p=$path</param-value>
+ *     </init-param>
+ * </filter>
+ * 
+ * For a request such as {@code /context/path/to/resource.ext}, this filter will + * try to serve the {@code /maintenance.html} file if it finds it; failing that, + * it will try to serve the {@code /path/to/resource.ext} file if it finds it; + * failing that it will forward the request to {@code /index.php?p=/path/to/resource.ext}. + * The last file URI specified in the list is therefore the "fallback" to which the request + * is forwarded to in case no previous files can be found. + *

+ * The files are resolved using {@link ServletContext#getResource(String)} to make sure + * that only files visible to the application are served. + * + * @see FastCGIProxyServlet + */ +public class TryFilesFilter implements Filter +{ + public static final String FILES_INIT_PARAM = "files"; + + private String[] files; + + @Override + public void init(FilterConfig config) throws ServletException + { + String param = config.getInitParameter(FILES_INIT_PARAM); + if (param == null) + throw new ServletException(String.format("Missing mandatory parameter '%s'", FILES_INIT_PARAM)); + files = param.split(" "); + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException + { + HttpServletRequest httpRequest = (HttpServletRequest)request; + HttpServletResponse httpResponse = (HttpServletResponse)response; + + for (int i = 0; i < files.length - 1; ++i) + { + String file = files[i]; + String resolved = resolve(httpRequest, file); + + URL url = request.getServletContext().getResource(resolved); + if (url == null) + continue; + + if (Files.isReadable(toPath(url))) + { + chain.doFilter(httpRequest, httpResponse); + return; + } + } + + // The last one is the fallback + fallback(httpRequest, httpResponse, chain, files[files.length - 1]); + } + + private Path toPath(URL url) throws IOException + { + try + { + return Paths.get(url.toURI()); + } + catch (URISyntaxException x) + { + throw new IOException(x); + } + } + + protected void fallback(HttpServletRequest request, HttpServletResponse response, FilterChain chain, String fallback) throws IOException, ServletException + { + String resolved = resolve(request, fallback); + request.getServletContext().getRequestDispatcher(resolved).forward(request, response); + } + + private String resolve(HttpServletRequest request, String value) + { + String path = request.getServletPath(); + String info = request.getPathInfo(); + if (info != null) + path += info; + if (!path.startsWith("/")) + path = "/" + path; + return StringUtil.replace(value, "$path", path); + } + + @Override + public void destroy() + { + } +} diff --git a/jetty-ee11/jetty-ee11-fcgi-proxy/src/test/java/org/eclipse/jetty/ee11/fcgi/proxy/TryFilesFilterTest.java b/jetty-ee11/jetty-ee11-fcgi-proxy/src/test/java/org/eclipse/jetty/ee11/fcgi/proxy/TryFilesFilterTest.java new file mode 100644 index 00000000000..3602aa362ae --- /dev/null +++ b/jetty-ee11/jetty-ee11-fcgi-proxy/src/test/java/org/eclipse/jetty/ee11/fcgi/proxy/TryFilesFilterTest.java @@ -0,0 +1,108 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.fcgi.proxy; + +import java.util.EnumSet; + +import jakarta.servlet.DispatcherType; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.eclipse.jetty.client.ContentResponse; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.transport.HttpClientTransportOverHTTP; +import org.eclipse.jetty.ee11.servlet.FilterHolder; +import org.eclipse.jetty.ee11.servlet.ServletContextHandler; +import org.eclipse.jetty.ee11.servlet.ServletHolder; +import org.eclipse.jetty.io.ClientConnector; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class TryFilesFilterTest +{ + private Server server; + private ServerConnector connector; + private ServerConnector sslConnector; + private HttpClient client; + private String forwardPath; + + public void prepare(HttpServlet servlet) throws Exception + { + server = new Server(); + connector = new ServerConnector(server); + server.addConnector(connector); + + SslContextFactory.Server serverSslContextFactory = new SslContextFactory.Server(); + serverSslContextFactory.setKeyStorePath("src/test/resources/keystore.p12"); + serverSslContextFactory.setKeyStorePassword("storepwd"); + sslConnector = new ServerConnector(server, serverSslContextFactory); + server.addConnector(sslConnector); + + ServletContextHandler context = new ServletContextHandler("/"); + server.setHandler(context); + + FilterHolder filterHolder = context.addFilter(TryFilesFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST)); + forwardPath = "/index.php"; + filterHolder.setInitParameter(TryFilesFilter.FILES_INIT_PARAM, "$path " + forwardPath + "?p=$path"); + + context.addServlet(new ServletHolder(servlet), "/*"); + + ClientConnector clientConnector = new ClientConnector(); + SslContextFactory.Client clientSslContextFactory = new SslContextFactory.Client(true); +// clientSslContextFactory.setEndpointIdentificationAlgorithm(null); +// clientSslContextFactory.setKeyStorePath("src/test/resources/keystore.p12"); +// clientSslContextFactory.setKeyStorePassword("storepwd"); + clientConnector.setSslContextFactory(clientSslContextFactory); + client = new HttpClient(new HttpClientTransportOverHTTP(clientConnector)); + server.addBean(client); + + server.start(); + } + + @AfterEach + public void dispose() throws Exception + { + server.stop(); + } + + @Test + public void testHTTPSRequestIsForwarded() throws Exception + { + final String path = "/one/"; + prepare(new HttpServlet() + { + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + { + assertTrue("https".equalsIgnoreCase(req.getScheme())); + assertTrue(req.isSecure()); + assertEquals(forwardPath, req.getRequestURI()); + assertTrue(req.getQueryString().endsWith(path)); + } + }); + + ContentResponse response = client.newRequest("localhost", sslConnector.getLocalPort()) + .scheme("https") + .path(path) + .send(); + + assertEquals(200, response.getStatus()); + } +} diff --git a/jetty-ee11/jetty-ee11-fcgi-proxy/src/test/resources/jetty-logging.properties b/jetty-ee11/jetty-ee11-fcgi-proxy/src/test/resources/jetty-logging.properties new file mode 100644 index 00000000000..37021aa1510 --- /dev/null +++ b/jetty-ee11/jetty-ee11-fcgi-proxy/src/test/resources/jetty-logging.properties @@ -0,0 +1,2 @@ +#org.eclipse.jetty.LEVEL=DEBUG +#org.eclipse.jetty.fcgi.proxy.LEVEL=DEBUG diff --git a/jetty-ee11/jetty-ee11-fcgi-proxy/src/test/resources/keystore.p12 b/jetty-ee11/jetty-ee11-fcgi-proxy/src/test/resources/keystore.p12 new file mode 100644 index 00000000000..8ab40f72afd Binary files /dev/null and b/jetty-ee11/jetty-ee11-fcgi-proxy/src/test/resources/keystore.p12 differ diff --git a/jetty-ee11/jetty-ee11-glassfish-jstl/pom.xml b/jetty-ee11/jetty-ee11-glassfish-jstl/pom.xml new file mode 100644 index 00000000000..2980bc9f8ba --- /dev/null +++ b/jetty-ee11/jetty-ee11-glassfish-jstl/pom.xml @@ -0,0 +1,73 @@ + + + 4.0.0 + + org.eclipse.jetty.ee11 + jetty-ee11 + 12.1.0-SNAPSHOT + + jetty-ee11-glassfish-jstl + jar + EE11 :: Glassfish JSTL + https://projects.eclipse.org/projects/ee4j.glassfish + + ${project.groupId}.glassfish.jstl + true + + + + + + jakarta.servlet.jsp.jstl + jakarta.servlet.jsp.jstl-api + + + + org.eclipse.jetty.ee11 + jetty-ee11-apache-jsp + + + + org.glassfish.web + jakarta.servlet.jsp.jstl + + + + org.eclipse.jetty.ee11 + jetty-ee11-annotations + test + + + + org.eclipse.jetty.ee11 + jetty-ee11-webapp + test + + + + org.eclipse.jetty.toolchain + jetty-test-helper + test + + + + org.slf4j + slf4j-simple + test + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + false + + + + + + diff --git a/jetty-ee11/jetty-ee11-glassfish-jstl/src/main/config/modules/ee11-glassfish-jstl.mod b/jetty-ee11/jetty-ee11-glassfish-jstl/src/main/config/modules/ee11-glassfish-jstl.mod new file mode 100644 index 00000000000..9aa2b5871f9 --- /dev/null +++ b/jetty-ee11/jetty-ee11-glassfish-jstl/src/main/config/modules/ee11-glassfish-jstl.mod @@ -0,0 +1,18 @@ +# DO NOT EDIT THIS FILE - See: https://eclipse.dev/jetty/documentation/ + +[description] +Enables the glassfish version of JSTL for all webapps. + +[environment] +ee11 + +[depends] +ee11-apache-jsp + +[ini] +ee11.jakarta.servlet.jsp.jstl.api.version?=@jakarta.servlet.jsp.jstl.api.version@ +ee11.jakarta.servlet.jsp.jstl.impl.version?=@jakarta.servlet.jsp.jstl.impl.version@ + +[lib] +lib/ee11-glassfish-jstl/jakarta.servlet.jsp.jstl.jakarta.servlet.jsp.jstl-api-${ee11.jakarta.servlet.jsp.jstl.api.version}.jar +lib/ee11-glassfish-jstl/org.glassfish.web.jakarta.servlet.jsp.jstl-${ee11.jakarta.servlet.jsp.jstl.impl.version}.jar diff --git a/jetty-ee11/jetty-ee11-glassfish-jstl/src/main/resources/readme.txt b/jetty-ee11/jetty-ee11-glassfish-jstl/src/main/resources/readme.txt new file mode 100644 index 00000000000..a516023c352 --- /dev/null +++ b/jetty-ee11/jetty-ee11-glassfish-jstl/src/main/resources/readme.txt @@ -0,0 +1,4 @@ +This empty jar file is purely to work around a problem with the Maven Dependency plugin. +Several modules in jetty use the Dependency plugin to copy or unpack the dependencies of other modules. +However, the Dependency plugin is not capable of unpacking or copying a dependency of type 'pom', which +this module is, as it consists purely of external dependencies needed to run jsp. diff --git a/jetty-ee11/jetty-ee11-glassfish-jstl/src/test/java/org/eclipse/jetty/ee11/jstl/JspConfig.java b/jetty-ee11/jetty-ee11-glassfish-jstl/src/test/java/org/eclipse/jetty/ee11/jstl/JspConfig.java new file mode 100644 index 00000000000..deb61b1abe1 --- /dev/null +++ b/jetty-ee11/jetty-ee11-glassfish-jstl/src/test/java/org/eclipse/jetty/ee11/jstl/JspConfig.java @@ -0,0 +1,37 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.jstl; + +import java.io.File; +import java.net.URI; +import java.nio.file.Path; + +import jakarta.servlet.ServletContext; +import org.eclipse.jetty.ee11.webapp.WebAppContext; + +/** + * Attempt at collecting up all of the JSP specific configuration bits and pieces into a single place + * for WebAppContext users to utilize. + */ +public class JspConfig +{ + public static void init(WebAppContext context, URI baseUri, File scratchDir) + { + context.setAttribute(ServletContext.TEMPDIR, scratchDir); + context.setAttribute("org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern", + ".*/jetty-jakarta-servlet-api-[^/]*\\.jar$|.*jakarta.servlet.jsp.jstl-[^/]*\\.jar|.*taglibs-standard.*\\.jar"); + context.setWar(baseUri.toASCIIString()); + context.setBaseResourceAsPath(Path.of(baseUri)); + } +} diff --git a/jetty-ee11/jetty-ee11-glassfish-jstl/src/test/java/org/eclipse/jetty/ee11/jstl/JspIncludeTest.java b/jetty-ee11/jetty-ee11-glassfish-jstl/src/test/java/org/eclipse/jetty/ee11/jstl/JspIncludeTest.java new file mode 100644 index 00000000000..f04da53deea --- /dev/null +++ b/jetty-ee11/jetty-ee11-glassfish-jstl/src/test/java/org/eclipse/jetty/ee11/jstl/JspIncludeTest.java @@ -0,0 +1,178 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.jstl; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.StringWriter; +import java.net.HttpURLConnection; +import java.net.URI; +import java.nio.file.Path; + +import org.eclipse.jetty.ee11.annotations.AnnotationConfiguration; +import org.eclipse.jetty.ee11.webapp.Configurations; +import org.eclipse.jetty.ee11.webapp.JettyWebXmlConfiguration; +import org.eclipse.jetty.ee11.webapp.WebAppContext; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.toolchain.test.FS; +import org.eclipse.jetty.toolchain.test.IO; +import org.eclipse.jetty.toolchain.test.JAR; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +@ExtendWith(WorkDirExtension.class) +public class JspIncludeTest +{ + private static Server server; + private static URI baseUri; + + @BeforeAll + public static void startServer(WorkDir workDir) throws Exception + { + Path tmpPath = workDir.getEmptyPathDir(); + // Setup Server + server = new Server(); + ServerConnector connector = new ServerConnector(server); + connector.setPort(0); + server.addConnector(connector); + + //Base dir for test + File testDir = tmpPath.toFile(); + File testLibDir = new File(testDir, "WEB-INF/lib"); + FS.ensureDirExists(testLibDir); + + //Make a taglib jar + File srcTagLibDir = MavenTestingUtils.getProjectDir("src/test/taglibjar"); + File scratchTagLibDir = new File(testDir, JspIncludeTest.class.getSimpleName() + "-taglib-scratch"); + IO.copy(srcTagLibDir, scratchTagLibDir); + File tagLibJar = new File(testLibDir, "testtaglib.jar"); + JAR.create(scratchTagLibDir, tagLibJar); + + //Copy content + File destWebAppDir = new File(testDir, "webapp"); + FS.ensureDirExists(destWebAppDir); + File srcWebAppDir = MavenTestingUtils.getProjectDir("src/test/webapp"); + IO.copyDir(srcWebAppDir, destWebAppDir); + + // Configure WebAppContext + Configurations.setServerDefault(server).add(new JettyWebXmlConfiguration(), new AnnotationConfiguration()); + + WebAppContext context = new WebAppContext(); + context.setContextPath("/"); + + File scratchDir = new File(testDir, JspIncludeTest.class.getSimpleName() + "-scratch"); + FS.ensureEmpty(scratchDir); + JspConfig.init(context, destWebAppDir.toURI(), scratchDir); + + server.setHandler(context); + + // Start Server + server.start(); + + // Figure out Base URI + String host = connector.getHost(); + if (host == null) + { + host = "localhost"; + } + int port = connector.getLocalPort(); + baseUri = new URI(String.format("http://%s:%d/", host, port)); + } + + @AfterAll + public static void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testTopWithIncluded() throws IOException + { + URI uri = baseUri.resolve("/top.jsp"); + + InputStream in = null; + InputStreamReader reader = null; + HttpURLConnection connection = null; + + try + { + connection = (HttpURLConnection)uri.toURL().openConnection(); + connection.connect(); + if (HttpURLConnection.HTTP_OK != connection.getResponseCode()) + { + String body = getPotentialBody(connection); + String err = String.format("GET request failed (%d %s) %s%n%s", connection.getResponseCode(), connection.getResponseMessage(), + uri.toASCIIString(), body); + throw new IOException(err); + } + in = connection.getInputStream(); + reader = new InputStreamReader(in); + StringWriter writer = new StringWriter(); + IO.copy(reader, writer); + + String response = writer.toString(); + // System.out.printf("Response%n%s",response); + assertThat("Response", response, containsString("

Hello, this is the top page.")); + assertThat("Response", response, containsString("

This is the included page")); + assertThat("Response Header[main-page-key]", connection.getHeaderField("main-page-key"), is("main-page-value")); + assertThat("Response Header[included-page-key]", connection.getHeaderField("included-page-key"), is("included-page-value")); + } + finally + { + IO.close(reader); + IO.close(in); + } + } + + /** + * Attempt to obtain the body text if available. Do not throw an exception if body is unable to be fetched. + * + * @param connection the connection to fetch the body content from. + * @return the body content, if present. + */ + private String getPotentialBody(HttpURLConnection connection) + { + InputStream in = null; + InputStreamReader reader = null; + try + { + in = connection.getInputStream(); + reader = new InputStreamReader(in); + StringWriter writer = new StringWriter(); + IO.copy(reader, writer); + return writer.toString(); + } + catch (IOException e) + { + return ""; + } + finally + { + IO.close(reader); + IO.close(in); + } + } +} diff --git a/jetty-ee11/jetty-ee11-glassfish-jstl/src/test/java/org/eclipse/jetty/ee11/jstl/JstlTest.java b/jetty-ee11/jetty-ee11-glassfish-jstl/src/test/java/org/eclipse/jetty/ee11/jstl/JstlTest.java new file mode 100644 index 00000000000..dc4fe19721d --- /dev/null +++ b/jetty-ee11/jetty-ee11-glassfish-jstl/src/test/java/org/eclipse/jetty/ee11/jstl/JstlTest.java @@ -0,0 +1,144 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.jstl; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URI; +import java.nio.charset.StandardCharsets; + +import jakarta.servlet.jsp.JspException; +import org.eclipse.jetty.ee11.annotations.AnnotationConfiguration; +import org.eclipse.jetty.ee11.webapp.WebAppContext; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.toolchain.test.FS; +import org.eclipse.jetty.toolchain.test.JAR; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.util.IO; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; + +public class JstlTest +{ + private static Server server; + private static URI baseUri; + + @BeforeAll + public static void startServer() throws Exception + { + // Setup Server + server = new Server(); + ServerConnector connector = new ServerConnector(server); + connector.setPort(0); + server.addConnector(connector); + + //Base dir for test + File testDir = MavenTestingUtils.getTargetTestingDir("jstl"); + File testLibDir = new File(testDir, "WEB-INF/lib"); + FS.ensureDirExists(testLibDir); + + //Make a taglib jar + File srcTagLibDir = MavenTestingUtils.getProjectDir("src/test/taglibjar"); + File scratchTagLibDir = MavenTestingUtils.getTargetFile("tests/" + JstlTest.class.getSimpleName() + "-taglib-scratch"); + IO.copy(srcTagLibDir, scratchTagLibDir); + File tagLibJar = new File(testLibDir, "testtaglib.jar"); + JAR.create(scratchTagLibDir, tagLibJar); + + //Copy content + File srcWebAppDir = MavenTestingUtils.getProjectDir("src/test/webapp"); + IO.copyDir(srcWebAppDir, testDir); + + // Configure WebAppCont + WebAppContext context = new WebAppContext(); + context.setContextPath("/"); + + File scratchDir = MavenTestingUtils.getTargetFile("tests/" + JstlTest.class.getSimpleName() + "-scratch"); + FS.ensureEmpty(scratchDir); + JspConfig.init(context, testDir.toURI(), scratchDir); + + context.addConfiguration(new AnnotationConfiguration()); + + server.setHandler(context); + + // Start Server + server.start(); + + // Figure out Base URI + String host = connector.getHost(); + if (host == null) + { + host = "localhost"; + } + int port = connector.getLocalPort(); + baseUri = new URI(String.format("http://%s:%d/", host, port)); + } + + @AfterAll + public static void stopServer() throws Exception + { + if (server != null) + server.stop(); + } + + @Test + public void testUrlsBasic() throws IOException + { + HttpURLConnection http = (HttpURLConnection)baseUri.resolve("/urls.jsp").toURL().openConnection(); + assertThat("http response", http.getResponseCode(), is(200)); + try (InputStream input = http.getInputStream()) + { + String resp = IO.toString(input, StandardCharsets.UTF_8); + assertThat("Response should be JSP processed", resp, not(containsString(""))); + assertThat("Response should be JSP processed", resp, not(containsString(""))); + assertThat("Response", resp, not(containsString("[jtest:errorhandler] exception is null"))); + } + } +} diff --git a/jetty-ee11/jetty-ee11-glassfish-jstl/src/test/resources/jetty-logging.properties b/jetty-ee11/jetty-ee11-glassfish-jstl/src/test/resources/jetty-logging.properties new file mode 100644 index 00000000000..c1f44baf179 --- /dev/null +++ b/jetty-ee11/jetty-ee11-glassfish-jstl/src/test/resources/jetty-logging.properties @@ -0,0 +1,3 @@ +# Jetty Logging using jetty-slf4j-impl +# org.eclipse.jetty.LEVEL=INFO +# org.eclipse.jetty.util.LEVEL=DEBUG diff --git a/jetty-ee11/jetty-ee11-glassfish-jstl/src/test/taglibjar/META-INF/etag.tld b/jetty-ee11/jetty-ee11-glassfish-jstl/src/test/taglibjar/META-INF/etag.tld new file mode 100644 index 00000000000..de59ff05a3a --- /dev/null +++ b/jetty-ee11/jetty-ee11-glassfish-jstl/src/test/taglibjar/META-INF/etag.tld @@ -0,0 +1,16 @@ + + + + eclipse jetty test taglib + 1.0 + + jtest + org.eclipse.jetty.jstl.jtest + + + errorhandler + /META-INF/tags/errorhandler.tag + + \ No newline at end of file diff --git a/jetty-ee11/jetty-ee11-glassfish-jstl/src/test/taglibjar/META-INF/tags/errorhandler.tag b/jetty-ee11/jetty-ee11-glassfish-jstl/src/test/taglibjar/META-INF/tags/errorhandler.tag new file mode 100644 index 00000000000..ddd44dd4910 --- /dev/null +++ b/jetty-ee11/jetty-ee11-glassfish-jstl/src/test/taglibjar/META-INF/tags/errorhandler.tag @@ -0,0 +1,12 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> + + + + + +[jtest:errorhandler] exception : ${tossable} +[jtest:errorhandler] exception.message : ${tossable.message} + + +[jtest:errorhandler] exception is null + diff --git a/jetty-ee11/jetty-ee11-glassfish-jstl/src/test/webapp/WEB-INF/web.xml b/jetty-ee11/jetty-ee11-glassfish-jstl/src/test/webapp/WEB-INF/web.xml new file mode 100644 index 00000000000..1043ddea79f --- /dev/null +++ b/jetty-ee11/jetty-ee11-glassfish-jstl/src/test/webapp/WEB-INF/web.xml @@ -0,0 +1,7 @@ + + + Test webapp for JSTL + \ No newline at end of file diff --git a/jetty-ee11/jetty-ee11-glassfish-jstl/src/test/webapp/catch-basic.jsp b/jetty-ee11/jetty-ee11-glassfish-jstl/src/test/webapp/catch-basic.jsp new file mode 100644 index 00000000000..06e2bcfd2bc --- /dev/null +++ b/jetty-ee11/jetty-ee11-glassfish-jstl/src/test/webapp/catch-basic.jsp @@ -0,0 +1,16 @@ +<%@ page contentType="text/plain; charset=UTF-8" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %> +Title: JSTL c:catch test + + + + + + +[c:catch] exception : ${catchException} +[c:catch] exception.message : ${catchException.message} + + +[c:catch] exception is null + diff --git a/jetty-ee11/jetty-ee11-glassfish-jstl/src/test/webapp/catch-taglib.jsp b/jetty-ee11/jetty-ee11-glassfish-jstl/src/test/webapp/catch-taglib.jsp new file mode 100644 index 00000000000..28f7488e2ab --- /dev/null +++ b/jetty-ee11/jetty-ee11-glassfish-jstl/src/test/webapp/catch-taglib.jsp @@ -0,0 +1,11 @@ +<%@ page contentType="text/plain; charset=UTF-8" %> +<%@ taglib uri="org.eclipse.jetty.jstl.jtest" prefix="jtest" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %> +Title: JSTL c:catch test + + + + + +parsedNum = \ No newline at end of file diff --git a/jetty-ee11/jetty-ee11-glassfish-jstl/src/test/webapp/included.jsp b/jetty-ee11/jetty-ee11-glassfish-jstl/src/test/webapp/included.jsp new file mode 100644 index 00000000000..dde5f29c45d --- /dev/null +++ b/jetty-ee11/jetty-ee11-glassfish-jstl/src/test/webapp/included.jsp @@ -0,0 +1,8 @@ +<% + String headerPrefix = ""; + if(request.getDispatcherType() == DispatcherType.INCLUDE) + headerPrefix = "org.eclipse.jetty.server.include."; + + response.setHeader(headerPrefix + "included-page-key","included-page-value"); +%> +

This is the included page \ No newline at end of file diff --git a/jetty-ee11/jetty-ee11-glassfish-jstl/src/test/webapp/ref.jsp b/jetty-ee11/jetty-ee11-glassfish-jstl/src/test/webapp/ref.jsp new file mode 100644 index 00000000000..0debf754890 --- /dev/null +++ b/jetty-ee11/jetty-ee11-glassfish-jstl/src/test/webapp/ref.jsp @@ -0,0 +1,2 @@ +<%@ page contentType="text/plain; charset=UTF-8" %> +Reference Page: No useful content here, just used for other tests \ No newline at end of file diff --git a/jetty-ee11/jetty-ee11-glassfish-jstl/src/test/webapp/top.jsp b/jetty-ee11/jetty-ee11-glassfish-jstl/src/test/webapp/top.jsp new file mode 100644 index 00000000000..6feebaea4eb --- /dev/null +++ b/jetty-ee11/jetty-ee11-glassfish-jstl/src/test/webapp/top.jsp @@ -0,0 +1,5 @@ +<% + application.getRequestDispatcher("/included.jsp").include(request,response); + response.setHeader("main-page-key", "main-page-value"); +%> +

Hello, this is the top page. diff --git a/jetty-ee11/jetty-ee11-glassfish-jstl/src/test/webapp/urls.jsp b/jetty-ee11/jetty-ee11-glassfish-jstl/src/test/webapp/urls.jsp new file mode 100644 index 00000000000..bc18e9ac5f5 --- /dev/null +++ b/jetty-ee11/jetty-ee11-glassfish-jstl/src/test/webapp/urls.jsp @@ -0,0 +1,6 @@ +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> +<%@ page contentType="text/plain; charset=UTF-8" %> +Title: JSTL c:url Tests +[c:url value] = + +[c:url param] = diff --git a/jetty-ee11/jetty-ee11-home/pom.xml b/jetty-ee11/jetty-ee11-home/pom.xml new file mode 100644 index 00000000000..af868381e8c --- /dev/null +++ b/jetty-ee11/jetty-ee11-home/pom.xml @@ -0,0 +1,576 @@ + + + 4.0.0 + + org.eclipse.jetty.ee11 + jetty-ee11 + 12.1.0-SNAPSHOT + ../pom.xml + + jetty-ee11-home + pom + EE11 :: Home Assembly + + + ${basedir}/target/jetty-ee11-home + ${basedir}/target/jetty-ee11-home-sources + true + + + + + org.eclipse.jetty + jetty-openid + true + + + org.eclipse.jetty + jetty-security + true + + + org.eclipse.jetty.ee11 + jetty-ee11-annotations + + + org.eclipse.jetty.ee11 + jetty-ee11-apache-jsp + + + org.eclipse.jetty.ee11 + jetty-ee11-cdi + true + + + org.eclipse.jetty.ee11 + jetty-ee11-fcgi-proxy + true + + + org.eclipse.jetty.ee11 + jetty-ee11-glassfish-jstl + + + jakarta.el + jakarta.el-api + + + javax.el + el-api + + + + + org.eclipse.jetty.ee11 + jetty-ee11-jaspi + true + + + org.eclipse.jetty.ee11 + jetty-ee11-jndi + true + + + org.eclipse.jetty.ee11 + jetty-ee11-plus + + + org.eclipse.jetty.ee11 + jetty-ee11-proxy + + + + + org.eclipse.jetty.ee11 + jetty-ee11-quickstart + + + org.eclipse.jetty.ee11 + jetty-ee11-servlet + + + org.eclipse.jetty.ee11 + jetty-ee11-servlets + + + org.eclipse.jetty.ee11.demos + jetty-ee11-demo-async-rest-webapp + ${project.version} + config + jar + true + + + org.eclipse.jetty.ee11.demos + jetty-ee11-demo-jaas-webapp + ${project.version} + config + jar + true + + + org.eclipse.jetty.ee11.demos + jetty-ee11-demo-jetty-webapp + ${project.version} + config + jar + true + + + javax.el + el-api + + + + + org.eclipse.jetty.ee11.demos + jetty-ee11-demo-jndi-webapp + ${project.version} + config + jar + true + + + org.eclipse.jetty.ee11.demos + jetty-ee11-demo-jsp-webapp + ${project.version} + config + jar + true + + + + org.eclipse.jetty.ee11.demos + jetty-ee11-demo-mock-resources + ${project.version} + config + jar + true + + + org.eclipse.jetty.ee11.demos + jetty-ee11-demo-proxy-webapp + ${project.version} + config + jar + true + + + org.eclipse.jetty.ee11.demos + jetty-ee11-demo-simple-webapp + ${project.version} + config + jar + true + + + org.eclipse.jetty.ee11.demos + jetty-ee11-demo-spec-webapp + ${project.version} + config + jar + true + + + org.eclipse.jetty.ee11.websocket + jetty-ee11-websocket-jakarta-server + + + org.eclipse.jetty.ee11.websocket + jetty-ee11-websocket-jetty-client-webapp + + + org.eclipse.jetty.ee11.websocket + jetty-ee11-websocket-jetty-server + + + org.eclipse.jetty.ee11.websocket + jetty-ee11-websocket-servlet + + + org.ow2.asm + asm + + + org.ow2.asm + asm-analysis + + + org.ow2.asm + asm-commons + + + org.ow2.asm + asm-tree + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + posix + false + + + + binary + + single + + package + + + src/main/assembly/jetty-assembly.xml + + + + + sources + + single + + package + + + src/main/assembly/jetty-source-assembly.xml + + true + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy-ee11-annotations-deps + + copy-dependencies + + generate-resources + + jakarta.annotation,org.ow2.asm + jakarta.annotation-api,asm,asm-commons,asm-tree,asm-analysis + jar + ${assembly-directory}/lib/ee11-annotations + + + + copy-ee11-annotations-src-deps + + copy-dependencies + + generate-resources + + jakarta.annotation,org.ow2.asm + jakarta.annotation-api,asm,asm-commons,asm-tree,asm-analysis + jar + sources + ${source-assembly-directory}/lib/ee11-annotations + + + + + copy-ee11-deps + + copy-dependencies + + generate-resources + + org.eclipse.jetty.ee11 + jetty-websocket-core-client,jetty-websocket-core-common,jetty-websocket-core-server,jetty-websocket-jetty-api,jetty-websocket-jetty-client,jetty-websocket-jetty-common,jetty-websocket-jetty-server + org.eclipse.jetty.ee11.demos,org.eclipse.jetty.ee11.websocket + jar + ${assembly-directory}/lib + + + + copy-ee11-jaspi-deps + + copy-dependencies + + generate-resources + + jakarta.authentication + jakarta.authentication-api + jar + ${assembly-directory}/lib/ee11-jaspi + + + + copy-ee11-jaspi-src-deps + + copy-dependencies + + generate-resources + + jakarta.authentication + jakarta.authentication-api + jar + sources + ${source-assembly-directory}/lib/ee11-jaspi + + + + copy-ee11-jsp-deps + + copy-dependencies + + generate-resources + + true + jakarta.servlet.jsp,jakarta.el,org.mortbay.jasper,org.eclipse.jdt + jakarta.servlet.jsp-api,jakarta.el-api,apache-el,apache-jsp,ecj + jar + ${assembly-directory}/lib/ee11-apache-jsp + + + + copy-ee11-jsp-src-deps + + copy-dependencies + + generate-resources + + true + jakarta.servlet.jsp,jakart.el,org.mortbay.jasper,org.eclipse.jdt + jakart.servlet.jsp-api,jakarta.el-api,apache-el,apache-jsp,ecj + jar + sources + ${source-assembly-directory}/lib/ee11-apache-jsp + + + + copy-ee11-jstl-deps + + copy-dependencies + + generate-resources + + true + jakarta.servlet.jsp.jstl,org.glassfish.web + jakarta.servlet.jsp.jstl-api,jakarta.servlet.jsp.jstl + jar + ${assembly-directory}/lib/ee11-glassfish-jstl + + + + copy-ee11-jstl-src-deps + + copy-dependencies + + generate-resources + + true + jakarta.servlet.jsp.jstl,org.glassfish.web + jakarta.servlet.jsp.jstl-api,jakarta.servlet.jsp.jstl + jar + sources + ${source-assembly-directory}/lib/ee11-glassfish-jstl + + + + copy-ee11-lib-jakarta-websocket-deps + + copy-dependencies + + generate-resources + + jakarta.websocket + jar + ${assembly-directory}/lib/ee11-websocket + + + + copy-ee11-lib-jakarta-websocket-src-deps + + copy-dependencies + + generate-resources + + jakarta.websocket + jar + sources + ${source-assembly-directory}/lib/ee11-websocket + + + + copy-ee11-src-deps + + copy-dependencies + + generate-resources + + org.eclipse.jetty.ee11 + jar + org.eclipse.jetty.ee11.demos,org.eclipse.jetty.ee11.websocket + sources + ${source-assembly-directory}/lib + + + + copy-lib-core-websocket-deps + + copy-dependencies + + generate-resources + + org.eclipse.jetty.websocket + jar + ${assembly-directory}/lib + + + + copy-lib-core-websocket-src-deps + + copy-dependencies + + generate-resources + + org.eclipse.jetty.websocket + jar + sources + ${source-assembly-directory}/lib + + + + copy-lib-ee11-websocket-deps + + copy-dependencies + + generate-resources + + org.eclipse.jetty.ee11.websocket + jar + ${assembly-directory}/lib/ee11-websocket + + + + copy-lib-ee11-websocket-src-deps + + copy-dependencies + + generate-resources + + org.eclipse.jetty.ee11.websocket + jar + sources + ${source-assembly-directory}/lib/ee11-websocket + + + + copy-lib-servlet-api-deps + + copy + + generate-resources + + + + jakarta.servlet + jakarta.servlet-api + ${jakarta.servlet.api.version} + + + ${assembly-directory}/lib + + + + copy-lib-servlet-api-src-deps + + copy + + generate-resources + + + + jakarta.servlet + jakarta.servlet-api + ${jakarta.servlet.api.version} + sources + + + ${source-assembly-directory}/lib + + + + copy-lib-transaction-api-deps + + copy + + generate-resources + + + + jakarta.transaction + jakarta.transaction-api + ${jakarta.transaction-api.version} + + + jakarta.interceptor + jakarta.interceptor-api + ${jakarta.interceptor.api.version} + + + jakarta.enterprise + jakarta.enterprise.cdi-api + ${jakarta.enterprise.cdi.api.version} + + + jakarta.inject + jakarta.inject-api + ${jakarta.inject.api.version} + + + jakarta.enterprise + jakarta.enterprise.lang-model + ${jakarta.enterprise.lang.model.version} + + + ${assembly-directory}/lib + + + + + copy-lib-transaction-api-src-deps + + copy + + generate-resources + + + + jakarta.transaction + jakarta.transaction-api + ${jakarta.transaction-api.version} + sources + + + ${source-assembly-directory}/lib + + + + unpack-config-deps + + unpack-dependencies + + generate-resources + + org.eclipse.jetty.ee11, org.eclipse.jetty.ee11.demos + + config + false + META-INF/**,webapps/**,start.d/**,start.ini + ${assembly-directory} + + + + + + + + diff --git a/jetty-ee11/jetty-ee11-home/src/main/assembly/jetty-assembly.xml b/jetty-ee11/jetty-ee11-home/src/main/assembly/jetty-assembly.xml new file mode 100644 index 00000000000..dc2f3d984d6 --- /dev/null +++ b/jetty-ee11/jetty-ee11-home/src/main/assembly/jetty-assembly.xml @@ -0,0 +1,20 @@ + + binary-assembly + + tar.gz + zip + + false + + + ${assembly-directory} + + + ** + + + **/META-INF/** + + + + diff --git a/jetty-ee11/jetty-ee11-home/src/main/assembly/jetty-source-assembly.xml b/jetty-ee11/jetty-ee11-home/src/main/assembly/jetty-source-assembly.xml new file mode 100644 index 00000000000..cf9b6f56ffe --- /dev/null +++ b/jetty-ee11/jetty-ee11-home/src/main/assembly/jetty-source-assembly.xml @@ -0,0 +1,17 @@ + + sources + + tar.gz + zip + + false + + + ${source-assembly-directory} + + + ** + + + + diff --git a/jetty-ee11/jetty-ee11-jaspi/pom.xml b/jetty-ee11/jetty-ee11-jaspi/pom.xml new file mode 100644 index 00000000000..9746107889d --- /dev/null +++ b/jetty-ee11/jetty-ee11-jaspi/pom.xml @@ -0,0 +1,75 @@ + + + + 4.0.0 + + org.eclipse.jetty.ee11 + jetty-ee11 + 12.1.0-SNAPSHOT + + jetty-ee11-jaspi + EE11 :: JASPI + Jetty security infrastructure + + + ${project.groupId}.security.jaspi + org.eclipse.jetty.ee11.security.jaspi.* + + + + + jakarta.authentication + jakarta.authentication-api + + + jakarta.servlet + jakarta.servlet-api + + + org.eclipse.jetty.ee11 + jetty-ee11-servlet + + + org.slf4j + slf4j-api + + + jakarta.activation + jakarta.activation-api + test + + + org.eclipse.jetty + jetty-slf4j-impl + test + + + org.eclipse.jetty.toolchain + jetty-test-helper + test + + + + + + + org.apache.felix + maven-bundle-plugin + true + + + + manifest + + + + osgi.extender; filter:="(osgi.extender=osgi.serviceloader.registrar)" + osgi.serviceloader;osgi.serviceloader=org.eclipse.jetty.ee11.servlet.security.Authenticator$Factory + + + + + + + + diff --git a/jetty-ee11/jetty-ee11-jaspi/src/main/config/etc/jaspi/jetty-ee11-jaspi-authmoduleconfig.xml b/jetty-ee11/jetty-ee11-jaspi/src/main/config/etc/jaspi/jetty-ee11-jaspi-authmoduleconfig.xml new file mode 100644 index 00000000000..189888d9d57 --- /dev/null +++ b/jetty-ee11/jetty-ee11-jaspi/src/main/config/etc/jaspi/jetty-ee11-jaspi-authmoduleconfig.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/jetty-ee11/jetty-ee11-jaspi/src/main/config/etc/jaspi/jetty-ee11-jaspi-default.xml b/jetty-ee11/jetty-ee11-jaspi/src/main/config/etc/jaspi/jetty-ee11-jaspi-default.xml new file mode 100644 index 00000000000..e7060566cb9 --- /dev/null +++ b/jetty-ee11/jetty-ee11-jaspi/src/main/config/etc/jaspi/jetty-ee11-jaspi-default.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + false + + diff --git a/jetty-ee11/jetty-ee11-jaspi/src/main/config/etc/jaspi/jetty-ee11-jaspi-demo.xml b/jetty-ee11/jetty-ee11-jaspi/src/main/config/etc/jaspi/jetty-ee11-jaspi-demo.xml new file mode 100644 index 00000000000..2b5ac1534c1 --- /dev/null +++ b/jetty-ee11/jetty-ee11-jaspi/src/main/config/etc/jaspi/jetty-ee11-jaspi-demo.xml @@ -0,0 +1,48 @@ + + + + + + + + + org.eclipse.jetty.ee11.security.jaspi.provider.JaspiAuthConfigProvider + + + + + + + ServerAuthModule + org.eclipse.jetty.ee11.security.jaspi.modules.BasicAuthenticationAuthModule + + + + org.eclipse.jetty.ee11.security.jaspi.modules.RealmName + Test Realm + + + + + + HttpServlet + + + server /test + + + A simple provider using HTTP BASIC authentication. + + + diff --git a/jetty-ee11/jetty-ee11-jaspi/src/main/config/modules/ee11-jaspi-default-auth-config-factory.mod b/jetty-ee11/jetty-ee11-jaspi/src/main/config/modules/ee11-jaspi-default-auth-config-factory.mod new file mode 100644 index 00000000000..1fb25c739eb --- /dev/null +++ b/jetty-ee11/jetty-ee11-jaspi/src/main/config/modules/ee11-jaspi-default-auth-config-factory.mod @@ -0,0 +1,19 @@ +# DO NOT EDIT THIS FILE - See: https://eclipse.dev/jetty/documentation/ + +[description] +Provides a DefaultAuthConfigFactory for jaspi + +[environment] +ee11 + +[tags] +security + +[depend] +ee11-security + +[provide] +auth-config-factory + +[xml] +etc/jaspi/jetty-ee11-jaspi-default.xml diff --git a/jetty-ee11/jetty-ee11-jaspi/src/main/config/modules/ee11-jaspi-demo.mod b/jetty-ee11/jetty-ee11-jaspi/src/main/config/modules/ee11-jaspi-demo.mod new file mode 100644 index 00000000000..d32296a3190 --- /dev/null +++ b/jetty-ee11/jetty-ee11-jaspi/src/main/config/modules/ee11-jaspi-demo.mod @@ -0,0 +1,19 @@ +# DO NOT EDIT THIS FILE - See: https://eclipse.dev/jetty/documentation/ + +[description] +Enables JASPI basic authentication the /test context path. + +[environment] +ee11 + +[tags] +security + +[depend] +jaspi + +[xml] +etc/jaspi/jetty-ee11-jaspi-demo.xml + +[files] +basehome:etc/jaspi/jetty-ee11-jaspi-demo.xml|etc/jaspi/jetty-ee11-jaspi-demo.xml diff --git a/jetty-ee11/jetty-ee11-jaspi/src/main/config/modules/ee11-jaspi.mod b/jetty-ee11/jetty-ee11-jaspi/src/main/config/modules/ee11-jaspi.mod new file mode 100644 index 00000000000..e18fcf6da8b --- /dev/null +++ b/jetty-ee11/jetty-ee11-jaspi/src/main/config/modules/ee11-jaspi.mod @@ -0,0 +1,28 @@ +# DO NOT EDIT THIS FILE - See: https://eclipse.dev/jetty/documentation/ + +[description] +Enables JASPI authentication for deployed web applications. + +[environment] +ee11 + +[tags] +security + +[depend] +ee11-security +auth-config-factory + +[ini] +ee11.jakarta.authentication.api.version?=@jakarta.authentication.api.version@ + +[lib] +lib/jetty-ee11-jaspi-${jetty.version}.jar +lib/ee11-jaspi/jakarta.authentication-api-${ee11.jakarta.authentication.api.version}.jar + +[xml] +etc/jaspi/jetty-ee11-jaspi-authmoduleconfig.xml + +[files] +basehome:etc/jaspi/jetty-ee11-jaspi-authmoduleconfig.xml|etc/jaspi/jetty-ee11-jaspi-authmoduleconfig.xml + diff --git a/jetty-ee11/jetty-ee11-jaspi/src/main/java/module-info.java b/jetty-ee11/jetty-ee11-jaspi/src/main/java/module-info.java new file mode 100644 index 00000000000..ece6a09d3ff --- /dev/null +++ b/jetty-ee11/jetty-ee11-jaspi/src/main/java/module-info.java @@ -0,0 +1,29 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +module org.eclipse.jetty.ee11.security.jaspi +{ + requires jakarta.servlet; + requires org.slf4j; + + requires transitive jakarta.security.auth.message; + requires transitive org.eclipse.jetty.ee11.servlet; + + exports org.eclipse.jetty.ee11.security.jaspi; + exports org.eclipse.jetty.ee11.security.jaspi.callback; + exports org.eclipse.jetty.ee11.security.jaspi.modules; + exports org.eclipse.jetty.ee11.security.jaspi.provider; + + provides org.eclipse.jetty.security.Authenticator.Factory with + org.eclipse.jetty.ee11.security.jaspi.JaspiAuthenticatorFactory; +} diff --git a/jetty-ee11/jetty-ee11-jaspi/src/main/java/org/eclipse/jetty/ee11/security/jaspi/DefaultAuthConfigFactory.java b/jetty-ee11/jetty-ee11-jaspi/src/main/java/org/eclipse/jetty/ee11/security/jaspi/DefaultAuthConfigFactory.java new file mode 100644 index 00000000000..362f6527751 --- /dev/null +++ b/jetty-ee11/jetty-ee11-jaspi/src/main/java/org/eclipse/jetty/ee11/security/jaspi/DefaultAuthConfigFactory.java @@ -0,0 +1,260 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.security.jaspi; + +import java.security.SecurityPermission; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; + +import jakarta.security.auth.message.config.AuthConfigFactory; +import jakarta.security.auth.message.config.AuthConfigProvider; +import jakarta.security.auth.message.config.RegistrationListener; +import jakarta.security.auth.message.module.ServerAuthModule; +import org.eclipse.jetty.util.security.SecurityUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A very basic {@link AuthConfigFactory} that allows for registering providers programmatically. + */ +public class DefaultAuthConfigFactory extends AuthConfigFactory +{ + private static final Logger LOG = LoggerFactory.getLogger(DefaultAuthConfigFactory.class); + private final Map _registrations = new ConcurrentHashMap<>(); + + public DefaultAuthConfigFactory() + { + } + + @Override + public AuthConfigProvider getConfigProvider(String layer, String appContext, RegistrationListener listener) + { + DefaultRegistrationContext registrationContext = _registrations.get(getKey(layer, appContext)); + if (registrationContext == null) + registrationContext = _registrations.get(getKey(null, appContext)); + if (registrationContext == null) + registrationContext = _registrations.get(getKey(layer, null)); + if (registrationContext == null) + registrationContext = _registrations.get(getKey(null, null)); + if (registrationContext == null) + return null; + + // TODO: according to the javadoc you're supposed to register listener even if there is no context available. + if (listener != null) + registrationContext.addListener(listener); + return registrationContext.getProvider(); + } + + @Override + public String registerConfigProvider(String className, Map properties, String layer, String appContext, String description) + { + checkPermission(); + + String key = getKey(layer, appContext); + AuthConfigProvider configProvider = createConfigProvider(className, properties); + DefaultRegistrationContext context = new DefaultRegistrationContext(configProvider, layer, appContext, description, true); + DefaultRegistrationContext oldContext = _registrations.put(key, context); + if (oldContext != null) + oldContext.notifyListeners(); + return key; + } + + @Override + public String registerConfigProvider(AuthConfigProvider provider, String layer, String appContext, String description) + { + checkPermission(); + + String key = getKey(layer, appContext); + DefaultRegistrationContext context = new DefaultRegistrationContext(provider, layer, appContext, description, false); + DefaultRegistrationContext oldContext = _registrations.put(key, context); + if (oldContext != null) + oldContext.notifyListeners(); + return key; + } + + @Override + public boolean removeRegistration(String registrationID) + { + checkPermission(); + + DefaultRegistrationContext registrationContext = _registrations.remove(registrationID); + if (registrationContext == null) + return false; + + registrationContext.notifyListeners(); + return true; + } + + @Override + public String registerServerAuthModule(ServerAuthModule serverAuthModule, Object context) + { + // TODO + throw new UnsupportedOperationException(); + } + + @Override + public void removeServerAuthModule(Object context) + { + // TODO + throw new UnsupportedOperationException(); + } + + @Override + public String[] detachListener(RegistrationListener listener, String layer, String appContext) + { + checkPermission(); + + List registrationIds = new ArrayList<>(); + for (DefaultRegistrationContext registration : _registrations.values()) + { + if ((layer == null || layer.equals(registration.getMessageLayer())) && (appContext == null || appContext.equals(registration.getAppContext()))) + { + if (registration.removeListener(listener)) + registrationIds.add(getKey(registration.getMessageLayer(), registration.getAppContext())); + } + } + + return registrationIds.toArray(new String[0]); + } + + @Override + public String[] getRegistrationIDs(AuthConfigProvider provider) + { + List registrationIds = new ArrayList<>(); + for (DefaultRegistrationContext registration : _registrations.values()) + { + if (provider == registration.getProvider()) + registrationIds.add(getKey(registration.getMessageLayer(), registration.getAppContext())); + } + + return registrationIds.toArray(new String[0]); + } + + @Override + public RegistrationContext getRegistrationContext(String registrationID) + { + return _registrations.get(registrationID); + } + + @Override + public void refresh() + { + checkPermission(); + + // TODO: maybe we should re-construct providers created from classname. + } + + private static void checkPermission() + { + SecurityUtils.checkPermission(new SecurityPermission(PROVIDER_REGISTRATION_PERMISSION_NAME)); + } + + private static String getKey(String layer, String appContext) + { + return layer + "/" + appContext; + } + + @SuppressWarnings("rawtypes") + private AuthConfigProvider createConfigProvider(String className, Map properties) + { + try + { + // Javadoc specifies all AuthConfigProvider implementations must have this constructor, and that + // to construct this we must pass a null value for the factory argument of the constructor. + return (AuthConfigProvider)Class.forName(className) + .getConstructor(Map.class, AuthConfigFactory.class) + .newInstance(properties, null); + } + catch (ReflectiveOperationException e) + { + throw new SecurityException(e); + } + } + + private static class DefaultRegistrationContext implements RegistrationContext + { + private final String _layer; + private final String _appContext; + private final boolean _persistent; + private final AuthConfigProvider _provider; + private final String _description; + private final List _listeners = new CopyOnWriteArrayList<>(); + + public DefaultRegistrationContext(AuthConfigProvider provider, String layer, String appContext, String description, boolean persistent) + { + _provider = provider; + _layer = layer; + _appContext = appContext; + _description = description; + _persistent = persistent; + } + + public AuthConfigProvider getProvider() + { + return _provider; + } + + @Override + public String getMessageLayer() + { + return _layer; + } + + @Override + public String getAppContext() + { + return _appContext; + } + + @Override + public String getDescription() + { + return _description; + } + + @Override + public boolean isPersistent() + { + return false; + } + + public void addListener(RegistrationListener listener) + { + _listeners.add(listener); + } + + public void notifyListeners() + { + for (RegistrationListener listener : _listeners) + { + try + { + listener.notify(_layer, _appContext); + } + catch (Throwable t) + { + LOG.warn("Error from RegistrationListener", t); + } + } + } + + public boolean removeListener(RegistrationListener listener) + { + return _listeners.remove(listener); + } + } +} diff --git a/jetty-ee11/jetty-ee11-jaspi/src/main/java/org/eclipse/jetty/ee11/security/jaspi/JaspiAuthenticator.java b/jetty-ee11/jetty-ee11-jaspi/src/main/java/org/eclipse/jetty/ee11/security/jaspi/JaspiAuthenticator.java new file mode 100644 index 00000000000..7e4974584a1 --- /dev/null +++ b/jetty-ee11/jetty-ee11-jaspi/src/main/java/org/eclipse/jetty/ee11/security/jaspi/JaspiAuthenticator.java @@ -0,0 +1,294 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.security.jaspi; + +import java.security.Principal; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import javax.security.auth.Subject; + +import jakarta.security.auth.message.AuthException; +import jakarta.security.auth.message.AuthStatus; +import jakarta.security.auth.message.callback.CallerPrincipalCallback; +import jakarta.security.auth.message.callback.GroupPrincipalCallback; +import jakarta.security.auth.message.config.AuthConfigFactory; +import jakarta.security.auth.message.config.AuthConfigProvider; +import jakarta.security.auth.message.config.RegistrationListener; +import jakarta.security.auth.message.config.ServerAuthConfig; +import jakarta.security.auth.message.config.ServerAuthContext; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; +import org.eclipse.jetty.ee11.servlet.ServletContextRequest; +import org.eclipse.jetty.security.AuthenticationState; +import org.eclipse.jetty.security.EmptyLoginService; +import org.eclipse.jetty.security.IdentityService; +import org.eclipse.jetty.security.LoginService; +import org.eclipse.jetty.security.ServerAuthException; +import org.eclipse.jetty.security.UserIdentity; +import org.eclipse.jetty.security.UserPrincipal; +import org.eclipse.jetty.security.authentication.LoginAuthenticator; +import org.eclipse.jetty.security.authentication.SessionAuthentication; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Response; +import org.eclipse.jetty.util.Callback; + +import static org.eclipse.jetty.ee11.security.jaspi.JaspiAuthenticatorFactory.MESSAGE_LAYER; + +/** + * Implementation of Jetty {@link LoginAuthenticator} that is a bridge from Jakarta Authentication to Jetty Security. + */ +@SuppressWarnings({"rawtypes", "unchecked"}) +public class JaspiAuthenticator extends LoginAuthenticator +{ + private final Subject _serviceSubject; + private final String _appContext; + private final boolean _allowLazyAuthentication; + private final AuthConfigFactory _authConfigFactory = AuthConfigFactory.getFactory(); + private Map _authProperties; + private IdentityService _identityService; + private ServletCallbackHandler _callbackHandler; + private ServerAuthConfig _authConfig; + + public JaspiAuthenticator(Subject serviceSubject, String appContext, boolean allowLazyAuthentication) + { + _serviceSubject = serviceSubject; + _appContext = appContext; + _allowLazyAuthentication = allowLazyAuthentication; + } + + @Deprecated + public JaspiAuthenticator(ServerAuthConfig authConfig, Map authProperties, ServletCallbackHandler callbackHandler, Subject serviceSubject, boolean allowLazyAuthentication, IdentityService identityService) + { + if (callbackHandler == null) + throw new NullPointerException("No CallbackHandler"); + if (authConfig == null) + throw new NullPointerException("No AuthConfig"); + this._authProperties = authProperties; + this._callbackHandler = callbackHandler; + this._serviceSubject = serviceSubject; + this._allowLazyAuthentication = allowLazyAuthentication; + this._identityService = identityService; + this._appContext = null; + this._authConfig = authConfig; + } + + @Override + public void setConfiguration(Configuration configuration) + { + LoginService loginService = configuration.getLoginService(); + if (loginService == null) + { + // Add an empty login service so we can use JASPI without tying into Jetty auth mechanisms. + configuration = new JaspiAuthenticatorConfiguration(configuration); + loginService = configuration.getLoginService(); + } + + super.setConfiguration(configuration); + + // Only do this if the new constructor was used. + if (_authConfig == null) + { + _identityService = configuration.getIdentityService(); + _callbackHandler = new ServletCallbackHandler(loginService); + _authProperties = new HashMap(); + for (String key : configuration.getParameterNames()) + { + _authProperties.put(key, configuration.getParameter(key)); + } + } + } + + private ServerAuthConfig getAuthConfig() throws AuthException + { + if (_authConfig != null) + return _authConfig; + + RegistrationListener listener = (layer, appContext) -> _authConfig = null; + AuthConfigProvider authConfigProvider = _authConfigFactory.getConfigProvider(MESSAGE_LAYER, _appContext, listener); + if (authConfigProvider == null) + { + _authConfigFactory.detachListener(listener, MESSAGE_LAYER, _appContext); + return null; + } + + _authConfig = authConfigProvider.getServerAuthConfig(MESSAGE_LAYER, _appContext, _callbackHandler); + return _authConfig; + } + + @Override + public String getAuthenticationType() + { + return "JASPI"; + } + + @Override + public UserIdentity login(String username, Object password, Request request, Response response) + { + UserIdentity user = _loginService.login(username, password, request, request::getSession); + if (user != null) + { + updateSession(request, response); + HttpSession session = ((HttpServletRequest)request).getSession(true); + if (session != null) + { + SessionAuthentication sessionAuth = new SessionAuthentication(getAuthenticationType(), user, password); + session.setAttribute(SessionAuthentication.AUTHENTICATED_ATTRIBUTE, sessionAuth); + } + } + return user; + } + + @Override + public AuthenticationState validateRequest(Request request, Response response, Callback callback) throws ServerAuthException + { + JaspiMessageInfo info = new JaspiMessageInfo(request, response, callback); + request.setAttribute("org.eclipse.jetty.ee11.security.jaspi.info", info); + + return validateRequest(info); + } + + public AuthenticationState validateRequest(JaspiMessageInfo messageInfo) throws ServerAuthException + { + try + { + ServerAuthConfig authConfig = getAuthConfig(); + if (authConfig == null) + throw new ServerAuthException("No ServerAuthConfig"); + + String authContextId = authConfig.getAuthContextID(messageInfo); + ServerAuthContext authContext = authConfig.getAuthContext(authContextId, _serviceSubject, _authProperties); + Subject clientSubject = new Subject(); + + AuthStatus authStatus = authContext.validateRequest(messageInfo, clientSubject, _serviceSubject); + + if (authStatus == AuthStatus.SEND_CONTINUE) + return AuthenticationState.CHALLENGE; + if (authStatus == AuthStatus.SEND_FAILURE) + return AuthenticationState.SEND_FAILURE; + + if (authStatus == AuthStatus.SUCCESS) + { + Set ids = clientSubject.getPrivateCredentials(UserIdentity.class); + UserIdentity userIdentity; + if (ids.size() > 0) + { + userIdentity = ids.iterator().next(); + } + else + { + CallerPrincipalCallback principalCallback = _callbackHandler.getThreadCallerPrincipalCallback(); + if (principalCallback == null) + { + return null; + } + Principal principal = principalCallback.getPrincipal(); + if (principal == null) + { + String principalName = principalCallback.getName(); + + // TODO: if the Principal class is provided it doesn't need to be in subject, why do we enforce this here? + Set principals = principalCallback.getSubject().getPrincipals(); + for (Principal p : principals) + { + if (p.getName().equals(principalName)) + { + principal = p; + break; + } + } + if (principal == null) + { + principal = new UserPrincipal(principalName, null); + } + } + GroupPrincipalCallback groupPrincipalCallback = _callbackHandler.getThreadGroupPrincipalCallback(); + String[] groups = groupPrincipalCallback == null ? null : groupPrincipalCallback.getGroups(); + userIdentity = _identityService.newUserIdentity(clientSubject, principal, groups); + } + + HttpSession session = ((HttpServletRequest)messageInfo.getRequestMessage()).getSession(false); + AuthenticationState cached = (session == null ? null : (SessionAuthentication)session.getAttribute(SessionAuthentication.AUTHENTICATED_ATTRIBUTE)); + if (cached != null) + return cached; + + return new UserAuthenticationSucceeded(getAuthenticationType(), userIdentity); + } + if (authStatus == AuthStatus.SEND_SUCCESS) + { + // we are processing a message in a secureResponse dialog. + return AuthenticationState.SEND_SUCCESS; + } + if (authStatus == AuthStatus.FAILURE) + { + Response.writeError(messageInfo.getBaseRequest(), messageInfo.getBaseResponse(), messageInfo.getCallback(), HttpServletResponse.SC_FORBIDDEN); + return AuthenticationState.SEND_FAILURE; + } + // should not happen + throw new IllegalStateException("No AuthStatus returned"); + } + catch (AuthException e) + { + throw new ServerAuthException(e); + } + } + + // TODO This is not longer supported by core security + public boolean secureResponse(Request request, Response response, Callback callback, boolean mandatory, AuthenticationState.Succeeded validatedSucceeded) throws ServerAuthException + { + ServletContextRequest servletContextRequest = Request.as(request, ServletContextRequest.class); + JaspiMessageInfo info = (JaspiMessageInfo)servletContextRequest.getServletApiRequest().getAttribute("org.eclipse.jetty.ee11.security.jaspi.info"); + if (info == null) + throw new NullPointerException("MessageInfo from request missing: " + request); + return secureResponse(info, validatedSucceeded); + } + + public boolean secureResponse(JaspiMessageInfo messageInfo, AuthenticationState validatedUser) throws ServerAuthException + { + try + { + ServerAuthConfig authConfig = getAuthConfig(); + if (authConfig == null) + throw new NullPointerException("no ServerAuthConfig found for context"); + + String authContextId = authConfig.getAuthContextID(messageInfo); + ServerAuthContext authContext = authConfig.getAuthContext(authContextId, _serviceSubject, _authProperties); + if (validatedUser instanceof AuthenticationState.Succeeded userAuthenticated) + authContext.cleanSubject(messageInfo, userAuthenticated.getUserIdentity().getSubject()); + AuthStatus status = authContext.secureResponse(messageInfo, _serviceSubject); + return (AuthStatus.SEND_SUCCESS.equals(status)); + } + catch (AuthException e) + { + throw new ServerAuthException(e); + } + } + + private static class JaspiAuthenticatorConfiguration extends Configuration.Wrapper + { + private final LoginService loginService = new EmptyLoginService(); + + public JaspiAuthenticatorConfiguration(Configuration configuration) + { + super(configuration); + } + + @Override + public LoginService getLoginService() + { + return loginService; + } + } +} diff --git a/jetty-ee11/jetty-ee11-jaspi/src/main/java/org/eclipse/jetty/ee11/security/jaspi/JaspiAuthenticatorFactory.java b/jetty-ee11/jetty-ee11-jaspi/src/main/java/org/eclipse/jetty/ee11/security/jaspi/JaspiAuthenticatorFactory.java new file mode 100644 index 00000000000..7d00399a637 --- /dev/null +++ b/jetty-ee11/jetty-ee11-jaspi/src/main/java/org/eclipse/jetty/ee11/security/jaspi/JaspiAuthenticatorFactory.java @@ -0,0 +1,153 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.security.jaspi; + +import java.security.Principal; +import java.util.List; +import java.util.Set; +import javax.security.auth.Subject; + +import jakarta.security.auth.message.config.AuthConfigFactory; +import org.eclipse.jetty.security.Authenticator; +import org.eclipse.jetty.security.DefaultAuthenticatorFactory; +import org.eclipse.jetty.server.Context; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.StringUtil; + +/** + * Jakarta Authentication (JASPI) Authenticator Factory. + * + * This is used to link a jetty-security {@link Authenticator.Factory} to a Jakarta Authentication {@link AuthConfigFactory}. + *

+ * This should be initialized with the provided {@link DefaultAuthConfigFactory} to set up Jakarta Authentication {@link AuthConfigFactory} before use. + * (A different {@link AuthConfigFactory} may also be provided using the same steps below) + *

+ * To initialize either: + *

    + *
  • invoke {@link AuthConfigFactory#setFactory(AuthConfigFactory)}
  • + *
  • Alternatively: set {@link AuthConfigFactory#DEFAULT_FACTORY_SECURITY_PROPERTY}
  • + *
+ * + */ +public class JaspiAuthenticatorFactory extends DefaultAuthenticatorFactory +{ + public static final String MESSAGE_LAYER = "HttpServlet"; + + private Subject _serviceSubject; + private String _serverName; + + /** + * Get the serviceSubject. + * @return the serviceSubject + */ + public Subject getServiceSubject() + { + return _serviceSubject; + } + + /** + * Set the serviceSubject to set. + * @param serviceSubject the serviceSubject to set + */ + public void setServiceSubject(Subject serviceSubject) + { + _serviceSubject = serviceSubject; + } + + /** + * Get the serverName. + * @return the serverName + */ + public String getServerName() + { + return _serverName; + } + + /** + * Set the serverName to set. + * @param serverName the serverName to set + */ + public void setServerName(String serverName) + { + _serverName = serverName; + } + + @Override + public Authenticator getAuthenticator(Server server, Context context, Authenticator.Configuration configuration) + { + AuthConfigFactory factory = AuthConfigFactory.getFactory(); + if (factory == null) + return null; + + String serverName = findServerName(context, server); + Subject serviceSubject = findServiceSubject(server); + String contextPath = StringUtil.isEmpty(context.getContextPath()) ? "/" : context.getContextPath(); + String appContext = serverName + " " + contextPath; + + // We will only create the Authenticator if an AuthConfigProvider matches this context. + if (factory.getConfigProvider(MESSAGE_LAYER, appContext, null) == null) + return null; + + return new JaspiAuthenticator(serviceSubject, appContext, true); + } + + /** + * Find a service Subject. If {@link #setServiceSubject(Subject)} has not been + * used to set a subject, then the {@link Server#getBeans(Class)} method is used + * to look for a Subject. + * + * @param server the server to pull the Subject from + * @return the subject + */ + protected Subject findServiceSubject(Server server) + { + if (_serviceSubject != null) + return _serviceSubject; + List subjects = (List)server.getBeans(Subject.class); + if (subjects.size() > 0) + return subjects.get(0); + return null; + } + + /** + * Find a servername. If {@link #setServerName(String)} has not been called, + * then use the virtualServerName of the context. + * If this is also null, then use the name of the a principal in the service subject. + * If none are found, return "server". + * @param context the context + * @param server the server to find the name of + * @return the server name from the service Subject (or default value if not + * found in subject or principals) + */ + protected String findServerName(Context context, Server server) + { + if (_serverName != null) + return _serverName; + + List virtualHosts = context.getVirtualHosts(); + + if (virtualHosts != null && !virtualHosts.isEmpty()) + return virtualHosts.get(0); + + Subject subject = findServiceSubject(server); + if (subject != null) + { + Set principals = subject.getPrincipals(); + if (principals != null && !principals.isEmpty()) + return principals.iterator().next().getName(); + } + + return "server"; + } +} diff --git a/jetty-ee11/jetty-ee11-jaspi/src/main/java/org/eclipse/jetty/ee11/security/jaspi/JaspiMessageInfo.java b/jetty-ee11/jetty-ee11-jaspi/src/main/java/org/eclipse/jetty/ee11/security/jaspi/JaspiMessageInfo.java new file mode 100644 index 00000000000..bf0e0204190 --- /dev/null +++ b/jetty-ee11/jetty-ee11-jaspi/src/main/java/org/eclipse/jetty/ee11/security/jaspi/JaspiMessageInfo.java @@ -0,0 +1,238 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.security.jaspi; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import jakarta.security.auth.message.MessageInfo; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import org.eclipse.jetty.ee11.servlet.ServletContextRequest; +import org.eclipse.jetty.ee11.servlet.ServletContextResponse; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Response; +import org.eclipse.jetty.util.Callback; + +/** + * Almost an implementation of jaspi MessageInfo. + */ +public class JaspiMessageInfo implements MessageInfo +{ + public static final String AUTHENTICATION_TYPE_KEY = "jakarta.servlet.http.authType"; + private final Callback _callback; + private Request _request; + private Response _response; + private final MIMap _map; + + public JaspiMessageInfo(Request request, Response response, Callback callback) + { + _request = request; + _response = response; + _callback = callback; + //JASPI 3.8.1 + _map = new MIMap(); + } + + public Callback getCallback() + { + return _callback; + } + + @Override + public Map getMap() + { + return _map; + } + + public Request getBaseRequest() + { + return _request; + } + + public Response getBaseResponse() + { + return _response; + } + + @Override + public Object getRequestMessage() + { + if (_request == null) + return null; + return Request.as(_request, ServletContextRequest.class).getServletApiRequest(); + } + + @Override + public Object getResponseMessage() + { + if (_response == null) + return null; + return Response.as(_response, ServletContextResponse.class).getServletApiResponse(); + } + + @Override + public void setRequestMessage(Object request) + { + if (!(request instanceof ServletRequest)) + throw new IllegalStateException("Not a ServletRequest"); + _request = ServletContextRequest.getServletContextRequest((ServletRequest)request); + } + + @Override + public void setResponseMessage(Object response) + { + if (!(response instanceof ServletResponse)) + throw new IllegalStateException("Not a ServletResponse"); + _response = ServletContextResponse.getServletContextResponse((ServletResponse)response); + } + + //TODO this has bugs in the view implementations. Changing them will not affect the hardcoded values. + private static class MIMap implements Map + { + private String authenticationType; + private Map delegate; + + private MIMap() + { + } + + @Override + public int size() + { + return delegate.size(); + } + + @Override + public boolean isEmpty() + { + return delegate == null || delegate.isEmpty(); + } + + @Override + public boolean containsKey(Object key) + { + if (AUTHENTICATION_TYPE_KEY.equals(key)) + return authenticationType != null; + return delegate != null && delegate.containsKey(key); + } + + @Override + public boolean containsValue(Object value) + { + if (authenticationType == value || (authenticationType != null && authenticationType.equals(value))) + return true; + return delegate != null && delegate.containsValue(value); + } + + @Override + public Object get(Object key) + { + if (AUTHENTICATION_TYPE_KEY.equals(key)) + return authenticationType; + if (delegate == null) + return null; + return delegate.get(key); + } + + @Override + public Object put(Object key, Object value) + { + if (AUTHENTICATION_TYPE_KEY.equals(key)) + { + String authenticationType = this.authenticationType; + this.authenticationType = (String)value; + if (delegate != null) + delegate.put(AUTHENTICATION_TYPE_KEY, value); + return authenticationType; + } + + return getDelegate(true).put(key, value); + } + + @Override + public Object remove(Object key) + { + if (AUTHENTICATION_TYPE_KEY.equals(key)) + { + String authenticationType = this.authenticationType; + this.authenticationType = null; + if (delegate != null) + delegate.remove(AUTHENTICATION_TYPE_KEY); + return authenticationType; + } + if (delegate == null) + return null; + return delegate.remove(key); + } + + @Override + public void putAll(Map map) + { + if (map != null) + { + for (Object o : map.entrySet()) + { + Map.Entry entry = (Entry)o; + put(entry.getKey(), entry.getValue()); + } + } + } + + @Override + public void clear() + { + authenticationType = null; + delegate = null; + } + + @Override + public Set keySet() + { + return getDelegate(true).keySet(); + } + + @Override + public Collection values() + { + return getDelegate(true).values(); + } + + @Override + public Set entrySet() + { + return getDelegate(true).entrySet(); + } + + private Map getDelegate(boolean create) + { + if (!create || delegate != null) + return delegate; + if (create) + { + delegate = new HashMap(); + if (authenticationType != null) + delegate.put(AUTHENTICATION_TYPE_KEY, authenticationType); + } + return delegate; + } + + String getAuthenticationType() + { + return authenticationType; + } + } +} diff --git a/jetty-ee11/jetty-ee11-jaspi/src/main/java/org/eclipse/jetty/ee11/security/jaspi/ServletCallbackHandler.java b/jetty-ee11/jetty-ee11-jaspi/src/main/java/org/eclipse/jetty/ee11/security/jaspi/ServletCallbackHandler.java new file mode 100644 index 00000000000..f4ba919ffae --- /dev/null +++ b/jetty-ee11/jetty-ee11-jaspi/src/main/java/org/eclipse/jetty/ee11/security/jaspi/ServletCallbackHandler.java @@ -0,0 +1,132 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.security.jaspi; + +import java.io.IOException; +import javax.security.auth.Subject; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.UnsupportedCallbackException; + +import jakarta.security.auth.message.callback.CallerPrincipalCallback; +import jakarta.security.auth.message.callback.CertStoreCallback; +import jakarta.security.auth.message.callback.GroupPrincipalCallback; +import jakarta.security.auth.message.callback.PasswordValidationCallback; +import jakarta.security.auth.message.callback.PrivateKeyCallback; +import jakarta.security.auth.message.callback.SecretKeyCallback; +import jakarta.security.auth.message.callback.TrustStoreCallback; +import org.eclipse.jetty.ee11.security.jaspi.callback.CredentialValidationCallback; +import org.eclipse.jetty.ee11.servlet.security.authentication.LoginCallback; +import org.eclipse.jetty.ee11.servlet.security.authentication.LoginCallbackImpl; +import org.eclipse.jetty.security.LoginService; +import org.eclipse.jetty.security.UserIdentity; + +/** + * This {@link CallbackHandler} will bridge {@link Callback}s to handle to the given to the Jetty {@link LoginService}. + */ +public class ServletCallbackHandler implements CallbackHandler +{ + private final LoginService _loginService; + private final ThreadLocal _callerPrincipals = new ThreadLocal<>(); + private final ThreadLocal _groupPrincipals = new ThreadLocal<>(); + + public ServletCallbackHandler(LoginService loginService) + { + _loginService = loginService; + } + + @Override + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException + { + for (Callback callback : callbacks) + { + // jaspi to server communication + if (callback instanceof CallerPrincipalCallback) + { + _callerPrincipals.set((CallerPrincipalCallback)callback); + } + else if (callback instanceof GroupPrincipalCallback) + { + _groupPrincipals.set((GroupPrincipalCallback)callback); + } + else if (callback instanceof PasswordValidationCallback passwordValidationCallback) + { + @SuppressWarnings("unused") + Subject subject = passwordValidationCallback.getSubject(); + + UserIdentity user = _loginService.login(passwordValidationCallback.getUsername(), passwordValidationCallback.getPassword(), null, null); + + if (user != null) + { + passwordValidationCallback.setResult(true); + passwordValidationCallback.getSubject().getPrincipals().addAll(user.getSubject().getPrincipals()); + passwordValidationCallback.getSubject().getPrivateCredentials().add(user); + } + } + else if (callback instanceof CredentialValidationCallback credentialValidationCallback) + { + Subject subject = credentialValidationCallback.getSubject(); + LoginCallback loginCallback = new LoginCallbackImpl(subject, + credentialValidationCallback.getUsername(), + credentialValidationCallback.getCredential()); + + UserIdentity user = _loginService.login(credentialValidationCallback.getUsername(), credentialValidationCallback.getCredential(), null, null); + + if (user != null) + { + loginCallback.setUserPrincipal(user.getUserPrincipal()); + credentialValidationCallback.getSubject().getPrivateCredentials().add(loginCallback); + credentialValidationCallback.setResult(true); + credentialValidationCallback.getSubject().getPrincipals().addAll(user.getSubject().getPrincipals()); + credentialValidationCallback.getSubject().getPrivateCredentials().add(user); + } + } + // server to jaspi communication + else if (callback instanceof CertStoreCallback) + { + // TODO implement this + } + else if (callback instanceof PrivateKeyCallback) + { + // TODO implement this + } + else if (callback instanceof SecretKeyCallback) + { + // TODO implement this + } + else if (callback instanceof TrustStoreCallback) + { + // TODO implement this + } + else + { + throw new UnsupportedCallbackException(callback); + } + } + } + + public CallerPrincipalCallback getThreadCallerPrincipalCallback() + { + CallerPrincipalCallback callerPrincipalCallback = _callerPrincipals.get(); + _callerPrincipals.set(null); + return callerPrincipalCallback; + } + + public GroupPrincipalCallback getThreadGroupPrincipalCallback() + { + GroupPrincipalCallback groupPrincipalCallback = _groupPrincipals.get(); + _groupPrincipals.set(null); + return groupPrincipalCallback; + } +} diff --git a/jetty-ee11/jetty-ee11-jaspi/src/main/java/org/eclipse/jetty/ee11/security/jaspi/SimpleAuthConfig.java b/jetty-ee11/jetty-ee11-jaspi/src/main/java/org/eclipse/jetty/ee11/security/jaspi/SimpleAuthConfig.java new file mode 100644 index 00000000000..34b14f0ed39 --- /dev/null +++ b/jetty-ee11/jetty-ee11-jaspi/src/main/java/org/eclipse/jetty/ee11/security/jaspi/SimpleAuthConfig.java @@ -0,0 +1,78 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.security.jaspi; + +import java.util.Map; +import javax.security.auth.Subject; + +import jakarta.security.auth.message.MessageInfo; +import jakarta.security.auth.message.config.ServerAuthConfig; +import jakarta.security.auth.message.config.ServerAuthContext; +import org.eclipse.jetty.ee11.security.jaspi.provider.JaspiAuthConfigProvider; + +/** + * @deprecated use {@link JaspiAuthConfigProvider}. + */ +@Deprecated +public class SimpleAuthConfig implements ServerAuthConfig +{ + public static final String HTTP_SERVLET = "HttpServlet"; + + private final String _appContext; + + private final ServerAuthContext _serverAuthContext; + + public SimpleAuthConfig(String appContext, ServerAuthContext serverAuthContext) + { + this._appContext = appContext; + this._serverAuthContext = serverAuthContext; + } + + @Override + public ServerAuthContext getAuthContext(String authContextID, Subject serviceSubject, Map properties) + { + return _serverAuthContext; + } + + // supposed to be of form host-namecontext-path + @Override + public String getAppContext() + { + return _appContext; + } + + // not used yet + @Override + public String getAuthContextID(MessageInfo messageInfo) throws IllegalArgumentException + { + return null; + } + + @Override + public String getMessageLayer() + { + return HTTP_SERVLET; + } + + @Override + public boolean isProtected() + { + return true; + } + + @Override + public void refresh() + { + } +} diff --git a/jetty-ee11/jetty-ee11-jaspi/src/main/java/org/eclipse/jetty/ee11/security/jaspi/callback/CredentialValidationCallback.java b/jetty-ee11/jetty-ee11-jaspi/src/main/java/org/eclipse/jetty/ee11/security/jaspi/callback/CredentialValidationCallback.java new file mode 100644 index 00000000000..e07b8980c1a --- /dev/null +++ b/jetty-ee11/jetty-ee11-jaspi/src/main/java/org/eclipse/jetty/ee11/security/jaspi/callback/CredentialValidationCallback.java @@ -0,0 +1,70 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.security.jaspi.callback; + +import javax.security.auth.Subject; +import javax.security.auth.callback.Callback; + +import org.eclipse.jetty.util.security.Credential; + +/** + * CredentialValidationCallback + * + * Store a jetty Credential for a user so that it can be + * validated by jaspi + */ +public class CredentialValidationCallback implements Callback +{ + private Credential _credential; + private boolean _result; + private Subject _subject; + private String _userName; + + public CredentialValidationCallback(Subject subject, String userName, Credential credential) + { + _subject = subject; + _userName = userName; + _credential = credential; + } + + public Credential getCredential() + { + return _credential; + } + + public void clearCredential() + { + _credential = null; + } + + public boolean getResult() + { + return _result; + } + + public javax.security.auth.Subject getSubject() + { + return _subject; + } + + public java.lang.String getUsername() + { + return _userName; + } + + public void setResult(boolean result) + { + _result = result; + } +} diff --git a/jetty-ee11/jetty-ee11-jaspi/src/main/java/org/eclipse/jetty/ee11/security/jaspi/callback/package-info.java b/jetty-ee11/jetty-ee11-jaspi/src/main/java/org/eclipse/jetty/ee11/security/jaspi/callback/package-info.java new file mode 100644 index 00000000000..3b612429ce2 --- /dev/null +++ b/jetty-ee11/jetty-ee11-jaspi/src/main/java/org/eclipse/jetty/ee11/security/jaspi/callback/package-info.java @@ -0,0 +1,18 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +/** + * Jetty Jaspi : Callbacks + */ +package org.eclipse.jetty.ee11.security.jaspi.callback; + diff --git a/jetty-ee11/jetty-ee11-jaspi/src/main/java/org/eclipse/jetty/ee11/security/jaspi/modules/BaseAuthModule.java b/jetty-ee11/jetty-ee11-jaspi/src/main/java/org/eclipse/jetty/ee11/security/jaspi/modules/BaseAuthModule.java new file mode 100644 index 00000000000..0afaa60d3c0 --- /dev/null +++ b/jetty-ee11/jetty-ee11-jaspi/src/main/java/org/eclipse/jetty/ee11/security/jaspi/modules/BaseAuthModule.java @@ -0,0 +1,127 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.security.jaspi.modules; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.Map; +import java.util.Set; +import javax.security.auth.Subject; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.UnsupportedCallbackException; + +import jakarta.security.auth.message.AuthException; +import jakarta.security.auth.message.AuthStatus; +import jakarta.security.auth.message.MessageInfo; +import jakarta.security.auth.message.MessagePolicy; +import jakarta.security.auth.message.callback.CallerPrincipalCallback; +import jakarta.security.auth.message.callback.GroupPrincipalCallback; +import jakarta.security.auth.message.config.ServerAuthContext; +import jakarta.security.auth.message.module.ServerAuthModule; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.eclipse.jetty.ee11.security.jaspi.JaspiMessageInfo; +import org.eclipse.jetty.ee11.security.jaspi.callback.CredentialValidationCallback; +import org.eclipse.jetty.ee11.servlet.security.authentication.LoginCallbackImpl; +import org.eclipse.jetty.util.security.Credential; +import org.eclipse.jetty.util.security.Password; + +/** + * Simple abstract module implementing a Jakarta Authentication {@link ServerAuthModule} and {@link ServerAuthContext}. + * To be used as a building block for building more sophisticated auth modules. + */ +public abstract class BaseAuthModule implements ServerAuthModule, ServerAuthContext +{ + private static final Class[] SUPPORTED_MESSAGE_TYPES = new Class[]{HttpServletRequest.class, HttpServletResponse.class}; + + protected static final String LOGIN_SERVICE_KEY = "org.eclipse.jetty.ee11.security.jaspi.modules.LoginService"; + + protected CallbackHandler callbackHandler; + + @Override + public Class[] getSupportedMessageTypes() + { + return SUPPORTED_MESSAGE_TYPES; + } + + public BaseAuthModule() + { + } + + public BaseAuthModule(CallbackHandler callbackHandler) + { + this.callbackHandler = callbackHandler; + } + + @Override + public void initialize(MessagePolicy requestPolicy, MessagePolicy responsePolicy, CallbackHandler handler, Map options) throws AuthException + { + this.callbackHandler = handler; + } + + @Override + public void cleanSubject(MessageInfo messageInfo, Subject subject) throws AuthException + { + // TODO apparently we either get the LoginCallback or the LoginService + // but not both :-( + // Set loginCallbacks = + // subject.getPrivateCredentials(LoginCallback.class); + // if (!loginCallbacks.isEmpty()) { + // LoginCallback loginCallback = loginCallbacks.iterator().next(); + // } + // try { + // loginService.logout(subject); + // } catch (ServerAuthException e) { + // throw new AuthException(e.getMessage()); + // } + } + + @Override + public AuthStatus secureResponse(MessageInfo messageInfo, Subject serviceSubject) throws AuthException + { + // servlets do not need secured responses + return AuthStatus.SEND_SUCCESS; + } + + protected boolean login(Subject clientSubject, String credentials, String authenticationType, MessageInfo messageInfo) throws IOException, UnsupportedCallbackException + { + credentials = credentials.substring(credentials.indexOf(' ') + 1); + credentials = new String(Base64.getDecoder().decode(credentials), StandardCharsets.ISO_8859_1); + int i = credentials.indexOf(':'); + String userName = credentials.substring(0, i); + String password = credentials.substring(i + 1); + return login(clientSubject, userName, new Password(password), authenticationType, messageInfo); + } + + protected boolean login(Subject clientSubject, String username, Credential credential, String authenticationType, MessageInfo messageInfo) throws IOException, UnsupportedCallbackException + { + CredentialValidationCallback credValidationCallback = new CredentialValidationCallback(clientSubject, username, credential); + callbackHandler.handle(new Callback[]{credValidationCallback}); + if (credValidationCallback.getResult()) + { + Set loginCallbacks = clientSubject.getPrivateCredentials(LoginCallbackImpl.class); + if (!loginCallbacks.isEmpty()) + { + LoginCallbackImpl loginCallback = loginCallbacks.iterator().next(); + CallerPrincipalCallback callerPrincipalCallback = new CallerPrincipalCallback(clientSubject, loginCallback.getUserPrincipal()); + GroupPrincipalCallback groupPrincipalCallback = new GroupPrincipalCallback(clientSubject, loginCallback.getRoles()); + callbackHandler.handle(new Callback[]{callerPrincipalCallback, groupPrincipalCallback}); + } + messageInfo.getMap().put(JaspiMessageInfo.AUTHENTICATION_TYPE_KEY, authenticationType); + } + return credValidationCallback.getResult(); + } +} diff --git a/jetty-ee11/jetty-ee11-jaspi/src/main/java/org/eclipse/jetty/ee11/security/jaspi/modules/BasicAuthenticationAuthModule.java b/jetty-ee11/jetty-ee11-jaspi/src/main/java/org/eclipse/jetty/ee11/security/jaspi/modules/BasicAuthenticationAuthModule.java new file mode 100644 index 00000000000..09a462067f2 --- /dev/null +++ b/jetty-ee11/jetty-ee11-jaspi/src/main/java/org/eclipse/jetty/ee11/security/jaspi/modules/BasicAuthenticationAuthModule.java @@ -0,0 +1,97 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.security.jaspi.modules; + +import java.io.IOException; +import java.util.Map; +import javax.security.auth.Subject; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.UnsupportedCallbackException; + +import jakarta.security.auth.message.AuthException; +import jakarta.security.auth.message.AuthStatus; +import jakarta.security.auth.message.MessageInfo; +import jakarta.security.auth.message.MessagePolicy; +import jakarta.security.auth.message.module.ServerAuthModule; +import jakarta.servlet.http.HttpServletResponse; +import org.eclipse.jetty.ee11.security.jaspi.JaspiMessageInfo; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.security.Authenticator; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Response; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A {@link ServerAuthModule} implementation of HTTP Basic Authentication. + */ +public class BasicAuthenticationAuthModule extends BaseAuthModule +{ + private static final Logger LOG = LoggerFactory.getLogger(BasicAuthenticationAuthModule.class); + + private String _realmName; + + private static final String REALM_KEY = "org.eclipse.jetty.ee11.security.jaspi.modules.RealmName"; + + public BasicAuthenticationAuthModule() + { + } + + public BasicAuthenticationAuthModule(CallbackHandler callbackHandler, String realmName) + { + super(callbackHandler); + _realmName = realmName; + } + + @Override + public void initialize(MessagePolicy requestPolicy, MessagePolicy responsePolicy, CallbackHandler callbackHandler, Map options) throws AuthException + { + super.initialize(requestPolicy, responsePolicy, callbackHandler, options); + _realmName = (String)options.get(REALM_KEY); + } + + @Override + public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException + { + if (!(messageInfo instanceof JaspiMessageInfo)) + throw new IllegalStateException("Not a JaspiMessageInfo"); + JaspiMessageInfo jaspiMessageInfo = (JaspiMessageInfo)messageInfo; + + Request request = jaspiMessageInfo.getBaseRequest(); + Response response = jaspiMessageInfo.getBaseResponse(); + + String credentials = request.getHeaders().get(HttpHeader.AUTHORIZATION.asString()); + + try + { + if (credentials != null) + { + if (LOG.isDebugEnabled()) + LOG.debug("Credentials: {}", credentials); + if (login(clientSubject, credentials, Authenticator.BASIC_AUTH, messageInfo)) + { + return AuthStatus.SUCCESS; + } + } + + response.getHeaders().put(HttpHeader.WWW_AUTHENTICATE.asString(), "Basic realm=\"" + _realmName + '"'); + Response.writeError(request, response, jaspiMessageInfo.getCallback(), HttpServletResponse.SC_UNAUTHORIZED); + return AuthStatus.SEND_CONTINUE; + } + catch (IOException | UnsupportedCallbackException e) + { + throw new AuthException(e.getMessage()); + } + } +} diff --git a/jetty-ee11/jetty-ee11-jaspi/src/main/java/org/eclipse/jetty/ee11/security/jaspi/modules/package-info.java b/jetty-ee11/jetty-ee11-jaspi/src/main/java/org/eclipse/jetty/ee11/security/jaspi/modules/package-info.java new file mode 100644 index 00000000000..ba8c59c26ae --- /dev/null +++ b/jetty-ee11/jetty-ee11-jaspi/src/main/java/org/eclipse/jetty/ee11/security/jaspi/modules/package-info.java @@ -0,0 +1,18 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +/** + * Jetty Jaspi : Authentication Modules + */ +package org.eclipse.jetty.ee11.security.jaspi.modules; + diff --git a/jetty-ee11/jetty-ee11-jaspi/src/main/java/org/eclipse/jetty/ee11/security/jaspi/package-info.java b/jetty-ee11/jetty-ee11-jaspi/src/main/java/org/eclipse/jetty/ee11/security/jaspi/package-info.java new file mode 100644 index 00000000000..bd1ce831035 --- /dev/null +++ b/jetty-ee11/jetty-ee11-jaspi/src/main/java/org/eclipse/jetty/ee11/security/jaspi/package-info.java @@ -0,0 +1,18 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +/** + * Jetty Jaspi : Java Authentication SPI + */ +package org.eclipse.jetty.ee11.security.jaspi; + diff --git a/jetty-ee11/jetty-ee11-jaspi/src/main/java/org/eclipse/jetty/ee11/security/jaspi/provider/JaspiAuthConfigProvider.java b/jetty-ee11/jetty-ee11-jaspi/src/main/java/org/eclipse/jetty/ee11/security/jaspi/provider/JaspiAuthConfigProvider.java new file mode 100644 index 00000000000..e3283b32e4c --- /dev/null +++ b/jetty-ee11/jetty-ee11-jaspi/src/main/java/org/eclipse/jetty/ee11/security/jaspi/provider/JaspiAuthConfigProvider.java @@ -0,0 +1,132 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.security.jaspi.provider; + +import java.util.Collections; +import java.util.Map; +import java.util.Objects; +import javax.security.auth.callback.CallbackHandler; + +import jakarta.security.auth.message.config.AuthConfigFactory; +import jakarta.security.auth.message.config.AuthConfigProvider; +import jakarta.security.auth.message.config.ClientAuthConfig; +import jakarta.security.auth.message.config.ServerAuthConfig; +import jakarta.security.auth.message.module.ServerAuthModule; +import org.eclipse.jetty.ee11.security.jaspi.JaspiAuthenticatorFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + *

A Jetty implementation of the {@link AuthConfigProvider} to allow registration of a {@link ServerAuthModule} + * directly without having to write a custom {@link AuthConfigProvider}.

+ *

If this is being constructed by an {@link AuthConfigFactory} after being passed in as a className, then + * you will need to provide the property {@code ServerAuthModule} containing the fully qualified name of + * the {@link ServerAuthModule} class you wish to use.

+ */ +@SuppressWarnings("rawtypes") +public class JaspiAuthConfigProvider implements AuthConfigProvider +{ + private static final Logger LOG = LoggerFactory.getLogger(JaspiAuthConfigProvider.class); + private final Map providerProperties; + private final ServerAuthModule serverAuthModule; + + /** + *

Constructor with signature and implementation that's required by API.

+ *

The property map must contain the {@code ServerAuthModule} property containing the fully qualified name of + * the {@link ServerAuthModule} class you wish to use. If this constructor is being used for self-registration an + * optional property of {@code appContext} can be used specify the appContext value to register the provider.

+ * + * @param properties A Map of initialization properties. + * @param factory The {@link AuthConfigFactory} to register on. + */ + public JaspiAuthConfigProvider(Map properties, AuthConfigFactory factory) + { + if (properties == null || !properties.containsKey("ServerAuthModule")) + throw new IllegalArgumentException("Missing property 'ServerAuthModule', cannot create JaspiAuthConfigProvider"); + + this.providerProperties = Map.copyOf(properties); + this.serverAuthModule = createServerAuthModule((String)properties.get("ServerAuthModule")); + + // API requires self registration if factory is provided. + if (factory != null) + factory.registerConfigProvider(this, JaspiAuthenticatorFactory.MESSAGE_LAYER, (String)properties.get("appContext"), "Self Registration"); + } + + /** + * @param className The fully qualified name of a {@link ServerAuthModule} class. + */ + public JaspiAuthConfigProvider(String className) + { + this(className, null); + } + + /** + * @param className The fully qualified name of a {@link ServerAuthModule} class. + * @param properties A Map of initialization properties. + */ + public JaspiAuthConfigProvider(String className, Map properties) + { + this(createServerAuthModule(className), properties); + } + + /** + * @param serverAuthModule The instance of {@link ServerAuthModule} to use. + */ + public JaspiAuthConfigProvider(ServerAuthModule serverAuthModule) + { + this.serverAuthModule = Objects.requireNonNull(serverAuthModule); + this.providerProperties = Collections.emptyMap(); + } + + /** + * @param serverAuthModule The instance of {@link ServerAuthModule} to use. + * @param properties A Map of initialization properties. + */ + public JaspiAuthConfigProvider(ServerAuthModule serverAuthModule, Map properties) + { + this.serverAuthModule = Objects.requireNonNull(serverAuthModule); + this.providerProperties = properties == null ? Collections.emptyMap() : Map.copyOf(properties); + } + + @Override + public ClientAuthConfig getClientAuthConfig(String layer, String appContext, CallbackHandler handler) + { + return null; + } + + @Override + public ServerAuthConfig getServerAuthConfig(String layer, String appContext, CallbackHandler handler) + { + if (LOG.isDebugEnabled()) + LOG.debug("getServerAuthConfig"); + return new SimpleAuthConfig(layer, appContext, handler, providerProperties, serverAuthModule); + } + + @Override + public void refresh() + { + } + + private static ServerAuthModule createServerAuthModule(String serverAuthModuleClassName) + { + try + { + return (ServerAuthModule)Class.forName(serverAuthModuleClassName).getDeclaredConstructor().newInstance(); + } + catch (ReflectiveOperationException e) + { + throw new IllegalStateException(e); + } + } +} \ No newline at end of file diff --git a/jetty-ee11/jetty-ee11-jaspi/src/main/java/org/eclipse/jetty/ee11/security/jaspi/provider/SimpleAuthConfig.java b/jetty-ee11/jetty-ee11-jaspi/src/main/java/org/eclipse/jetty/ee11/security/jaspi/provider/SimpleAuthConfig.java new file mode 100644 index 00000000000..cb7b6281256 --- /dev/null +++ b/jetty-ee11/jetty-ee11-jaspi/src/main/java/org/eclipse/jetty/ee11/security/jaspi/provider/SimpleAuthConfig.java @@ -0,0 +1,84 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.security.jaspi.provider; + +import java.util.Map; +import javax.security.auth.Subject; +import javax.security.auth.callback.CallbackHandler; + +import jakarta.security.auth.message.AuthException; +import jakarta.security.auth.message.MessageInfo; +import jakarta.security.auth.message.config.ServerAuthConfig; +import jakarta.security.auth.message.config.ServerAuthContext; +import jakarta.security.auth.message.module.ServerAuthModule; + +/** + * Simple implementation of the {@link ServerAuthConfig} interface. + * + * This implementation wires up the given {@link ServerAuthModule} to the appropriate Jakarta Authentication {@link ServerAuthContext} responsible + * for providing it. + */ +@SuppressWarnings("rawtypes") +class SimpleAuthConfig implements ServerAuthConfig +{ + private final String _messageLayer; + private final String _appContext; + private final CallbackHandler _callbackHandler; + private final Map _properties; + private final ServerAuthModule _serverAuthModule; + + public SimpleAuthConfig(String messageLayer, String appContext, CallbackHandler callbackHandler, Map properties, ServerAuthModule serverAuthModule) + { + _messageLayer = messageLayer; + _appContext = appContext; + _callbackHandler = callbackHandler; + _properties = properties; + _serverAuthModule = serverAuthModule; + } + + @Override + public ServerAuthContext getAuthContext(String authContextID, Subject serviceSubject, Map properties) throws AuthException + { + return new SimpleServerAuthContext(_callbackHandler, _serverAuthModule, _properties); + } + + @Override + public String getAppContext() + { + return _appContext; + } + + @Override + public String getAuthContextID(MessageInfo messageInfo) + { + return null; + } + + @Override + public String getMessageLayer() + { + return _messageLayer; + } + + @Override + public boolean isProtected() + { + return true; + } + + @Override + public void refresh() + { + } +} \ No newline at end of file diff --git a/jetty-ee11/jetty-ee11-jaspi/src/main/java/org/eclipse/jetty/ee11/security/jaspi/provider/SimpleServerAuthContext.java b/jetty-ee11/jetty-ee11-jaspi/src/main/java/org/eclipse/jetty/ee11/security/jaspi/provider/SimpleServerAuthContext.java new file mode 100644 index 00000000000..150957c9365 --- /dev/null +++ b/jetty-ee11/jetty-ee11-jaspi/src/main/java/org/eclipse/jetty/ee11/security/jaspi/provider/SimpleServerAuthContext.java @@ -0,0 +1,59 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.security.jaspi.provider; + +import java.util.Map; +import javax.security.auth.Subject; +import javax.security.auth.callback.CallbackHandler; + +import jakarta.security.auth.message.AuthException; +import jakarta.security.auth.message.AuthStatus; +import jakarta.security.auth.message.MessageInfo; +import jakarta.security.auth.message.config.ServerAuthContext; +import jakarta.security.auth.message.module.ServerAuthModule; + +/** + * Simple bridge implementation of the Jakarta Authentication {@link ServerAuthContext} interface. + * + * This implementation will only delegate to the provided {@link ServerAuthModule} implementation. + */ +class SimpleServerAuthContext implements ServerAuthContext +{ + private final ServerAuthModule serverAuthModule; + + @SuppressWarnings("rawtypes") + public SimpleServerAuthContext(CallbackHandler callbackHandler, ServerAuthModule serverAuthModule, Map properties) throws AuthException + { + this.serverAuthModule = serverAuthModule; + serverAuthModule.initialize(null, null, callbackHandler, properties); + } + + @Override + public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException + { + return serverAuthModule.validateRequest(messageInfo, clientSubject, serviceSubject); + } + + @Override + public AuthStatus secureResponse(MessageInfo messageInfo, Subject serviceSubject) throws AuthException + { + return serverAuthModule.secureResponse(messageInfo, serviceSubject); + } + + @Override + public void cleanSubject(MessageInfo messageInfo, Subject subject) throws AuthException + { + serverAuthModule.cleanSubject(messageInfo, subject); + } +} \ No newline at end of file diff --git a/jetty-ee11/jetty-ee11-jaspi/src/main/resources/META-INF/services/org.eclipse.jetty.security.Authenticator$Factory b/jetty-ee11/jetty-ee11-jaspi/src/main/resources/META-INF/services/org.eclipse.jetty.security.Authenticator$Factory new file mode 100644 index 00000000000..4986436902a --- /dev/null +++ b/jetty-ee11/jetty-ee11-jaspi/src/main/resources/META-INF/services/org.eclipse.jetty.security.Authenticator$Factory @@ -0,0 +1 @@ +org.eclipse.jetty.ee11.security.jaspi.JaspiAuthenticatorFactory diff --git a/jetty-ee11/jetty-ee11-jaspi/src/test/java/org/eclipse/jetty/ee11/security/jaspi/DefaultAuthConfigFactoryTest.java b/jetty-ee11/jetty-ee11-jaspi/src/test/java/org/eclipse/jetty/ee11/security/jaspi/DefaultAuthConfigFactoryTest.java new file mode 100644 index 00000000000..ee6a81d6e28 --- /dev/null +++ b/jetty-ee11/jetty-ee11-jaspi/src/test/java/org/eclipse/jetty/ee11/security/jaspi/DefaultAuthConfigFactoryTest.java @@ -0,0 +1,115 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.security.jaspi; + +import java.util.Map; + +import jakarta.security.auth.message.config.AuthConfigFactory; +import jakarta.security.auth.message.config.AuthConfigProvider; +import jakarta.security.auth.message.config.RegistrationListener; +import org.eclipse.jetty.ee11.security.jaspi.provider.JaspiAuthConfigProvider; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.arrayContaining; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; + +public class DefaultAuthConfigFactoryTest +{ + + private static final String MESSAGE_LAYER = "HttpServlet"; + + private final String jettyAuthConfigProvider = "org.eclipse.jetty.ee11.security.jaspi.provider.JaspiAuthConfigProvider"; + private final String appContext = "server /test"; + + private final Map serverAuthModuleProperties = Map.of("ServerAuthModule", + "org.eclipse.jetty.ee11.security.jaspi.modules.BasicAuthenticationAuthModule", "AppContextID", appContext, + "org.eclipse.jetty.ee11.security.jaspi.modules.RealmName", "TestRealm"); + + private final String serverAuthModuleClassName = "org.eclipse.jetty.ee11.security.jaspi.modules.BasicAuthenticationAuthModule"; + + @Test + public void testRegisterConfigProviderByClassName() throws Exception + { + AuthConfigFactory factory = new DefaultAuthConfigFactory(); + String registrationId = factory.registerConfigProvider(jettyAuthConfigProvider, + serverAuthModuleProperties, MESSAGE_LAYER, appContext, "a test provider"); + AuthConfigProvider registeredProvider = factory.getConfigProvider(MESSAGE_LAYER, appContext, null); + assertThat(registeredProvider, instanceOf(JaspiAuthConfigProvider.class)); + assertThat(registeredProvider.getServerAuthConfig(MESSAGE_LAYER, appContext, null), notNullValue()); + + assertThat(factory.getRegistrationContext(registrationId), notNullValue()); + assertThat(factory.getRegistrationIDs(registeredProvider), arrayContaining(registrationId)); + } + + @Test + public void testRegisterAuthConfigProviderDirect() throws Exception + { + AuthConfigProvider provider = new JaspiAuthConfigProvider( + serverAuthModuleClassName, + serverAuthModuleProperties); + + AuthConfigFactory factory = new DefaultAuthConfigFactory(); + String registrationId = factory.registerConfigProvider(provider, MESSAGE_LAYER, appContext, "a test provider"); + + AuthConfigProvider registeredProvider = factory.getConfigProvider(MESSAGE_LAYER, appContext, null); + assertThat(registeredProvider, instanceOf(JaspiAuthConfigProvider.class)); + assertThat(registeredProvider.getServerAuthConfig(MESSAGE_LAYER, appContext, null), notNullValue()); + + assertThat(factory.getRegistrationContext(registrationId), notNullValue()); + assertThat(factory.getRegistrationIDs(registeredProvider), arrayContaining(registrationId)); + } + + @Test + public void testRemoveRegistration() throws Exception + { + // Arrange + AuthConfigProvider provider = new JaspiAuthConfigProvider( + serverAuthModuleClassName, + serverAuthModuleProperties); + + AuthConfigFactory factory = new DefaultAuthConfigFactory(); + String registrationId = factory.registerConfigProvider(provider, MESSAGE_LAYER, appContext, "a test provider"); + + DummyRegistrationListener dummyListener = new DummyRegistrationListener(); + assertThat(factory.getConfigProvider(MESSAGE_LAYER, appContext, dummyListener), notNullValue()); + + // Act + factory.removeRegistration(registrationId); + + // Assert config provider removed + assertThat(factory.getConfigProvider(MESSAGE_LAYER, appContext, null), nullValue()); + + // Assert listeners invoked + assertThat(dummyListener.appContext, equalTo(appContext)); + assertThat(dummyListener.layer, equalTo(MESSAGE_LAYER)); + + } + + static class DummyRegistrationListener implements RegistrationListener + { + String layer; + String appContext; + + @Override + public void notify(String layer, String appContext) + { + this.layer = layer; + this.appContext = appContext; + } + } +} diff --git a/jetty-ee11/jetty-ee11-jaspi/src/test/java/org/eclipse/jetty/ee11/security/jaspi/HttpHeaderAuthModule.java b/jetty-ee11/jetty-ee11-jaspi/src/test/java/org/eclipse/jetty/ee11/security/jaspi/HttpHeaderAuthModule.java new file mode 100644 index 00000000000..921e233ee16 --- /dev/null +++ b/jetty-ee11/jetty-ee11-jaspi/src/test/java/org/eclipse/jetty/ee11/security/jaspi/HttpHeaderAuthModule.java @@ -0,0 +1,117 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.security.jaspi; + +import java.util.Map; +import javax.security.auth.Subject; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; + +import jakarta.security.auth.message.AuthException; +import jakarta.security.auth.message.AuthStatus; +import jakarta.security.auth.message.MessageInfo; +import jakarta.security.auth.message.MessagePolicy; +import jakarta.security.auth.message.callback.CallerPrincipalCallback; +import jakarta.security.auth.message.callback.GroupPrincipalCallback; +import jakarta.security.auth.message.module.ServerAuthModule; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +/** + * Example JASPI Auth Module based on http://www.trajano.net/2014/06/creating-a-simple-jaspic-auth-module/ + */ +public class HttpHeaderAuthModule implements ServerAuthModule +{ + + /** + * Supported message types. For our case we only need to deal with HTTP servlet request and responses. On Java EE 7 this will handle WebSockets as well. + */ + private static final Class[] SUPPORTED_MESSAGE_TYPES = new Class[] + {HttpServletRequest.class, HttpServletResponse.class}; + + /** + * Callback handler that is passed in initialize by the container. This processes the callbacks which are objects that populate the "subject". + */ + private CallbackHandler handler; + + /** + * Does nothing of note for what we need. + */ + @Override + public void cleanSubject(final MessageInfo messageInfo, final Subject subject) throws AuthException + { + } + + @SuppressWarnings("rawtypes") + @Override + public Class[] getSupportedMessageTypes() + { + return SUPPORTED_MESSAGE_TYPES; + } + + /** + * Initializes the module. Allows you to pass in options. + * + * @param requestPolicy request policy, ignored + * @param responsePolicy response policy, ignored + * @param h callback handler + * @param options options + */ + @Override + public void initialize(final MessagePolicy requestPolicy, MessagePolicy responsePolicy, CallbackHandler h, Map options) throws AuthException + { + handler = h; + } + + /** + * @return AuthStatus.SEND_SUCCESS + */ + @Override + public AuthStatus secureResponse(final MessageInfo paramMessageInfo, final Subject subject) throws AuthException + { + return AuthStatus.SEND_SUCCESS; + } + + /** + * Validation occurs here. + */ + @Override + public AuthStatus validateRequest(final MessageInfo messageInfo, final Subject client, final Subject serviceSubject) throws AuthException + { + // Take the request from the messageInfo structure. + final HttpServletRequest req = (HttpServletRequest)messageInfo.getRequestMessage(); + try + { + // Get the user name from the header. If not there then fail authentication. + final String userName = req.getHeader("X-Forwarded-User"); + if (userName == null) + { + return AuthStatus.FAILURE; + } + + // Store the user name that was in the header and also set a group. + handler.handle(new Callback[] + { + new CallerPrincipalCallback(client, userName), + new GroupPrincipalCallback(client, new String[] + {"users"}) + }); + return AuthStatus.SUCCESS; + } + catch (final Exception e) + { + throw new AuthException(e.getMessage()); + } + } +} diff --git a/jetty-ee11/jetty-ee11-jaspi/src/test/java/org/eclipse/jetty/ee11/security/jaspi/JaspiTest.java b/jetty-ee11/jetty-ee11-jaspi/src/test/java/org/eclipse/jetty/ee11/security/jaspi/JaspiTest.java new file mode 100644 index 00000000000..c1498d1d354 --- /dev/null +++ b/jetty-ee11/jetty-ee11-jaspi/src/test/java/org/eclipse/jetty/ee11/security/jaspi/JaspiTest.java @@ -0,0 +1,229 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.security.jaspi; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Base64; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import jakarta.security.auth.message.config.AuthConfigFactory; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.eclipse.jetty.ee11.servlet.ServletContextHandler; +import org.eclipse.jetty.ee11.servlet.security.ConstraintMapping; +import org.eclipse.jetty.ee11.servlet.security.ConstraintSecurityHandler; +import org.eclipse.jetty.security.AbstractLoginService; +import org.eclipse.jetty.security.Constraint; +import org.eclipse.jetty.security.RolePrincipal; +import org.eclipse.jetty.security.UserPrincipal; +import org.eclipse.jetty.server.LocalConnector; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.ContextHandlerCollection; +import org.eclipse.jetty.util.security.Credential; +import org.eclipse.jetty.util.security.Password; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static java.nio.charset.StandardCharsets.ISO_8859_1; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.startsWith; + +public class JaspiTest +{ + Server _server; + LocalConnector _connector; + + public static class TestLoginService extends AbstractLoginService + { + protected Map _users = new HashMap<>(); + protected Map> _roles = new HashMap<>(); + + public TestLoginService(String name) + { + setName(name); + } + + public void putUser(String username, Credential credential, String[] roles) + { + UserPrincipal userPrincipal = new UserPrincipal(username, credential); + _users.put(username, userPrincipal); + if (roles != null) + { + List rps = Arrays.stream(roles).map(RolePrincipal::new).collect(Collectors.toList()); + _roles.put(username, rps); + } + } + + @Override + protected List loadRoleInfo(UserPrincipal user) + { + return _roles.get(user.getName()); + } + + @Override + protected UserPrincipal loadUserInfo(String username) + { + return _users.get(username); + } + } + + @BeforeAll + public static void beforeAll() throws Exception + { + AuthConfigFactory factory = new DefaultAuthConfigFactory(); + + factory.registerConfigProvider("org.eclipse.jetty.ee11.security.jaspi.provider.JaspiAuthConfigProvider", + Map.of("ServerAuthModule", "org.eclipse.jetty.ee11.security.jaspi.modules.BasicAuthenticationAuthModule", + "AppContextID", "server /ctx", + "org.eclipse.jetty.ee11.security.jaspi.modules.RealmName", "TestRealm"), + "HttpServlet", "server /ctx", "a test provider"); + + factory.registerConfigProvider("org.eclipse.jetty.ee11.security.jaspi.provider.JaspiAuthConfigProvider", + Map.of("ServerAuthModule", "org.eclipse.jetty.ee11.security.jaspi.HttpHeaderAuthModule", + "AppContextID", "server /other"), + "HttpServlet", "server /other", "another test provider"); + + AuthConfigFactory.setFactory(factory); + } + + @AfterAll + public static void afterAll() throws Exception + { + AuthConfigFactory.setFactory(null); + } + + @BeforeEach + public void before() throws Exception + { + _server = new Server(); + _connector = new LocalConnector(_server); + _server.addConnector(_connector); + + ContextHandlerCollection contexts = new ContextHandlerCollection(); + _server.setHandler(contexts); + + TestLoginService loginService = new TestLoginService("TestRealm"); + loginService.putUser("user", new Password("password"), new String[] {"users"}); + loginService.putUser("admin", new Password("secret"), new String[] {"users", "admins"}); + _server.addBean(loginService); + + ServletContextHandler context = new ServletContextHandler(); + contexts.addHandler(context); + context.setContextPath("/ctx"); + context.addServlet(new TestServlet(), "/"); + + JaspiAuthenticatorFactory jaspiAuthFactory = new JaspiAuthenticatorFactory(); + + ConstraintSecurityHandler security = new ConstraintSecurityHandler(); + context.setSecurityHandler(security); + security.setAuthenticatorFactory(jaspiAuthFactory); + + Constraint constraint = new Constraint.Builder() + .name("All") + .roles("users") + .build(); + ConstraintMapping mapping = new ConstraintMapping(); + mapping.setPathSpec("/jaspi/*"); + mapping.setConstraint(constraint); + security.addConstraintMapping(mapping); + + ServletContextHandler other = new ServletContextHandler(); + contexts.addHandler(other); + other.setContextPath("/other"); + other.addServlet(new TestServlet(), "/"); + ConstraintSecurityHandler securityOther = new ConstraintSecurityHandler(); + other.setSecurityHandler(securityOther); + securityOther.setAuthenticatorFactory(jaspiAuthFactory); + securityOther.addConstraintMapping(mapping); + + _server.start(); + } + + @AfterEach + public void after() throws Exception + { + _server.stop(); + } + + @Test + public void testNoConstraint() throws Exception + { + String response = _connector.getResponse("GET /ctx/test HTTP/1.0\n\n"); + assertThat(response, startsWith("HTTP/1.1 200 OK")); + } + + @Test + public void testConstraintNoAuth() throws Exception + { + String response = _connector.getResponse("GET /ctx/jaspi/test HTTP/1.0\n\n"); + assertThat(response, startsWith("HTTP/1.1 401 Unauthorized")); + assertThat(response, Matchers.containsString("WWW-Authenticate: Basic realm=\"TestRealm\"")); + } + + @Test + public void testConstraintWrongAuth() throws Exception + { + String response = _connector.getResponse("GET /ctx/jaspi/test HTTP/1.0\n" + "Authorization: Basic " + + Base64.getEncoder().encodeToString("user:wrong".getBytes(ISO_8859_1)) + "\n\n"); + assertThat(response, startsWith("HTTP/1.1 401 Unauthorized")); + assertThat(response, Matchers.containsString("WWW-Authenticate: Basic realm=\"TestRealm\"")); + } + + @Test + public void testConstraintAuth() throws Exception + { + String response = _connector.getResponse("GET /ctx/jaspi/test HTTP/1.0\n" + "Authorization: Basic " + + Base64.getEncoder().encodeToString("user:password".getBytes(ISO_8859_1)) + "\n\n"); + assertThat(response, startsWith("HTTP/1.1 200 OK")); + } + + @Test + public void testOtherNoAuth() throws Exception + { + String response = _connector.getResponse("GET /other/jaspi/test HTTP/1.0\n\n"); + assertThat(response, startsWith("HTTP/1.1 403 Forbidden")); + } + + @Test + public void testOtherAuth() throws Exception + { + String response = _connector.getResponse(""" + GET /other/jaspi/test HTTP/1.0 + X-Forwarded-User: user + + """); + assertThat(response, startsWith("HTTP/1.1 200 OK")); + } + + public static class TestServlet extends HttpServlet + { + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException + { + resp.setStatus(200); + resp.setContentType("text/plain"); + resp.getWriter().println("All OK"); + resp.getWriter().println("requestURI=" + req.getRequestURI()); + } + } +} diff --git a/jetty-ee11/jetty-ee11-jndi/pom.xml b/jetty-ee11/jetty-ee11-jndi/pom.xml new file mode 100644 index 00000000000..5e0ad875562 --- /dev/null +++ b/jetty-ee11/jetty-ee11-jndi/pom.xml @@ -0,0 +1,64 @@ + + + + 4.0.0 + + org.eclipse.jetty.ee11 + jetty-ee11 + 12.1.0-SNAPSHOT + + jetty-ee11-jndi + EE11 :: JNDI + EE11 JNDI factories + + + ${project.groupId}.jndi + org.eclipse.jetty.ee11.jndi.* + + + + + jakarta.activation + jakarta.activation-api + true + + + org.eclipse.jetty + jetty-util + + + org.slf4j + slf4j-api + + + jakarta.mail + jakarta.mail-api + provided + + + org.eclipse.jetty + jetty-slf4j-impl + test + + + org.eclipse.jetty.toolchain + jetty-test-helper + test + + + + + + + org.apache.felix + maven-bundle-plugin + true + + + ${osgi.slf4j.import.packages},jakarta.mail.*;resolution:=optional,* + + + + + + diff --git a/jetty-ee11/jetty-ee11-jndi/src/main/config/modules/ee11-jndi.mod b/jetty-ee11/jetty-ee11-jndi/src/main/config/modules/ee11-jndi.mod new file mode 100644 index 00000000000..144db52c08a --- /dev/null +++ b/jetty-ee11/jetty-ee11-jndi/src/main/config/modules/ee11-jndi.mod @@ -0,0 +1,13 @@ +# DO NOT EDIT THIS FILE - See: https://eclipse.dev/jetty/documentation/ + +[description] +Adds the Jetty EE11 JNDI reference factories + +[environment] +ee11 + +[depend] +jndi + +[lib] +lib/jetty-ee11-jndi-${jetty.version}.jar diff --git a/jetty-ee11/jetty-ee11-jndi/src/main/java/module-info.java b/jetty-ee11/jetty-ee11-jndi/src/main/java/module-info.java new file mode 100644 index 00000000000..b4e80d9390c --- /dev/null +++ b/jetty-ee11/jetty-ee11-jndi/src/main/java/module-info.java @@ -0,0 +1,25 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +module org.eclipse.jetty.ee11.jndi +{ + requires org.slf4j; + + requires transitive org.eclipse.jetty.util; + requires transitive java.naming; + + // Only required if using MailSessionReference. + requires static jakarta.mail; + + exports org.eclipse.jetty.ee11.jndi.factories; +} diff --git a/jetty-ee11/jetty-ee11-jndi/src/main/java/org/eclipse/jetty/ee11/jndi/factories/MailSessionReference.java b/jetty-ee11/jetty-ee11-jndi/src/main/java/org/eclipse/jetty/ee11/jndi/factories/MailSessionReference.java new file mode 100644 index 00000000000..29e10b1ee52 --- /dev/null +++ b/jetty-ee11/jetty-ee11-jndi/src/main/java/org/eclipse/jetty/ee11/jndi/factories/MailSessionReference.java @@ -0,0 +1,168 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.jndi.factories; + +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Map; +import java.util.Properties; +import javax.naming.Context; +import javax.naming.Name; +import javax.naming.RefAddr; +import javax.naming.Reference; +import javax.naming.StringRefAddr; +import javax.naming.spi.ObjectFactory; + +import jakarta.mail.Authenticator; +import jakarta.mail.PasswordAuthentication; +import jakarta.mail.Session; +import org.eclipse.jetty.util.security.Password; + +/** + * MailSessionReference + * + * This is a subclass of jakarta.mail.Reference and an ObjectFactory for jakarta.mail.Session objects. + * + * The subclassing of Reference allows all of the setup for a jakarta.mail.Session + * to be captured without necessitating first instantiating a Session object. The + * reference is bound into JNDI and it is only when the reference is looked up that + * this object factory will create an instance of jakarta.mail.Session using the + * information captured in the Reference. + */ +public class MailSessionReference extends Reference implements ObjectFactory +{ + public static class PasswordAuthenticator extends Authenticator + { + PasswordAuthentication passwordAuthentication; + private String user; + private String password; + + public PasswordAuthenticator() + { + + } + + public PasswordAuthenticator(String user, String password) + { + passwordAuthentication = new PasswordAuthentication(user, (password.startsWith(Password.__OBFUSCATE) ? Password.deobfuscate(password) : password)); + } + + @Override + public PasswordAuthentication getPasswordAuthentication() + { + return passwordAuthentication; + } + + public void setUser(String user) + { + this.user = user; + } + + public String getUser() + { + return this.user; + } + + public String getPassword() + { + return this.password; + } + + public void setPassword(String password) + { + this.password = password; + } + } + + public MailSessionReference() + { + super("jakarta.mail.Session", MailSessionReference.class.getName(), null); + } + + + /** + * Create a jakarta.mail.Session instance based on the information passed in the Reference + * + * @param ref the Reference + * @param arg1 not used + * @param arg2 not used + * @param arg3 not used + * @return the object found + * @throws Exception if unable to get object instance + * @see javax.naming.spi.ObjectFactory#getObjectInstance(java.lang.Object, javax.naming.Name, javax.naming.Context, java.util.Hashtable) + */ + @Override + public Object getObjectInstance(Object ref, Name arg1, Context arg2, Hashtable arg3) throws Exception + { + if (ref == null) + return null; + + Reference reference = (Reference)ref; + + Properties props = new Properties(); + String user = null; + String password = null; + + Enumeration refs = reference.getAll(); + while (refs.hasMoreElements()) + { + RefAddr refAddr = (RefAddr)refs.nextElement(); + String name = refAddr.getType(); + String value = (String)refAddr.getContent(); + if (name.equalsIgnoreCase("user")) + user = value; + else if (name.equalsIgnoreCase("pwd")) + password = value; + else + props.put(name, value); + } + + if (password == null) + return Session.getInstance(props); + else + return Session.getInstance(props, new PasswordAuthenticator(user, password)); + } + + public void setUser(String user) + { + StringRefAddr addr = (StringRefAddr)get("user"); + if (addr != null) + { + throw new RuntimeException("user already set on SessionReference, can't be changed"); + } + add(new StringRefAddr("user", user)); + } + + public void setPassword(String password) + { + StringRefAddr addr = (StringRefAddr)get("pwd"); + if (addr != null) + throw new RuntimeException("password already set on SessionReference, can't be changed"); + add(new StringRefAddr("pwd", password)); + } + + public void setProperties(Properties properties) + { + Iterator entries = properties.entrySet().iterator(); + while (entries.hasNext()) + { + Map.Entry e = (Map.Entry)entries.next(); + StringRefAddr sref = (StringRefAddr)get((String)e.getKey()); + if (sref != null) + throw new RuntimeException("property " + e.getKey() + " already set on Session reference, can't be changed"); + add(new StringRefAddr((String)e.getKey(), (String)e.getValue())); + } + } +} diff --git a/jetty-ee11/jetty-ee11-jndi/src/main/java/org/eclipse/jetty/ee11/jndi/factories/package-info.java b/jetty-ee11/jetty-ee11-jndi/src/main/java/org/eclipse/jetty/ee11/jndi/factories/package-info.java new file mode 100644 index 00000000000..f9ec719ac4d --- /dev/null +++ b/jetty-ee11/jetty-ee11-jndi/src/main/java/org/eclipse/jetty/ee11/jndi/factories/package-info.java @@ -0,0 +1,18 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +/** + * Jetty EE11 Jndi : Factories + */ +package org.eclipse.jetty.ee11.jndi.factories; + diff --git a/jetty-ee11/jetty-ee11-jspc-maven-plugin/pom.xml b/jetty-ee11/jetty-ee11-jspc-maven-plugin/pom.xml new file mode 100644 index 00000000000..52a73b3f257 --- /dev/null +++ b/jetty-ee11/jetty-ee11-jspc-maven-plugin/pom.xml @@ -0,0 +1,123 @@ + + + 4.0.0 + + org.eclipse.jetty.ee11 + jetty-ee11 + 12.1.0-SNAPSHOT + + jetty-ee11-jspc-maven-plugin + maven-plugin + EE11 :: Jetty JSPC Maven Plugin + + ${project.groupId}.jspc.plugin + true + + + + org.apache.ant + ant + + + org.apache.maven.plugin-tools + maven-plugin-tools-api + + + junit + junit + + + + + org.eclipse.jetty + jetty-util + + + org.eclipse.jetty.ee11 + jetty-ee11-apache-jsp + ${project.version} + + + org.eclipse.jetty.ee11 + jetty-ee11-glassfish-jstl + ${project.version} + + + org.apache.maven + maven-artifact + provided + + + org.apache.maven + maven-core + provided + + + javax.annotation + javax.annotation-api + + + + + org.apache.maven + maven-model + provided + + + org.apache.maven + maven-plugin-api + provided + + + javax.annotation + javax.annotation-api + + + + + org.apache.maven.plugin-tools + maven-plugin-annotations + provided + + + + + + org.apache.maven.plugins + maven-invoker-plugin + + + ${maven.surefire.plugin.version} + + + clean + + + + + integration-test + + install + integration-test + verify + + + + + + org.apache.maven.plugins + maven-plugin-plugin + + + exec-plugin-doc + + descriptor + helpmojo + + process-classes + + + + + + diff --git a/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/it/package-root/invoker.properties b/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/it/package-root/invoker.properties new file mode 100644 index 00000000000..df6cbf2d0bc --- /dev/null +++ b/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/it/package-root/invoker.properties @@ -0,0 +1,2 @@ +invoker.goals = test -fae +invoker.debug = true diff --git a/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/it/package-root/pom.xml b/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/it/package-root/pom.xml new file mode 100644 index 00000000000..9d83a70810b --- /dev/null +++ b/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/it/package-root/pom.xml @@ -0,0 +1,42 @@ + + + 4.0.0 + + org.eclipse.jetty.its.jspc + simple-jsp + 0.0.1-SNAPSHOT + pom + + EE11 :: Simple Jsp + + + UTF-8 + UTF-8 + 1.8 + @project.version@ + + + + + + org.eclipse.jetty.ee11 + jetty-ee11-jspc-maven-plugin + ${jetty.version} + + + + jspc + + compile + + + org.eclipse.jetty.test + + + + + + + + + diff --git a/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/it/package-root/postbuild.groovy b/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/it/package-root/postbuild.groovy new file mode 100644 index 00000000000..4a73995f2a0 --- /dev/null +++ b/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/it/package-root/postbuild.groovy @@ -0,0 +1,9 @@ + + +System.out.println( "running postbuild.groovy" ) + +File file = new File( basedir, "target/classes/org/eclipse/jetty/test/foo_jsp.class" ); +if ( !file.isFile() ) +{ + throw new FileNotFoundException( "Could not find generated class in the proper package name: " + file ); +} diff --git a/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/it/package-root/src/main/webapp/foo.jsp b/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/it/package-root/src/main/webapp/foo.jsp new file mode 100644 index 00000000000..fb73b0b0002 --- /dev/null +++ b/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/it/package-root/src/main/webapp/foo.jsp @@ -0,0 +1,23 @@ + +<%@ page import="java.util.Enumeration" %> + +

JSP Dump

+ + + + + + +<% + Enumeration e =request.getParameterNames(); + while(e.hasMoreElements()) + { + String name = (String)e.nextElement(); +%> + + + +<% } %> + +
Request URI:<%= request.getRequestURI() %>
ServletPath:<%= request.getServletPath() %>
PathInfo:<%= request.getPathInfo() %>
getParameter("<%= name %>")<%= request.getParameter(name) %>
+ diff --git a/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/it/settings.xml b/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/it/settings.xml new file mode 100644 index 00000000000..d64bdb89034 --- /dev/null +++ b/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/it/settings.xml @@ -0,0 +1,36 @@ + + + + + + it-repo + + true + + + + local.central + @localRepositoryUrl@ + + true + + + true + + + + + + local.central + @localRepositoryUrl@ + + true + + + true + + + + + + diff --git a/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/it/simple-jsp-fail/invoker.properties b/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/it/simple-jsp-fail/invoker.properties new file mode 100644 index 00000000000..8a7cae8ef84 --- /dev/null +++ b/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/it/simple-jsp-fail/invoker.properties @@ -0,0 +1,3 @@ +invoker.goals = test -fae +invoker.buildResult = failure +invoker.debug = true diff --git a/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/it/simple-jsp-fail/pom.xml b/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/it/simple-jsp-fail/pom.xml new file mode 100644 index 00000000000..d83afb88774 --- /dev/null +++ b/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/it/simple-jsp-fail/pom.xml @@ -0,0 +1,41 @@ + + + 4.0.0 + + org.eclipse.jetty.its.jspc + simple-jsp-fail + 0.0.1-SNAPSHOT + pom + + EE11 :: Simple Jsp Fail + + + UTF-8 + UTF-8 + 1.8 + 3.0.0 + @project.version@ + + + + + + org.eclipse.jetty.ee11 + jetty-ee11-jspc-maven-plugin + ${jetty.version} + + + + jspc + + compile + + src/main/jsp + + + + + + + + diff --git a/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/it/simple-jsp-fail/postbuild.groovy b/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/it/simple-jsp-fail/postbuild.groovy new file mode 100644 index 00000000000..cdc73a7cef1 --- /dev/null +++ b/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/it/simple-jsp-fail/postbuild.groovy @@ -0,0 +1 @@ +System.out.println( "running postbuild.groovy" ) diff --git a/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/it/simple-jsp-fail/src/main/jsp/foo.jsp b/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/it/simple-jsp-fail/src/main/jsp/foo.jsp new file mode 100644 index 00000000000..00b636ccd93 --- /dev/null +++ b/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/it/simple-jsp-fail/src/main/jsp/foo.jsp @@ -0,0 +1,23 @@ + +<%@ page import="java.util.Enumeration" %> + +

JSP Dump

+ + + + + + +<% + Enumeration e =request.getParameterNames(); + while(e.hasMoreElements()) + { + String name = (String)e.nextElement(); +%> + + + +<% } %> + +
Request URI:<%= request.getFoo() %>
ServletPath:<%= request.getServletPath() %>
PathInfo:<%= request.getPathInfo() %>
getParameter("<%= name %>")<%= request.getParameter(name) %>
+ diff --git a/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/it/simple-jsp/invoker.properties b/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/it/simple-jsp/invoker.properties new file mode 100644 index 00000000000..df6cbf2d0bc --- /dev/null +++ b/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/it/simple-jsp/invoker.properties @@ -0,0 +1,2 @@ +invoker.goals = test -fae +invoker.debug = true diff --git a/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/it/simple-jsp/pom.xml b/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/it/simple-jsp/pom.xml new file mode 100644 index 00000000000..ce444ac58be --- /dev/null +++ b/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/it/simple-jsp/pom.xml @@ -0,0 +1,37 @@ + + + 4.0.0 + + org.eclipse.jetty.its.jspc + simple-jsp + 0.0.1-SNAPSHOT + pom + + EE11 :: Simple Jsp + + + UTF-8 + UTF-8 + 1.8 + @project.version@ + + + + + + org.eclipse.jetty.ee11 + jetty-ee11-jspc-maven-plugin + ${jetty.version} + + + + jspc + + compile + + + + + + + diff --git a/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/it/simple-jsp/postbuild.groovy b/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/it/simple-jsp/postbuild.groovy new file mode 100644 index 00000000000..77118926cfd --- /dev/null +++ b/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/it/simple-jsp/postbuild.groovy @@ -0,0 +1,20 @@ +import groovy.xml.XmlSlurper + +System.out.println( "running postbuild.groovy" ) + +File webfrag = new File(basedir, 'target/webfrag.xml') +assert webfrag.exists() +assert webfrag.text.contains("org.apache.jsp.foo_jsp") +assert webfrag.text.contains("org.apache.jsp.foo_jsp") +assert webfrag.text.contains("/foo.jsp") + +// cannot use such parsing as it is not real xml +//def rootXml = new XmlSlurper().parse(new File(basedir, 'target/webfrag.xml')) +// so fake it +def rootXml = new XmlSlurper().parseText(""+webfrag.text+"") + +assert rootXml.servlet.'servlet-name'.text() == "org.apache.jsp.foo_jsp" +assert rootXml.servlet.'servlet-class'.text() == "org.apache.jsp.foo_jsp" + +assert rootXml.'servlet-mapping'.'servlet-name'.text() == "org.apache.jsp.foo_jsp" +assert rootXml.'servlet-mapping'.'url-pattern'.text() == "/foo.jsp" diff --git a/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/it/simple-jsp/src/main/webapp/foo.jsp b/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/it/simple-jsp/src/main/webapp/foo.jsp new file mode 100644 index 00000000000..fb73b0b0002 --- /dev/null +++ b/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/it/simple-jsp/src/main/webapp/foo.jsp @@ -0,0 +1,23 @@ + +<%@ page import="java.util.Enumeration" %> + +

JSP Dump

+ + + + + + +<% + Enumeration e =request.getParameterNames(); + while(e.hasMoreElements()) + { + String name = (String)e.nextElement(); +%> + + + +<% } %> + +
Request URI:<%= request.getRequestURI() %>
ServletPath:<%= request.getServletPath() %>
PathInfo:<%= request.getPathInfo() %>
getParameter("<%= name %>")<%= request.getParameter(name) %>
+ diff --git a/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/main/java/org/eclipse/jetty/ee11/jspc/plugin/JspcMojo.java b/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/main/java/org/eclipse/jetty/ee11/jspc/plugin/JspcMojo.java new file mode 100644 index 00000000000..84bf821ee63 --- /dev/null +++ b/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/main/java/org/eclipse/jetty/ee11/jspc/plugin/JspcMojo.java @@ -0,0 +1,623 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.jspc.plugin; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileFilter; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import org.apache.jasper.JspC; +import org.apache.jasper.servlet.JspCServletContext; +import org.apache.jasper.servlet.TldScanner; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.annotations.ResolutionScope; +import org.apache.maven.project.MavenProject; +import org.apache.tomcat.JarScanner; +import org.apache.tomcat.util.scan.StandardJarScanner; +import org.codehaus.plexus.util.FileUtils; +import org.codehaus.plexus.util.StringUtils; +import org.eclipse.jetty.util.IO; + +/** + * This goal will compile jsps for a webapp so that they can be included in a + * war. + *

+ * At runtime, the plugin will use the jspc compiler to precompile jsps and tags. + *

+ *

+ * Note that the same java compiler will be used as for on-the-fly compiled + * jsps, which will be the Eclipse java compiler. + *

+ * See Usage Guide for + * instructions on using this plugin. + *

+ * Runs jspc compiler to produce .java and .class files + */ +@Mojo(name = "jspc", defaultPhase = LifecyclePhase.PROCESS_CLASSES, requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME, + threadSafe = true) +public class JspcMojo extends AbstractMojo +{ + public static final String END_OF_WEBAPP = ""; + public static final String PRECOMPILED_FLAG = "org.eclipse.jetty.jsp.precompiled"; + + /** + * JettyJspC + * + * Add some extra setters to standard JspC class to help configure it + * for running in maven. + * + * TODO move all setters on the plugin onto this jspc class instead. + */ + public static class JettyJspC extends JspC + { + + private boolean scanAll; + private boolean scanManifest; + + public void setClassLoader(ClassLoader loader) + { + this.loader = loader; + } + + public void setScanAllDirectories(boolean scanAll) + { + this.scanAll = scanAll; + } + + public boolean getScanAllDirectories() + { + return this.scanAll; + } + + public void setScanManifest(boolean scanManifest) + { + this.scanManifest = scanManifest; + } + + public boolean getScanManifest() + { + return this.scanManifest; + } + + @Override + protected TldScanner newTldScanner(JspCServletContext context, boolean namespaceAware, boolean validate, boolean blockExternal) + { + if (context != null && context.getAttribute(JarScanner.class.getName()) == null) + { + StandardJarScanner jarScanner = new StandardJarScanner(); + jarScanner.setScanAllDirectories(getScanAllDirectories()); + jarScanner.setScanManifest(getScanManifest()); + context.setAttribute(JarScanner.class.getName(), jarScanner); + } + + return super.newTldScanner(context, namespaceAware, validate, blockExternal); + } + } + + /** + * Whether or not to include dependencies on the plugin's classpath with <scope>provided</scope> + * Use WITH CAUTION as you may wind up with duplicate jars/classes. + * + * @since jetty-7.6.3 + */ + @Parameter(defaultValue = "false") + private boolean useProvidedScope; + + /** + * The artifacts for the project. + * + * @since jetty-7.6.3 + */ + @Parameter(defaultValue = "${project.artifacts}", readonly = true) + private Set projectArtifacts; + + /** + * The maven project. + */ + @Parameter(defaultValue = "${project}", readonly = true, required = true) + private MavenProject project; + + /** + * The artifacts for the plugin itself. + */ + @Parameter(defaultValue = "${plugin.artifacts}", readonly = true) + private List pluginArtifacts; + + /** + * File into which to generate the <servlet> and + * <servlet-mapping> tags for the compiled jsps + */ + @Parameter(defaultValue = "${basedir}/target/webfrag.xml") + private String webXmlFragment; + + /** + * Optional. A marker string in the src web.xml file which indicates where + * to merge in the generated web.xml fragment. Note that the marker string + * will NOT be preserved during the insertion. Can be left blank, in which + * case the generated fragment is inserted just before the </web-app> + * line + */ + @Parameter + private String insertionMarker; + + /** + * Merge the generated fragment file with the web.xml from + * webAppSourceDirectory. The merged file will go into the same directory as + * the webXmlFragment. + */ + @Parameter(defaultValue = "true") + private boolean mergeFragment; + + /** + * The destination directory into which to put the compiled jsps. + */ + @Parameter(defaultValue = "${project.build.outputDirectory}") + private String generatedClasses; + + /** + * Controls whether or not .java files generated during compilation will be + * preserved. + */ + @Parameter(defaultValue = "false") + private boolean keepSources; + + /** + * Root directory for all html/jsp etc files + */ + @Parameter(defaultValue = "${basedir}/src/main/webapp") + private String webAppSourceDirectory; + + /** + * Location of web.xml. Defaults to src/main/webapp/web.xml. + */ + @Parameter(defaultValue = "${basedir}/src/main/webapp/WEB-INF/web.xml") + private String webXml; + + /** + * The comma separated list of patterns for file extensions to be processed. By default + * will include all .jsp and .jspx files. + */ + @Parameter(defaultValue = "**\\/*.jsp, **\\/*.jspx") + private String includes; + + /** + * The comma separated list of file name patters to exclude from compilation. + */ + @Parameter(defaultValue = "**\\/.svn\\/**") + private String excludes; + + /** + * The location of the compiled classes for the webapp + */ + @Parameter(defaultValue = "${project.build.outputDirectory}") + private File classesDirectory; + + /** + * Patterns of jars on the system path that contain tlds. Use | to separate each pattern. + */ + @Parameter(defaultValue = ".*taglibs[^/]*\\.jar|.*jstl[^/]*\\.jar$") + private String tldJarNamePatterns; + + /** + * Source version - if not set defaults to jsp default (currently 1.7) + */ + @Parameter + private String sourceVersion; + + /** + * Target version - if not set defaults to jsp default (currently 1.7) + */ + @Parameter + private String targetVersion; + + /** + * The JspC instance being used to compile the jsps. + */ + @Parameter + private JettyJspC jspc; + + /** + * Whether dirs on the classpath should be scanned as well as jars. + * True by default. This allows for scanning for tlds of dependent projects that + * are in the reactor as unassembled jars. + */ + @Parameter(defaultValue = "true") + private boolean scanAllDirectories; + + /** + * Determines if the manifest of JAR files found on the classpath should be scanned. + * True by default. + */ + @Parameter(defaultValue = "true") + private boolean scanManifest; + + @Override + public void execute() throws MojoExecutionException, MojoFailureException + { + if (getLog().isDebugEnabled()) + { + + getLog().info("webAppSourceDirectory=" + webAppSourceDirectory); + getLog().info("generatedClasses=" + generatedClasses); + getLog().info("webXmlFragment=" + webXmlFragment); + getLog().info("webXml=" + webXml); + getLog().info("insertionMarker=" + (insertionMarker == null || insertionMarker.isEmpty() ? END_OF_WEBAPP : insertionMarker)); + getLog().info("keepSources=" + keepSources); + getLog().info("mergeFragment=" + mergeFragment); + if (sourceVersion != null) + getLog().info("sourceVersion=" + sourceVersion); + if (targetVersion != null) + getLog().info("targetVersion=" + targetVersion); + } + try + { + prepare(); + compile(); + cleanupSrcs(); + mergeWebXml(); + } + catch (Exception e) + { + throw new MojoExecutionException("Failure processing jsps", e); + } + } + + public void compile() throws Exception + { + ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader(); + + //set up the classpath of the webapp + List webAppUrls = setUpWebAppClassPath(); + + //set up the classpath of the container (ie jetty and jsp jars) + Set pluginJars = getPluginJars(); + Set providedJars = getProvidedScopeJars(pluginJars); + + //Make a classloader so provided jars will be on the classpath + List sysUrls = new ArrayList<>(); + sysUrls.addAll(providedJars); + URLClassLoader sysClassLoader = new URLClassLoader(sysUrls.toArray(new URL[0]), currentClassLoader); + + //make a classloader with the webapp classpath + URLClassLoader webAppClassLoader = new URLClassLoader(webAppUrls.toArray(new URL[0]), sysClassLoader); + StringBuilder webAppClassPath = new StringBuilder(); + + for (int i = 0; i < webAppUrls.size(); i++) + { + if (getLog().isDebugEnabled()) + getLog().debug("webappclassloader contains: " + webAppUrls.get(i)); + webAppClassPath.append(new File(webAppUrls.get(i).toURI()).getCanonicalPath()); + if (getLog().isDebugEnabled()) + getLog().debug("added to classpath: " + (webAppUrls.get(i)).getFile()); + if (i + 1 < webAppUrls.size()) + webAppClassPath.append(System.getProperty("path.separator")); + } + + //Interpose a fake classloader as the webapp class loader. This is because the Apache JspC class + //uses a TldScanner which ignores jars outside of the WEB-INF/lib path on the webapp classloader. + //It will, however, look at all jars on the parents of the webapp classloader. + URLClassLoader fakeWebAppClassLoader = new URLClassLoader(new URL[0], webAppClassLoader); + Thread.currentThread().setContextClassLoader(fakeWebAppClassLoader); + + if (jspc == null) + jspc = new JettyJspC(); + + jspc.setWebXmlInclude(webXmlFragment); + jspc.setUriroot(webAppSourceDirectory); + jspc.setOutputDir(generatedClasses); + jspc.setClassLoader(fakeWebAppClassLoader); + jspc.setScanAllDirectories(scanAllDirectories); + jspc.setScanManifest(scanManifest); + jspc.setCompile(true); + if (sourceVersion != null) + jspc.setCompilerSourceVM(sourceVersion); + if (targetVersion != null) + jspc.setCompilerTargetVM(targetVersion); + + // JspC#setExtensions() does not exist, so + // always set concrete list of files that will be processed. + String jspFiles = getJspFiles(webAppSourceDirectory); + + try + { + if (jspFiles == null | jspFiles.isEmpty()) + { + getLog().info("No files selected to precompile"); + } + else + { + getLog().info("Compiling " + jspFiles + " from includes=" + includes + " excludes=" + excludes); + jspc.setJspFiles(jspFiles); + jspc.execute(); + } + } + finally + { + Thread.currentThread().setContextClassLoader(currentClassLoader); + } + } + + private String getJspFiles(String webAppSourceDirectory) + throws Exception + { + List fileNames = FileUtils.getFileNames(new File(webAppSourceDirectory), includes, excludes, false); + return StringUtils.join(fileNames.toArray(new String[0]), ","); + } + + /** + * Until Jasper supports the option to generate the srcs in a different dir + * than the classes, this is the best we can do. + * + * @throws Exception if unable to clean srcs + */ + public void cleanupSrcs() throws Exception + { + // delete the .java files - depending on keepGenerated setting + if (!keepSources) + { + File generatedClassesDir = new File(generatedClasses); + + if (generatedClassesDir.exists() && generatedClassesDir.isDirectory()) + { + delete(generatedClassesDir, pathname -> + { + return pathname.isDirectory() || pathname.getName().endsWith(".java"); + }); + } + } + } + + static void delete(File dir, FileFilter filter) + { + File[] files = dir.listFiles(filter); + if (files != null) + { + for (File f : files) + { + if (f.isDirectory()) + delete(f, filter); + else + f.delete(); + } + } + } + + /** + * Take the web fragment and put it inside a copy of the web.xml. + * + * You can specify the insertion point by specifying the string in the + * insertionMarker configuration entry. + * + * If you dont specify the insertionMarker, then the fragment will be + * inserted at the end of the file just before the </webapp> + * + * @throws Exception if unable to merge the web xml + */ + public void mergeWebXml() throws Exception + { + if (mergeFragment) + { + // open the src web.xml + File webXml = getWebXmlFile(); + + if (!webXml.exists()) + { + getLog().info(webXml.toString() + " does not exist, cannot merge with generated fragment"); + return; + } + + File fragmentWebXml = new File(webXmlFragment); + File mergedWebXml = new File(fragmentWebXml.getParentFile(), "web.xml"); + + try (BufferedReader webXmlReader = new BufferedReader(new FileReader(webXml)); + PrintWriter mergedWebXmlWriter = new PrintWriter(new FileWriter(mergedWebXml))) + { + + if (!fragmentWebXml.exists()) + { + getLog().info("No fragment web.xml file generated"); + //just copy existing web.xml to expected position + IO.copy(webXmlReader, mergedWebXmlWriter); + } + else + { + // read up to the insertion marker or the if there is no + // marker + boolean atInsertPoint = false; + boolean atEOF = false; + String marker = (insertionMarker == null || insertionMarker.isEmpty() ? END_OF_WEBAPP : insertionMarker); + while (!atInsertPoint && !atEOF) + { + String line = webXmlReader.readLine(); + if (line == null) + atEOF = true; + else if (line.indexOf(marker) >= 0) + { + atInsertPoint = true; + } + else + { + mergedWebXmlWriter.println(line); + } + } + + if (atEOF && !atInsertPoint) + throw new IllegalStateException("web.xml does not contain insertionMarker " + insertionMarker); + + //put in a context init-param to flag that the contents have been precompiled + mergedWebXmlWriter.println("" + PRECOMPILED_FLAG + "true"); + + // put in the generated fragment + try (BufferedReader fragmentWebXmlReader = + new BufferedReader(new FileReader(fragmentWebXml))) + { + IO.copy(fragmentWebXmlReader, mergedWebXmlWriter); + + // if we inserted just before the , put it back in + if (marker.equals(END_OF_WEBAPP)) + mergedWebXmlWriter.println(END_OF_WEBAPP); + + // copy in the rest of the original web.xml file + IO.copy(webXmlReader, mergedWebXmlWriter); + } + } + } + } + } + + private void prepare() throws Exception + { + // For some reason JspC doesn't like it if the dir doesn't + // already exist and refuses to create the web.xml fragment + File generatedSourceDirectoryFile = new File(generatedClasses); + if (!generatedSourceDirectoryFile.exists()) + generatedSourceDirectoryFile.mkdirs(); + } + + /** + * Set up the execution classpath for Jasper. + * + * Put everything in the classesDirectory and all of the dependencies on the + * classpath. + * + * @returns a list of the urls of the dependencies + */ + private List setUpWebAppClassPath() throws Exception + { + //add any classes from the webapp + List urls = new ArrayList(); + urls.add(classesDirectory.toURI().toURL()); + + if (getLog().isDebugEnabled()) + getLog().debug("Adding to classpath classes dir: " + classesDirectory); + + //add the dependencies of the webapp (which will form WEB-INF/lib) + for (Iterator iter = project.getArtifacts().iterator(); iter.hasNext(); ) + { + Artifact artifact = iter.next(); + + // Include runtime and compile time libraries + if (!Artifact.SCOPE_TEST.equals(artifact.getScope()) && !Artifact.SCOPE_PROVIDED.equals(artifact.getScope())) + { + String filePath = artifact.getFile().getCanonicalPath(); + if (getLog().isDebugEnabled()) + getLog().debug("Adding to classpath dependency file: " + filePath); + + urls.add(artifact.getFile().toURI().toURL()); + } + } + return urls; + } + + /** + * + */ + private Set getPluginJars() throws MalformedURLException + { + HashSet pluginJars = new HashSet<>(); + for (Iterator iter = pluginArtifacts.iterator(); iter.hasNext(); ) + { + Artifact pluginArtifact = iter.next(); + if ("jar".equalsIgnoreCase(pluginArtifact.getType())) + { + if (getLog().isDebugEnabled()) + { + getLog().debug("Adding plugin artifact " + pluginArtifact); + } + pluginJars.add(pluginArtifact.getFile().toURI().toURL()); + } + } + + return pluginJars; + } + + /** + * + */ + private Set getProvidedScopeJars(Set pluginJars) throws MalformedURLException + { + if (!useProvidedScope) + return Collections.emptySet(); + + HashSet providedJars = new HashSet<>(); + + for (Iterator iter = projectArtifacts.iterator(); iter.hasNext(); ) + { + Artifact artifact = iter.next(); + if (Artifact.SCOPE_PROVIDED.equals(artifact.getScope())) + { + //test to see if the provided artifact was amongst the plugin artifacts + URL jar = artifact.getFile().toURI().toURL(); + if (!pluginJars.contains(jar)) + { + providedJars.add(jar); + if (getLog().isDebugEnabled()) + { + getLog().debug("Adding provided artifact: " + artifact); + } + } + else + { + if (getLog().isDebugEnabled()) + { + getLog().debug("Skipping provided artifact: " + artifact); + } + } + } + } + return providedJars; + } + + private File getWebXmlFile() + throws IOException + { + File file = null; + File baseDir = project.getBasedir().getCanonicalFile(); + File defaultWebAppSrcDir = new File(baseDir, "src/main/webapp").getCanonicalFile(); + File webAppSrcDir = new File(webAppSourceDirectory).getCanonicalFile(); + File defaultWebXml = new File(defaultWebAppSrcDir, "web.xml").getCanonicalFile(); + + //If the web.xml has been changed from the default, try that + File webXmlFile = new File(webXml).getCanonicalFile(); + if (webXmlFile.compareTo(defaultWebXml) != 0) + { + file = new File(webXml); + return file; + } + + //If the web app src directory has not been changed from the default, use whatever + //is set for the web.xml location + file = new File(webAppSrcDir, "web.xml"); + return file; + } +} diff --git a/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/main/java/org/eclipse/jetty/ee11/jspc/plugin/package-info.java b/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/main/java/org/eclipse/jetty/ee11/jspc/plugin/package-info.java new file mode 100644 index 00000000000..2bce6ad70f8 --- /dev/null +++ b/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/main/java/org/eclipse/jetty/ee11/jspc/plugin/package-info.java @@ -0,0 +1,18 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +/** + * Jetty Jspc Maven Plugin : Support for precompiling jsps + */ +package org.eclipse.jetty.ee11.jspc.plugin; + diff --git a/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/main/resources/META-INF/m2e/lifecycle-mapping-metadata.xml b/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/main/resources/META-INF/m2e/lifecycle-mapping-metadata.xml new file mode 100644 index 00000000000..4f28b2b73cf --- /dev/null +++ b/jetty-ee11/jetty-ee11-jspc-maven-plugin/src/main/resources/META-INF/m2e/lifecycle-mapping-metadata.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + jspc + + + + + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/README_INTEGRATION_TEST.md b/jetty-ee11/jetty-ee11-maven-plugin/README_INTEGRATION_TEST.md new file mode 100644 index 00000000000..1ef95192deb --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/README_INTEGRATION_TEST.md @@ -0,0 +1,22 @@ +Running Maven Integration tests +===================== +The project contains a set of Maven Integration test projects. +They are running using the Maven Invoker plugin which starts an external Maven build to run the project and some post build check. +More details [http://maven.apache.org/plugins/maven-invoker-plugin/]. + +Integration tests location +-------------------- +Test projects are located within the folder: src/it + +Running single test +-------------------- +You can run single or set of test as well using the command line argument: ```-Dinvoker.test=it-parent-pom,jetty-run-mojo-it,jetty-run-war*-it,!jetty-run-distro*``` +The parameter supports pattern and exclusion with ! + +Due to [files filtering](http://maven.apache.org/plugins/maven-invoker-plugin/examples/filtering.html), ```it-parent-pom``` must be included - otherwise tests will fail during execution. + +Running Logs +-------------------- +The output of each Maven build will be located in /target/it/${project-name}/build.log + +The jetty log output for those goals that fork a new process (currently "home" and "run-forked") can be found in /target/it/${project-name}/jetty-simple-webapp/target/jetty.out. diff --git a/jetty-ee11/jetty-ee11-maven-plugin/pom.xml b/jetty-ee11/jetty-ee11-maven-plugin/pom.xml new file mode 100644 index 00000000000..03c3d9ebae9 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/pom.xml @@ -0,0 +1,359 @@ + + + 4.0.0 + + org.eclipse.jetty.ee11 + jetty-ee11 + 12.1.0-SNAPSHOT + ../pom.xml + + jetty-ee11-maven-plugin + maven-plugin + EE11 :: Jetty Maven Plugin + Jetty EE11 maven plugins + + ${project.groupId}.maven.plugin + true + + FREEBEER + false + + + + jakarta.transaction + jakarta.transaction-api + true + + + org.apache.maven.plugin-tools + maven-plugin-tools-api + + + org.codehaus.plexus + plexus-xml + + + javax.annotation + javax.annotation-api + + + org.apache.maven + maven-api-xml + + + org.apache.maven + maven-xml-impl + + + org.apache.maven + maven-xml-meta + + + + + org.eclipse.jetty + jetty-home + zip + + + * + * + + + + + org.eclipse.jetty + jetty-http + true + + + org.eclipse.jetty + jetty-io + true + + + org.eclipse.jetty + jetty-jmx + true + + + org.eclipse.jetty + jetty-jndi + true + + + org.eclipse.jetty + jetty-maven + true + + + org.eclipse.jetty + jetty-security + true + + + org.eclipse.jetty + jetty-server + true + + + org.eclipse.jetty + jetty-util + + + org.eclipse.jetty.ee11 + jetty-ee11-annotations + true + + + org.eclipse.jetty.ee11 + jetty-ee11-apache-jsp + true + + + org.eclipse.jetty.ee11 + jetty-ee11-glassfish-jstl + true + + + org.eclipse.jetty.ee11 + jetty-ee11-plus + true + + + org.eclipse.jetty.ee11 + jetty-ee11-quickstart + true + + + org.eclipse.jetty.ee11 + jetty-ee11-servlet + true + + + org.eclipse.jetty.ee11 + jetty-ee11-webapp + true + + + jakarta.servlet + servlet-api + + + + + org.eclipse.jetty.ee11.websocket + jetty-ee11-websocket-jakarta-server + true + + + org.eclipse.jetty.ee11.websocket + jetty-ee11-websocket-jetty-server + true + + + org.slf4j + slf4j-api + true + + + org.apache.maven + maven-artifact + provided + + + org.apache.maven + maven-core + provided + + + javax.annotation + javax.annotation-api + + + + + org.apache.maven + maven-model + provided + + + org.apache.maven + maven-plugin-api + provided + + + javax.annotation + javax.annotation-api + + + + + org.apache.maven.plugin-tools + maven-plugin-annotations + provided + + + org.awaitility + awaitility + test + + + org.eclipse.jetty + jetty-client + test + + + org.eclipse.jetty + jetty-slf4j-impl + test + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + + org.eclipse.jetty + jetty-home + zip + false + ${project.build.directory} + ${jettyHomeZipFileName} + + + false + true + + + + copy + + copy + + package + + + + + org.apache.maven.plugins + maven-invoker-plugin + + + org.eclipse.jetty:jetty-slf4j-impl:${project.version} + org.eclipse.jetty.ee11:jetty-ee11-apache-jsp:${project.version} + org.eclipse.jetty.ee11:jetty-ee11-glassfish-jstl:${project.version} + org.eclipse.jetty.ee11:jetty-ee11-webapp:${project.version} + org.eclipse.jetty:jetty-server:${project.version} + org.eclipse.jetty:jetty-deploy:${project.version} + org.eclipse.jetty:jetty-home:${project.version}:zip + + org.eclipse.jetty.maven.its.ee11 + + it-parent-pom/pom.xml + + + + javax-annotation-api/pom.xml + + jetty-start-gwt-it/pom.xml + + jetty-start-war-mojo-it/pom.xml + + + ${jetty.stopKey} + ${jetty.stopPort} + ${maven.surefire.plugin.version} + ${localRepoPath} + ${jettyHomeZip} + + + clean + + + + + integration-test + + install + integration-test + verify + + integration-test + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + test-jar + + + + + + org.apache.maven.plugins + maven-plugin-plugin + + jetty + + + + exec-plugin-doc + + helpmojo + + generate-sources + + + + + org.apache.maven.plugins + maven-surefire-plugin + + -Dstop.port=@{test.stopPort} -Djetty.port=@{test.jettyPort} + + **/IntegrationTest*.java + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + test-reserve-ports + + reserve-network-port + + process-test-classes + + + test.stopPort + test.jettyPort + + + + + reserve-ports + + reserve-network-port + + pre-integration-test + + + jetty.stopPort + + + + + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/it-parent-pom/invoker.properties b/jetty-ee11/jetty-ee11-maven-plugin/src/it/it-parent-pom/invoker.properties new file mode 100644 index 00000000000..521301a6d91 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/it-parent-pom/invoker.properties @@ -0,0 +1 @@ +invoker.goals = install \ No newline at end of file diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/it-parent-pom/pom.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/it-parent-pom/pom.xml new file mode 100644 index 00000000000..d6b50e3fd58 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/it-parent-pom/pom.xml @@ -0,0 +1,152 @@ + + + 4.0.0 + + org.eclipse.jetty.ee11.its + it-parent-pom + 0.0.1-SNAPSHOT + pom + + + @project.version@ + UTF-8 + + + + + + commons-io + commons-io + 2.7 + + + jakarta.servlet + jakarta.servlet-api + @jakarta.servlet.api.version@ + provided + + + org.eclipse.jetty.toolchain + jetty-perf-helper + @jetty.perf-helper.version@ + + + com.fasterxml.jackson.core + jackson-databind + @jackson.version@ + + + org.slf4j + slf4j-api + @slf4j.version@ + + + org.eclipse.jetty.ee11 + jetty-ee11-servlet + @project.version@ + + + org.eclipse.jetty + jetty-client + @project.version@ + + + org.eclipse.jetty + jetty-util + @project.version@ + + + org.eclipse.jetty + jetty-http + @project.version@ + + + org.eclipse.jetty + jetty-io + @project.version@ + + + org.eclipse.jetty.ee11 + jetty-ee11-maven-plugin + @project.version@ + tests + test-jar + test + + + org.junit.jupiter + junit-jupiter-engine + @junit.version@ + test + + + org.awaitility + awaitility + @awaitility.version@ + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + @maven.compiler.plugin.version@ + + 11 + 11 + 11 + + + + org.apache.maven.plugins + maven-dependency-plugin + @maven.dependency.plugin.version@ + + + org.apache.maven.plugins + maven-failsafe-plugin + @maven.surefire.plugin.version@ + + + org.apache.maven.plugins + maven-resources-plugin + @maven.resources.plugin.version@ + + + org.apache.maven.plugins + maven-source-plugin + @maven.source.plugin.version@ + + + org.eclipse.jetty.ee11 + jetty-ee11-maven-plugin + ${jetty.version} + + + org.apache.maven.plugins + maven-surefire-plugin + @maven.surefire.plugin.version@ + + + org.apache.maven.plugins + maven-war-plugin + @maven.war.plugin.version@ + + + org.apache.maven.plugins + maven-install-plugin + @maven.install.plugin.version@ + + + org.apache.maven.plugins + maven-deploy-plugin + @maven.deploy.plugin.version@ + + + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-cdi-start-forked/invoker.properties b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-cdi-start-forked/invoker.properties new file mode 100644 index 00000000000..2fc6409821b --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-cdi-start-forked/invoker.properties @@ -0,0 +1 @@ +invoker.goals = test -fae \ No newline at end of file diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-cdi-start-forked/pom.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-cdi-start-forked/pom.xml new file mode 100644 index 00000000000..b4ed00b2126 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-cdi-start-forked/pom.xml @@ -0,0 +1,106 @@ + + + 4.0.0 + + + org.eclipse.jetty.ee11.its + it-parent-pom + 0.0.1-SNAPSHOT + + + org.eclipse.jetty.ee11.its.jetty-cdi-start-forked-mojo-it + jetty-weld-minimal + 1.0-SNAPSHOT + war + + + ${project.build.directory}/jetty-cdi-start-forked-port.txt + FORK + + + + + jakarta.servlet + jakarta.servlet-api + provided + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.eclipse.jetty + jetty-client + test + + + org.awaitility + awaitility + test + + + org.eclipse.jetty.ee11 + jetty-ee11-maven-plugin + tests + test-jar + test + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + IntegrationTest*.java + + + ${jetty.port.file} + true + ${project.groupId}:${project.artifactId} + + + org.eclipse.jetty.ee11:jetty-ee11-maven-plugin + + + + + org.eclipse.jetty.ee11 + jetty-ee11-maven-plugin + + + org.eclipse.jetty + jetty-slf4j-impl + ${jetty.version} + + + + + start-jetty + process-test-classes + + start + + + + ${basedir}/src/main/jetty/jetty-context.xml + + ${basedir}/src/main/jetty/jetty.xml + + @jetty.stopPort@ + @jetty.stopKey@ + ${jetty.jvmArgs} + + ${jetty.port.file} + + + + + + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-cdi-start-forked/postbuild.groovy b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-cdi-start-forked/postbuild.groovy new file mode 100644 index 00000000000..fd16d88a69b --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-cdi-start-forked/postbuild.groovy @@ -0,0 +1,17 @@ + +System.out.println( "postbuild.groovy port " + jettyStopPort + ", key:" + jettyStopKey ) + +int port = Integer.parseInt( jettyStopPort ) + +Socket s=new Socket(InetAddress.getByName("127.0.0.1"),port ) + +OutputStream out=s.getOutputStream() +out.write(( jettyStopKey +"\r\nforcestop\r\n").getBytes()) +out.flush() +s.close() + +File buildLog = new File( basedir, 'build.log' ) +assert buildLog.text.contains( 'Forked process starting' ) +assert buildLog.text.contains( 'Running org.eclipse.jetty.ee11.maven.plugin.it.IntegrationTestGetContent') +assert buildLog.text.contains( 'helloServlet') + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-cdi-start-forked/src/main/java/test/Greeter.java b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-cdi-start-forked/src/main/java/test/Greeter.java new file mode 100644 index 00000000000..913f2fb08dd --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-cdi-start-forked/src/main/java/test/Greeter.java @@ -0,0 +1,37 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package test; + +import java.io.IOException; + +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +@WebServlet("/*") +public class Greeter + extends HttpServlet +{ + + @Override + protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) + throws ServletException, IOException + { + String who = req.getParameter("name"); + + resp.getWriter().write("Hello " + (who == null ? "unknown" : who)); + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-cdi-start-forked/src/main/jetty/jetty-context.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-cdi-start-forked/src/main/jetty/jetty-context.xml new file mode 100644 index 00000000000..78aea23ca19 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-cdi-start-forked/src/main/jetty/jetty-context.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + -org.eclipse.jetty.util.Decorator + -org.eclipse.jetty.util.DecoratedObjectFactory + -org.eclipse.jetty.server.handler.ContextHandler. + -org.eclipse.jetty.server.handler.ContextHandler + -org.eclipse.jetty.ee11.servlet.ServletContextHandler + + + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-cdi-start-forked/src/main/jetty/jetty.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-cdi-start-forked/src/main/jetty/jetty.xml new file mode 100644 index 00000000000..8a8f26b49f4 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-cdi-start-forked/src/main/jetty/jetty.xml @@ -0,0 +1,40 @@ + + + + + + https + + 32768 + 8192 + 8192 + 1024 + + + + + + + + + + + + + + + + + + + + + + + + 0 + 30000 + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-combinedresource-it/invoker.properties b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-combinedresource-it/invoker.properties new file mode 100644 index 00000000000..816c3f38def --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-combinedresource-it/invoker.properties @@ -0,0 +1,2 @@ +invoker.goals = verify -V -e +#test-compile failsafe:integration-test \ No newline at end of file diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-combinedresource-it/pom.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-combinedresource-it/pom.xml new file mode 100644 index 00000000000..b6342e9984a --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-combinedresource-it/pom.xml @@ -0,0 +1,113 @@ + + + 4.0.0 + + + org.eclipse.jetty.ee11.its + it-parent-pom + 0.0.1-SNAPSHOT + + + org.eclipse.jetty.ee11.its.jetty-combinedresource-it + jetty-simple-project + 0.0.1-SNAPSHOT + pom + + EE11 :: Webapp with multiple base resources + + + ${project.build.directory}/jetty-combinedresource-port.txt + + + + + org.eclipse.jetty.ee11 + jetty-ee11-maven-plugin + tests + test-jar + test + + + org.eclipse.jetty + jetty-client + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.awaitility + awaitility + test + + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + ${jetty.port.file} + ${project.groupId}:${project.artifactId} + BLAH + /blah.html + + + org.eclipse.jetty.ee11:jetty-ee11-maven-plugin + + + **/*TestGetContent* + + + + + integration-test + + integration-test + + + + verify + + verify + + + + + + org.eclipse.jetty.ee11 + jetty-ee11-maven-plugin + + + start-jetty + test-compile + + start + + + + pom + + + + ${project.basedir}/public + ${project.basedir}/src/main/webapp + + + + ${jetty.port.file} + + + ${basedir}/src/config/jetty.xml + + + + + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-combinedresource-it/postbuild.groovy b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-combinedresource-it/postbuild.groovy new file mode 100644 index 00000000000..4f05a1ca8f1 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-combinedresource-it/postbuild.groovy @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +File buildLog = new File( basedir, 'build.log' ) +assert buildLog.text.contains( 'Started Server' ) +assert buildLog.text.contains( 'Running org.eclipse.jetty.ee11.maven.plugin.it.IntegrationTestGetContent') +assert buildLog.text.contains( 'contentCheck') diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-combinedresource-it/public/blah.html b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-combinedresource-it/public/blah.html new file mode 100644 index 00000000000..e4e860f38ac --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-combinedresource-it/public/blah.html @@ -0,0 +1 @@ +BLAH diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-combinedresource-it/src/config/jetty.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-combinedresource-it/src/config/jetty.xml new file mode 100644 index 00000000000..343f5e7f99b --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-combinedresource-it/src/config/jetty.xml @@ -0,0 +1,40 @@ + + + + + + https + + 32768 + 8192 + 8192 + 1024 + + + + + + + + + + + + + + + + + + + + + + + + + 30000 + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-combinedresource-it/src/main/webapp/WEB-INF/web.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-combinedresource-it/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000000..7a8e003f946 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-combinedresource-it/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,14 @@ + + + + test + + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-combinedresource-it/src/main/webapp/index.html b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-combinedresource-it/src/main/webapp/index.html new file mode 100644 index 00000000000..593ac2e6079 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-combinedresource-it/src/main/webapp/index.html @@ -0,0 +1 @@ +JJJJJ diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-effective-web-xml-it/invoker.properties b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-effective-web-xml-it/invoker.properties new file mode 100644 index 00000000000..fd18ebccf10 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-effective-web-xml-it/invoker.properties @@ -0,0 +1 @@ +invoker.goals = test diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-effective-web-xml-it/pom.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-effective-web-xml-it/pom.xml new file mode 100644 index 00000000000..2d1a8b78afc --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-effective-web-xml-it/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + + + org.eclipse.jetty.ee11.its + it-parent-pom + 0.0.1-SNAPSHOT + + + org.eclipse.jetty.ee11.its.jetty-effective-web-xml-it + jetty-effective-web-xml-project + 0.0.1-SNAPSHOT + pom + + Jetty :: effective web xml multi-module project + + + resources + webapp-war + + + + UTF-8 + UTF-8 + 1.8 + @project.version@ + + + + + + org.eclipse.jetty.ee11.its.jetty-effective-web-xml-it + resources + ${project.version} + + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-effective-web-xml-it/postbuild.groovy b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-effective-web-xml-it/postbuild.groovy new file mode 100644 index 00000000000..c7c6437beb2 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-effective-web-xml-it/postbuild.groovy @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 groovy.xml.XmlParser + +def rootNode = new XmlParser().parse(new File( basedir, 'webapp-war/target/effective-web.xml')) +// find context-param node with param-name == org.eclipse.jetty.resources +def ctxParam = rootNode.'**'.find{it.text() == "org.eclipse.jetty.resources"}.parent() +def paramValue = ctxParam.'param-value'.get(0).text().trim() +// assert the value of param-value child node +assert paramValue.contains('${user.dir.uri}/resources/target/classes/META-INF/resources') + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-effective-web-xml-it/resources/pom.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-effective-web-xml-it/resources/pom.xml new file mode 100644 index 00000000000..7db12ad6fbd --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-effective-web-xml-it/resources/pom.xml @@ -0,0 +1,11 @@ + + + 4.0.0 + + org.eclipse.jetty.ee11.its.jetty-effective-web-xml-it + jetty-effective-web-xml-project + 0.0.1-SNAPSHOT + + resources + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-effective-web-xml-it/resources/src/main/java/Placeholder.java b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-effective-web-xml-it/resources/src/main/java/Placeholder.java new file mode 100644 index 00000000000..f5eee5ac95a --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-effective-web-xml-it/resources/src/main/java/Placeholder.java @@ -0,0 +1,16 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +public class Placeholder +{ +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-effective-web-xml-it/resources/src/main/resources/META-INF/resources/extra.html b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-effective-web-xml-it/resources/src/main/resources/META-INF/resources/extra.html new file mode 100644 index 00000000000..5ddcf18b968 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-effective-web-xml-it/resources/src/main/resources/META-INF/resources/extra.html @@ -0,0 +1,3 @@ + +

Extra Resource

+ diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-effective-web-xml-it/webapp-war/pom.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-effective-web-xml-it/webapp-war/pom.xml new file mode 100644 index 00000000000..c33ee91baba --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-effective-web-xml-it/webapp-war/pom.xml @@ -0,0 +1,57 @@ + + + 4.0.0 + + org.eclipse.jetty.ee11.its.jetty-effective-web-xml-it + jetty-effective-web-xml-project + 0.0.1-SNAPSHOT + + webapp-war + war + + + + org.eclipse.jetty.ee11 + jetty-ee11-servlet + + + org.eclipse.jetty.ee11.its.jetty-effective-web-xml-it + resources + + + + + ${project.build.directory}/jetty-effective-web-xml-mojo.txt + EMBED + + + + + + org.eclipse.jetty.ee11 + jetty-ee11-maven-plugin + + + gen-effective-web-xml + test-compile + + effective-web-xml + + + + src/main/webapp + + + ${jetty.port.file} + + + ${basedir}/src/config/jetty.xml + + + + + + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-effective-web-xml-it/webapp-war/src/config/jetty.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-effective-web-xml-it/webapp-war/src/config/jetty.xml new file mode 100644 index 00000000000..4e43b5305df --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-effective-web-xml-it/webapp-war/src/config/jetty.xml @@ -0,0 +1,40 @@ + + + + + + https + + 32768 + 8192 + 8192 + 1024 + + + + + + + + + + + + + + + + + + + + + + + + + 30000 + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-effective-web-xml-it/webapp-war/src/main/java/WebAppServletListener.java b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-effective-web-xml-it/webapp-war/src/main/java/WebAppServletListener.java new file mode 100644 index 00000000000..8f60688e24c --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-effective-web-xml-it/webapp-war/src/main/java/WebAppServletListener.java @@ -0,0 +1,33 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +import java.net.URL; +import jakarta.servlet.ServletContextEvent; +import jakarta.servlet.ServletContextListener; + +import static java.lang.String.format; + +public class WebAppServletListener implements ServletContextListener +{ + + //Empty class, just to have something in src/main/java + @Override + public void contextInitialized(ServletContextEvent servletContextEvent) + { + } + + @Override + public void contextDestroyed(ServletContextEvent servletContextEvent) + { + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-effective-web-xml-it/webapp-war/src/main/webapp/WEB-INF/web.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-effective-web-xml-it/webapp-war/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000000..4de3fcefbce --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-effective-web-xml-it/webapp-war/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,11 @@ + + + + + + + WebAppServletListener + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/api/pom.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/api/pom.xml new file mode 100755 index 00000000000..c2546ccaa02 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/api/pom.xml @@ -0,0 +1,11 @@ + + 4.0.0 + + + test.jetty-ee11-maven-plugin-provided-module-dep + parent + 1.0-SNAPSHOT + + api + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/api/src/main/java/test/Api.java b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/api/src/main/java/test/Api.java new file mode 100755 index 00000000000..681defb764b --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/api/src/main/java/test/Api.java @@ -0,0 +1,22 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package test; + +public class Api +{ + + public void noOp() + { + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/invoker.properties b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/invoker.properties new file mode 100644 index 00000000000..816c3f38def --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/invoker.properties @@ -0,0 +1,2 @@ +invoker.goals = verify -V -e +#test-compile failsafe:integration-test \ No newline at end of file diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/pom.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/pom.xml new file mode 100755 index 00000000000..589fda49d37 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/pom.xml @@ -0,0 +1,20 @@ + + 4.0.0 + + + org.eclipse.jetty.ee11.its + it-parent-pom + 0.0.1-SNAPSHOT + + + test.jetty-ee11-maven-plugin-provided-module-dep + parent + 1.0-SNAPSHOT + pom + + + api + web + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/postbuild.groovy b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/postbuild.groovy new file mode 100644 index 00000000000..bfadcee89c4 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/postbuild.groovy @@ -0,0 +1,21 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +File buildLog = new File( basedir, 'build.log' ) +assert buildLog.text.contains( 'Started Server' ) +assert buildLog.text.contains( 'ClassNotFoundException') diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/web/pom.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/web/pom.xml new file mode 100755 index 00000000000..e78eea1e0f0 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/web/pom.xml @@ -0,0 +1,62 @@ + + 4.0.0 + + + test.jetty-ee11-maven-plugin-provided-module-dep + parent + 1.0-SNAPSHOT + + web + war + + + EMBED + + + + + ${project.groupId} + api + ${project.version} + provided + + + jakarta.servlet + jakarta.servlet-api + provided + + + + + + + org.apache.maven.plugins + maven-war-plugin + + false + + + + + + + org.eclipse.jetty.ee11 + jetty-ee11-maven-plugin + + + start-jetty + pre-integration-test + + start + + + + ${basedir}/src/config/jetty.xml + + + + + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/web/src/config/jetty.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/web/src/config/jetty.xml new file mode 100644 index 00000000000..ef0c9bcad93 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/web/src/config/jetty.xml @@ -0,0 +1,48 @@ + + + + + + https + + + + 32768 + 8192 + 8192 + 1024 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + 30000 + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/web/src/main/java/test/ClassLoadingTestingServletContextListener.java b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/web/src/main/java/test/ClassLoadingTestingServletContextListener.java new file mode 100755 index 00000000000..62ae1c193bf --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/web/src/main/java/test/ClassLoadingTestingServletContextListener.java @@ -0,0 +1,70 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package test; + +import java.net.URL; +import java.net.URLClassLoader; + +import jakarta.servlet.ServletContextEvent; +import jakarta.servlet.ServletContextListener; +import jakarta.servlet.annotation.WebListener; + + +@WebListener +public class ClassLoadingTestingServletContextListener + implements ServletContextListener +{ + + @Override + public void contextInitialized(ServletContextEvent sce) + { + try + { + Api api = new Api(); + System.err.println("Class " + api.getClass().getName() + " is available and loaded by classloader " + api.getClass().getClassLoader().toString() + ". Expected CNFE."); + ClassLoader cl = api.getClass().getClassLoader(); + while (cl != null) + { + if (cl instanceof URLClassLoader) + { + URLClassLoader ucl = (URLClassLoader)cl; + System.err.println("-----"); + printURLs(ucl); + System.err.println("-----"); + } + cl = cl.getParent(); + } + } + catch (java.lang.Exception exception) + { + exception.printStackTrace(); + } + } + + @Override + public void contextDestroyed(ServletContextEvent sce) + { + } + + private void printURLs(URLClassLoader l) + { + if (l == null) + return; + + for (URL u: l.getURLs()) + { + System.err.println(u); + } + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyLibrary/pom.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyLibrary/pom.xml new file mode 100644 index 00000000000..748f0321323 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyLibrary/pom.xml @@ -0,0 +1,21 @@ + + + + org.eclipse.jetty.ee11.its.jetty-run-mojo-jar-scan-it + jetty-jar-scan + 1.0-SNAPSHOT + + 4.0.0 + + jetty-jar-scan-library + + + + jakarta.servlet + jakarta.servlet-api + provided + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyLibrary/src/main/java/jettyissue/MyAnnotation.java b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyLibrary/src/main/java/jettyissue/MyAnnotation.java new file mode 100644 index 00000000000..368684a98b1 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyLibrary/src/main/java/jettyissue/MyAnnotation.java @@ -0,0 +1,24 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package jettyissue; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface MyAnnotation { +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyLibrary/src/main/java/jettyissue/MyServletContainerInitializer.java b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyLibrary/src/main/java/jettyissue/MyServletContainerInitializer.java new file mode 100644 index 00000000000..7fbccdf3adc --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyLibrary/src/main/java/jettyissue/MyServletContainerInitializer.java @@ -0,0 +1,28 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package jettyissue; + +import java.util.Set; + +import jakarta.servlet.ServletContainerInitializer; +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.HandlesTypes; + +@HandlesTypes({MyAnnotation.class}) +public class MyServletContainerInitializer implements ServletContainerInitializer { + public void onStartup(Set> c, ServletContext ctx) throws ServletException { + System.out.println("STARTED"+c); + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyLibrary/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyLibrary/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer new file mode 100644 index 00000000000..9e9784f1616 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyLibrary/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer @@ -0,0 +1 @@ +jettyissue.MyServletContainerInitializer diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/pom.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/pom.xml new file mode 100644 index 00000000000..c88a8a1bbed --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/pom.xml @@ -0,0 +1,64 @@ + + + + org.eclipse.jetty.ee11.its.jetty-run-mojo-jar-scan-it + jetty-jar-scan + 1.0-SNAPSHOT + + 4.0.0 + + jetty-jar-scan-webapp + jar + + + ${project.build.directory}/jetty-run-mojo.txt + + + + + jakarta.servlet + jakarta.servlet-api + provided + + + org.eclipse.jetty.ee11.its.jetty-run-mojo-jar-scan-it + jetty-jar-scan-library + + + + + + + + + org.eclipse.jetty.ee11 + jetty-ee11-maven-plugin + + + start-jetty + test-compile + + start + + + + ${jetty.port.file} + + + ${basedir}/src/config/jetty.xml + + ${basedir}/src/config/context.xml + true + + jar + + + + + + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/src/config/context.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/src/config/context.xml new file mode 100644 index 00000000000..f09f38e188e --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/src/config/context.xml @@ -0,0 +1,7 @@ + + + + + /setbycontextxml + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/src/config/jetty.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/src/config/jetty.xml new file mode 100644 index 00000000000..58c5b275121 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/src/config/jetty.xml @@ -0,0 +1,39 @@ + + + + + https + + 32768 + 8192 + 8192 + 1024 + + + + + + + + + + + + + + + + + + + + + + + + 0 + 30000 + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/src/main/java/jettyissue/NormalClass.java b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/src/main/java/jettyissue/NormalClass.java new file mode 100644 index 00000000000..987f8d89d42 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/src/main/java/jettyissue/NormalClass.java @@ -0,0 +1,19 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package jettyissue; + + +@MyAnnotation +public class NormalClass { +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/src/main/webapp/index.html b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/src/main/webapp/index.html new file mode 100644 index 00000000000..b7b5cdc61de --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/src/main/webapp/index.html @@ -0,0 +1,10 @@ + + + + + Title + + + Hello World! + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/invoker.properties b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/invoker.properties new file mode 100644 index 00000000000..ac620b04a8b --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/invoker.properties @@ -0,0 +1 @@ +invoker.goals = test -e diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/pom.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/pom.xml new file mode 100644 index 00000000000..5ac2f521163 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/pom.xml @@ -0,0 +1,32 @@ + + + 4.0.0 + + org.eclipse.jetty.ee11.its + it-parent-pom + 0.0.1-SNAPSHOT + + + + org.eclipse.jetty.ee11.its.jetty-run-mojo-jar-scan-it + jetty-jar-scan + pom + 1.0-SNAPSHOT + + MyLibrary + MyWebApp + + + + + + org.eclipse.jetty.ee11.its.jetty-run-mojo-jar-scan-it + jetty-jar-scan-library + ${project.version} + + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/postbuild.groovy b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/postbuild.groovy new file mode 100644 index 00000000000..caf7a534eab --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/postbuild.groovy @@ -0,0 +1,3 @@ +File buildLog = new File( basedir, 'build.log' ) +assert buildLog.text.contains( 'Started Server' ) +assert buildLog.text.contains( 'STARTED[class jettyissue.NormalClass]') diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-distro-mojo-it/invoker.properties b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-distro-mojo-it/invoker.properties new file mode 100644 index 00000000000..94591882576 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-distro-mojo-it/invoker.properties @@ -0,0 +1,2 @@ +invoker.goals = verify -fae -e +#invoker.debug = true diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-base/pom.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-base/pom.xml new file mode 100644 index 00000000000..1455fc33a6d --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-base/pom.xml @@ -0,0 +1,41 @@ + + + 4.0.0 + + + org.eclipse.jetty.ee11.its.jetty-start-distro-mojo-it + jetty-simple-project + 0.0.1-SNAPSHOT + + + jetty-simple-base + jar + + EE11 :: Simple :: Base + + + + jakarta.servlet + jakarta.servlet-api + provided + + + org.slf4j + slf4j-api + + + commons-io + commons-io + + + org.eclipse.jetty.toolchain + jetty-perf-helper + + + com.fasterxml.jackson.core + jackson-databind + + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-base/src/main/java/org/eclipse/jetty/its/jetty_start_distro_mojo_it/HelloServlet.java b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-base/src/main/java/org/eclipse/jetty/its/jetty_start_distro_mojo_it/HelloServlet.java new file mode 100644 index 00000000000..c9e27791a2c --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-base/src/main/java/org/eclipse/jetty/its/jetty_start_distro_mojo_it/HelloServlet.java @@ -0,0 +1,40 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.its.jetty_start_distro_mojo_it; + +import java.io.IOException; + +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +/** + * + */ +@WebServlet("/hello") +public class HelloServlet + extends HttpServlet +{ + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException + { + String who = req.getParameter("name"); + + resp.getWriter().write("Hello " + (who == null ? "unknown" : who)); + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-base/src/main/java/org/eclipse/jetty/its/jetty_start_distro_mojo_it/PingServlet.java b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-base/src/main/java/org/eclipse/jetty/its/jetty_start_distro_mojo_it/PingServlet.java new file mode 100644 index 00000000000..64b854660e8 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-base/src/main/java/org/eclipse/jetty/its/jetty_start_distro_mojo_it/PingServlet.java @@ -0,0 +1,35 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.its.jetty_start_distro_mojo_it; + +import java.io.IOException; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +public class PingServlet + extends HttpServlet +{ + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException + { + String who = req.getParameter("name"); + + resp.getWriter().write("pong " + (who == null ? "unknown" : who)); + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-base/src/main/resources/META-INF/web-fragment.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-base/src/main/resources/META-INF/web-fragment.xml new file mode 100644 index 00000000000..3198a38948d --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-base/src/main/resources/META-INF/web-fragment.xml @@ -0,0 +1,33 @@ + + + + + FragmentA + + + + + + + Ping + org.eclipse.jetty.its.jetty_start_distro_mojo_it.PingServlet + 1 + + extra1123 + + + extra2345 + + + + + Ping + /ping + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-webapp/pom.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-webapp/pom.xml new file mode 100644 index 00000000000..90967a23e52 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-webapp/pom.xml @@ -0,0 +1,132 @@ + + + 4.0.0 + + + org.eclipse.jetty.ee11.its.jetty-start-distro-mojo-it + jetty-simple-project + 0.0.1-SNAPSHOT + + + jetty-simple-webapp + war + + EE11 :: Simple :: WebApp + + + ${project.build.directory}/jetty-start-distro-port.txt + @jetty.jvmArgs@ + EXTERNAL + + + + + + org.eclipse.jetty.ee11.its.jetty-start-distro-mojo-it + jetty-simple-base + + + + org.eclipse.jetty.ee11 + jetty-ee11-servlet + provided + + + + org.eclipse.jetty.ee11 + jetty-ee11-maven-plugin + tests + test-jar + test + + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.eclipse.jetty + jetty-client + test + + + org.awaitility + awaitility + test + + + + + + + + + + org.apache.maven.plugins + maven-war-plugin + + false + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + IntegrationTest*.java + + + ${jetty.port.file} + true + true + ${project.groupId}:${project.artifactId} + + + org.eclipse.jetty.ee11:jetty-ee11-maven-plugin + + + + + org.eclipse.jetty.ee11 + jetty-ee11-maven-plugin + + + org.eclipse.jetty + jetty-slf4j-impl + ${jetty.version} + + + + @jetty.stopPort@ + @jetty.stopKey@ + + + + start-jetty + process-test-classes + + start + + + ${basedir}/src/base + + true + ${jetty.port.file} + 0 + @localRepoPath@ + + ee11-apache-jsp,ee11-glassfish-jstl,ee11-testmod,resources + --debug + @jettyHomeZip@ + + + + + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-webapp/src/base/etc/test-jetty-ee11.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-webapp/src/base/etc/test-jetty-ee11.xml new file mode 100644 index 00000000000..9857cb546ef --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-webapp/src/base/etc/test-jetty-ee11.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-webapp/src/base/modules/ee11-testmod.mod b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-webapp/src/base/modules/ee11-testmod.mod new file mode 100644 index 00000000000..200733fb07e --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-webapp/src/base/modules/ee11-testmod.mod @@ -0,0 +1,14 @@ +# DO NOT EDIT THIS FILE - See: https://eclipse.dev/jetty/documentation/ + +[description] +Enables test setup + +[environment] +ee11 + +[depend] +http + +[xml] +etc/test-jetty-ee11.xml + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-webapp/src/base/resources/jetty-logging.properties b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-webapp/src/base/resources/jetty-logging.properties new file mode 100644 index 00000000000..3c430260203 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-webapp/src/base/resources/jetty-logging.properties @@ -0,0 +1,8 @@ +# Jetty Logging using jetty-slf4j-impl +org.eclipse.jetty.LEVEL=INFO +#org.eclipse.jetty.deploy.LEVEL=DEBUG +#org.eclipse.jetty.server.LEVEL=DEBUG +#org.eclipse.jetty.ee11.servlet.LEVEL=DEBUG +#org.eclipse.jetty.io.SocketChannelEndPoint.LEVEL=DEBUG +#org.eclipse.jetty.server.DebugListener.LEVEL=DEBUG +#org.eclipse.jetty.server.internal.HttpChannelState.LEVEL=DEBUG diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-webapp/src/main/webapp/WEB-INF/web.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-webapp/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000000..2a5ac4b71bf --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-webapp/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,7 @@ + + + Jetty Simple Webapp run-mojo-it + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-distro-mojo-it/pom.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-distro-mojo-it/pom.xml new file mode 100644 index 00000000000..5536c0c1722 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-distro-mojo-it/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + + + org.eclipse.jetty.ee11.its + it-parent-pom + 0.0.1-SNAPSHOT + + + org.eclipse.jetty.ee11.its.jetty-start-distro-mojo-it + jetty-simple-project + 0.0.1-SNAPSHOT + pom + + EE11 :: Simple + + + UTF-8 + UTF-8 + 1.8 + @project.version@ + + + + jetty-simple-base + jetty-simple-webapp + + + + + + org.eclipse.jetty.ee11.its.jetty-start-distro-mojo-it + jetty-simple-base + ${project.version} + + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-distro-mojo-it/postbuild.groovy b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-distro-mojo-it/postbuild.groovy new file mode 100644 index 00000000000..d412d3a7822 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-distro-mojo-it/postbuild.groovy @@ -0,0 +1,18 @@ + +System.out.println( "running postbuild.groovy port " + jettyStopPort + ", key:" + jettyStopKey ) + +int port = Integer.parseInt( jettyStopPort ) + +Socket s=new Socket(InetAddress.getByName("127.0.0.1"),port ) + +OutputStream out=s.getOutputStream() +out.write(( jettyStopKey +"\r\nforcestop\r\n").getBytes()) +out.flush() +s.close() + + +File buildLog = new File( basedir, 'build.log' ) +assert buildLog.text.contains( 'Home process starting' ) +assert buildLog.text.contains( 'Running org.eclipse.jetty.ee11.maven.plugin.it.IntegrationTestGetContent') +assert buildLog.text.contains( 'pingServlet ok') +assert buildLog.text.contains( 'helloServlet') diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-forked-mojo-it/invoker.properties b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-forked-mojo-it/invoker.properties new file mode 100644 index 00000000000..850d38aa127 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-forked-mojo-it/invoker.properties @@ -0,0 +1 @@ +invoker.goals = verify -fae diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-base/pom.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-base/pom.xml new file mode 100644 index 00000000000..ed3d1892a5f --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-base/pom.xml @@ -0,0 +1,41 @@ + + + 4.0.0 + + + org.eclipse.jetty.ee11.its.jetty-start-forked-mojo-it + jetty-simple-project + 0.0.1-SNAPSHOT + + + jetty-simple-base + jar + + EE11 :: Simple :: Base + + + + jakarta.servlet + jakarta.servlet-api + provided + + + org.slf4j + slf4j-api + + + commons-io + commons-io + + + org.eclipse.jetty.toolchain + jetty-perf-helper + + + com.fasterxml.jackson.core + jackson-databind + + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-base/src/main/java/org/eclipse/jetty/its/jetty_start_forked/Counter.java b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-base/src/main/java/org/eclipse/jetty/its/jetty_start_forked/Counter.java new file mode 100644 index 00000000000..4adc06e2bfe --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-base/src/main/java/org/eclipse/jetty/its/jetty_start_forked/Counter.java @@ -0,0 +1,38 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.its.jetty_start_forked; + +@SuppressWarnings("serial") +public class Counter implements java.io.Serializable +{ + int counter = 0; + String last; + + public int getCount() + { + counter++; + return counter; + } + + public void setLast(String uri) + { + last = uri; + } + + public String getLast() + { + return last; + } +} + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-base/src/main/java/org/eclipse/jetty/its/jetty_start_forked/HelloServlet.java b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-base/src/main/java/org/eclipse/jetty/its/jetty_start_forked/HelloServlet.java new file mode 100644 index 00000000000..4030055d398 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-base/src/main/java/org/eclipse/jetty/its/jetty_start_forked/HelloServlet.java @@ -0,0 +1,40 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.its.jetty_start_forked; + +import java.io.IOException; + +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +/** + * + */ +@WebServlet("/hello") +public class HelloServlet + extends HttpServlet +{ + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException + { + String who = req.getParameter("name"); + + resp.getWriter().write("Hello " + (who == null ? "unknown" : who)); + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-base/src/main/java/org/eclipse/jetty/its/jetty_start_forked/PingServlet.java b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-base/src/main/java/org/eclipse/jetty/its/jetty_start_forked/PingServlet.java new file mode 100644 index 00000000000..99b6be59233 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-base/src/main/java/org/eclipse/jetty/its/jetty_start_forked/PingServlet.java @@ -0,0 +1,35 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.its.jetty_start_forked; + +import java.io.IOException; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +public class PingServlet + extends HttpServlet +{ + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException + { + String who = req.getParameter("name"); + + resp.getWriter().write("pong " + (who == null ? "unknown" : who)); + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-base/src/main/resources/META-INF/web-fragment.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-base/src/main/resources/META-INF/web-fragment.xml new file mode 100644 index 00000000000..028bdc91255 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-base/src/main/resources/META-INF/web-fragment.xml @@ -0,0 +1,32 @@ + + + + + FragmentA + + + + + + + Ping + org.eclipse.jetty.its.jetty_start_forked.PingServlet + + extra1123 + + + extra2345 + + + + + Ping + /ping + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-webapp/pom.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-webapp/pom.xml new file mode 100644 index 00000000000..792b96f3c3c --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-webapp/pom.xml @@ -0,0 +1,131 @@ + + + 4.0.0 + + + org.eclipse.jetty.ee11.its.jetty-start-forked-mojo-it + jetty-simple-project + 0.0.1-SNAPSHOT + + + jetty-simple-webapp + war + + EE11 :: Simple :: WebApp + + + @jetty.jvmArgs@ + ${project.build.directory}/jetty-start-forked-port.txt + FORK + + + + + + org.eclipse.jetty.ee11.its.jetty-start-forked-mojo-it + jetty-simple-base + + + + org.eclipse.jetty.ee11 + jetty-ee11-servlet + provided + + + + org.eclipse.jetty.ee11 + jetty-ee11-maven-plugin + tests + test-jar + test + + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.eclipse.jetty + jetty-client + test + + + org.awaitility + awaitility + test + + + + + + + + + + org.apache.maven.plugins + maven-war-plugin + + false + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + IntegrationTest*.java + + + ${jetty.port.file} + true + true + Counter accessed 1 times. + /jsp/bean1.jsp + ${project.groupId}:${project.artifactId} + + + org.eclipse.jetty.ee11:jetty-ee11-maven-plugin + + + + + org.eclipse.jetty.ee11 + jetty-ee11-maven-plugin + + @jetty.stopPort@ + @jetty.stopKey@ + + + + org.eclipse.jetty + jetty-slf4j-impl + ${jetty.version} + + + + + start-jetty + process-test-classes + + start + + + + ${basedir}/src/config/jetty.xml + + ${jetty.jvmArgs} + + ${jetty.port.file} + + + + + + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-webapp/src/config/jetty.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-webapp/src/config/jetty.xml new file mode 100644 index 00000000000..343f5e7f99b --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-webapp/src/config/jetty.xml @@ -0,0 +1,40 @@ + + + + + + https + + 32768 + 8192 + 8192 + 1024 + + + + + + + + + + + + + + + + + + + + + + + + + 30000 + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-webapp/src/main/webapp/WEB-INF/web.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-webapp/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000000..b2264670c78 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-webapp/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,7 @@ + + + Jetty Simple Webapp start-jetty-forked + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-webapp/src/main/webapp/jsp/bean1.jsp b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-webapp/src/main/webapp/jsp/bean1.jsp new file mode 100644 index 00000000000..08dd1ce9280 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-webapp/src/main/webapp/jsp/bean1.jsp @@ -0,0 +1,13 @@ + +<%@ page session="true"%> + + + +

JSP1.2 Beans: 1

+ +Counter accessed times.
+Counter last accessed by
+ + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-forked-mojo-it/pom.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-forked-mojo-it/pom.xml new file mode 100644 index 00000000000..db48a3f0b63 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-forked-mojo-it/pom.xml @@ -0,0 +1,41 @@ + + + 4.0.0 + + + org.eclipse.jetty.ee11.its + it-parent-pom + 0.0.1-SNAPSHOT + + + org.eclipse.jetty.ee11.its.jetty-start-forked-mojo-it + jetty-simple-project + 0.0.1-SNAPSHOT + pom + + EE11 :: Simple + + + UTF-8 + UTF-8 + 1.8 + 3.0.0 + @project.version@ + + + + jetty-simple-base + jetty-simple-webapp + + + + + + org.eclipse.jetty.ee11.its.jetty-start-forked-mojo-it + jetty-simple-base + ${project.version} + + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-forked-mojo-it/postbuild.groovy b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-forked-mojo-it/postbuild.groovy new file mode 100644 index 00000000000..e193aaab819 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-forked-mojo-it/postbuild.groovy @@ -0,0 +1,19 @@ + + +System.out.println( "running postbuild.groovy port " + jettyStopPort + ", key:" + jettyStopKey ) + +int port = Integer.parseInt( jettyStopPort ) + +Socket s=new Socket(InetAddress.getByName("127.0.0.1"),port ) + +OutputStream out=s.getOutputStream() +out.write(( jettyStopKey +"\r\nforcestop\r\n").getBytes()) +out.flush() +s.close() + + +File buildLog = new File( basedir, 'build.log' ) +assert buildLog.text.contains( 'Forked process starting' ) +assert buildLog.text.contains( 'Running org.eclipse.jetty.ee11.maven.plugin.it.IntegrationTestGetContent') +assert buildLog.text.contains( 'pingServlet ok') +assert buildLog.text.contains( 'helloServlet') diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-client/pom.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-client/pom.xml new file mode 100644 index 00000000000..d1602c95c72 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-client/pom.xml @@ -0,0 +1,48 @@ + + + 4.0.0 + + + org.olamy + beer + 1.0-SNAPSHOT + + + beer-client + gwt-app + + + + ${project.groupId} + beer-shared + ${project.version} + + + ${project.groupId} + beer-shared + ${project.version} + sources + + + com.google.gwt + gwt-user + + + com.google.gwt + gwt-dev + + + + + + + net.ltgt.gwt.maven + gwt-maven-plugin + + org.olamy.App + app + + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-client/src/main/java/org/olamy/App.java b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-client/src/main/java/org/olamy/App.java new file mode 100644 index 00000000000..5e229d1082c --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-client/src/main/java/org/olamy/App.java @@ -0,0 +1,184 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.olamy; + +import com.google.gwt.core.client.EntryPoint; +import com.google.gwt.core.client.GWT; +import com.google.gwt.event.dom.client.ClickEvent; +import com.google.gwt.event.dom.client.ClickHandler; +import com.google.gwt.event.dom.client.KeyCodes; +import com.google.gwt.event.dom.client.KeyUpEvent; +import com.google.gwt.event.dom.client.KeyUpHandler; +import com.google.gwt.safehtml.shared.SafeHtmlBuilder; +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.google.gwt.user.client.ui.Button; +import com.google.gwt.user.client.ui.DialogBox; +import com.google.gwt.user.client.ui.HTML; +import com.google.gwt.user.client.ui.Label; +import com.google.gwt.user.client.ui.RootPanel; +import com.google.gwt.user.client.ui.TextBox; +import com.google.gwt.user.client.ui.VerticalPanel; + +/** + * Entry point classes define onModuleLoad(). + */ +public class App implements EntryPoint +{ + /** + * The message displayed to the user when the server cannot be reached or + * returns an error. + */ + private static final String SERVER_ERROR = "An error occurred while " + + "attempting to contact the server. Please check your network " + + "connection and try again."; + + /** + * Create a remote service proxy to talk to the server-side Greeting service. + */ + private final GreetingServiceAsync greetingService = GWT + .create(GreetingService.class); + + /** + * This is the entry point method. + */ + public void onModuleLoad() + { + final Button sendButton = new Button("Send"); + final TextBox nameField = new TextBox(); + nameField.setText("GWT User"); + final Label errorLabel = new Label(); + + // We can add style names to widgets + sendButton.addStyleName("sendButton"); + + // Add the nameField and sendButton to the RootPanel + // Use RootPanel.get() to get the entire body element + RootPanel.get("nameFieldContainer").add(nameField); + RootPanel.get("sendButtonContainer").add(sendButton); + RootPanel.get("errorLabelContainer").add(errorLabel); + + // Focus the cursor on the name field when the app loads + nameField.setFocus(true); + nameField.selectAll(); + + // Create the popup dialog box + final DialogBox dialogBox = new DialogBox(); + dialogBox.setText("Remote Procedure Call"); + dialogBox.setAnimationEnabled(true); + final Button closeButton = new Button("Close"); + // We can set the id of a widget by accessing its Element + closeButton.getElement().setId("closeButton"); + final Label textToServerLabel = new Label(); + final HTML serverResponseLabel = new HTML(); + VerticalPanel dialogVPanel = new VerticalPanel(); + dialogVPanel.addStyleName("dialogVPanel"); + dialogVPanel.add(new HTML("Sending name to the server:")); + dialogVPanel.add(textToServerLabel); + dialogVPanel.add(new HTML("
Server replies:")); + dialogVPanel.add(serverResponseLabel); + dialogVPanel.setHorizontalAlignment(VerticalPanel.ALIGN_RIGHT); + dialogVPanel.add(closeButton); + dialogBox.setWidget(dialogVPanel); + + // Add a handler to close the DialogBox + closeButton.addClickHandler(new ClickHandler() + { + public void onClick(ClickEvent event) + { + dialogBox.hide(); + sendButton.setEnabled(true); + sendButton.setFocus(true); + } + }); + + // Create a handler for the sendButton and nameField + class MyHandler implements ClickHandler, KeyUpHandler + { + /** + * Fired when the user clicks on the sendButton. + */ + public void onClick(ClickEvent event) + { + sendNameToServer(); + } + + /** + * Fired when the user types in the nameField. + */ + public void onKeyUp(KeyUpEvent event) + { + if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) + { + sendNameToServer(); + } + } + + /** + * Send the name from the nameField to the server and wait for a response. + */ + private void sendNameToServer() + { + // First, we validate the input. + errorLabel.setText(""); + String textToServer = nameField.getText(); + if (!FieldVerifier.isValidName(textToServer)) + { + errorLabel.setText("Please enter at least four characters"); + return; + } + + // Then, we send the input to the server. + sendButton.setEnabled(false); + textToServerLabel.setText(textToServer); + serverResponseLabel.setText(""); + greetingService.greetServer(textToServer, + new AsyncCallback() + { + public void onFailure(Throwable caught) + { + // Show the RPC error message to the user + dialogBox + .setText("Remote Procedure Call - Failure"); + serverResponseLabel + .addStyleName("serverResponseLabelError"); + serverResponseLabel.setHTML(SERVER_ERROR); + dialogBox.center(); + closeButton.setFocus(true); + } + + public void onSuccess(GreetingResponse result) + { + dialogBox.setText("Remote Procedure Call"); + serverResponseLabel + .removeStyleName("serverResponseLabelError"); + serverResponseLabel.setHTML(new SafeHtmlBuilder() + .appendEscaped(result.getGreeting()) + .appendHtmlConstant("

I am running ") + .appendEscaped(result.getServerInfo()) + .appendHtmlConstant(".

It looks like you are using:
") + .appendEscaped(result.getUserAgent()) + .toSafeHtml()); + dialogBox.center(); + closeButton.setFocus(true); + } + }); + } + } + + // Add a handler to send the name to the server + MyHandler handler = new MyHandler(); + sendButton.addClickHandler(handler); + nameField.addKeyUpHandler(handler); + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-client/src/main/module.gwt.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-client/src/main/module.gwt.xml new file mode 100644 index 00000000000..6cda0e54be6 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-client/src/main/module.gwt.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-server/pom.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-server/pom.xml new file mode 100644 index 00000000000..400ef1b6676 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-server/pom.xml @@ -0,0 +1,118 @@ + + + 4.0.0 + + + org.olamy + beer + 1.0-SNAPSHOT + + + beer-server + war + + + ${project.build.directory}/jetty-start-mojo.txt + EMBED + + + + + ${project.groupId} + beer-shared + ${project.version} + + + com.google.gwt + gwt-servlet + + + jakarta.servlet + jakarta.servlet-api + provided + + + + org.eclipse.jetty + jetty-client + test + + + org.awaitility + awaitility + test + + + org.eclipse.jetty + jetty-util + test + + + org.eclipse.jetty + jetty-http + test + + + org.eclipse.jetty + jetty-io + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.eclipse.jetty.ee11 + jetty-ee11-maven-plugin + tests + test-jar + test + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + IntegrationTest*.java + + + ${jetty.port.file} + Please enter your name + ${project.groupId}:${project.artifactId} + + + org.eclipse.jetty.ee11:jetty-ee11-maven-plugin + + + + + org.eclipse.jetty.ee11 + jetty-ee11-maven-plugin + + + run + test-compile + + start + + + + ${jetty.port.file} + + ${basedir}/src/main/jettyconf/context.xml + + ${basedir}/src/config/jetty.xml + + + + + + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-server/src/config/jetty.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-server/src/config/jetty.xml new file mode 100644 index 00000000000..343f5e7f99b --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-server/src/config/jetty.xml @@ -0,0 +1,40 @@ + + + + + + https + + 32768 + 8192 + 8192 + 1024 + + + + + + + + + + + + + + + + + + + + + + + + + 30000 + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-server/src/main/java/org/olamy/GreetingServiceImpl.java b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-server/src/main/java/org/olamy/GreetingServiceImpl.java new file mode 100644 index 00000000000..fa439e7b5a0 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-server/src/main/java/org/olamy/GreetingServiceImpl.java @@ -0,0 +1,46 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.olamy; + +import com.google.gwt.user.server.rpc.RemoteServiceServlet; + +/** + * The server side implementation of the RPC service. + */ +@SuppressWarnings("serial") +public class GreetingServiceImpl extends RemoteServiceServlet implements + GreetingService +{ + + public GreetingResponse greetServer(String input) throws IllegalArgumentException + { + // Verify that the input is valid. + if (!FieldVerifier.isValidName(input)) + { + // If the input is not valid, throw an IllegalArgumentException back to + // the client. + throw new IllegalArgumentException( + "Name must be at least 4 characters long"); + } + + GreetingResponse response = new GreetingResponse(); + + response.setServerInfo(getServletContext().getServerInfo()); + response.setUserAgent(getThreadLocalRequest().getHeader("User-Agent")); + + response.setGreeting("Hello, " + input + "!"); + + return response; + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-server/src/main/jettyconf/context.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-server/src/main/jettyconf/context.xml new file mode 100644 index 00000000000..00798e56645 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-server/src/main/jettyconf/context.xml @@ -0,0 +1,8 @@ + + + + + org.eclipse.jetty.ee11.servlet.Default.useFileMappedBuffer + false + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-server/src/main/webapp/WEB-INF/web.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-server/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000000..fe9829584d3 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-server/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,20 @@ + + + + + + greetServlet + org.olamy.GreetingServiceImpl + + + + greetServlet + /app/greet + + + + + index.html + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-server/src/main/webapp/beer.css b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-server/src/main/webapp/beer.css new file mode 100644 index 00000000000..7aca7ac7b65 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-server/src/main/webapp/beer.css @@ -0,0 +1,34 @@ +/** Add css rules here for your application. */ + + +/** Example rules used by the template application (remove for your app) */ +h1 { + font-size: 2em; + font-weight: bold; + color: #777777; + margin: 40px 0px 70px; + text-align: center; +} + +.sendButton { + display: block; + font-size: 16pt; +} + +/** Most GWT widgets already have a style name defined */ +.gwt-DialogBox { + width: 400px; +} + +.dialogVPanel { + margin: 5px; +} + +.serverResponseLabelError { + color: red; +} + +/** Set ids using widget.getElement().setId("idOfElement") */ +#closeButton { + margin: 15px 6px 6px; +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-server/src/main/webapp/favicon.ico b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-server/src/main/webapp/favicon.ico new file mode 100644 index 00000000000..858a707523f Binary files /dev/null and b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-server/src/main/webapp/favicon.ico differ diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-server/src/main/webapp/index.html b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-server/src/main/webapp/index.html new file mode 100644 index 00000000000..96cb49b86b1 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-server/src/main/webapp/index.html @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + Web Application Starter Project + + + + + + + + + + + + + + + + + + + +

Web Application Starter Project

+ + + + + + + + + + + + +
Please enter your name:
+ + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-shared/pom.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-shared/pom.xml new file mode 100644 index 00000000000..054ba242e95 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-shared/pom.xml @@ -0,0 +1,29 @@ + + + 4.0.0 + + + org.olamy + beer + 1.0-SNAPSHOT + + + beer-shared + + + + com.google.gwt + gwt-servlet + provided + + + + + + + org.apache.maven.plugins + maven-source-plugin + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-shared/src/main/java/org/olamy/FieldVerifier.java b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-shared/src/main/java/org/olamy/FieldVerifier.java new file mode 100644 index 00000000000..6f56ee3a65a --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-shared/src/main/java/org/olamy/FieldVerifier.java @@ -0,0 +1,58 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.olamy; + +/** + *

+ * FieldVerifier validates that the name the user enters is valid. + *

+ *

+ * This class is in the shared project because we use it in both + * the client code and on the server. On the client, we verify that the name is + * valid before sending an RPC request so the user doesn't have to wait for a + * network round trip to get feedback. On the server, we verify that the name is + * correct to ensure that the input is correct regardless of where the RPC + * originates. + *

+ *

+ * When creating a class that is used on both the client and the server, be sure + * that all code is translatable and does not use native JavaScript. Code that + * is not translatable (such as code that interacts with a database or the file + * system) cannot be compiled into client side JavaScript. Code that uses native + * JavaScript (such as Widgets) cannot be run on the server. + *

+ */ +public class FieldVerifier +{ + + /** + * Verifies that the specified name is valid for our service. + * + * In this example, we only require that the name is at least four + * characters. In your application, you can use more complex checks to ensure + * that usernames, passwords, email addresses, URLs, and other fields have the + * proper syntax. + * + * @param name the name to validate + * @return true if valid, false if invalid + */ + public static boolean isValidName(String name) + { + if (name == null) + { + return false; + } + return name.length() > 3; + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-shared/src/main/java/org/olamy/GreetingResponse.java b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-shared/src/main/java/org/olamy/GreetingResponse.java new file mode 100644 index 00000000000..d5e04f8ec4a --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-shared/src/main/java/org/olamy/GreetingResponse.java @@ -0,0 +1,54 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.olamy; + +import java.io.Serializable; + +@SuppressWarnings("serial") +public class GreetingResponse implements Serializable +{ + private String greeting; + private String serverInfo; + private String userAgent; + + public String getGreeting() + { + return greeting; + } + + public void setGreeting(String greeting) + { + this.greeting = greeting; + } + + public String getServerInfo() + { + return serverInfo; + } + + public void setServerInfo(String serverInfo) + { + this.serverInfo = serverInfo; + } + + public String getUserAgent() + { + return userAgent; + } + + public void setUserAgent(String userAgent) + { + this.userAgent = userAgent; + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-shared/src/main/java/org/olamy/GreetingService.java b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-shared/src/main/java/org/olamy/GreetingService.java new file mode 100644 index 00000000000..707679cd41f --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-shared/src/main/java/org/olamy/GreetingService.java @@ -0,0 +1,26 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.olamy; + +import com.google.gwt.user.client.rpc.RemoteService; +import com.google.gwt.user.client.rpc.RemoteServiceRelativePath; + +/** + * The client side stub for the RPC service. + */ +@RemoteServiceRelativePath("greet") +public interface GreetingService extends RemoteService +{ + GreetingResponse greetServer(String name) throws IllegalArgumentException; +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-shared/src/main/java/org/olamy/GreetingServiceAsync.java b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-shared/src/main/java/org/olamy/GreetingServiceAsync.java new file mode 100644 index 00000000000..0351adfd0a7 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/beer-shared/src/main/java/org/olamy/GreetingServiceAsync.java @@ -0,0 +1,25 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.olamy; + +import com.google.gwt.user.client.rpc.AsyncCallback; + +/** + * The async counterpart of GreetingService. + */ +public interface GreetingServiceAsync +{ + void greetServer(String input, AsyncCallback callback) + throws IllegalArgumentException; +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/invoker.properties b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/invoker.properties new file mode 100644 index 00000000000..26652d0273a --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/invoker.properties @@ -0,0 +1 @@ +invoker.goals = verify \ No newline at end of file diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/pom.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/pom.xml new file mode 100644 index 00000000000..b1430b7b082 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/pom.xml @@ -0,0 +1,82 @@ + + + 4.0.0 + + + org.eclipse.jetty.ee11.its + it-parent-pom + 0.0.1-SNAPSHOT + + + org.olamy + beer + 1.0-SNAPSHOT + pom + + + UTF-8 + @project.version@ + + + + + + com.google.gwt + gwt + 2.8.2 + pom + import + + + + + + + + net.ltgt.gwt.maven + gwt-maven-plugin + false + + ${project.build.directory}/gwt/launcherDir + + + + + + + org.eclipse.jetty.ee11 + jetty-ee11-maven-plugin + + + + net.ltgt.gwt.maven + gwt-maven-plugin + 1.0-rc-9 + true + + 1.8 + true + + + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + package + + jar-no-fork + + + + + + + + + beer-client + beer-shared + beer-server + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/postbuild.groovy b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/postbuild.groovy new file mode 100644 index 00000000000..4f05a1ca8f1 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-gwt-it/postbuild.groovy @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +File buildLog = new File( basedir, 'build.log' ) +assert buildLog.text.contains( 'Started Server' ) +assert buildLog.text.contains( 'Running org.eclipse.jetty.ee11.maven.plugin.it.IntegrationTestGetContent') +assert buildLog.text.contains( 'contentCheck') diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-it/invoker.properties b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-it/invoker.properties new file mode 100644 index 00000000000..998d6702ac5 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-it/invoker.properties @@ -0,0 +1 @@ +invoker.goals = test -e \ No newline at end of file diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-base/pom.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-base/pom.xml new file mode 100644 index 00000000000..85b298f8eb3 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-base/pom.xml @@ -0,0 +1,39 @@ + + + 4.0.0 + + + org.eclipse.jetty.ee11.its.jetty-start-mojo-it + jetty-simple-project + 0.0.1-SNAPSHOT + + + jetty-simple-base + jar + + EE11 :: Simple :: Base + + + + jakarta.servlet + jakarta.servlet-api + provided + + + org.slf4j + slf4j-api + + + commons-io + commons-io + + + org.eclipse.jetty.toolchain + jetty-perf-helper + + + com.fasterxml.jackson.core + jackson-databind + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-base/src/main/java/org/eclipse/jetty/its/jetty_start_mojo_it/Counter.java b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-base/src/main/java/org/eclipse/jetty/its/jetty_start_mojo_it/Counter.java new file mode 100644 index 00000000000..89f6c4b0f05 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-base/src/main/java/org/eclipse/jetty/its/jetty_start_mojo_it/Counter.java @@ -0,0 +1,38 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.its.jetty_start_mojo_it; + +@SuppressWarnings("serial") +public class Counter implements java.io.Serializable +{ + int counter = 0; + String last; + + public int getCount() + { + counter++; + return counter; + } + + public void setLast(String uri) + { + last = uri; + } + + public String getLast() + { + return last; + } +} + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-base/src/main/java/org/eclipse/jetty/its/jetty_start_mojo_it/HelloServlet.java b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-base/src/main/java/org/eclipse/jetty/its/jetty_start_mojo_it/HelloServlet.java new file mode 100644 index 00000000000..e653b64b039 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-base/src/main/java/org/eclipse/jetty/its/jetty_start_mojo_it/HelloServlet.java @@ -0,0 +1,40 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.its.jetty_start_mojo_it; + +import java.io.IOException; + +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +/** + * + */ +@WebServlet("/hello") +public class HelloServlet + extends HttpServlet +{ + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException + { + String who = req.getParameter("name"); + + resp.getWriter().write("Hello " + (who == null ? "unknown" : who)); + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-base/src/main/java/org/eclipse/jetty/its/jetty_start_mojo_it/PingServlet.java b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-base/src/main/java/org/eclipse/jetty/its/jetty_start_mojo_it/PingServlet.java new file mode 100644 index 00000000000..995072a4c99 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-base/src/main/java/org/eclipse/jetty/its/jetty_start_mojo_it/PingServlet.java @@ -0,0 +1,35 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.its.jetty_start_mojo_it; + +import java.io.IOException; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +public class PingServlet + extends HttpServlet +{ + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException + { + String who = req.getParameter("name"); + + resp.getWriter().write("pong " + (who == null ? "unknown" : who)); + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-base/src/main/resources/META-INF/web-fragment.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-base/src/main/resources/META-INF/web-fragment.xml new file mode 100644 index 00000000000..5a8075691c8 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-base/src/main/resources/META-INF/web-fragment.xml @@ -0,0 +1,32 @@ + + + + + FragmentA + + + + + + + Ping + org.eclipse.jetty.its.jetty_start_mojo_it.PingServlet + + extra1123 + + + extra2345 + + + + + Ping + /ping + + + + \ No newline at end of file diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-webapp/pom.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-webapp/pom.xml new file mode 100644 index 00000000000..f5676b3ddb6 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-webapp/pom.xml @@ -0,0 +1,130 @@ + + + 4.0.0 + + + org.eclipse.jetty.ee11.its.jetty-start-mojo-it + jetty-simple-project + 0.0.1-SNAPSHOT + + + jetty-simple-webapp + war + + EE11 :: Simple :: WebApp + + + ${project.build.directory}/jetty-start-port.txt + + + + + + org.eclipse.jetty.ee11.its.jetty-start-mojo-it + jetty-simple-base + + + + org.eclipse.jetty.ee11 + jetty-ee11-servlet + provided + + + + org.eclipse.jetty.ee11 + jetty-ee11-maven-plugin + tests + test-jar + test + + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.eclipse.jetty + jetty-client + test + + + org.awaitility + awaitility + test + + + + + + + + + + org.apache.maven.plugins + maven-war-plugin + + false + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + IntegrationTest*.java + + + ${jetty.port.file} + /setbycontextxml + true + true + Counter accessed 1 times. + /jsp/bean1.jsp + ${project.groupId}:${project.artifactId} + + + org.eclipse.jetty.ee11:jetty-ee11-maven-plugin + + + + + org.eclipse.jetty.ee11 + jetty-ee11-maven-plugin + + + start-jetty + test-compile + + start + + + ${basedir}/src/config/context.xml + + ${jetty.port.file} + EMBED + + + ${basedir}/src/config/jetty.xml + + + + + ${basedir}/src/config/login.xml + + + + + ${basedir}/src/config/jetty-env.xml + + + + + + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-webapp/src/config/context.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-webapp/src/config/context.xml new file mode 100644 index 00000000000..f09f38e188e --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-webapp/src/config/context.xml @@ -0,0 +1,7 @@ + + + + + /setbycontextxml + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-webapp/src/config/jetty-env.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-webapp/src/config/jetty-env.xml new file mode 100644 index 00000000000..ac26e7a42f0 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-webapp/src/config/jetty-env.xml @@ -0,0 +1,12 @@ + + + + + + + + fooBoolean + 100 + true + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-webapp/src/config/jetty.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-webapp/src/config/jetty.xml new file mode 100644 index 00000000000..343f5e7f99b --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-webapp/src/config/jetty.xml @@ -0,0 +1,40 @@ + + + + + + https + + 32768 + 8192 + 8192 + 1024 + + + + + + + + + + + + + + + + + + + + + + + + + 30000 + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-webapp/src/config/login.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-webapp/src/config/login.xml new file mode 100644 index 00000000000..2cfa722dd89 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-webapp/src/config/login.xml @@ -0,0 +1,6 @@ +jetty: MD5:164c88b302622e17050af52c89945d44,user +admin: CRYPT:adpexzg3FUZAk,server-administrator,content-administrator,admin +other: OBF:1xmk1w261u9r1w1c1xmq,user +plain: plain,user +user: password,user + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-webapp/src/main/webapp/WEB-INF/web.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-webapp/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000000..2a5ac4b71bf --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-webapp/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,7 @@ + + + Jetty Simple Webapp run-mojo-it + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-webapp/src/main/webapp/jsp/bean1.jsp b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-webapp/src/main/webapp/jsp/bean1.jsp new file mode 100644 index 00000000000..586a27ebf2e --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-webapp/src/main/webapp/jsp/bean1.jsp @@ -0,0 +1,13 @@ + +<%@ page session="true"%> + + + +

JSP1.2 Beans: 1

+ +Counter accessed times.
+Counter last accessed by
+ + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-it/pom.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-it/pom.xml new file mode 100644 index 00000000000..e2d56685855 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-it/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + + + org.eclipse.jetty.ee11.its + it-parent-pom + 0.0.1-SNAPSHOT + + + org.eclipse.jetty.ee11.its.jetty-start-mojo-it + jetty-simple-project + 0.0.1-SNAPSHOT + pom + + EE11 :: Simple + + + UTF-8 + UTF-8 + 1.8 + @project.version@ + + + + jetty-simple-base + jetty-simple-webapp + + + + + + org.eclipse.jetty.ee11.its.jetty-start-mojo-it + jetty-simple-base + ${project.version} + + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-it/postbuild.groovy b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-it/postbuild.groovy new file mode 100644 index 00000000000..10d76c83e13 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-it/postbuild.groovy @@ -0,0 +1,23 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +File buildLog = new File( basedir, 'build.log' ) +assert buildLog.text.contains( 'Started Server' ) +assert buildLog.text.contains( 'Running org.eclipse.jetty.ee11.maven.plugin.it.IntegrationTestGetContent') +assert buildLog.text.contains( 'pingServlet ok') +assert buildLog.text.contains( 'helloServlet') diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/common/pom.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/common/pom.xml new file mode 100644 index 00000000000..8c694e8e536 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/common/pom.xml @@ -0,0 +1,11 @@ + + + 4.0.0 + + test.jetty-ee11-start-mojo-multi-module-single-war-it + jetty-multi-module-project + 1.0-SNAPSHOT + + common + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/common/src/main/java/mca/common/CommonService.java b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/common/src/main/java/mca/common/CommonService.java new file mode 100644 index 00000000000..3c4d6bca665 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/common/src/main/java/mca/common/CommonService.java @@ -0,0 +1,19 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package mca.common; + +public class CommonService +{ + +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/invoker.properties b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/invoker.properties new file mode 100644 index 00000000000..fd18ebccf10 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/invoker.properties @@ -0,0 +1 @@ +invoker.goals = test diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/module/module-api/pom.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/module/module-api/pom.xml new file mode 100644 index 00000000000..6a2e9932521 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/module/module-api/pom.xml @@ -0,0 +1,11 @@ + + + 4.0.0 + + test.jetty-ee11-start-mojo-multi-module-single-war-it + module + 1.0-SNAPSHOT + + module-api + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/module/module-api/src/main/java/mca/module/ModuleApi.java b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/module/module-api/src/main/java/mca/module/ModuleApi.java new file mode 100644 index 00000000000..46af5853ead --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/module/module-api/src/main/java/mca/module/ModuleApi.java @@ -0,0 +1,19 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package mca.module; + +public interface ModuleApi +{ + +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/module/module-impl/pom.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/module/module-impl/pom.xml new file mode 100644 index 00000000000..aec295601ce --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/module/module-impl/pom.xml @@ -0,0 +1,17 @@ + + + 4.0.0 + + test.jetty-ee11-start-mojo-multi-module-single-war-it + module + 1.0-SNAPSHOT + + module-impl + + + + test.jetty-ee11-start-mojo-multi-module-single-war-it + module-api + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/module/module-impl/src/main/java/mca/module/ModuleImpl.java b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/module/module-impl/src/main/java/mca/module/ModuleImpl.java new file mode 100644 index 00000000000..60f6567ee01 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/module/module-impl/src/main/java/mca/module/ModuleImpl.java @@ -0,0 +1,22 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package mca.module; + +import mca.common.CommonService; + +public class ModuleImpl implements ModuleApi +{ + + private static final CommonService cs = new CommonService(); +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/module/pom.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/module/pom.xml new file mode 100644 index 00000000000..42fa93fed4b --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/module/pom.xml @@ -0,0 +1,24 @@ + + + 4.0.0 + + test.jetty-ee11-start-mojo-multi-module-single-war-it + jetty-multi-module-project + 1.0-SNAPSHOT + + module + pom + + + module-api + module-impl + + + + + test.jetty-ee11-start-mojo-multi-module-single-war-it + common + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/pom.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/pom.xml new file mode 100644 index 00000000000..611a0e41f18 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/pom.xml @@ -0,0 +1,51 @@ + + + 4.0.0 + + + org.eclipse.jetty.ee11.its + it-parent-pom + 0.0.1-SNAPSHOT + + + test.jetty-ee11-start-mojo-multi-module-single-war-it + jetty-multi-module-project + 1.0-SNAPSHOT + pom + + EE11 :: multi-module project + + + common + module + webapp-war + + + + UTF-8 + UTF-8 + 1.8 + @project.version@ + + + + + + test.jetty-ee11-start-mojo-multi-module-single-war-it + common + ${project.version} + + + test.jetty-ee11-start-mojo-multi-module-single-war-it + module-api + ${project.version} + + + test.jetty-ee11-start-mojo-multi-module-single-war-it + module-impl + ${project.version} + + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/postbuild.groovy b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/postbuild.groovy new file mode 100644 index 00000000000..dff8bd20fe7 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/postbuild.groovy @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +File buildLog = new File( basedir, 'build.log' ) +assert buildLog.text.contains( 'Started Server' ) + +assert buildLog.text.contains( '(1a) >> jakarta.servlet.ServletContextListener loaded from jar:' ) + +assert buildLog.text.contains( '(2a) >> mca.common.CommonService loaded from file:' ) +assert buildLog.text.contains( 'common/target/classes/mca/common/CommonService.class << (2b)' ) + +assert buildLog.text.contains( '(3a) >> mca.module.ModuleApi loaded from file:' ) +assert buildLog.text.contains( 'module/module-api/target/classes/mca/module/ModuleApi.class << (3b)' ) + +assert buildLog.text.contains( '(4a) >> mca.module.ModuleImpl loaded from file:' ) +assert buildLog.text.contains( 'module/module-impl/target/classes/mca/module/ModuleImpl.class << (4b)' ) + +assert buildLog.text.contains( '(5a) >> mca.webapp.WebAppServletListener loaded from file:' ) +assert buildLog.text.contains( 'webapp-war/target/classes/mca/webapp/WebAppServletListener.class << (5b)' ) diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/webapp-war/pom.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/webapp-war/pom.xml new file mode 100644 index 00000000000..bcf40c55f23 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/webapp-war/pom.xml @@ -0,0 +1,59 @@ + + + 4.0.0 + + test.jetty-ee11-start-mojo-multi-module-single-war-it + jetty-multi-module-project + 1.0-SNAPSHOT + + webapp-war + war + + + + org.eclipse.jetty.ee11 + jetty-ee11-servlet + + + jakarta.servlet + jakarta.servlet-api + provided + + + test.jetty-ee11-start-mojo-multi-module-single-war-it + module-impl + + + + + ${project.build.directory}/jetty-start-mojo.txt + EMBED + + + + + + org.eclipse.jetty.ee11 + jetty-ee11-maven-plugin + + + start-jetty + test-compile + + start + + + + ${jetty.port.file} + + + ${basedir}/src/config/jetty.xml + + + + + + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/webapp-war/src/config/jetty.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/webapp-war/src/config/jetty.xml new file mode 100644 index 00000000000..343f5e7f99b --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/webapp-war/src/config/jetty.xml @@ -0,0 +1,40 @@ + + + + + + https + + 32768 + 8192 + 8192 + 1024 + + + + + + + + + + + + + + + + + + + + + + + + + 30000 + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/webapp-war/src/main/java/mca/webapp/WebAppServletListener.java b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/webapp-war/src/main/java/mca/webapp/WebAppServletListener.java new file mode 100644 index 00000000000..8ac69a87091 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/webapp-war/src/main/java/mca/webapp/WebAppServletListener.java @@ -0,0 +1,51 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package mca.webapp; + +import java.net.URL; + +import jakarta.servlet.ServletContextEvent; +import jakarta.servlet.ServletContextListener; + +import static java.lang.String.format; + +public class WebAppServletListener implements ServletContextListener +{ + + @Override + public void contextInitialized(ServletContextEvent servletContextEvent) + { + print("1", "jakarta.servlet.ServletContextListener"); + print("2", "mca.common.CommonService"); + print("3", "mca.module.ModuleApi"); + print("4", "mca.module.ModuleImpl"); + print("5", "mca.webapp.WebAppServletListener"); + } + + @Override + public void contextDestroyed(ServletContextEvent servletContextEvent) + { + + } + + private void print(String counter, String className) + { + String res = className.replaceAll("\\.", "/") + ".class"; + URL url = Thread.currentThread().getContextClassLoader().getResource(res); + System.out.println( + format("(%sa) >> %s loaded from %s << (%sb)", + counter, className, url, counter) + ); + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/webapp-war/src/main/webapp/WEB-INF/web.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/webapp-war/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000000..5d48dd82060 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/webapp-war/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,11 @@ + + + + + + + mca.webapp.WebAppServletListener + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-overlay-it/invoker.properties b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-overlay-it/invoker.properties new file mode 100644 index 00000000000..26652d0273a --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-overlay-it/invoker.properties @@ -0,0 +1 @@ +invoker.goals = verify \ No newline at end of file diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-overlay-it/jetty-simple-base-webapp/pom.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-overlay-it/jetty-simple-base-webapp/pom.xml new file mode 100644 index 00000000000..3f5ccb0d529 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-overlay-it/jetty-simple-base-webapp/pom.xml @@ -0,0 +1,28 @@ + + + 4.0.0 + + + org.eclipse.jetty.ee11.its.jetty-start-overlay-it + jetty-simple-project + 0.0.1-SNAPSHOT + + + jetty-simple-base-webapp + war + + Jetty :: Simple :: Base WebApp + + + + + org.apache.maven.plugins + maven-war-plugin + + false + + + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-overlay-it/jetty-simple-base-webapp/src/main/java/org/eclipse/jetty/its/jetty_simple_base_webapp/Foo.java b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-overlay-it/jetty-simple-base-webapp/src/main/java/org/eclipse/jetty/its/jetty_simple_base_webapp/Foo.java new file mode 100644 index 00000000000..e18d66fe310 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-overlay-it/jetty-simple-base-webapp/src/main/java/org/eclipse/jetty/its/jetty_simple_base_webapp/Foo.java @@ -0,0 +1,19 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.its.jetty_simple_base_webapp; + +public class Foo +{ + +} \ No newline at end of file diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-overlay-it/jetty-simple-base-webapp/src/main/webapp/base/index.html b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-overlay-it/jetty-simple-base-webapp/src/main/webapp/base/index.html new file mode 100644 index 00000000000..66174ffb095 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-overlay-it/jetty-simple-base-webapp/src/main/webapp/base/index.html @@ -0,0 +1,5 @@ + + +

Base Index

+ + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-overlay-it/jetty-simple-base-webapp/src/main/webapp/index.html b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-overlay-it/jetty-simple-base-webapp/src/main/webapp/index.html new file mode 100644 index 00000000000..c8c318c711f --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-overlay-it/jetty-simple-base-webapp/src/main/webapp/index.html @@ -0,0 +1,5 @@ + + +

Simple Base Webapp Index

+ + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-overlay-it/jetty-simple-webapp/pom.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-overlay-it/jetty-simple-webapp/pom.xml new file mode 100644 index 00000000000..755d570f1ca --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-overlay-it/jetty-simple-webapp/pom.xml @@ -0,0 +1,142 @@ + + + 4.0.0 + + + org.eclipse.jetty.ee11.its.jetty-start-overlay-it + jetty-simple-project + 0.0.1-SNAPSHOT + + + jetty-simple-webapp + war + + Jetty :: Simple :: WebApp + + + ${project.build.directory}/jetty-start-port.txt + + + + + + org.eclipse.jetty.ee11.its.jetty-start-overlay-it + jetty-simple-base-webapp + war + + + + org.eclipse.jetty.ee11 + jetty-ee11-servlet + provided + + + + org.eclipse.jetty.ee11 + jetty-ee11-maven-plugin + tests + test-jar + test + + + + org.eclipse.jetty + jetty-client + test + + + + org.awaitility + awaitility + test + + + + org.junit.jupiter + junit-jupiter-engine + test + + + + + + + + + + org.apache.maven.plugins + maven-war-plugin + + false + + + org.eclipse.jetty.ee11.its.jetty-start-overlay-it + jetty-simple-base-webapp + + index.html + base/** + WEB-INF/** + + + + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + run-tests + integration-test + + test + + + + IntegrationTest*.java + + + ${jetty.port.file} + /setbycontextxml + Base Index + /base/index.html + ${project.groupId}:${project.artifactId} + + + org.eclipse.jetty.ee11:jetty-ee11-maven-plugin + + + + + + + org.eclipse.jetty.ee11 + jetty-ee11-maven-plugin + + + start-jetty + pre-integration-test + + start + + + ${basedir}/src/config/context.xml + + ${jetty.port.file} + EMBED + + + ${basedir}/src/config/jetty.xml + + + + + + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-overlay-it/jetty-simple-webapp/src/config/context.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-overlay-it/jetty-simple-webapp/src/config/context.xml new file mode 100644 index 00000000000..f09f38e188e --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-overlay-it/jetty-simple-webapp/src/config/context.xml @@ -0,0 +1,7 @@ + + + + + /setbycontextxml + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-overlay-it/jetty-simple-webapp/src/config/jetty.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-overlay-it/jetty-simple-webapp/src/config/jetty.xml new file mode 100644 index 00000000000..343f5e7f99b --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-overlay-it/jetty-simple-webapp/src/config/jetty.xml @@ -0,0 +1,40 @@ + + + + + + https + + 32768 + 8192 + 8192 + 1024 + + + + + + + + + + + + + + + + + + + + + + + + + 30000 + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-overlay-it/jetty-simple-webapp/src/main/java/org/eclipse/jetty/its/jetty_simple_webapp/Bar.java b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-overlay-it/jetty-simple-webapp/src/main/java/org/eclipse/jetty/its/jetty_simple_webapp/Bar.java new file mode 100644 index 00000000000..b535e611732 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-overlay-it/jetty-simple-webapp/src/main/java/org/eclipse/jetty/its/jetty_simple_webapp/Bar.java @@ -0,0 +1,18 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.its.jetty_simple_webapp; + +public class Bar +{ +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-overlay-it/jetty-simple-webapp/src/main/webapp/index.html b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-overlay-it/jetty-simple-webapp/src/main/webapp/index.html new file mode 100644 index 00000000000..a92e124a123 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-overlay-it/jetty-simple-webapp/src/main/webapp/index.html @@ -0,0 +1,5 @@ + + +

Simple WebApp Index

+ + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-overlay-it/pom.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-overlay-it/pom.xml new file mode 100644 index 00000000000..9ed08baea62 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-overlay-it/pom.xml @@ -0,0 +1,41 @@ + + + 4.0.0 + + + org.eclipse.jetty.ee11.its + it-parent-pom + 0.0.1-SNAPSHOT + + + org.eclipse.jetty.ee11.its.jetty-start-overlay-it + jetty-simple-project + 0.0.1-SNAPSHOT + pom + + Jetty :: Simple Overlay + + + UTF-8 + UTF-8 + 1.8 + @project.version@ + + + + jetty-simple-base-webapp + jetty-simple-webapp + + + + + + org.eclipse.jetty.ee11.its.jetty-start-overlay-it + jetty-simple-base-webapp + ${project.version} + war + + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-overlay-it/postbuild.groovy b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-overlay-it/postbuild.groovy new file mode 100644 index 00000000000..7d175d16693 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-overlay-it/postbuild.groovy @@ -0,0 +1,21 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +File buildLog = new File( basedir, 'build.log' ) +assert buildLog.text.contains( 'Started Server' ) +assert buildLog.text.contains( 'Running org.eclipse.jetty.ee11.maven.plugin.it.IntegrationTestGetContent') diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-distro-mojo-it/invoker.properties b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-distro-mojo-it/invoker.properties new file mode 100644 index 00000000000..55675300e77 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-distro-mojo-it/invoker.properties @@ -0,0 +1,2 @@ +invoker.goals = verify -V +#test-compile failsafe:integration-test diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-distro-mojo-it/jetty-simple-base/pom.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-distro-mojo-it/jetty-simple-base/pom.xml new file mode 100644 index 00000000000..4b8e12659c6 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-distro-mojo-it/jetty-simple-base/pom.xml @@ -0,0 +1,39 @@ + + + 4.0.0 + + + org.eclipse.jetty.ee11.its.jetty-start-war-distro-mojo-it + jetty-simple-project + 0.0.1-SNAPSHOT + + + jetty-simple-base + jar + + EE11 :: Simple :: Base + + + + jakarta.servlet + jakarta.servlet-api + provided + + + org.slf4j + slf4j-api + + + commons-io + commons-io + + + org.eclipse.jetty.toolchain + jetty-perf-helper + + + com.fasterxml.jackson.core + jackson-databind + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-distro-mojo-it/jetty-simple-base/src/main/java/org/eclipse/jetty/its/jetty_run_mojo_it/HelloServlet.java b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-distro-mojo-it/jetty-simple-base/src/main/java/org/eclipse/jetty/its/jetty_run_mojo_it/HelloServlet.java new file mode 100644 index 00000000000..629b83ad4c5 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-distro-mojo-it/jetty-simple-base/src/main/java/org/eclipse/jetty/its/jetty_run_mojo_it/HelloServlet.java @@ -0,0 +1,40 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.its.jetty_run_mojo_it; + +import java.io.IOException; + +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +/** + * + */ +@WebServlet("/hello") +public class HelloServlet + extends HttpServlet +{ + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException + { + String who = req.getParameter("name"); + + resp.getWriter().write("Hello " + (who == null ? "unknown" : who)); + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-distro-mojo-it/jetty-simple-base/src/main/java/org/eclipse/jetty/its/jetty_run_mojo_it/PingServlet.java b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-distro-mojo-it/jetty-simple-base/src/main/java/org/eclipse/jetty/its/jetty_run_mojo_it/PingServlet.java new file mode 100644 index 00000000000..b41b2ab0974 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-distro-mojo-it/jetty-simple-base/src/main/java/org/eclipse/jetty/its/jetty_run_mojo_it/PingServlet.java @@ -0,0 +1,35 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.its.jetty_run_mojo_it; + +import java.io.IOException; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +public class PingServlet + extends HttpServlet +{ + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException + { + String who = req.getParameter("name"); + + resp.getWriter().write("pong " + (who == null ? "unknown" : who)); + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-distro-mojo-it/jetty-simple-base/src/main/resources/META-INF/web-fragment.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-distro-mojo-it/jetty-simple-base/src/main/resources/META-INF/web-fragment.xml new file mode 100644 index 00000000000..a1ec4e27ce4 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-distro-mojo-it/jetty-simple-base/src/main/resources/META-INF/web-fragment.xml @@ -0,0 +1,32 @@ + + + + + FragmentA + + + + + + + Ping + org.eclipse.jetty.its.jetty_run_mojo_it.PingServlet + + extra1123 + + + extra2345 + + + + + Ping + /ping + + + + \ No newline at end of file diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-distro-mojo-it/jetty-simple-webapp/pom.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-distro-mojo-it/jetty-simple-webapp/pom.xml new file mode 100644 index 00000000000..8f6bd908b82 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-distro-mojo-it/jetty-simple-webapp/pom.xml @@ -0,0 +1,148 @@ + + + 4.0.0 + + + org.eclipse.jetty.ee11.its.jetty-start-war-distro-mojo-it + jetty-simple-project + 0.0.1-SNAPSHOT + + + jetty-simple-webapp + war + + EE11 :: Simple :: WebApp + + + ${project.build.directory}/jetty-start-war-distro-port.txt + @jetty.jvmArgs@ + HOME + + + + + org.eclipse.jetty.ee11.its.jetty-start-war-distro-mojo-it + jetty-simple-base + + + org.eclipse.jetty.ee11 + jetty-ee11-servlet + provided + + + org.eclipse.jetty.ee11 + jetty-ee11-maven-plugin + tests + test-jar + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.eclipse.jetty + jetty-client + test + + + org.awaitility + awaitility + test + + + + + + + + + org.apache.maven.plugins + maven-war-plugin + + false + + + + org.apache.maven.plugins + maven-surefire-plugin + + true + + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + IntegrationTest*.java + + + ${jetty.port.file} + true + true + ${project.groupId}:${project.artifactId} + + + org.eclipse.jetty.ee11:jetty-ee11-maven-plugin + + + + + integration-test + + integration-test + + + + verify + + verify + + + + + + org.eclipse.jetty.ee11 + jetty-ee11-maven-plugin + + + org.eclipse.jetty + jetty-slf4j-impl + ${jetty.version} + + + + + start-jetty + pre-integration-test + + start-war + + + @jetty.stopPort@ + @jetty.stopKey@ + + + + ${basedir}/src/base + ${java.home}/bin/java + + true + ${jetty.port.file} + 0 + + @jettyHomeZip@ + ee11-apache-jsp,ee11-glassfish-jstl,ee11-testmod,resources + + + + + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-distro-mojo-it/jetty-simple-webapp/src/base/etc/test-jetty-ee11.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-distro-mojo-it/jetty-simple-webapp/src/base/etc/test-jetty-ee11.xml new file mode 100644 index 00000000000..9857cb546ef --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-distro-mojo-it/jetty-simple-webapp/src/base/etc/test-jetty-ee11.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-distro-mojo-it/jetty-simple-webapp/src/base/modules/ee11-testmod.mod b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-distro-mojo-it/jetty-simple-webapp/src/base/modules/ee11-testmod.mod new file mode 100644 index 00000000000..9270d5e3ad6 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-distro-mojo-it/jetty-simple-webapp/src/base/modules/ee11-testmod.mod @@ -0,0 +1,13 @@ +# DO NOT EDIT THIS FILE - See: https://eclipse.dev/jetty/documentation/ + +[description] +Enables test setup + +[environment] +ee11 + +[depend] +http + +[xml] +etc/test-jetty-ee11.xml diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-distro-mojo-it/jetty-simple-webapp/src/main/webapp/WEB-INF/web.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-distro-mojo-it/jetty-simple-webapp/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000000..2a5ac4b71bf --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-distro-mojo-it/jetty-simple-webapp/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,7 @@ + + + Jetty Simple Webapp run-mojo-it + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-distro-mojo-it/pom.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-distro-mojo-it/pom.xml new file mode 100644 index 00000000000..0d7cedbdc7f --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-distro-mojo-it/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + + + org.eclipse.jetty.ee11.its + it-parent-pom + 0.0.1-SNAPSHOT + + + org.eclipse.jetty.ee11.its.jetty-start-war-distro-mojo-it + jetty-simple-project + 0.0.1-SNAPSHOT + pom + + EE11 :: Simple + + + UTF-8 + UTF-8 + 1.8 + @project.version@ + + + + jetty-simple-base + jetty-simple-webapp + + + + + + org.eclipse.jetty.ee11.its.jetty-start-war-distro-mojo-it + jetty-simple-base + ${project.version} + + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-distro-mojo-it/postbuild.groovy b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-distro-mojo-it/postbuild.groovy new file mode 100644 index 00000000000..66b37d6a318 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-distro-mojo-it/postbuild.groovy @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +System.out.println( "running postbuild.groovy port " + jettyStopPort + ", key:" + jettyStopKey ) + +int port = Integer.parseInt( jettyStopPort ) + +Socket s=new Socket(InetAddress.getByName("127.0.0.1"),port ) + +OutputStream out=s.getOutputStream() +out.write(( jettyStopKey +"\r\nforcestop\r\n").getBytes()) +out.flush() +s.close() + + +File buildLog = new File( basedir, 'build.log' ) +assert buildLog.text.contains( 'Home process starting' ) +assert buildLog.text.contains( 'Running org.eclipse.jetty.ee11.maven.plugin.it.IntegrationTestGetContent') +assert buildLog.text.contains( 'pingServlet ok') +assert buildLog.text.contains( 'helloServlet') diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-forked-mojo-it/invoker.properties b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-forked-mojo-it/invoker.properties new file mode 100644 index 00000000000..55675300e77 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-forked-mojo-it/invoker.properties @@ -0,0 +1,2 @@ +invoker.goals = verify -V +#test-compile failsafe:integration-test diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-forked-mojo-it/jetty-simple-base/pom.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-forked-mojo-it/jetty-simple-base/pom.xml new file mode 100644 index 00000000000..e5ccb260119 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-forked-mojo-it/jetty-simple-base/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + + + org.eclipse.jetty.ee11.its.jetty-start-war-forked-mojo-it + jetty-simple-project + 0.0.1-SNAPSHOT + + + jetty-simple-base + jar + + EE11 :: Simple :: Base + + + + jakarta.servlet + jakarta.servlet-api + provided + + + org.slf4j + slf4j-api + + + commons-io + commons-io + + + org.eclipse.jetty.toolchain + jetty-perf-helper + + + com.fasterxml.jackson.core + jackson-databind + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-forked-mojo-it/jetty-simple-base/src/main/java/org/eclipse/jetty/its/jetty_run_war_exploded_mojo_it/HelloServlet.java b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-forked-mojo-it/jetty-simple-base/src/main/java/org/eclipse/jetty/its/jetty_run_war_exploded_mojo_it/HelloServlet.java new file mode 100644 index 00000000000..4eda9384269 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-forked-mojo-it/jetty-simple-base/src/main/java/org/eclipse/jetty/its/jetty_run_war_exploded_mojo_it/HelloServlet.java @@ -0,0 +1,40 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.its.jetty_run_war_exploded_mojo_it; + +import java.io.IOException; + +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +/** + * + */ +@WebServlet("/hello") +public class HelloServlet + extends HttpServlet +{ + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException + { + String who = req.getParameter("name"); + + resp.getWriter().write("Hello " + (who == null ? "unknown" : who)); + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-forked-mojo-it/jetty-simple-base/src/main/java/org/eclipse/jetty/its/jetty_run_war_exploded_mojo_it/PingServlet.java b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-forked-mojo-it/jetty-simple-base/src/main/java/org/eclipse/jetty/its/jetty_run_war_exploded_mojo_it/PingServlet.java new file mode 100644 index 00000000000..66d7be783fe --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-forked-mojo-it/jetty-simple-base/src/main/java/org/eclipse/jetty/its/jetty_run_war_exploded_mojo_it/PingServlet.java @@ -0,0 +1,35 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.its.jetty_run_war_exploded_mojo_it; + +import java.io.IOException; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +public class PingServlet + extends HttpServlet +{ + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException + { + String who = req.getParameter("name"); + + resp.getWriter().write("pong " + (who == null ? "unknown" : who)); + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-forked-mojo-it/jetty-simple-base/src/main/resources/META-INF/web-fragment.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-forked-mojo-it/jetty-simple-base/src/main/resources/META-INF/web-fragment.xml new file mode 100644 index 00000000000..827c1f7b294 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-forked-mojo-it/jetty-simple-base/src/main/resources/META-INF/web-fragment.xml @@ -0,0 +1,32 @@ + + + + + FragmentA + + + + + + + Ping + org.eclipse.jetty.its.jetty_run_war_exploded_mojo_it.PingServlet + + extra1123 + + + extra2345 + + + + + Ping + /ping + + + + \ No newline at end of file diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-forked-mojo-it/jetty-simple-webapp/pom.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-forked-mojo-it/jetty-simple-webapp/pom.xml new file mode 100644 index 00000000000..4d522c4d055 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-forked-mojo-it/jetty-simple-webapp/pom.xml @@ -0,0 +1,145 @@ + + + 4.0.0 + + + org.eclipse.jetty.ee11.its.jetty-start-war-forked-mojo-it + jetty-simple-project + 0.0.1-SNAPSHOT + ../pom.xml + + + jetty-simple-webapp + war + + EE11 :: Simple :: WebApp + + + ${project.build.directory}/jetty-start-war-forked-port.txt + FORK + + + + + org.eclipse.jetty.ee11.its.jetty-start-war-forked-mojo-it + jetty-simple-base + + + org.eclipse.jetty.ee11 + jetty-ee11-servlet + provided + + + org.eclipse.jetty.ee11 + jetty-ee11-maven-plugin + tests + test-jar + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.eclipse.jetty + jetty-client + test + + + org.awaitility + awaitility + test + + + + + + + + + org.apache.maven.plugins + maven-war-plugin + + false + + + + org.apache.maven.plugins + maven-surefire-plugin + + true + + + + + + + org.eclipse.jetty.ee11 + jetty-ee11-maven-plugin + + + org.eclipse.jetty + jetty-slf4j-impl + ${jetty.version} + + + + + start-jetty + pre-integration-test + + start-war + + + @jetty.stopPort@ + @jetty.stopKey@ + + ${project.build.directory}/${project.artifactId}-${project.version} + + + ${jetty.port.file} + + + ${basedir}/src/config/jetty.xml + + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + ${jetty.port.file} + true + true + ${project.groupId}:${project.artifactId} + + + org.eclipse.jetty.ee11:jetty-ee11-maven-plugin + + + **/*TestGetContent* + + + + + integration-test + + integration-test + + + + verify + + verify + + + + + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-forked-mojo-it/jetty-simple-webapp/src/config/jetty.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-forked-mojo-it/jetty-simple-webapp/src/config/jetty.xml new file mode 100644 index 00000000000..343f5e7f99b --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-forked-mojo-it/jetty-simple-webapp/src/config/jetty.xml @@ -0,0 +1,40 @@ + + + + + + https + + 32768 + 8192 + 8192 + 1024 + + + + + + + + + + + + + + + + + + + + + + + + + 30000 + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-forked-mojo-it/jetty-simple-webapp/src/main/webapp/WEB-INF/web.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-forked-mojo-it/jetty-simple-webapp/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000000..2a5ac4b71bf --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-forked-mojo-it/jetty-simple-webapp/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,7 @@ + + + Jetty Simple Webapp run-mojo-it + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-forked-mojo-it/pom.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-forked-mojo-it/pom.xml new file mode 100644 index 00000000000..2121f8f7a84 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-forked-mojo-it/pom.xml @@ -0,0 +1,41 @@ + + + 4.0.0 + + + org.eclipse.jetty.ee11.its + it-parent-pom + 0.0.1-SNAPSHOT + ../it-parent-pom/pom.xml + + + org.eclipse.jetty.ee11.its.jetty-start-war-forked-mojo-it + jetty-simple-project + 0.0.1-SNAPSHOT + pom + + EE11 :: Simple + + + UTF-8 + UTF-8 + 1.8 + @project.version@ + + + + jetty-simple-base + jetty-simple-webapp + + + + + + org.eclipse.jetty.ee11.its.jetty-start-war-forked-mojo-it + jetty-simple-base + ${project.version} + + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-forked-mojo-it/postbuild.groovy b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-forked-mojo-it/postbuild.groovy new file mode 100644 index 00000000000..f570f904507 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-forked-mojo-it/postbuild.groovy @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +System.out.println( "running postbuild.groovy port " + jettyStopPort + ", key:" + jettyStopKey ) + +int port = Integer.parseInt( jettyStopPort ) + +Socket s=new Socket(InetAddress.getByName("127.0.0.1"),port ) + +OutputStream out=s.getOutputStream() +out.write(( jettyStopKey +"\r\nforcestop\r\n").getBytes()) +out.flush() +s.close() + +File outputLog = new File( basedir, 'build.log' ) +assert outputLog.text.contains( 'Forked process starting' ) +assert outputLog.text.contains( 'Running org.eclipse.jetty.ee11.maven.plugin.it.IntegrationTestGetContent') +assert outputLog.text.contains( 'pingServlet ok') +assert outputLog.text.contains( 'helloServlet') diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-mojo-it/invoker.properties b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-mojo-it/invoker.properties new file mode 100644 index 00000000000..816c3f38def --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-mojo-it/invoker.properties @@ -0,0 +1,2 @@ +invoker.goals = verify -V -e +#test-compile failsafe:integration-test \ No newline at end of file diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-mojo-it/pom.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-mojo-it/pom.xml new file mode 100644 index 00000000000..176905b7dc7 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-mojo-it/pom.xml @@ -0,0 +1,139 @@ + + + 4.0.0 + + + org.eclipse.jetty.ee11.its + it-parent-pom + 0.0.1-SNAPSHOT + + + org.eclipse.jetty.ee11.its.jetty-start-war-mojo-it + jetty-simple-project + 0.0.1-SNAPSHOT + pom + + EE11 :: Simple start war mojo test + + + ${project.build.directory}/jetty-start-war-port.txt + + + + + org.eclipse.jetty.ee11 + jetty-ee11-maven-plugin + tests + test-jar + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + ${jetty.port.file} + ${project.groupId}:${project.artifactId} + Bean Validation Webapp example + + + org.eclipse.jetty.ee11:jetty-ee11-maven-plugin + + + **/*TestGetContent* + + + + + integration-test + + integration-test + + + + verify + + verify + + + + + + org.eclipse.jetty.ee11 + jetty-ee11-maven-plugin + + + start-jetty + test-compile + + start-war + + + + pom + + + ${project.build.directory}/bean-validation-webapp-2.25.1.war + + + ${jetty.port.file} + + + ${basedir}/src/config/jetty.xml + + + + + + + jakarta.xml.bind + jakarta.xml.bind-api + 2.3.3 + + + jakarta.activation + jakarta.activation-api + 1.2.2 + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + load-war + generate-resources + + copy + + + + + org.glassfish.jersey.examples + bean-validation-webapp + 2.25.1 + war + true + ** + ${project.build.directory} + + + + + + + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-mojo-it/postbuild.groovy b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-mojo-it/postbuild.groovy new file mode 100644 index 00000000000..4f05a1ca8f1 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-mojo-it/postbuild.groovy @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +File buildLog = new File( basedir, 'build.log' ) +assert buildLog.text.contains( 'Started Server' ) +assert buildLog.text.contains( 'Running org.eclipse.jetty.ee11.maven.plugin.it.IntegrationTestGetContent') +assert buildLog.text.contains( 'contentCheck') diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-mojo-it/src/config/jetty.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-mojo-it/src/config/jetty.xml new file mode 100644 index 00000000000..343f5e7f99b --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/jetty-start-war-mojo-it/src/config/jetty.xml @@ -0,0 +1,40 @@ + + + + + + https + + 32768 + 8192 + 8192 + 1024 + + + + + + + + + + + + + + + + + + + + + + + + + 30000 + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/it/settings.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/it/settings.xml new file mode 100644 index 00000000000..e62d9bab8b8 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/it/settings.xml @@ -0,0 +1,37 @@ + + + + @localRepoPath@ + + + it-repo + + true + + + + local.central + @localRepositoryUrl@ + + true + + + true + + + + + + local.central + @localRepositoryUrl@ + + true + + + true + + + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/AbstractUnassembledWebAppMojo.java b/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/AbstractUnassembledWebAppMojo.java new file mode 100644 index 00000000000..61abf6bb7a9 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/AbstractUnassembledWebAppMojo.java @@ -0,0 +1,255 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.maven.plugin; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.Parameter; +import org.eclipse.jetty.maven.ScanPattern; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.util.resource.Resources; + +/** + * Base class for all goals that operate on unassembled webapps. + * + */ +public abstract class AbstractUnassembledWebAppMojo extends AbstractWebAppMojo +{ + /** + * The default location of the web.xml file. Will be used + * if <webApp><descriptor> is not set. + */ + @Parameter (defaultValue = "${project.baseDir}/src/main/webapp/WEB-INF/web.xml") + protected File webXml; + + /** + * The directory containing generated test classes. + * + */ + @Parameter (defaultValue = "${project.build.testOutputDirectory}", required = true) + protected File testClassesDirectory; + + /** + * An optional pattern for includes/excludes of classes in the testClassesDirectory + */ + @Parameter + protected ScanPattern scanTestClassesPattern; + + /** + * The directory containing generated classes. + */ + @Parameter (defaultValue = "${project.build.outputDirectory}", required = true) + protected File classesDirectory; + + + /** + * An optional pattern for includes/excludes of classes in the classesDirectory + */ + @Parameter + protected ScanPattern scanClassesPattern; + + + /** + * Default root directory for all html/jsp etc files. + * Used to initialize webApp.setBaseResource(). + */ + @Parameter (defaultValue = "${project.basedir}/src/main/webapp", readonly = true) + protected File webAppSourceDirectory; + + protected void verifyPomConfiguration() throws MojoExecutionException + { + //Does the default webapp static resource location exist? + if (!webAppSourceDirectory.exists()) + { + //try last resort of a fake directory + File target = new File(project.getBuild().getDirectory()); + webAppSourceDirectory = new File(target, FAKE_WEBAPP); + } + + // check the classes to form a classpath with + try + { + //allow a webapp with no classes in it (just jsps/html) + if (classesDirectory != null) + { + if (!classesDirectory.exists()) + getLog().info("Classes directory " + classesDirectory.getCanonicalPath() + " does not exist"); + else + getLog().info("Classes = " + classesDirectory.getCanonicalPath()); + } + else + getLog().info("Classes directory not set"); + } + catch (IOException e) + { + throw new MojoExecutionException("Location of classesDirectory does not exist"); + } + } + + @Override + protected void configureWebApp() throws Exception + { + super.configureWebApp(); + try + { + configureUnassembledWebApp(); + } + catch (IOException e) + { + throw new MojoFailureException("Unable to configure unassembled webapp", e); + } + } + + /** + * Configure a webapp that has not been assembled into a war. + * + * @throws IOException if there is an IO problem + */ + protected void configureUnassembledWebApp() throws Exception + { + //Set up the location of the webapp. + //There are 2 parts to this: setWar() and setBaseResource(). On standalone jetty, + //the former could be the location of a packed war, while the latter is the location + //after any unpacking. With this mojo, you are running an unpacked, unassembled webapp, + //so the two locations should be equal. + + //The first time we run, remember the original base dir + if (originalBaseResource == null) + { + if (webApp.getBaseResource() == null) + { + //Use the default static resource location + if (!webAppSourceDirectory.exists()) + webAppSourceDirectory.mkdirs(); + originalBaseResource = webApp.getResourceFactory().newResource(webAppSourceDirectory.getCanonicalPath()); + } + else + originalBaseResource = webApp.getBaseResource(); + } + + //On every subsequent re-run set it back to the original base dir before + //we might have applied any war overlays onto it + webApp.setBaseResource(originalBaseResource); + + //TODO the war does not need to be set, _except_ that QuickStartConfiguration checks for non null + if (webApp.getWar() == null) + webApp.setWar(originalBaseResource.toString()); + + if (classesDirectory != null) + webApp.setClasses(classesDirectory); + + if (useTestScope && (testClassesDirectory != null)) + webApp.setTestClasses(testClassesDirectory); + + List webInfLibs = getWebInfLibArtifacts().stream() + .map(a -> + { + Path p = mavenProjectHelper.getPathFor(a); + getLog().debug("Artifact " + a.getId() + " loaded from " + p + " added to WEB-INF/lib"); + return p.toFile(); + }).collect(Collectors.toList()); + + webApp.setWebInfLib(webInfLibs); + + //if we have not already set web.xml location, need to set one up + if (webApp.getDescriptor() == null) + { + //Has an explicit web.xml file been configured to use? + if (webXml != null) + { + Resource r = webApp.getResourceFactory().newResource(webXml.toPath()); + if (Resources.isReadableFile(r)) + { + webApp.setDescriptor(r.getURI().toASCIIString()); + } + } + + //Still don't have a web.xml file: try the resourceBase of the webapp, if it is set + if (webApp.getDescriptor() == null && webApp.getBaseResource() != null) + { + // TODO: should never return from WEB-INF/lib/foo.jar!/WEB-INF/web.xml + // TODO: should also never return from a META-INF/versions/#/WEB-INF/web.xml location + Resource r = webApp.getBaseResource().resolve("WEB-INF/web.xml"); + if (Resources.isReadableFile(r)) + { + webApp.setDescriptor(r.getURI().toASCIIString()); + } + } + + //Still don't have a web.xml file: finally try the configured static resource directory if there is one + if (webApp.getDescriptor() == null && (webAppSourceDirectory != null)) + { + // TODO: fix, use Resource or Path + File f = new File(new File(webAppSourceDirectory, "WEB-INF"), "web.xml"); + if (f.exists() && f.isFile()) + { + webApp.setDescriptor(f.getCanonicalPath()); + } + } + } + + //process any overlays and the war type artifacts, and + //sets up the base resource collection for the webapp + mavenProjectHelper.getOverlayManager().applyOverlays(webApp, webApp.getBaseAppFirst()); + + getLog().info("web.xml file = " + webApp.getDescriptor()); + getLog().info("Webapp directory = " + webAppSourceDirectory.getCanonicalPath()); + getLog().info("Web defaults = " + (webApp.getDefaultsDescriptor() == null ? " jetty default" : webApp.getDefaultsDescriptor())); + getLog().info("Web overrides = " + (webApp.getOverrideDescriptor() == null ? " none" : webApp.getOverrideDescriptor())); + } + + /** + * Find which dependencies are suitable for addition to the virtual + * WEB-INF lib. + */ + protected Collection getWebInfLibArtifacts() + { + return project.getArtifacts().stream() + .filter(this::isArtifactOKForWebInfLib) + .collect(Collectors.toList()); + } + + /** + * Check if the artifact is suitable to be considered part of the + * virtual web-inf/lib. + * + * @param artifact the artifact to check + * @return true if the artifact represents a jar, isn't scope provided and + * is scope test, if useTestScope is enabled. False otherwise. + */ + private boolean isArtifactOKForWebInfLib(Artifact artifact) + { + //The dependency cannot be of type war + if ("war".equalsIgnoreCase(artifact.getType())) + return false; + + //The dependency cannot be scope provided (those should be added to the plugin classpath) + if (Artifact.SCOPE_PROVIDED.equals(artifact.getScope())) + return false; + + //Test dependencies not added by default + if (Artifact.SCOPE_TEST.equals(artifact.getScope()) && !useTestScope) + return false; + + return true; + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/AbstractWebAppMojo.java b/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/AbstractWebAppMojo.java new file mode 100644 index 00000000000..ba04ae44029 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/AbstractWebAppMojo.java @@ -0,0 +1,888 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.maven.plugin; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Path; +import java.nio.file.PathMatcher; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.Random; +import java.util.Set; +import java.util.stream.Collectors; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.model.Dependency; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.AbstractMojoExecutionException; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugin.descriptor.PluginDescriptor; +import org.apache.maven.plugins.annotations.Component; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.util.StringUtils; +import org.eclipse.aether.RepositorySystem; +import org.eclipse.jetty.maven.MavenProjectHelper; +import org.eclipse.jetty.maven.MavenServerConnector; +import org.eclipse.jetty.maven.PluginLog; +import org.eclipse.jetty.maven.ScanTargetPattern; +import org.eclipse.jetty.security.LoginService; +import org.eclipse.jetty.server.RequestLog; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.util.IncludeExcludeSet; +import org.eclipse.jetty.util.Scanner; +import org.eclipse.jetty.util.StringUtil; +import org.eclipse.jetty.util.resource.Resource; + +/** + * AbstractWebAppMojo + * + * Base class for common behaviour of jetty mojos. + */ +public abstract class AbstractWebAppMojo extends AbstractMojo +{ + public static final String JETTY_HOME_GROUPID = "org.eclipse.jetty"; + public static final String JETTY_HOME_ARTIFACTID = "jetty-home"; + public static final String FAKE_WEBAPP = "webapp-synth"; + + public enum DeploymentMode + { + EMBED, + FORK, + HOME, //alias for EXTERNAL + DISTRO, //alias for EXTERNAL + EXTERNAL + } + + /** + * Max number of times to check to see if jetty has started correctly + * when running in FORK or EXTERNAL mode. + */ + @Parameter (defaultValue = "10") + protected int maxChildStartChecks; + + /** + * How long to wait in msec between checks to see if jetty has started + * correctly when running in FORK or EXTERNAL mode. + */ + @Parameter (defaultValue = "200") + protected long maxChildStartCheckMs; + /** + * Whether or not to include dependencies on the plugin's classpath with <scope>provided</scope> + * Use WITH CAUTION as you may wind up with duplicate jars/classes. + * + * @since jetty-7.5.2 + */ + @Parameter (defaultValue = "false") + protected boolean useProvidedScope; + + + /** + * List of goals that are NOT to be used + * + * @since jetty-7.5.2 + */ + @Parameter + protected String[] excludedGoals; + + /** + * An instance of org.eclipse.jetty.ee11.webapp.WebAppContext that represents the webapp. + * Use any of its setters to configure the webapp. This is the preferred and most + * flexible method of configuration, rather than using the (deprecated) individual + * parameters like "tmpDirectory", "contextPath" etc. + * + */ + @Parameter + protected MavenWebAppContext webApp; + + /** + * Skip this mojo execution. + */ + @Parameter (property = "jetty.skip", defaultValue = "false") + protected boolean skip; + + + /** + * Location of a context xml configuration file whose contents + * will be applied to the webapp AFTER anything in <webApp>.Optional. + */ + @Parameter + protected String contextXml; + + + /** + * The maven project. + */ + @Parameter(defaultValue = "${project}", readonly = true) + protected MavenProject project; + + + /** + * The artifacts for the project. + */ + @Parameter (defaultValue = "${project.artifacts}", readonly = true) + protected Set projectArtifacts; + + /** + * The maven build executing. + */ + @Parameter (defaultValue = "${mojoExecution}", readonly = true) + protected org.apache.maven.plugin.MojoExecution execution; + + + /** + * The artifacts for the plugin itself. + */ + @Parameter (defaultValue = "${plugin.artifacts}", readonly = true) + protected List pluginArtifacts; + + + /** + * If true, the <testOutputDirectory> + * and the dependencies of <scope>test<scope> + * will be put first on the runtime classpath. + */ + @Parameter (defaultValue = "false") + protected boolean useTestScope; + + /** + * List of directories with ant-style <include> and <exclude> patterns + * for extra targets to periodically scan for changes.Optional. + */ + @Parameter + protected List scanTargetPatterns; + + @Parameter(defaultValue = "${reactorProjects}", readonly = true, required = true) + protected List reactorProjects; + + /** + * The target directory + */ + @Parameter (defaultValue = "${project.build.directory}", required = true, readonly = true) + protected File target; + + + /** + * List of jetty xml configuration files whose contents + * will be applied (in order declared) before any plugin configuration. Optional. + */ + @Parameter + protected List jettyXmls; + + + /** + * Optional jetty properties to put on the command line + */ + @Parameter + protected Map jettyProperties; + + + /** + * File containing system properties to be set before execution + * + * Note that these properties will NOT override System properties + * that have been set on the command line, by the JVM, or directly + * in the POM via systemProperties. Optional. + * + * + */ + @Parameter (property = "jetty.systemPropertiesFile") + protected File systemPropertiesFile; + + + /** + * System properties to set before execution. + * Note that these properties will NOT override System properties + * that have been set on the command line or by the JVM. They WILL + * override System properties that have been set via systemPropertiesFile. + * Optional. + */ + @Parameter + protected Map systemProperties; + + /** + * Controls how to run jetty. Valid values are EMBED,FORK,EXTERNAL. + */ + @Parameter (property = "jetty.deployMode", defaultValue = "EMBED") + protected DeploymentMode deployMode; + + + /** + * List of other contexts to set up. Consider using instead + * the <jettyXml> element to specify external jetty xml config file. + * Optional. + */ + @Parameter + protected List contextHandlers; + + /** + * List of security realms to set up. Consider using instead + * the <jettyXml> element to specify external jetty xml config file. + * Optional. + */ + @Parameter + protected List loginServices; + + /** + * A RequestLog implementation to use for the webapp at runtime. + * Consider using instead the <jettyXml> element to specify external jetty xml config file. + * Optional. + */ + @Parameter + protected RequestLog requestLog; + + /** + * A ServerConnector to use. + */ + @Parameter + protected MavenServerConnector httpConnector; + + + /** + * A wrapper for the Server object + */ + @Parameter + protected Server server; + //End of EMBED only + + + //Start of parameters only valid for FORK/EXTERNAL + /** + * Extra environment variables to be passed to the forked process + */ + @Parameter + protected Map env = new HashMap<>(); + + /** + * Arbitrary jvm args to pass to the forked process + */ + @Parameter (property = "jetty.jvmArgs") + protected String jvmArgs; + + /** + * Port to listen to stop jetty on executing -DSTOP.PORT=<stopPort> + * -DSTOP.KEY=<stopKey> -jar start.jar --stop + * + */ + @Parameter + protected int stopPort; + + /** + * Key to provide when stopping jetty on executing java -DSTOP.KEY=<stopKey> + * -DSTOP.PORT=<stopPort> -jar start.jar --stop + * + */ + @Parameter + protected String stopKey; + //End of FORK or EXTERNAL parameters + + //Start of parameters only valid for EXTERNAL + /** + * Location of jetty home directory + */ + @Parameter + protected File jettyHome; + + /** + * Location of Jetty home zipped + */ + @Parameter + public File jettyHomeZip; + + /** + * Location of jetty base directory + */ + @Parameter + protected File jettyBase; + + /** + * Optional list of other modules to + * activate. + */ + @Parameter + protected String[] modules; + + /** + * Extra options that can be passed to the jetty command line + */ + @Parameter (property = "jetty.options") + protected String jettyOptions; + + //End of EXTERNAL only parameters + + //Start of parameters only valid for FORK + /** + * The file into which to generate the quickstart web xml for the forked process to use + * + */ + @Parameter (defaultValue = "${project.build.directory}/fork-web.xml") + protected File forkWebXml; + //End of FORK only parameters + + /** + * Helper for interacting with the maven project space + */ + protected MavenProjectHelper mavenProjectHelper; + + /** + * This plugin + */ + @Parameter (defaultValue = "${plugin}", readonly = true, required = true) + protected PluginDescriptor plugin; + + /** + * The project's remote repositories to use for the resolution. + */ + @Parameter (defaultValue = "${project.remoteArtifactRepositories}", readonly = true, required = true) + private List remoteRepositories; + + /** + * + */ + @Component + private RepositorySystem repositorySystem; + + /** + * The current maven session + */ + @Parameter (defaultValue = "${session}", required = true, readonly = true) + private MavenSession session; + + /** + * Default supported project type is war packaging. + */ + @Parameter + protected List supportedPackagings = Collections.singletonList("war"); + + /** + * List of deps that are wars + */ + protected List warArtifacts; + + /** + * Webapp base before applying overlays etc + */ + protected Resource originalBaseResource; + + /** + * List of jars with scope=provided + */ + protected List providedJars; + + /** + * System properties from both systemPropertyFile and systemProperties. + */ + protected Map mergedSystemProperties; + + @Override + public void execute() throws MojoExecutionException, MojoFailureException + { + if (isPackagingSupported()) + { + if (skip) + { + getLog().info("Skipping Jetty start: jetty.skip==true"); + return; + } + + if (isExcludedGoal(execution.getMojoDescriptor().getGoal())) + { + getLog().info("The goal \"" + execution.getMojoDescriptor().getFullGoalName() + + "\" is unavailable for this web app because of an configuration."); + return; + } + + getLog().info("Configuring Jetty for project: " + getProjectName()); + mavenProjectHelper = new MavenProjectHelper(project, repositorySystem, remoteRepositories, session); + mergedSystemProperties = mergeSystemProperties(); + configureSystemProperties(); + augmentPluginClasspath(); + PluginLog.setLog(getLog()); + verifyPomConfiguration(); + startJetty(); + } + else + getLog().info("Packaging type [" + project.getPackaging() + "] is unsupported"); + } + + protected void startJetty() + throws MojoExecutionException, MojoFailureException + { + try + { + configureWebApp(); + } + catch (Exception e) + { + throw new MojoExecutionException("Webapp config failure", e); + } + + switch (deployMode) + { + case EMBED: + { + startJettyEmbedded(); + break; + } + case FORK: + { + startJettyForked(); + break; + } + case DISTRO: + case HOME: + case EXTERNAL: + { + if (deployMode != DeploymentMode.EXTERNAL) + getLog().warn(deployMode + " mode is deprecated, use mode EXTERNAL"); + startJettyHome(); + break; + } + default: + throw new MojoExecutionException("Unrecognized runType=" + deployMode); + } + + } + + protected abstract void startJettyEmbedded() throws MojoExecutionException; + + protected abstract void startJettyForked() throws MojoExecutionException; + + protected abstract void startJettyHome() throws MojoExecutionException; + + protected JettyEmbedder newJettyEmbedder() + throws Exception + { + JettyEmbedder jetty = new JettyEmbedder(); + jetty.setStopKey(stopKey); + jetty.setStopPort(stopPort); + jetty.setServer(server); + jetty.setContextHandlers(contextHandlers); + jetty.setRequestLog(requestLog); + jetty.setJettyXmlFiles(jettyXmls); + jetty.setHttpConnector(httpConnector); + jetty.setJettyProperties(jettyProperties); + jetty.setLoginServices(loginServices); + jetty.setContextXml(contextXml); + jetty.setWebApp(webApp); + return jetty; + } + + protected JettyForker newJettyForker() + throws Exception + { + JettyForker jetty = new JettyForker(); + jetty.setServer(server); + jetty.setWorkDir(project.getBasedir()); + jetty.setStopKey(stopKey); + jetty.setStopPort(stopPort); + jetty.setEnv(env); + jetty.setJvmArgs(jvmArgs); + jetty.setSystemProperties(mergedSystemProperties); + jetty.setContainerClassPath(getContainerClassPath()); + jetty.setJettyXmlFiles(jettyXmls); + jetty.setJettyProperties(jettyProperties); + jetty.setForkWebXml(forkWebXml); + jetty.setContextXml(contextXml); + jetty.setWebAppPropsFile(new File(target, "webApp.props")); + Random random = new Random(); + String token = Long.toString(random.nextLong() ^ System.currentTimeMillis(), 36).toUpperCase(Locale.ENGLISH); + jetty.setTokenFile(target.toPath().resolve(token + ".txt").toFile()); + jetty.setWebApp(webApp); + return jetty; + } + + protected JettyHomeForker newJettyHomeForker() + throws Exception + { + JettyHomeForker jetty = new JettyHomeForker(); + jetty.setStopKey(stopKey); + jetty.setStopPort(stopPort); + jetty.setEnv(env); + jetty.setJvmArgs(jvmArgs); + jetty.setJettyOptions(jettyOptions); + jetty.setJettyXmlFiles(jettyXmls); + jetty.setJettyProperties(jettyProperties); + jetty.setModules(modules); + jetty.setSystemProperties(mergedSystemProperties); + Random random = new Random(); + String token = Long.toString(random.nextLong() ^ System.currentTimeMillis(), 36).toUpperCase(Locale.ENGLISH); + jetty.setTokenFile(target.toPath().resolve(token + ".txt").toFile()); + + List libExtJars = new ArrayList<>(); + + List pdeps = plugin.getPlugin().getDependencies(); + if (pdeps != null && !pdeps.isEmpty()) + { + boolean warned = false; + for (Dependency d:pdeps) + { + if (d.getGroupId().equalsIgnoreCase("org.eclipse.jetty")) + { + if (!warned) + { + getLog().warn("Jetty jars detected in : use in parameter instead to select appropriate jetty modules."); + warned = true; + } + } + else + { + libExtJars.add(mavenProjectHelper.resolveArtifact(d.getGroupId(), d.getArtifactId(), d.getVersion(), d.getType())); + } + } + jetty.setLibExtJarFiles(libExtJars); + } + + jetty.setWebApp(webApp); + jetty.setContextXml(contextXml); + + if (jettyHome == null) + jetty.setJettyHomeZip(jettyHomeZip != null ? jettyHomeZip : mavenProjectHelper.resolveArtifact(JETTY_HOME_GROUPID, JETTY_HOME_ARTIFACTID, plugin.getVersion(), "zip")); + + jetty.setVersion(plugin.getVersion()); + jetty.setJettyHome(jettyHome); + jetty.setJettyBase(jettyBase); + jetty.setBaseDir(target); + + return jetty; + } + + /** + * Used by subclasses. + * @throws MojoExecutionException if there is a mojo execution problem + */ + protected void verifyPomConfiguration() throws MojoExecutionException + { + } + + /** + * Unite system properties set via systemPropertiesFile element and the systemProperties element. + * Properties from the pom override properties from the file. + * + * @return united properties map + * @throws MojoExecutionException if there is a mojo execution problem + */ + protected Map mergeSystemProperties() + throws MojoExecutionException + { + Map properties = new HashMap<>(); + + //Get the properties from any file first + if (systemPropertiesFile != null) + { + Properties tmp = new Properties(); + try (InputStream propFile = new FileInputStream(systemPropertiesFile)) + { + tmp.load(propFile); + for (Object k:tmp.keySet()) + properties.put(k.toString(), tmp.get(k).toString()); + } + catch (Exception e) + { + throw new MojoExecutionException("Problem applying system properties from file " + systemPropertiesFile.getName(), e); + } + } + //Allow systemProperties defined in the pom to override the file + if (systemProperties != null) + { + properties.putAll(systemProperties); + } + return properties; + } + + protected void configureSystemProperties() + throws MojoExecutionException + { + if (mergedSystemProperties != null) + { + for (Map.Entry e : mergedSystemProperties.entrySet()) + { + if (!StringUtil.isEmpty(e.getKey()) && !StringUtil.isEmpty(e.getValue())) + { + System.setProperty(e.getKey(), e.getValue()); + if (getLog().isDebugEnabled()) + getLog().debug("Set system property " + e.getKey() + "=" + e.getValue()); + } + } + } + } + + /** + * Augment jetty's classpath with dependencies marked as scope=provided + * if useProvidedScope==true. + * + * @throws MojoExecutionException if there is a mojo execution problem + */ + protected void augmentPluginClasspath() throws MojoExecutionException + { + //Filter out ones that will clash with jars that are plugin dependencies, then + //create a new classloader that we setup in the parent chain. + providedJars = getProvidedJars(); + + if (!providedJars.isEmpty()) + { + try + { + URL[] urls = new URL[providedJars.size()]; + int i = 0; + for (File providedJar:providedJars) + urls[i++] = providedJar.toURI().toURL(); + URLClassLoader loader = new URLClassLoader(urls, getClass().getClassLoader()); + Thread.currentThread().setContextClassLoader(loader); + getLog().info("Plugin classpath augmented with provided dependencies: " + Arrays.toString(urls)); + } + catch (MalformedURLException e) + { + throw new MojoExecutionException("Invalid url", e); + } + } + } + + /** + * Get any dependencies that are scope "provided" if useProvidedScope == true. Ensure + * that only those dependencies that are not already present via the plugin are + * included. + * + * @return provided scope dependencies that are not also plugin dependencies. + * @throws MojoExecutionException if there is a mojo execution problem + */ + protected List getProvidedJars() throws MojoExecutionException + { + if (useProvidedScope) + { + return project.getArtifacts() + .stream() + .filter(a -> Artifact.SCOPE_PROVIDED.equals(a.getScope()) && !isPluginArtifact(a)) + .map(a -> a.getFile()).collect(Collectors.toList()); + } + else + return Collections.emptyList(); + } + + /** + * Synthesize a classpath appropriate for a forked jetty based off + * the artifacts associated with the jetty plugin, plus any dependencies + * that are marked as provided and useProvidedScope is true. + * + * @return jetty classpath + * @throws Exception if there is an unspecified problem + */ + protected String getContainerClassPath() throws Exception + { + //Add in all the plugin artifacts + StringBuilder classPath = new StringBuilder(); + for (Object obj : pluginArtifacts) + { + Artifact artifact = (Artifact)obj; + if ("jar".equals(artifact.getType())) + { + if (classPath.length() > 0) + classPath.append(File.pathSeparator); + classPath.append(artifact.getFile().getAbsolutePath()); + } + else + { + if (artifact.getArtifactId().equals(plugin.getArtifactId())) //get the jetty-maven-plugin jar + classPath.append(artifact.getFile().getAbsolutePath()); + } + } + + //Any jars that we need from the project's dependencies because we're useProvided + if (providedJars != null && !providedJars.isEmpty()) + { + for (File jar:providedJars) + { + classPath.append(File.pathSeparator); + classPath.append(jar.getAbsolutePath()); + if (getLog().isDebugEnabled()) getLog().debug("Adding provided jar: " + jar); + } + } + + return classPath.toString(); + } + + /** + * Check to see if the given artifact is one of the dependency artifacts for this plugin. + * + * @param artifact to check + * @return true if it is a plugin dependency, false otherwise + */ + protected boolean isPluginArtifact(Artifact artifact) + { + if (pluginArtifacts == null) + return false; + + return pluginArtifacts.stream().anyMatch(pa -> pa.getGroupId().equals(artifact.getGroupId()) && pa.getArtifactId().equals(artifact.getArtifactId())); + } + + /** + * Check if the goal that we're executing as is excluded or not. + * + * @param goal the goal to check + * @return true if the goal is excluded, false otherwise + */ + protected boolean isExcludedGoal(String goal) + { + if (excludedGoals == null || goal == null) + return false; + + goal = goal.trim(); + if ("".equals(goal)) + return false; + + boolean excluded = false; + for (int i = 0; i < excludedGoals.length && !excluded; i++) + { + if (excludedGoals[i].equalsIgnoreCase(goal)) + excluded = true; + } + + return excluded; + } + + protected boolean isPackagingSupported() + { + if (!supportedPackagings.contains(project.getPackaging())) + return false; + return true; + } + + protected String getProjectName() + { + String projectName = project.getName(); + if (StringUtils.isBlank(projectName)) + { + projectName = project.getGroupId() + ":" + project.getArtifactId(); + } + return projectName; + } + + /** + * Ensure there is a webapp, and that some basic defaults are applied + * if the user has not supplied them. + * + * @throws AbstractMojoExecutionException if there is an abstract mojo execution problem + */ + protected void configureWebApp() + throws Exception + { + if (webApp == null) + webApp = new MavenWebAppContext(); + + //If no contextPath was specified, go with default of project artifactid + String cp = webApp.getContextPath(); + if (cp == null || "".equals(cp)) + { + cp = "/" + project.getArtifactId(); + webApp.setContextPath(cp); + } + + //If no tmp directory was specified, and we have one, use it + if (webApp.getTempDirectory() == null) + { + File target = new File(project.getBuild().getDirectory()); + File tmp = new File(target, "tmp"); + if (!tmp.exists()) + { + if (!tmp.mkdirs()) + { + throw new MojoFailureException("Unable to create temp directory: " + tmp); + } + } + + webApp.setTempDirectory(tmp); + } + + getLog().info("Context path = " + webApp.getContextPath()); + getLog().info("Tmp directory = " + (webApp.getTempDirectory() == null ? " determined at runtime" : webApp.getTempDirectory())); + } + + /** + * Try and find a jetty-web.xml file, using some + * historical naming conventions if necessary. + * + * @param webInfDir the web inf directory + * @return the jetty web xml file + */ + protected File findJettyWebXmlFile(File webInfDir) + { + if (webInfDir == null) + return null; + if (!webInfDir.exists()) + return null; + + File f = new File(webInfDir, "jetty-web.xml"); + if (f.exists()) + return f; + + //try some historical alternatives + f = new File(webInfDir, "web-jetty.xml"); + if (f.exists()) + return f; + + return null; + } + + /** + * Get a file into which to write output from jetty. + * + * @param name the name of the file + * @return the created file + * @throws Exception if there is an unspecified problem + */ + protected File getJettyOutputFile(String name) throws Exception + { + File outputFile = new File(target, name); + if (outputFile.exists()) + outputFile.delete(); + outputFile.createNewFile(); + return outputFile; + } + + /** + * Configure any extra files, directories or patterns thereof for the + * scanner to watch for changes. + * + * @param scanner Scanner that notices changes in files and dirs. + * @throws IOException + */ + protected void configureScanTargetPatterns(Scanner scanner) throws IOException + { + //handle the extra scan patterns + if (scanTargetPatterns != null) + { + for (ScanTargetPattern p : scanTargetPatterns) + { + IncludeExcludeSet includesExcludes = scanner.addDirectory(p.getDirectory().toPath()); + p.configureIncludesExcludeSet(includesExcludes); + } + } + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/JettyEffectiveWebXml.java b/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/JettyEffectiveWebXml.java new file mode 100644 index 00000000000..3e2ff76aa1b --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/JettyEffectiveWebXml.java @@ -0,0 +1,107 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.maven.plugin; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.annotations.ResolutionScope; + +/** + * Generate the effective web.xml for a pre-built webapp. This goal will NOT + * first build the webapp, it must already exist. + * + */ +@Mojo(name = "effective-web-xml", requiresDependencyResolution = ResolutionScope.RUNTIME) +public class JettyEffectiveWebXml extends AbstractUnassembledWebAppMojo +{ + /** + * The name of the file to generate into + */ + @Parameter (defaultValue = "${project.build.directory}/effective-web.xml") + protected File effectiveWebXml; + + @Override + public void configureWebApp() throws Exception + { + //Try to determine if we're using an unassembled webapp, or an + //external||prebuilt webapp + String war = webApp.getWar(); + Path path = null; + if (war != null) + { + try + { + URL url = new URL(war); + path = Paths.get(url.toURI()); + } + catch (MalformedURLException e) + { + path = Paths.get(war); + } + } + + Path start = path.getName(0); + int count = path.getNameCount(); + Path end = path.getName(count > 0 ? count - 1 : count); + //if the war is not assembled, we must configure it + if (start.startsWith("src") || !end.toString().endsWith(".war")) + super.configureUnassembledWebApp(); + } + + /** + * Override so we can call the parent's method in a different order. + */ + @Override + protected void configureUnassembledWebApp() + { + } + + @Override + protected void startJettyEmbedded() throws MojoExecutionException + { + generate(); + } + + @Override + protected void startJettyForked() throws MojoExecutionException + { + generate(); + } + + @Override + protected void startJettyHome() throws MojoExecutionException + { + generate(); + } + + private void generate() throws MojoExecutionException + { + try + { + QuickStartGenerator generator = new QuickStartGenerator(effectiveWebXml.toPath(), webApp); + generator.generate(); + } + catch (Exception e) + { + throw new MojoExecutionException("Error generating effective web xml", e); + } + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/JettyEmbedder.java b/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/JettyEmbedder.java new file mode 100644 index 00000000000..28c90ed646d --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/JettyEmbedder.java @@ -0,0 +1,109 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.maven.plugin; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +import org.eclipse.jetty.ee11.quickstart.QuickStartConfiguration; +import org.eclipse.jetty.ee11.quickstart.QuickStartConfiguration.Mode; +import org.eclipse.jetty.ee11.servlet.ServletHandler; +import org.eclipse.jetty.ee11.webapp.Configurations; +import org.eclipse.jetty.maven.AbstractJettyEmbedder; +import org.eclipse.jetty.maven.ServerSupport; +import org.eclipse.jetty.server.handler.ContextHandler; + +/** + * JettyEmbedder + * + * Starts jetty within the current process. + */ +public class JettyEmbedder extends AbstractJettyEmbedder +{ + protected MavenWebAppContext webApp; + + public List getContextHandlers() + { + return contextHandlers; + } + + public void setWebApp(MavenWebAppContext app) + { + webApp = app; + } + + protected void redeployWebApp() throws Exception + { + stopWebApp(); + + //clear the ServletHandler, which may have + //remembered "durable" Servlets, Filters, Listeners + //from the context xml file, but as we will re-apply + //the context xml, we should not retain them + webApp.setServletHandler(new ServletHandler()); + + //regenerate config properties + applyWebAppProperties(); + + webApp.start(); + } + + @Override + public void stopWebApp() throws Exception + { + if (webApp != null && !webApp.isStopped()) + webApp.stop(); + } + + /** + * Configure the webapp + * @throws Exception if there is an unspecified problem + */ + public void configureWebApp() throws Exception + { + //Set up list of default Configurations to apply to a webapp + Configurations.setServerDefault(server); + + /* Configure the webapp */ + if (webApp == null) + webApp = new MavenWebAppContext(); + + applyWebAppProperties(); + + //If there is a quickstart file, then quickstart the webapp. + if (webApp.getTempDirectory() != null) + { + Path qs = webApp.getTempDirectory().toPath().resolve("quickstart-web.xml"); + if (Files.exists(qs) && Files.isRegularFile(qs)) + { + webApp.addConfiguration(new MavenQuickStartConfiguration()); + webApp.setAttribute(QuickStartConfiguration.QUICKSTART_WEB_XML, qs); + webApp.setAttribute(QuickStartConfiguration.MODE, Mode.QUICKSTART); + } + } + } + + public void applyWebAppProperties() throws Exception + { + super.applyWebAppProperties(); + WebAppPropertyConverter.fromProperties(webApp, webAppProperties, server, jettyProperties); + } + + public void addWebAppToServer() throws Exception + { + //add the webapp to the server + ServerSupport.addWebApplication(server, webApp); + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/JettyForkedChild.java b/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/JettyForkedChild.java new file mode 100644 index 00000000000..93cd6a128c5 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/JettyForkedChild.java @@ -0,0 +1,55 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.maven.plugin; + +import org.eclipse.jetty.maven.AbstractForkedChild; +import org.eclipse.jetty.maven.AbstractJettyEmbedder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * JettyForkedChild + * + * This is the class that is executed when the jetty maven plugin + * forks a process when DeploymentMode=FORKED. + */ +public class JettyForkedChild extends AbstractForkedChild +{ + private static final Logger LOG = LoggerFactory.getLogger(JettyForkedChild.class); + + /** + * @param args arguments that were passed to main + * @throws Exception if unable to configure + */ + public JettyForkedChild(String[] args) throws Exception + { + super(args); + } + + @Override + protected AbstractJettyEmbedder newJettyEmbedder() + { + return new JettyEmbedder(); + } + + public static void main(String[] args) + throws Exception + { + if (args == null) + System.exit(1); + + JettyForkedChild child = new JettyForkedChild(args); + child.start(); + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/JettyForker.java b/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/JettyForker.java new file mode 100644 index 00000000000..72434aa0c66 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/JettyForker.java @@ -0,0 +1,56 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.maven.plugin; + +import org.eclipse.jetty.maven.AbstractServerForker; + +/** + * JettyForker + * + * Uses quickstart to generate a webapp and forks a process to run it. + */ +public class JettyForker extends AbstractServerForker +{ + protected MavenWebAppContext webApp; + QuickStartGenerator generator; + + public JettyForker() + { + executionClassName = JettyForkedChild.class.getCanonicalName(); + } + + public void setWebApp(MavenWebAppContext app) + { + webApp = app; + } + + @Override + public void generateWebApp() throws Exception + { + //Run the webapp to create the quickstart file and properties file + generator = new QuickStartGenerator(forkWebXml.toPath(), webApp); + generator.setContextXml(contextXml); + generator.setWebAppProps(webAppPropsFile.toPath()); + generator.setServer(server); + generator.generate(); + } + + protected void redeployWebApp() + throws Exception + { + //regenerating the quickstart will be noticed by the JettyForkedChild process + //which will redeploy the webapp + generator.generate(); + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/JettyHomeForker.java b/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/JettyHomeForker.java new file mode 100644 index 00000000000..0fe9c2c0419 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/JettyHomeForker.java @@ -0,0 +1,62 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.maven.plugin; + +import java.io.File; + +import org.eclipse.jetty.maven.AbstractHomeForker; + +/** + * JettyHomeBaseForker + * + * Unpacks a jetty-home and configures it with a base that allows it + * to run an unassembled webapp. + */ +public class JettyHomeForker extends AbstractHomeForker +{ + protected MavenWebAppContext webApp; + + public JettyHomeForker() + { + environment = "ee11"; + } + + public void setWebApp(MavenWebAppContext webApp) + { + this.webApp = webApp; + } + + public File getBaseDir() + { + return baseDir; + } + + public void setBaseDir(File baseDir) + { + this.baseDir = baseDir; + } + + protected void redeployWebApp() + throws Exception + { + generateWebAppPropertiesFile(); + webappPath.resolve("maven.xml").toFile().setLastModified(System.currentTimeMillis()); + } + + protected void generateWebAppPropertiesFile() + throws Exception + { + WebAppPropertyConverter.toProperties(webApp, etcPath.resolve("maven.props").toFile(), contextXml); + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/JettyRunMojo.java b/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/JettyRunMojo.java new file mode 100644 index 00000000000..261b635844a --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/JettyRunMojo.java @@ -0,0 +1,423 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.maven.plugin; + +import java.io.File; +import java.nio.file.Path; +import java.nio.file.PathMatcher; +import java.util.Date; +import java.util.Set; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.Execute; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.annotations.ResolutionScope; +import org.eclipse.jetty.ee11.webapp.WebAppContext; +import org.eclipse.jetty.maven.ConsoleReader; +import org.eclipse.jetty.util.IncludeExcludeSet; +import org.eclipse.jetty.util.Scanner; +import org.eclipse.jetty.util.component.LifeCycle; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.util.thread.Scheduler; + +/** + * This goal is used in-situ on a Maven project without first requiring that the project + * is assembled into a war, saving time during the development cycle. + *

+ * The plugin runs a parallel lifecycle to ensure that the "test-compile" phase has been completed before invoking Jetty. This means + * that you do not need to explicity execute a "mvn compile" first. It also means that a "mvn clean jetty:run" will ensure that + * a full fresh compile is done before invoking Jetty. + *

+ * Once invoked, the plugin can be configured to run continuously, scanning for changes in the project and automatically performing a + * hot redeploy when necessary. This allows the developer to concentrate on coding changes to the project using their IDE of choice and have those changes + * immediately and transparently reflected in the running web container, eliminating development time that is wasted on rebuilding, reassembling and redeploying. + * Alternatively, you can configure the plugin to wait for an <enter> at the command line to manually control redeployment. + *

+ * You can configure this goal to run your unassembled webapp either in-process with maven, or forked into a new process, or deployed into a + * jetty distribution. + */ +@Mojo (name = "run", requiresDependencyResolution = ResolutionScope.TEST) +@Execute (phase = LifecyclePhase.TEST_COMPILE) +public class JettyRunMojo extends AbstractUnassembledWebAppMojo +{ + //Start of parameters only valid for deploymentType=EMBED + /** + * Controls redeployment of the webapp. + *

    + *
  1. -1 : means no redeployment will be done
  2. + *
  3. 0 : means redeployment only occurs if you hit the ENTER key
  4. + *
  5. otherwise, the interval in seconds to pause before checking and redeploying if necessary
  6. + *
+ */ + @Parameter(defaultValue = "-1", property = "jetty.scan", required = true) + protected int scan; + + /** + * Scanner to check for files changes to cause redeploy + */ + protected Scanner scanner; + + /** + * Only one of the following will be used, depending the mode + * the mojo is started in: EMBED, FORK, EXTERNAL + */ + protected JettyEmbedder embedder; + protected JettyForker forker; + protected JettyHomeForker homeForker; + + @Override + public void execute() throws MojoExecutionException, MojoFailureException + { + super.execute(); + } + + @Override + public void startJettyEmbedded() throws MojoExecutionException + { + try + { + //start jetty + embedder = newJettyEmbedder(); + embedder.setExitVm(true); + embedder.setStopAtShutdown(true); + embedder.start(); + startScanner(); + embedder.join(); + } + catch (Exception e) + { + throw new MojoExecutionException("Error starting jetty", e); + } + } + + @Override + public void startJettyForked() throws MojoExecutionException + { + try + { + forker = newJettyForker(); + forker.setWaitForChild(true); //we run at the command line, echo child output and wait for it + //if we can do hot redeploy, tell the forked process to watch for changes to the generated webapp file + if (scan >= 0) + { + forker.setScan(true); + forker.setScanInterval((scan == 0 ? 1 : scan)); //if redeploying on ENTER key, the forked process still needs to watch the generated webapp file + } + //TODO is it ok to start the scanner before we start jetty? + startScanner(); + forker.start(); //forks jetty instance + } + catch (Exception e) + { + throw new MojoExecutionException("Error starting jetty", e); + } + } + + @Override + public void startJettyHome() throws MojoExecutionException + { + try + { + homeForker = newJettyHomeForker(); + homeForker.setWaitForChild(true); //we always run at the command line, echo child output and wait for it + //TODO is it ok to start the scanner before we start jetty? + startScanner(); + homeForker.start(); //forks a jetty distro + } + catch (Exception e) + { + throw new MojoExecutionException("Error starting jetty", e); + } + } + + private void startScanner() + throws Exception + { + if (scan < 0) + { + getLog().info("Automatic redeployment disabled, see 'mvn jetty:help' for more redeployment options"); + return; //no automatic or manual redeployment + } + + // start scanning for changes, or wait for linefeed on stdin + if (scan > 0) + { + scanner = new Scanner(); + scanner.setScanInterval(scan); + scanner.setScanDepth(Scanner.MAX_SCAN_DEPTH); //always fully walk directory hierarchies + scanner.setReportExistingFilesOnStartup(false); + scanner.addListener(new Scanner.BulkListener() + { + public void filesChanged(Set changes) + { + try + { + restartWebApp(changes.contains(project.getFile().getCanonicalPath())); + } + catch (Exception e) + { + getLog().error("Error reconfiguring/restarting webapp after change in watched files", e); + } + } + }); + configureScanner(); + getLog().info("Scan interval sec = " + scan); + + //unmanage scheduler so it is not stopped with the scanner + Scheduler scheduler = scanner.getBean(Scheduler.class); + scanner.unmanage(scheduler); + LifeCycle.start(scheduler); + scanner.start(); + } + else + { + ConsoleReader creader = new ConsoleReader(); + creader.addListener(new ConsoleReader.Listener() + { + @Override + public void consoleEvent(String line) + { + try + { + restartWebApp(false); + } + catch (Exception e) + { + getLog().debug(e); + } + } + }); + Thread cthread = new Thread(creader, "ConsoleReader"); + cthread.setDaemon(true); + cthread.start(); + } + } + + protected void configureScanner() + throws MojoExecutionException + { + try + { + gatherScannables(); + } + catch (Exception e) + { + throw new MojoExecutionException("Error forming scan list", e); + } + } + + public void gatherScannables() throws Exception + { + if (webApp.getDescriptor() != null) + { + Resource r = webApp.getResourceFactory().newResource(webApp.getDescriptor()); + scanner.addFile(r.getPath()); + } + + if (webApp.getJettyEnvXml() != null) + scanner.addFile(new File(webApp.getJettyEnvXml()).toPath()); + + if (webApp.getDefaultsDescriptor() != null) + { + if (!WebAppContext.WEB_DEFAULTS_XML.equals(webApp.getDefaultsDescriptor())) + scanner.addFile(new File(webApp.getDefaultsDescriptor()).toPath()); + } + + if (webApp.getOverrideDescriptor() != null) + { + scanner.addFile(new File(webApp.getOverrideDescriptor()).toPath()); + } + + File jettyWebXmlFile = findJettyWebXmlFile(new File(webAppSourceDirectory, "WEB-INF")); + if (jettyWebXmlFile != null) + { + scanner.addFile(jettyWebXmlFile.toPath()); + } + + //make sure each of the war artifacts is added to the scanner + for (Artifact a:mavenProjectHelper.getWarPluginInfo().getWarArtifacts()) + { + File f = a.getFile(); + if (a.getFile().isDirectory()) + scanner.addDirectory(f.toPath()); + else + scanner.addFile(f.toPath()); + } + + //set up any extra files or dirs to watch + configureScanTargetPatterns(scanner); + + scanner.addFile(project.getFile().toPath()); + + if (webApp.getTestClasses() != null && webApp.getTestClasses().exists()) + { + Path p = webApp.getTestClasses().toPath(); + IncludeExcludeSet includeExcludeSet = scanner.addDirectory(p); + if (scanTestClassesPattern != null) + { + for (String s : scanTestClassesPattern.getExcludes()) + { + if (!s.startsWith("glob:")) + s = "glob:" + s; + includeExcludeSet.exclude(p.getFileSystem().getPathMatcher(s)); + } + for (String s : scanTestClassesPattern.getIncludes()) + { + if (!s.startsWith("glob:")) + s = "glob:" + s; + includeExcludeSet.include(p.getFileSystem().getPathMatcher(s)); + } + } + } + + if (webApp.getClasses() != null && webApp.getClasses().exists()) + { + Path p = webApp.getClasses().toPath(); + IncludeExcludeSet includeExcludes = scanner.addDirectory(p); + if (scanClassesPattern != null) + { + for (String s : scanClassesPattern.getExcludes()) + { + if (!s.startsWith("glob:")) + s = "glob:" + s; + includeExcludes.exclude(p.getFileSystem().getPathMatcher(s)); + } + + for (String s : scanClassesPattern.getIncludes()) + { + if (!s.startsWith("glob:")) + s = "glob:" + s; + includeExcludes.include(p.getFileSystem().getPathMatcher(s)); + } + } + } + + if (webApp.getWebInfLib() != null) + { + for (File f : webApp.getWebInfLib()) + { + if (f.isDirectory()) + scanner.addDirectory(f.toPath()); + else + scanner.addFile(f.toPath()); + } + } + } + + /** + * Stop an executing webapp and restart it after optionally + * reconfiguring it. + * + * @param reconfigure if true, the scanner will + * be reconfigured after changes to the pom. If false, only + * the webapp will be reconfigured. + * + * @throws Exception if there is an unspecified problem + */ + public void restartWebApp(boolean reconfigure) throws Exception + { + getLog().info("Restarting " + webApp); + getLog().debug("Stopping webapp ..."); + if (scanner != null) + scanner.stop(); + + switch (deployMode) + { + case EMBED: + { + getLog().debug("Reconfiguring webapp ..."); + + verifyPomConfiguration(); + // check if we need to reconfigure the scanner, + // which is if the pom changes + if (reconfigure) + { + getLog().info("Reconfiguring scanner after change to pom.xml ..."); + warArtifacts = null; //will be regenerated by configureWebApp + if (scanner != null) + { + scanner.reset(); + configureScanner(); + } + } + + embedder.stopWebApp(); + configureWebApp(); + embedder.redeployWebApp(); + if (scanner != null) + scanner.start(); + getLog().info("Restart completed at " + new Date().toString()); + + break; + } + case FORK: + { + verifyPomConfiguration(); + if (reconfigure) + { + getLog().info("Reconfiguring scanner after change to pom.xml ..."); + warArtifacts = null; ///TODO if the pom changes for the forked case, how would we get the forked process to stop and restart? + if (scanner != null) + { + scanner.reset(); + configureScanner(); + } + } + configureWebApp(); + //regenerate with new config and restart the webapp + forker.redeployWebApp(); + //restart scanner + if (scanner != null) + scanner.start(); + break; + } + case DISTRO: + case HOME: + case EXTERNAL: + { + if (deployMode != DeploymentMode.EXTERNAL) + getLog().warn(deployMode + " mode is deprecated, use mode EXTERNAL"); + verifyPomConfiguration(); + if (reconfigure) + { + getLog().info("Reconfiguring scanner after change to pom.xml ..."); + + warArtifacts = null; //TODO if there are any changes to the pom, then we would have to tell the + //existing forked home process to stop, then rerun the configuration and then refork - too complicated??! + if (scanner != null) + { + scanner.reset(); + configureScanner(); + } + } + configureWebApp(); + //regenerate the webapp and redeploy it + homeForker.redeployWebApp(); + //restart scanner + if (scanner != null) + scanner.start(); + + break; + } + default: + { + throw new IllegalStateException("Unrecognized run type " + deployMode); + } + } + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/JettyRunWarMojo.java b/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/JettyRunWarMojo.java new file mode 100644 index 00000000000..7609f7c81fb --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/JettyRunWarMojo.java @@ -0,0 +1,295 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.maven.plugin; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Date; +import java.util.Set; + +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugins.annotations.Execute; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.annotations.ResolutionScope; +import org.eclipse.jetty.maven.ConsoleReader; +import org.eclipse.jetty.util.Scanner; +import org.eclipse.jetty.util.StringUtil; + +/** +*

+* This goal is used to assemble your webapp into a war and automatically deploy it to Jetty. +*

+*

+* Once invoked, the plugin runs continuously and can be configured to scan for changes in the project and to the +* war file and automatically perform a hot redeploy when necessary. +*

+*

+* You may also specify the location of a jetty.xml file whose contents will be applied before any plugin configuration. +*

+*

+* You can configure this goal to run your webapp either in-process with maven, or forked into a new process, or deployed into a +* jetty distribution. +*

+*/ +@Mojo(name = "run-war", requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME) +@Execute(phase = LifecyclePhase.PACKAGE) +public class JettyRunWarMojo extends AbstractWebAppMojo +{ + /** + * The interval in seconds to pause before checking if changes + * have occurred and re-deploying as necessary. A value + * of 0 indicates no re-deployment will be done. In that case, you + * can force redeployment by typing a linefeed character at the command line. + */ + @Parameter(defaultValue = "0", property = "jetty.scan", required = true) + protected int scan; + + /** + * Scanner to check for files changes to cause redeploy + */ + protected Scanner scanner; + protected JettyEmbedder embedder; + protected JettyForker forker; + protected JettyHomeForker homeForker; + protected Path war; + + @Override + public void configureWebApp() throws Exception + { + super.configureWebApp(); + //if no war has been explicitly configured, use the one from the webapp project + if (StringUtil.isBlank(webApp.getWar())) + { + war = target.toPath().resolve(project.getBuild().getFinalName() + ".war"); + webApp.setWar(war.toFile().getAbsolutePath()); + } + else + war = Paths.get(webApp.getWar()); + + getLog().info("War = " + war); + } + + /** + * Start a jetty instance in process to run the built war. + */ + @Override + public void startJettyEmbedded() throws MojoExecutionException + { + try + { + embedder = newJettyEmbedder(); + embedder.setExitVm(true); + embedder.setStopAtShutdown(true); + embedder.start(); + startScanner(); + embedder.join(); + } + catch (Exception e) + { + throw new MojoExecutionException("Error starting jetty", e); + } + } + + + /** + * Fork a jetty instance to run the built war. + */ + @Override + public void startJettyForked() throws MojoExecutionException + { + try + { + forker = newJettyForker(); + forker.setWaitForChild(true); //we run at the command line, echo child output and wait for it + startScanner(); + forker.start(); //forks jetty instance + + } + catch (Exception e) + { + throw new MojoExecutionException("Error starting jetty", e); + } + } + + /** + * Deploy the built war to a jetty distro. + */ + @Override + public void startJettyHome() throws MojoExecutionException + { + try + { + homeForker = newJettyHomeForker(); + homeForker.setWaitForChild(true); //we always run at the command line, echo child output and wait for it + startScanner(); + homeForker.start(); //forks a jetty distro + } + catch (Exception e) + { + throw new MojoExecutionException("Error starting jetty", e); + } + } + + public void startScanner() + throws Exception + { + // start scanning for changes, or wait for linefeed on stdin + if (scan > 0) + { + scanner = new Scanner(); + scanner.setScanInterval(scan); + scanner.setScanDepth(Scanner.MAX_SCAN_DEPTH); //always fully walk directory hierarchies + scanner.setReportExistingFilesOnStartup(false); + configureScanner(); + getLog().info("Scan interval ms = " + scan); + scanner.start(); + } + else + { + ConsoleReader creader = new ConsoleReader(); + creader.addListener(new ConsoleReader.Listener() + { + @Override + public void consoleEvent(String line) + { + try + { + restartWebApp(false); + } + catch (Exception e) + { + getLog().debug(e); + } + } + }); + Thread cthread = new Thread(creader, "ConsoleReader"); + cthread.setDaemon(true); + cthread.start(); + } + } + + public void configureScanner() throws MojoExecutionException + { + try + { + scanner.addFile(project.getFile().toPath()); + scanner.addFile(war); + + //set up any extra files or dirs to watch + configureScanTargetPatterns(scanner); + scanner.addListener(new Scanner.BulkListener() + { + public void filesChanged(Set changes) + { + try + { + boolean reconfigure = changes.contains(project.getFile().getCanonicalPath()); + restartWebApp(reconfigure); + } + catch (Exception e) + { + getLog().error("Error reconfiguring/restarting webapp after change in watched files", e); + } + } + }); + } + catch (IOException e) + { + throw new MojoExecutionException("Error configuring scanner", e); + } + } + + public void restartWebApp(boolean reconfigure) throws Exception + { + getLog().info("Restarting webapp ..."); + getLog().debug("Stopping scanner ..."); + if (scanner != null) + scanner.stop(); + + switch (deployMode) + { + case EMBED: + { + getLog().debug("Reconfiguring webapp ..."); + + verifyPomConfiguration(); + // check if we need to reconfigure the scanner, + // which is if the pom changes + if (reconfigure) + { + getLog().info("Reconfiguring scanner after change to pom.xml ..."); + scanner.reset(); + warArtifacts = null; + configureScanner(); + } + embedder.stopWebApp(); + configureWebApp(); + embedder.redeployWebApp(); + scanner.start(); + getLog().info("Restart completed at " + new Date().toString()); + + break; + } + case FORK: + { + verifyPomConfiguration(); + if (reconfigure) + { + getLog().info("Reconfiguring scanner after change to pom.xml ..."); + scanner.reset(); + warArtifacts = null; + configureScanner(); + } + + configureWebApp(); + //regenerate with new config and restart the webapp + forker.redeployWebApp(); + //restart scanner + scanner.start(); + + break; + } + case HOME: + case DISTRO: + case EXTERNAL: + { + if (deployMode != DeploymentMode.EXTERNAL) + getLog().warn(deployMode + " mode is deprecated, use mode EXTERNAL"); + verifyPomConfiguration(); + if (reconfigure) + { + getLog().info("Reconfiguring scanner after change to pom.xml ..."); + scanner.reset(); + warArtifacts = null; + configureScanner(); + } + configureWebApp(); + //regenerate the webapp and redeploy it + homeForker.redeployWebApp(); + //restart scanner + scanner.start(); + + break; + } + default: + { + throw new IllegalStateException("Unrecognized run type " + deployMode); + } + } + getLog().info("Restart completed."); + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/JettyStartMojo.java b/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/JettyStartMojo.java new file mode 100644 index 00000000000..69b09721fc1 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/JettyStartMojo.java @@ -0,0 +1,102 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.maven.plugin; + +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.ResolutionScope; + +/** + *

+ * This goal is similar to the jetty:run goal in that it it starts jetty on an unassembled webapp, + * EXCEPT that it is designed to be bound to an execution inside your pom. Thus, this goal does NOT + * run a parallel build cycle, so you must be careful to ensure that you bind it to a phase in + * which all necessary generated files and classes for the webapp have been created. + *

+ *

+ * This goal will NOT scan for changes in either the webapp project or any scanTargets or scanTargetPatterns. + *

+ *

+ * You can configure this goal to run your webapp either in-process with maven, or forked into a new process, or deployed into a + * jetty distribution. + *

+ */ +@Mojo(name = "start", requiresDependencyResolution = ResolutionScope.TEST) +public class JettyStartMojo extends AbstractUnassembledWebAppMojo +{ + + /** + * Starts the webapp - without first compiling the classes - + * in the same process as maven. + */ + @Override + public void startJettyEmbedded() throws MojoExecutionException + { + try + { + JettyEmbedder jetty = newJettyEmbedder(); + jetty.setExitVm(false); + jetty.setStopAtShutdown(false); + jetty.start(); + } + catch (Exception e) + { + throw new MojoExecutionException("Error starting jetty", e); + } + } + + /** + * Start the webapp in a forked jetty process. Use the + * jetty:stop goal to terminate. + */ + @Override + public void startJettyForked() throws MojoExecutionException + { + try + { + JettyForker jetty = newJettyForker(); + jetty.setWaitForChild(false); //we never wait for child to finish + jetty.setMaxChildStartChecks(maxChildStartChecks); + jetty.setMaxChildStartCheckMs(maxChildStartCheckMs); + jetty.setJettyOutputFile(getJettyOutputFile("jetty-start.out")); + jetty.start(); //forks jetty instance + } + catch (Exception e) + { + throw new MojoExecutionException("Error starting jetty", e); + } + } + + /** + * Start the webapp in a forked jetty distribution. Use the + * jetty:stop goal to terminate + */ + @Override + public void startJettyHome() throws MojoExecutionException + { + try + { + JettyHomeForker jetty = newJettyHomeForker(); + jetty.setWaitForChild(false); //never wait for child to finish + jetty.setMaxChildStartChecks(maxChildStartChecks); + jetty.setMaxChildStartCheckMs(maxChildStartCheckMs); + jetty.setJettyOutputFile(getJettyOutputFile("jetty-start.out")); + jetty.start(); //forks a jetty home + } + catch (Exception e) + { + throw new MojoExecutionException("Error starting jetty", e); + } + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/JettyStartWarMojo.java b/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/JettyStartWarMojo.java new file mode 100644 index 00000000000..0b042925950 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/JettyStartWarMojo.java @@ -0,0 +1,144 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.maven.plugin; + +import java.io.File; +import java.nio.file.Path; + +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.annotations.ResolutionScope; +import org.eclipse.jetty.util.StringUtil; + +/** + *

+ * This goal is used to run Jetty with any pre-assembled war. This goal does not have + * to be used with a project of packaging type "war". + *

+ *

+ * You can configure the "webApp" element with the location of either a war file or + * an unpacked war that you wish to deploy - in either case, the webapp must be + * fully compiled and assembled as this goal does not do anything other than start + * jetty with the given webapp. If you do not configure the "webApp" element, then + * the goal will default to using the war of the webapp project. + *

+ *

+ * This goal is designed to be bound to a build phase, and NOT to be run at the + * command line. It will not block waiting for jetty to execute, but rather continue + * execution. + *

+ *

+ * This goal is useful e.g. for launching a web app in Jetty as a target for unit-tested + * HTTP client components via binding to the test-integration build phase. + *

+ *

+ * You can configure this goal to run the webapp either in-process with maven, or + * forked into a new process, or deployed into a {@code ${jetty.base}} directory. + *

+ */ +@Mojo(name = "start-war", requiresDependencyResolution = ResolutionScope.RUNTIME) +public class JettyStartWarMojo extends AbstractWebAppMojo +{ + @Parameter (defaultValue = "${project.baseDir}/src/main/webapp") + protected File webAppSourceDirectory; + + protected JettyEmbedder embedder; + protected JettyForker forker; + protected JettyHomeForker homeForker; + + @Override + public void configureWebApp() throws Exception + { + super.configureWebApp(); + //if a war has not been explicitly configured, use the one from the project + if (StringUtil.isBlank(webApp.getWar())) + { + Path war = target.toPath().resolve(project.getBuild().getFinalName() + ".war"); + webApp.setWar(war.toFile().getAbsolutePath()); + } + + getLog().info("War = " + webApp.getWar()); + } + + /** + * Start a jetty instance in process to run given war. + */ + @Override + public void startJettyEmbedded() throws MojoExecutionException + { + try + { + embedder = newJettyEmbedder(); + embedder.setExitVm(false); + embedder.setStopAtShutdown(false); + embedder.start(); + } + catch (Exception e) + { + throw new MojoExecutionException("Error starting jetty", e); + } + } + + /** + * Fork a jetty instance to run the given war. + */ + @Override + public void startJettyForked() throws MojoExecutionException + { + try + { + forker = newJettyForker(); + forker.setWaitForChild(false); //we never wait for child to finish + forker.setMaxChildStartChecks(maxChildStartChecks); + forker.setMaxChildStartCheckMs(maxChildStartCheckMs); + forker.setJettyOutputFile(getJettyOutputFile("jetty-start-war.out")); + forker.start(); //forks jetty instance + + } + catch (Exception e) + { + throw new MojoExecutionException("Error starting jetty", e); + } + } + + /** + * Fork a jetty distro to run the given war. + */ + @Override + public void startJettyHome() throws MojoExecutionException + { + try + { + homeForker = newJettyHomeForker(); + homeForker.setWaitForChild(false); //never wait for child tofinish + homeForker.setMaxChildStartCheckMs(maxChildStartCheckMs); + homeForker.setMaxChildStartChecks(maxChildStartChecks); + homeForker.setJettyOutputFile(getJettyOutputFile("jetty-start-war.out")); + homeForker.start(); //forks a jetty distro + } + catch (Exception e) + { + throw new MojoExecutionException("Error starting jetty", e); + } + } + + @Override + protected void verifyPomConfiguration() throws MojoExecutionException + { + //Do nothing here, as we want the user to configure a war to deploy, + //or we default to the webapp that is running the jetty plugin, but + //we need to delay that decision until configureWebApp(). + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/JettyStopMojo.java b/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/JettyStopMojo.java new file mode 100644 index 00000000000..3be4615abfa --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/JettyStopMojo.java @@ -0,0 +1,235 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.maven.plugin; + +import java.io.InputStreamReader; +import java.io.LineNumberReader; +import java.io.OutputStream; +import java.net.ConnectException; +import java.net.InetAddress; +import java.net.Socket; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; + +/** + * This goal stops a running instance of jetty. + * + * The stopPort and stopKey parameters can be used to + * configure which jetty to stop. + */ +@Mojo(name = "stop") +public class JettyStopMojo extends AbstractWebAppMojo +{ + /** + * Max time in seconds that the plugin will wait for confirmation that jetty has stopped. + */ + @Parameter + protected int stopWait; + + @Override + protected void startJettyEmbedded() throws MojoExecutionException + { + //Does not start jetty + return; + } + + @Override + protected void startJettyForked() throws MojoExecutionException + { + //Does not start jetty + return; + } + + @Override + protected void startJettyHome() throws MojoExecutionException + { + //Does not start jetty + return; + } + + @Override + public void execute() throws MojoExecutionException, MojoFailureException + { + if (stopPort <= 0) + throw new MojoExecutionException("Please specify a valid port"); + if (stopKey == null) + throw new MojoExecutionException("Please specify a valid stopKey"); + + String command = "forcestop"; + + if (stopWait > 0) + { + //try to get the pid of the forked jetty process + Long pid = null; + try + { + String response = send(stopKey + "\r\n" + "pid" + "\r\n", stopWait); + pid = Long.valueOf(response); + } + catch (NumberFormatException e) + { + if (getLog().isDebugEnabled()) + { + getLog().debug(e); + } + getLog().info("Server returned bad pid"); + } + catch (ConnectException e) + { + //jetty not running, no point continuing + getLog().info("Jetty not running!"); + return; + } + catch (Exception e) + { + //jetty running, try to stop it regardless of error + getLog().error(e); + } + + //now send the stop command and wait for confirmation - either an ack from jetty, or + //that the process has stopped + if (pid == null) + { + //no pid, so just wait until jetty reports itself stopped + try + { + getLog().info("Waiting " + stopWait + " seconds for jetty to stop"); + String response = send(stopKey + "\r\n" + command + "\r\n", stopWait); + + if ("Stopped".equals(response)) + getLog().info("Server reports itself as stopped"); + else + getLog().info("Couldn't verify server as stopped, received " + response); + } + catch (ConnectException e) + { + getLog().info("Jetty not running!"); + } + catch (Exception e) + { + getLog().error(e); + } + } + else + { + //wait for pid to stop + getLog().info("Waiting " + stopWait + " seconds for jetty " + pid + " to stop"); + Optional optional = ProcessHandle.of(pid); + final long remotePid = pid.longValue(); + optional.ifPresentOrElse(p -> + { + try + { + //if running in the same process, just send the stop + //command and wait for the response + if (ProcessHandle.current().pid() == remotePid) + { + send(stopKey + "\r\n" + command + "\r\n", stopWait); + } + else + { + //running forked, so wait for the process + send(stopKey + "\r\n" + command + "\r\n", 0); + CompletableFuture future = p.onExit(); + if (p.isAlive()) + { + p = future.get(stopWait, TimeUnit.SECONDS); + } + + if (p.isAlive()) + getLog().info("Couldn't verify server process stop"); + else + getLog().info("Server process stopped"); + } + } + catch (ConnectException e) + { + //jetty not listening on the given port, don't wait for the process + getLog().info("Jetty not running!"); + } + catch (TimeoutException e) + { + getLog().error("Timeout expired while waiting for server process to stop"); + } + catch (Throwable e) + { + getLog().error(e); + } + }, () -> getLog().info("Process not running")); + } + } + else + { + //send the stop command but don't wait to verify the stop + getLog().info("Stopping jetty"); + try + { + send(stopKey + "\r\n" + command + "\r\n", 0); + } + catch (ConnectException e) + { + getLog().info("Jetty not running!"); + } + catch (Exception e) + { + getLog().error(e); + } + } + } + + /** + * Send a command to a jetty process, optionally waiting for a response. + * + * @param command the command to send + * @param wait length of time in sec to wait for a response + * @return the response, if any, to the command + * @throws Exception if there is an unspecified problem + */ + private String send(String command, int wait) + throws Exception + { + String response = null; + try (Socket s = new Socket(InetAddress.getByName("127.0.0.1"), stopPort); OutputStream out = s.getOutputStream();) + { + out.write(command.getBytes()); + out.flush(); + + if (wait > 0) + { + //Wait for a response + s.setSoTimeout(wait * 1000); + + try (LineNumberReader lin = new LineNumberReader(new InputStreamReader(s.getInputStream()));) + { + response = lin.readLine(); + } + } + else + { + //Wait only a small amount of time to ensure TCP has sent the message + s.setSoTimeout(1000); + s.getInputStream().read(); + } + + return response; + } + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/MavenMetaInfConfiguration.java b/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/MavenMetaInfConfiguration.java new file mode 100644 index 00000000000..f6195fce9f5 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/MavenMetaInfConfiguration.java @@ -0,0 +1,121 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.maven.plugin; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jetty.ee11.webapp.Configuration; +import org.eclipse.jetty.ee11.webapp.MetaInfConfiguration; +import org.eclipse.jetty.ee11.webapp.WebAppContext; +import org.eclipse.jetty.util.FileID; +import org.eclipse.jetty.util.resource.Resource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * MavenMetaInfConfiguration + * + * MetaInfConfiguration to take account of overlaid wars expressed as project dependencies and + * potential configured via the maven-war-plugin. + */ +public class MavenMetaInfConfiguration extends MetaInfConfiguration +{ + private static final Logger LOG = LoggerFactory.getLogger(MavenMetaInfConfiguration.class); + + protected static int COUNTER = 0; + + @Override + public Class replaces() + { + return MetaInfConfiguration.class; + } + + /** + * Get the jars to examine from the files from which we have + * synthesized the classpath. Note that the classpath is not + * set at this point, so we cannot get them from the classpath. + * + * @param context the web app context + * @return the list of jars found + */ + @Override + protected List findJars(WebAppContext context) + throws Exception + { + List list = new ArrayList<>(); + MavenWebAppContext jwac = (MavenWebAppContext)context; + List files = jwac.getWebInfLib(); + if (files != null) + { + files.forEach(file -> + { + if (FileID.isJavaArchive(file.getName()) || file.isDirectory()) + { + try + { + LOG.debug(" add resource to resources to examine {}", file); + list.add(context.getResourceFactory().newResource(file.toURI())); + } + catch (Exception e) + { + LOG.warn("Bad url ", e); + } + } + }); + } + + List superList = super.findJars(context); + if (superList != null) + list.addAll(superList); + return list; + } + + /** + * Add in the classes dirs from test/classes and target/classes + */ + @Override + protected List findClassDirs(WebAppContext context) throws Exception + { + List list = new ArrayList<>(); + + MavenWebAppContext jwac = (MavenWebAppContext)context; + List files = jwac.getWebInfClasses(); + if (files != null) + { + files.forEach(file -> + { + if (file.exists() && file.isDirectory()) + { + try + { + if (LOG.isDebugEnabled()) + LOG.debug("Add file {}", file.toURI()); + list.add(context.getResourceFactory().newResource(file.toURI())); + } + catch (Exception e) + { + LOG.warn("Bad url ", e); + } + } + }); + } + + List classesDirs = super.findClassDirs(context); + if (classesDirs != null) + list.addAll(classesDirs); + return list; + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/MavenQuickStartConfiguration.java b/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/MavenQuickStartConfiguration.java new file mode 100644 index 00000000000..cb852e8f445 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/MavenQuickStartConfiguration.java @@ -0,0 +1,62 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.maven.plugin; + +import org.eclipse.jetty.ee11.quickstart.QuickStartConfiguration; +import org.eclipse.jetty.ee11.webapp.Configuration; +import org.eclipse.jetty.ee11.webapp.WebAppContext; +import org.eclipse.jetty.util.IO; +import org.eclipse.jetty.util.resource.CombinedResource; +import org.eclipse.jetty.util.resource.Resource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * MavenQuickStartConfiguration + */ +public class MavenQuickStartConfiguration extends QuickStartConfiguration +{ + private static final Logger LOG = LoggerFactory.getLogger(QuickStartConfiguration.class); + + @Override + public Class replaces() + { + return QuickStartConfiguration.class; + } + + @Override + public void deconfigure(WebAppContext context) throws Exception + { + //if we're not persisting the temp dir, get rid of any overlays + if (!context.isTempDirectoryPersistent()) + { + Resource originalBases = (Resource)context.getAttribute("org.eclipse.jetty.resources.originalBases"); + String originalBaseStr = originalBases.toString(); + + //Iterate over all of the resource bases and ignore any that were original bases, just + //deleting the overlays + Resource res = context.getBaseResource(); + if (res instanceof CombinedResource) + { + for (Resource r : ((CombinedResource)res).getResources()) + { + if (originalBaseStr.contains(r.toString())) + continue; + IO.delete(r.getPath()); + } + } + } + super.deconfigure(context); + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/MavenWebAppContext.java b/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/MavenWebAppContext.java new file mode 100644 index 00000000000..cd1099371ac --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/MavenWebAppContext.java @@ -0,0 +1,505 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.maven.plugin; + +import java.io.File; +import java.lang.reflect.Method; +import java.net.MalformedURLException; +import java.net.URI; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.TreeSet; +import java.util.stream.Stream; + +import org.eclipse.jetty.ee11.plus.webapp.EnvConfiguration; +import org.eclipse.jetty.ee11.quickstart.QuickStartConfiguration; +import org.eclipse.jetty.ee11.servlet.FilterHolder; +import org.eclipse.jetty.ee11.servlet.FilterMapping; +import org.eclipse.jetty.ee11.servlet.ServletHolder; +import org.eclipse.jetty.ee11.servlet.ServletMapping; +import org.eclipse.jetty.ee11.webapp.Configuration; +import org.eclipse.jetty.ee11.webapp.Configurations; +import org.eclipse.jetty.ee11.webapp.MetaInfConfiguration; +import org.eclipse.jetty.ee11.webapp.WebAppContext; +import org.eclipse.jetty.maven.Overlay; +import org.eclipse.jetty.util.FileID; +import org.eclipse.jetty.util.StringUtil; +import org.eclipse.jetty.util.URIUtil; +import org.eclipse.jetty.util.resource.CombinedResource; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.util.resource.Resources; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * MavenWebAppContext + *

+ * Extends the WebAppContext to specialize for the maven environment. We pass in + * the list of files that should form the classpath for the webapp when + * executing in the plugin, and any jetty-env.xml file that may have been + * configured. + */ +public class MavenWebAppContext extends WebAppContext +{ + private static final Logger LOG = LoggerFactory.getLogger(MavenWebAppContext.class); + + private static final String DEFAULT_CONTAINER_INCLUDE_JAR_PATTERN = ".*/jakarta.servlet-[^/]*\\.jar$|.*/jetty-jakarta-servlet-api-[^/]*\\.jar$|.*jakarta.servlet.jsp.jstl-[^/]*\\.jar|.*taglibs-standard-[^/]*\\.jar$"; + + private static final String WEB_INF_CLASSES_PREFIX = "/WEB-INF/classes"; + + private static final String WEB_INF_LIB_PREFIX = "/WEB-INF/lib"; + + private File _classes = null; + + private File _testClasses = null; + + private final List _webInfClasses = new ArrayList<>(); + + private final List _webInfJars = new ArrayList<>(); + + private final Map _webInfJarMap = new HashMap<>(); + + private List _classpathUris; // webInfClasses+testClasses+webInfJars + + private String _jettyEnvXml; + + private List _overlays; + + /** + * Set the "org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern" with + * a pattern for matching jars on container classpath to scan. This is + * analogous to the WebAppContext.setAttribute() call. + */ + private String _containerIncludeJarPattern = null; + + /** + * Set the "org.eclipse.jetty.server.webapp.WebInfIncludeJarPattern" with a + * pattern for matching jars on webapp's classpath to scan. This is + * analogous to the WebAppContext.setAttribute() call. + */ + private String _webInfIncludeJarPattern = null; + + /** + * If there is no maven-war-plugin config for ordering of the current + * project in the sequence of overlays, use this to control whether the + * current project is added first or last in list of overlaid resources + */ + private boolean _baseAppFirst = true; + + public MavenWebAppContext() + { + super(); + // Turn off copyWebInf option as it is not applicable for plugin. + super.setCopyWebInf(false); + } + + public void setContainerIncludeJarPattern(String pattern) + { + _containerIncludeJarPattern = pattern; + } + + public String getContainerIncludeJarPattern() + { + return _containerIncludeJarPattern; + } + + public String getWebInfIncludeJarPattern() + { + return _webInfIncludeJarPattern; + } + + public void setWebInfIncludeJarPattern(String pattern) + { + _webInfIncludeJarPattern = pattern; + } + + public List getClassPathUris() + { + return this._classpathUris; + } + + public void setJettyEnvXml(String jettyEnvXml) + { + this._jettyEnvXml = jettyEnvXml; + } + + public String getJettyEnvXml() + { + return this._jettyEnvXml; + } + + public void setClasses(File dir) + { + _classes = dir; + } + + public File getClasses() + { + return _classes; + } + + public void setWebInfLib(List jars) + { + _webInfJars.addAll(jars); + } + + public void setTestClasses(File dir) + { + _testClasses = dir; + } + + public File getTestClasses() + { + return _testClasses; + } + + /** + * Ordered list of wars to overlay on top of the current project. The list + * may contain an overlay that represents the current project. + * + * @param overlays the list of overlays + */ + public void setOverlays(List overlays) + { + _overlays = overlays; + } + + /** + * Set the name of the attribute that is used in each generated xml element + * to indicate the source of the xml element (eg annotation, web.xml etc). + * + * @param name the name of the attribute to use. + */ + public void setOriginAttribute(String name) + { + setAttribute(QuickStartConfiguration.ORIGIN_ATTRIBUTE, name); + } + + /** + * Get the originAttribute. + * @return the originAttribute + */ + public String getOriginAttribute() + { + Object attr = getAttribute(QuickStartConfiguration.ORIGIN_ATTRIBUTE); + return attr == null ? null : attr.toString(); + } + + public List getOverlays() + { + return _overlays; + } + + public void setBaseAppFirst(boolean value) + { + _baseAppFirst = value; + } + + public boolean getBaseAppFirst() + { + return _baseAppFirst; + } + + /** + * This method is provided as a convenience for jetty maven plugin + * configuration + * + * @param resourceBases Array of resources strings to set as a + * {@link CombinedResource}. + */ + public void setResourceBases(String[] resourceBases) + { + try + { + // TODO: what happens if this is called more than once? + + // This is a user provided list of configurations. + // We have to assume that mounting can happen. + List uris = Stream.of(resourceBases) + .map(URI::create) + .toList(); + + setBaseResource(this.getResourceFactory().newResource(uris)); + } + catch (Throwable t) + { + throw new IllegalArgumentException("Bad resourceBases: [" + String.join(", ", resourceBases) + "]", t); + } + } + + public List getWebInfLib() + { + return _webInfJars; + } + + public List getWebInfClasses() + { + return _webInfClasses; + } + + @Override + public void doStart() throws Exception + { + // Set up the pattern that tells us where the jars are that need + // scanning + + // Allow user to set up pattern for names of jars from the container + // classpath + // that will be scanned - note that by default NO jars are scanned + String tmp = _containerIncludeJarPattern; + if (tmp == null || "".equals(tmp)) + tmp = (String)getAttribute(MetaInfConfiguration.CONTAINER_JAR_PATTERN); + + tmp = addPattern(tmp, DEFAULT_CONTAINER_INCLUDE_JAR_PATTERN); + setAttribute(MetaInfConfiguration.CONTAINER_JAR_PATTERN, tmp); + + // Allow user to set up pattern of jar names from WEB-INF that will be + // scanned. + // Note that by default ALL jars considered to be in WEB-INF will be + // scanned - setting + // a pattern restricts scanning + if (_webInfIncludeJarPattern != null) + setAttribute(MetaInfConfiguration.WEBINF_JAR_PATTERN, _webInfIncludeJarPattern); + + // Set up the classes dirs that comprises the equivalent of + // WEB-INF/classes + if (_testClasses != null && _testClasses.exists()) + _webInfClasses.add(_testClasses); + if (_classes != null && _classes.exists()) + _webInfClasses.add(_classes); + + // Set up the classpath + _classpathUris = new ArrayList<>(); + _webInfClasses.forEach(f -> _classpathUris.add(f.toURI())); + _webInfJars.forEach(f -> + { + // ensure our JAR file references are `jar:file:...` URI references + URI jarFileUri = URIUtil.toJarFileUri(f.toURI()); + // else use file uri as-is + _classpathUris.add(Objects.requireNonNullElseGet(jarFileUri, f::toURI)); + }); + + // Initialize map containing all jars in /WEB-INF/lib + _webInfJarMap.clear(); + for (File file : _webInfJars) + { + // Return all jar files from class path + String fileName = file.getName(); + if (FileID.isJavaArchive(fileName)) + _webInfJarMap.put(fileName, file); + } + + // check for CDI + initCDI(); + + // CHECK setShutdown(false); + super.doStart(); + } + + @Override + protected Configurations newConfigurations() + { + Configurations configurations = super.newConfigurations(); + + if (getJettyEnvXml() != null) + { + // inject configurations with config from maven plugin + for (Configuration c : configurations) + { + if (c instanceof EnvConfiguration envConfiguration) + setAttribute(EnvConfiguration.JETTY_ENV_XML, this.getResourceFactory().newResource(getJettyEnvXml())); + } + } + + return configurations; + } + + @Override + public void doStop() throws Exception + { + if (_classpathUris != null) + _classpathUris.clear(); + _classpathUris = null; + + _classes = null; + _testClasses = null; + + if (_webInfJarMap != null) + _webInfJarMap.clear(); + + _webInfClasses.clear(); + _webInfJars.clear(); + + // CHECK setShutdown(true); + // just wait a little while to ensure no requests are still being processed + // TODO do better than a sleep + Thread.sleep(500L); + + super.doStop(); + + // remove all servlets and filters. This is because we will + // re-appy any context xml file, which means they would potentially be + // added multiple times. + getServletHandler().setFilters(new FilterHolder[0]); + getServletHandler().setFilterMappings(new FilterMapping[0]); + getServletHandler().setServlets(new ServletHolder[0]); + getServletHandler().setServletMappings(new ServletMapping[0]); + } + + @Override + public Resource getResource(String pathInContext) throws MalformedURLException + { + Resource resource; + // Try to get regular resource + resource = super.getResource(pathInContext); + + // If no regular resource exists check for access to /WEB-INF/lib or + // /WEB-INF/classes + if ((resource == null || !resource.exists()) && pathInContext != null && _classes != null) + { + // Normalize again to look for the resource inside /WEB-INF subdirectories. + String uri = URIUtil.normalizePath(pathInContext); + if (uri == null) + return null; + + // Replace /WEB-INF/classes with candidates for the classpath + if (uri.startsWith(WEB_INF_CLASSES_PREFIX)) + { + if (uri.equalsIgnoreCase(WEB_INF_CLASSES_PREFIX) || uri.equalsIgnoreCase(WEB_INF_CLASSES_PREFIX + "/")) + { + // exact match for a WEB-INF/classes, so preferentially + // return the resource matching the web-inf classes + // rather than the test classes + if (_classes != null) + return this.getResourceFactory().newResource(_classes.toPath()); + else if (_testClasses != null) + return this.getResourceFactory().newResource(_testClasses.toPath()); + } + else + { + // try matching + Resource res = null; + int i = 0; + while (Resources.missing(res) && (i < _webInfClasses.size())) + { + String newPath = StringUtil.replace(uri, WEB_INF_CLASSES_PREFIX, _webInfClasses.get(i).getPath()); + res = this.getResourceFactory().newResource(newPath); + if (Resources.missing(res)) + { + res = null; + i++; + } + } + return res; + } + } + else if (uri.startsWith(WEB_INF_LIB_PREFIX)) + { + // Return the real jar file for all accesses to + // /WEB-INF/lib/*.jar + String jarName = StringUtil.strip(uri, WEB_INF_LIB_PREFIX); + if (jarName.startsWith("/") || jarName.startsWith("\\")) + jarName = jarName.substring(1); + if (jarName.length() == 0) + return null; + File jarFile = _webInfJarMap.get(jarName); + if (jarFile != null) + return this.getResourceFactory().newResource(jarFile.getPath()); + + return null; + } + } + return resource; + } + + @Override + public Set getResourcePaths(String path) + { + // Try to get regular resource paths - this will get appropriate paths + // from any overlaid wars etc + Set paths = super.getResourcePaths(path); + + if (path != null) + { + TreeSet allPaths = new TreeSet<>(paths); + + // add in the dependency jars as a virtual WEB-INF/lib entry + if (path.startsWith(WEB_INF_LIB_PREFIX)) + { + for (String fileName : _webInfJarMap.keySet()) + { + // Return all jar files from class path + allPaths.add(WEB_INF_LIB_PREFIX + "/" + fileName); + } + } + else if (path.startsWith(WEB_INF_CLASSES_PREFIX)) + { + int i = 0; + + while (i < _webInfClasses.size()) + { + String newPath = StringUtil.replace(path, WEB_INF_CLASSES_PREFIX, _webInfClasses.get(i).getPath()); + allPaths.addAll(super.getResourcePaths(newPath)); + i++; + } + } + return allPaths; + } + return paths; + } + + public String addPattern(String s, String pattern) + { + if (s == null) + s = ""; + else + s = s.trim(); + + if (!s.contains(pattern)) + { + if (s.length() != 0) + s = s + "|"; + s = s + pattern; + } + + return s; + } + + public void initCDI() + { + Class cdiInitializer; + try + { + cdiInitializer = Thread.currentThread().getContextClassLoader().loadClass("org.eclipse.jetty.ee11.cdi.servlet.JettyWeldInitializer"); + Method initWebAppMethod = cdiInitializer.getMethod("initWebApp", WebAppContext.class); + initWebAppMethod.invoke(null, this); + } + catch (ClassNotFoundException e) + { + LOG.debug("o.e.j.cdi.servlet.JettyWeldInitializer not found, no cdi integration available"); + } + catch (NoSuchMethodException e) + { + LOG.warn("o.e.j.cdi.servlet.JettyWeldInitializer.initWebApp() not found, no cdi integration available"); + } + catch (Exception e) + { + LOG.warn("Problem initializing cdi", e); + } + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/MavenWebInfConfiguration.java b/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/MavenWebInfConfiguration.java new file mode 100644 index 00000000000..7b8fb55a29d --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/MavenWebInfConfiguration.java @@ -0,0 +1,68 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.maven.plugin; + +import java.net.URI; + +import org.eclipse.jetty.ee11.webapp.Configuration; +import org.eclipse.jetty.ee11.webapp.WebAppClassLoader; +import org.eclipse.jetty.ee11.webapp.WebAppContext; +import org.eclipse.jetty.ee11.webapp.WebInfConfiguration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * MavenWebInfConfiguration + *

+ * WebInfConfiguration to take account of overlaid wars expressed as project dependencies and + * potential configured via the maven-war-plugin. + */ +public class MavenWebInfConfiguration extends WebInfConfiguration +{ + private static final Logger LOG = LoggerFactory.getLogger(MavenWebInfConfiguration.class); + + public MavenWebInfConfiguration() + { + super(new Builder() + .hide("org.apache.maven.", + "org.codehaus.plexus.", + "jakarta.enterprise.", + "javax.decorator.")); + } + + @Override + public Class replaces() + { + return WebInfConfiguration.class; + } + + @Override + public void configure(WebAppContext context) throws Exception + { + MavenWebAppContext jwac = (MavenWebAppContext)context; + + //put the classes dir and all dependencies into the classpath + if (jwac.getClassPathUris() != null && context.getClassLoader() instanceof WebAppClassLoader loader) + { + if (LOG.isDebugEnabled()) + LOG.debug("Setting up classpath ..."); + for (URI uri : jwac.getClassPathUris()) + { + loader.addClassPath(uri.toASCIIString()); + } + } + + super.configure(context); + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/QuickStartGenerator.java b/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/QuickStartGenerator.java new file mode 100644 index 00000000000..5b1b406e09f --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/QuickStartGenerator.java @@ -0,0 +1,187 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.maven.plugin; + +import java.nio.file.Path; + +import org.eclipse.jetty.ee11.annotations.AnnotationConfiguration; +import org.eclipse.jetty.ee11.quickstart.QuickStartConfiguration; +import org.eclipse.jetty.ee11.quickstart.QuickStartConfiguration.Mode; +import org.eclipse.jetty.ee11.webapp.Configurations; +import org.eclipse.jetty.maven.ServerSupport; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.thread.QueuedThreadPool; + +/** + * Run enough of jetty in order to generate a quickstart file for a + * webapp. Optionally, some essential elements of the WebAppContext + * configuration can also be converted to properties and saved to + * a file after the quickstart generation. + * + */ +public class QuickStartGenerator +{ + private final Path quickstartXml; + private final MavenWebAppContext webApp; + private Path webAppProps; + private String contextXml; + private boolean prepared = false; + private Server server; + private QueuedThreadPool tpool; + + /** + * @param quickstartXml the file to generate quickstart into + * @param webApp the webapp for which to generate quickstart + */ + public QuickStartGenerator(Path quickstartXml, MavenWebAppContext webApp) + { + this.quickstartXml = quickstartXml; + this.webApp = webApp == null ? new MavenWebAppContext() : webApp; + } + + /** + * Get the webApp. + * @return the webApp + */ + public MavenWebAppContext getWebApp() + { + return webApp; + } + + /** + * Get the quickstartXml. + * @return the quickstartXml + */ + public Path getQuickstartXml() + { + return quickstartXml; + } + + /** + * Get the server. + * @return the server + */ + public Server getServer() + { + return server; + } + + /** + * Set the server to use. + * @param server the server to use + */ + public void setServer(Server server) + { + this.server = server; + } + + public Path getWebAppProps() + { + return webAppProps; + } + + /** + * Set properties file describing the webapp. + * @param webAppProps properties file describing the webapp + */ + public void setWebAppProps(Path webAppProps) + { + this.webAppProps = webAppProps; + } + + public String getContextXml() + { + return contextXml; + } + + /** + * Set a context xml file to apply to the webapp. + * @param contextXml a context xml file to apply to the webapp + */ + public void setContextXml(String contextXml) + { + this.contextXml = contextXml; + } + + /** + * Configure the webapp in preparation for quickstart generation. + */ + private void prepareWebApp() + { + //set the webapp up to do very little other than generate the quickstart-web.xml + webApp.addConfiguration(new MavenQuickStartConfiguration()); + webApp.setAttribute(QuickStartConfiguration.MODE, Mode.GENERATE); + webApp.setAttribute(QuickStartConfiguration.QUICKSTART_WEB_XML, quickstartXml); + webApp.setAttribute(QuickStartConfiguration.ORIGIN_ATTRIBUTE, "o"); + webApp.setCopyWebDir(false); + webApp.setCopyWebInf(false); + } + + /** + * Run enough of jetty to generate a full quickstart xml file for the + * webapp. The tmp directory is persisted. + * + * @throws Exception if there is an unspecified problem + */ + public void generate() throws Exception + { + if (quickstartXml == null) + throw new IllegalStateException("No quickstart xml output file"); + + if (!prepared) + { + prepared = true; + prepareWebApp(); + + if (server == null) + server = new Server(); + + //ensure handler structure enabled + ServerSupport.configureHandlers(server, null, null); + + Configurations.setServerDefault(server); + + //if our server has a thread pool associated we can do annotation scanning multithreaded, + //otherwise scanning will be single threaded + if (tpool == null) + tpool = server.getBean(QueuedThreadPool.class); + + //add webapp to our fake server instance + ServerSupport.addWebApplication(server, webApp); + + //leave everything unpacked for the forked process to use + webApp.setTempDirectoryPersistent(true); + } + + try + { + if (tpool != null) + tpool.start(); + else + webApp.setAttribute(AnnotationConfiguration.MULTI_THREADED, Boolean.FALSE.toString()); + + webApp.start(); //just enough to generate the quickstart + + //save config of the webapp BEFORE we stop + if (webAppProps != null) + WebAppPropertyConverter.toProperties(webApp, webAppProps.toFile(), contextXml); + } + finally + { + webApp.stop(); + if (tpool != null) + tpool.stop(); + } + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/WebAppPropertyConverter.java b/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/WebAppPropertyConverter.java new file mode 100644 index 00000000000..94a5088f914 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/WebAppPropertyConverter.java @@ -0,0 +1,342 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.maven.plugin; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.stream.Collectors; + +import org.eclipse.jetty.ee11.quickstart.QuickStartConfiguration; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.StringUtil; +import org.eclipse.jetty.util.resource.CombinedResource; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.util.resource.ResourceFactory; +import org.eclipse.jetty.xml.XmlConfiguration; + +/** + * WebAppPropertyConverter + * + * Converts a webapp's configuration to a properties file, and + * vice versa. + */ +public class WebAppPropertyConverter +{ + public static String WEB_XML = "web.xml"; + public static String QUICKSTART_WEB_XML = "quickstart.web.xml"; + public static String CONTEXT_XML = "context.xml"; + public static String CONTEXT_PATH = "context.path"; + public static String TMP_DIR = "tmp.dir"; + public static String TMP_DIR_PERSIST = "tmp.dir.persist"; + public static String BASE_DIRS = "base.dirs"; + public static String WAR_FILE = "war.file"; + public static String CLASSES_DIR = "classes.dir"; + public static String TEST_CLASSES_DIR = "testClasses.dir"; + public static String LIB_JARS = "lib.jars"; + public static String DEFAULTS_DESCRIPTOR = "web.default.xml"; + public static String OVERRIDE_DESCRIPTORS = "web.overrides.xml"; + + //TODO :Support defaults descriptor! + + /** + * Convert a webapp to properties stored in a file. + * + * @param webApp the webapp to convert + * @param propsFile the file to put the properties into + * @param contextXml the optional context xml file related to the webApp + * @throws IOException if any I/O exception occurs + */ + public static void toProperties(MavenWebAppContext webApp, File propsFile, String contextXml) + throws IOException + { + if (webApp == null) + throw new IllegalArgumentException("No webapp"); + if (propsFile == null) + throw new IllegalArgumentException("No properties file"); + + //work out the configuration based on what is configured in the pom + if (propsFile.exists()) + propsFile.delete(); + + propsFile.createNewFile(); + + Properties props = new Properties(); + //web.xml + if (webApp.getDescriptor() != null) + { + props.put(WEB_XML, webApp.getDescriptor()); + } + + Object tmp = webApp.getAttribute(QuickStartConfiguration.QUICKSTART_WEB_XML); + if (tmp != null) + { + props.put(QUICKSTART_WEB_XML, tmp.toString()); + } + + //sort out the context path + if (webApp.getContextPath() != null) + { + props.put(CONTEXT_PATH, webApp.getContextPath()); + } + + //tmp dir + props.put(TMP_DIR, webApp.getTempDirectory().getAbsolutePath()); + //props.put("tmp.dir.persist", Boolean.toString(originalPersistTemp)); + props.put(TMP_DIR_PERSIST, Boolean.toString(webApp.isTempDirectoryPersistent())); + + //send over the calculated resource bases that includes unpacked overlays + Resource baseResource = webApp.getBaseResource(); + if (baseResource instanceof CombinedResource) + props.put(BASE_DIRS, toCSV(((CombinedResource)webApp.getBaseResource()).getResources())); + else if (baseResource instanceof Resource) + props.put(BASE_DIRS, webApp.getBaseResource().toString()); + + //if there is a war file, use that + if (webApp.getWar() != null) + props.put(WAR_FILE, webApp.getWar()); + + //web-inf classes + if (webApp.getClasses() != null) + { + props.put(CLASSES_DIR, webApp.getClasses().getAbsolutePath()); + } + + if (webApp.getTestClasses() != null) + { + props.put(TEST_CLASSES_DIR, webApp.getTestClasses().getAbsolutePath()); + } + + //web-inf lib + List deps = webApp.getWebInfLib(); + StringBuilder strbuff = new StringBuilder(); + if (deps != null) + { + for (int i = 0; i < deps.size(); i++) + { + File d = deps.get(i); + strbuff.append(d.getAbsolutePath()); + if (i < deps.size() - 1) + strbuff.append(","); + } + } + props.put(LIB_JARS, strbuff.toString()); + + //context xml to apply + if (contextXml != null) + props.put(CONTEXT_XML, contextXml); + + if (webApp.getDefaultsDescriptor() != null) + props.put(DEFAULTS_DESCRIPTOR, webApp.getDefaultsDescriptor()); + + if (webApp.getOverrideDescriptors() != null) + { + props.put(OVERRIDE_DESCRIPTORS, String.join(",", webApp.getOverrideDescriptors())); + } + + try (BufferedWriter out = Files.newBufferedWriter(propsFile.toPath())) + { + props.store(out, "properties for webapp"); + } + } + + /** + * Configure a webapp from a properties file. + * + * @param webApp the webapp to configure + * @param resource the properties file to apply + * @param server the Server instance to use + * @param jettyProperties jetty properties to use if there is a context xml file to apply + * @throws Exception if there is an unspecified problem + */ + public static void fromProperties(MavenWebAppContext webApp, String resource, Server server, Map jettyProperties) + throws Exception + { + if (resource == null) + throw new IllegalStateException("No resource"); + + fromProperties(webApp, webApp.getResourceFactory().newResource(resource).getPath(), server, jettyProperties); + } + + /** + * Configure a webapp from properties. + * + * @param webApp the webapp to configure + * @param webAppProperties properties that describe the configuration of the webapp + * @param server the jetty Server instance + * @param jettyProperties jetty properties + * + * @throws Exception if there is an unspecified problem + */ + public static void fromProperties(MavenWebAppContext webApp, Properties webAppProperties, Server server, Map jettyProperties) + throws Exception + { + if (webApp == null) + throw new IllegalArgumentException("No webapp"); + + if (webAppProperties == null) + return; + + String str = webAppProperties.getProperty(CONTEXT_PATH); + if (!StringUtil.isBlank(str)) + webApp.setContextPath(str); + + // - web.xml + str = webAppProperties.getProperty(WEB_XML); + if (!StringUtil.isBlank(str)) + webApp.setDescriptor(str); + + //if there is a pregenerated quickstart file + str = webAppProperties.getProperty(QUICKSTART_WEB_XML); + if (!StringUtil.isBlank(str)) + { + webApp.setAttribute(QuickStartConfiguration.QUICKSTART_WEB_XML, webApp.getResourceFactory().newResource(str)); + } + + // - the tmp directory + str = webAppProperties.getProperty(TMP_DIR); + if (!StringUtil.isBlank(str)) + webApp.setTempDirectory(new File(str.trim())); + + str = webAppProperties.getProperty(TMP_DIR_PERSIST); + if (!StringUtil.isBlank(str)) + webApp.setTempDirectoryPersistent(Boolean.valueOf(str)); + + //Get the calculated base dirs which includes the overlays + str = webAppProperties.getProperty(BASE_DIRS); + if (!StringUtil.isBlank(str)) + { + // This is a use provided list of overlays, which could have mountable entries. + webApp.setWar(null); + webApp.setBaseResource(ResourceFactory.combine(webApp.getResourceFactory().split(str))); + } + + str = webAppProperties.getProperty(WAR_FILE); + if (!StringUtil.isBlank(str)) + { + webApp.setWar(str); + } + + // - the equivalent of web-inf classes + str = webAppProperties.getProperty(CLASSES_DIR); + if (!StringUtil.isBlank(str)) + { + webApp.setClasses(new File(str)); + } + + str = webAppProperties.getProperty(TEST_CLASSES_DIR); + if (!StringUtil.isBlank(str)) + { + webApp.setTestClasses(new File(str)); + } + + // - the equivalent of web-inf lib + str = webAppProperties.getProperty(LIB_JARS); + if (!StringUtil.isBlank(str)) + { + List jars = new ArrayList(); + String[] names = StringUtil.csvSplit(str); + for (int j = 0; names != null && j < names.length; j++) + { + jars.add(new File(names[j].trim())); + } + webApp.setWebInfLib(jars); + } + + //any defaults descriptor + str = (String)webAppProperties.getProperty(DEFAULTS_DESCRIPTOR); + if (!StringUtil.isBlank(str)) + { + webApp.setDefaultsDescriptor(str); + } + + //any override descriptors + str = (String)webAppProperties.getProperty(OVERRIDE_DESCRIPTORS); + if (!StringUtil.isBlank(str)) + { + String[] names = StringUtil.csvSplit(str); + for (int j = 0; names != null && j < names.length; j++) + { + webApp.addOverrideDescriptor(names[j]); + } + } + + //set up the webapp from the context xml file provided + //NOTE: just like jetty:run mojo this means that the context file can + //potentially override settings made in the pom. Ideally, we'd like + //the pom to override the context xml file, but as the other mojos all + //configure a WebAppContext in the pom (the element), it is + //already configured by the time the context xml file is applied. + str = (String)webAppProperties.getProperty(CONTEXT_XML); + if (!StringUtil.isBlank(str)) + { + XmlConfiguration xmlConfiguration = new XmlConfiguration(webApp.getResourceFactory().newResource(str)); + xmlConfiguration.getIdMap().put("Server", server); + //add in any properties + if (jettyProperties != null) + { + for (Map.Entry prop : jettyProperties.entrySet()) + { + xmlConfiguration.getProperties().put(prop.getKey(), prop.getValue()); + } + } + xmlConfiguration.configure(webApp); + } + } + + /** + * Configure a webapp from a properties file + * @param webApp the webapp to configure + * @param propsFile the properties to apply + * @param server the Server instance to use if there is a context xml file to apply + * @param jettyProperties jetty properties to use if there is a context xml file to apply + * @throws Exception if there is an unspecified problem + */ + public static void fromProperties(MavenWebAppContext webApp, Path propsFile, Server server, Map jettyProperties) + throws Exception + { + + if (propsFile == null) + throw new IllegalArgumentException("No properties file"); + + if (!Files.exists(propsFile)) + throw new IllegalArgumentException(propsFile + " does not exist"); + + Properties props = new Properties(); + try (InputStream in = Files.newInputStream(propsFile)) + { + props.load(in); + } + + fromProperties(webApp, props, server, jettyProperties); + } + + /** + * Convert an array of Resources to csv file names + * + * @param resources the resources to convert + * @return csv string of resource filenames + */ + private static String toCSV(List resources) + { + return resources.stream().map(Object::toString).collect(Collectors.joining(",")); + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/package-info.java b/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/package-info.java new file mode 100644 index 00000000000..debc817b63c --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/main/java/org/eclipse/jetty/ee11/maven/plugin/package-info.java @@ -0,0 +1,18 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +/** + * Jetty Maven Plugin : Support for Jetty in Maven build lifecycle + */ +package org.eclipse.jetty.ee11.maven.plugin; + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/main/resources/META-INF/services/org.eclipse.jetty.ee11.webapp.Configuration b/jetty-ee11/jetty-ee11-maven-plugin/src/main/resources/META-INF/services/org.eclipse.jetty.ee11.webapp.Configuration new file mode 100644 index 00000000000..dbe7f8cfec5 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/main/resources/META-INF/services/org.eclipse.jetty.ee11.webapp.Configuration @@ -0,0 +1,4 @@ +org.eclipse.jetty.ee11.maven.plugin.MavenWebInfConfiguration +org.eclipse.jetty.ee11.maven.plugin.MavenMetaInfConfiguration +#Do not list this because Quickstart is not present for distro +#org.eclipse.jetty.maven.plugin.MavenQuickStartConfiguration diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/main/resources/ee11-maven.mod b/jetty-ee11/jetty-ee11-maven-plugin/src/main/resources/ee11-maven.mod new file mode 100644 index 00000000000..9931f8669a6 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/main/resources/ee11-maven.mod @@ -0,0 +1,18 @@ +# DO NOT EDIT THIS FILE - See: https://eclipse.dev/jetty/documentation/ + +[description] +Enables an un-assembled Maven webapp to run in a Jetty distribution. + +[environment] +ee11 + +[depends] +server +ee11-webapp +ee11-annotations + +[lib] +lib/ee11-maven/*.jar + +[xml] +etc/jetty-ee11-maven.xml diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/main/resources/jetty-ee11-maven.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/main/resources/jetty-ee11-maven.xml new file mode 100644 index 00000000000..b83be87a647 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/main/resources/jetty-ee11-maven.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/main/resources/maven-ee11.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/main/resources/maven-ee11.xml new file mode 100644 index 00000000000..ef2f071ec1a --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/main/resources/maven-ee11.xml @@ -0,0 +1,17 @@ + + + + + + + + + + /etc/maven.props + + + + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/test/java/org/eclipse/jetty/ee11/maven/plugin/MockShutdownMonitor.java b/jetty-ee11/jetty-ee11-maven-plugin/src/test/java/org/eclipse/jetty/ee11/maven/plugin/MockShutdownMonitor.java new file mode 100644 index 00000000000..7f0f9490d8b --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/test/java/org/eclipse/jetty/ee11/maven/plugin/MockShutdownMonitor.java @@ -0,0 +1,74 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.maven.plugin; + +import java.net.ServerSocket; + +import org.eclipse.jetty.toolchain.test.IO; + +/** + * MockShutdownMonitor + * A helper class that grabs a ServerSocket, spawns a thread and then + * passes the ServerSocket to the Runnable. This class has a main so + * that it can be used for forking, to mimic the actions of the + * org.eclipse.jetty.server.ShutdownMonitor. + */ +public class MockShutdownMonitor +{ + String key; + MockShutdownMonitorRunnable testerRunnable; + ServerSocket serverSocket; + + public MockShutdownMonitor(String key, MockShutdownMonitorRunnable testerRunnable) + throws Exception + { + this.key = key; + this.testerRunnable = testerRunnable; + listen(); + } + + private ServerSocket listen() + throws Exception + { + serverSocket = new ServerSocket(0); + try + { + serverSocket.setReuseAddress(true); + return serverSocket; + } + catch (Throwable e) + { + IO.close(serverSocket); + throw e; + } + } + + public int getPort() + { + if (serverSocket == null) + return 0; + return serverSocket.getLocalPort(); + } + + public void start() + throws Exception + { + testerRunnable.setServerSocket(serverSocket); + testerRunnable.setKey(key); + Thread thread = new Thread(testerRunnable); + thread.setDaemon(true); + thread.setName("Tester Thread"); + thread.start(); + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/test/java/org/eclipse/jetty/ee11/maven/plugin/MockShutdownMonitorRunnable.java b/jetty-ee11/jetty-ee11-maven-plugin/src/test/java/org/eclipse/jetty/ee11/maven/plugin/MockShutdownMonitorRunnable.java new file mode 100644 index 00000000000..c8d6bebf76d --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/test/java/org/eclipse/jetty/ee11/maven/plugin/MockShutdownMonitorRunnable.java @@ -0,0 +1,111 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.maven.plugin; + +import java.io.InputStreamReader; +import java.io.LineNumberReader; +import java.io.OutputStream; +import java.net.ServerSocket; +import java.net.Socket; +import java.nio.charset.StandardCharsets; + +import org.eclipse.jetty.toolchain.test.IO; + +/** + * MockShutdownMonitorRunnable + * + * Mimics the actions of the org.eclipse.jetty.server.ShutdownMonitor.ShutdownMonitorRunnable + * to aid testing. + */ +public class MockShutdownMonitorRunnable implements Runnable +{ + ServerSocket serverSocket; + String key; + String statusResponse = "OK"; + String pidResponse; + String defaultResponse = "Stopped"; + boolean exit; + + public void setExit(boolean exit) + { + this.exit = exit; + } + + public void setKey(String key) + { + this.key = key; + } + + public void setServerSocket(ServerSocket serverSocket) + { + this.serverSocket = serverSocket; + } + + public void setPidResponse(String pidResponse) + { + this.pidResponse = pidResponse; + } + + public void run() + { + try + { + while (true) + { + try (Socket socket = serverSocket.accept()) + { + LineNumberReader reader = new LineNumberReader(new InputStreamReader(socket.getInputStream())); + String receivedKey = reader.readLine(); + if (!key.equals(receivedKey)) + { + continue; + } + + String cmd = reader.readLine(); + OutputStream out = socket.getOutputStream(); + + if ("status".equalsIgnoreCase(cmd)) + { + out.write((statusResponse + "\r\n").getBytes(StandardCharsets.UTF_8)); + out.flush(); + } + else if ("pid".equalsIgnoreCase(cmd)) + { + out.write((pidResponse + "\r\n").getBytes(StandardCharsets.UTF_8)); + out.flush(); + } + else + { + out.write((defaultResponse + "\r\n").getBytes(StandardCharsets.UTF_8)); + out.flush(); + if (exit) + System.exit(0); + } + } + catch (Throwable x) + { + x.printStackTrace(); + } + } + } + catch (Throwable x) + { + x.printStackTrace(); + } + finally + { + IO.close(serverSocket); + } + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/test/java/org/eclipse/jetty/ee11/maven/plugin/SomeListener.java b/jetty-ee11/jetty-ee11-maven-plugin/src/test/java/org/eclipse/jetty/ee11/maven/plugin/SomeListener.java new file mode 100644 index 00000000000..80ee3b06270 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/test/java/org/eclipse/jetty/ee11/maven/plugin/SomeListener.java @@ -0,0 +1,21 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.maven.plugin; + +import java.util.EventListener; + +public class SomeListener implements EventListener +{ + +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/test/java/org/eclipse/jetty/ee11/maven/plugin/TestForkedChild.java b/jetty-ee11/jetty-ee11-maven-plugin/src/test/java/org/eclipse/jetty/ee11/maven/plugin/TestForkedChild.java new file mode 100644 index 00000000000..5510f1b2fba --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/test/java/org/eclipse/jetty/ee11/maven/plugin/TestForkedChild.java @@ -0,0 +1,176 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.maven.plugin; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.InputStreamReader; +import java.io.LineNumberReader; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.InetAddress; +import java.net.Socket; +import java.net.URL; +import java.nio.file.Path; +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Random; + +import org.awaitility.Awaitility; +import org.eclipse.jetty.toolchain.test.FS; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; +import org.eclipse.jetty.util.IO; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +/** + * Test the JettyForkedChild class, which + * is the main that is executed by jetty:run/start in mode FORKED. + */ +@ExtendWith(WorkDirExtension.class) +public class TestForkedChild +{ + File testDir; + File baseDir; + Path tmpDir; + File tokenFile; + File webappPropsFile; + int stopPort; + String stopKey = "FERMATI"; + String jettyPortString; + int jettyPort; + String token; + JettyForkedChild child; + Thread starter; + JettyRunner runner = new JettyRunner(); + + public class JettyRunner implements Runnable + { + @Override + public void run() + { + try + { + List cmd = new ArrayList<>(); + cmd.add("--stop-port"); + cmd.add(String.valueOf(stopPort)); + cmd.add("--stop-key"); + cmd.add(stopKey); + cmd.add("--webprops"); + cmd.add(webappPropsFile.getAbsolutePath()); + cmd.add("--token"); + cmd.add(tokenFile.getAbsolutePath()); + + MavenWebAppContext webapp = new MavenWebAppContext(); + webapp.setContextPath("/foo"); + webapp.setTempDirectory(tmpDir.toFile()); + webapp.setBaseResourceAsPath(baseDir.toPath()); + WebAppPropertyConverter.toProperties(webapp, webappPropsFile, null); + child = new JettyForkedChild(cmd.toArray(new String[0])); + child.getJettyEmbedder().setExitVm(false); //ensure jetty doesn't stop vm for testing + child.start(); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + } + } + + @BeforeEach + public void setUp(WorkDir workDir) + { + tmpDir = workDir.getEmptyPathDir(); + baseDir = MavenTestingUtils.getTestResourceDir("root"); + testDir = MavenTestingUtils.getTargetTestingDir("forkedChild"); + FS.ensureEmpty(testDir); + webappPropsFile = new File(tmpDir.toFile(), "webapp.props"); + + String stopPortString = System.getProperty("stop.port"); + assertNotNull(stopPortString, "stop.port System property"); + stopPort = Integer.parseInt(stopPortString); + jettyPortString = System.getProperty("jetty.port"); + assertNotNull(jettyPortString, "jetty.port System property"); + jettyPort = Integer.parseInt(jettyPortString); + + Random random = new Random(); + token = Long.toString(random.nextLong() ^ System.currentTimeMillis(), 36).toUpperCase(Locale.ENGLISH); + tokenFile = tmpDir.resolve(token + ".txt").toFile(); + } + + @AfterEach + public void tearDown() throws Exception + { + String command = "forcestop"; + + try (Socket s = new Socket(InetAddress.getByName("127.0.0.1"), stopPort)) + { + OutputStream out = s.getOutputStream(); + out.write((stopKey + "\r\n" + command + "\r\n").getBytes()); + out.flush(); + + s.setSoTimeout(1000); + s.getInputStream(); + + LineNumberReader lin = new LineNumberReader(new InputStreamReader(s.getInputStream())); + String response; + boolean stopped = false; + while (!stopped && ((response = lin.readLine()) != null)) + { + if ("Stopped".equals(response)) + { + stopped = true; + } + } + } + } + + @Test + public void test() throws Exception + { + starter = new Thread(runner, "JettyForkedChild"); + starter.start(); + + //wait for the token file to be created + Awaitility.waitAtMost(Duration.ofSeconds(10)).until(tokenFile::exists); + + URL url = new URL("http://localhost:" + jettyPortString + "/foo/"); + HttpURLConnection connection = null; + + try + { + connection = (HttpURLConnection)url.openConnection(); + connection.connect(); + assertThat(connection.getResponseCode(), Matchers.is(200)); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + IO.copy(connection.getInputStream(), baos); + assertThat(baos.toString(), Matchers.containsString("ROOT")); + } + finally + { + if (connection != null) + connection.disconnect(); + } + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/test/java/org/eclipse/jetty/ee11/maven/plugin/TestJettyEmbedder.java b/jetty-ee11/jetty-ee11-maven-plugin/src/test/java/org/eclipse/jetty/ee11/maven/plugin/TestJettyEmbedder.java new file mode 100644 index 00000000000..249e7b9ce5f --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/test/java/org/eclipse/jetty/ee11/maven/plugin/TestJettyEmbedder.java @@ -0,0 +1,157 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.maven.plugin; + +import java.nio.file.Path; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.jetty.ee11.servlet.ListenerHolder; +import org.eclipse.jetty.maven.MavenServerConnector; +import org.eclipse.jetty.maven.ServerSupport; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.server.handler.ContextHandlerCollection; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +@ExtendWith(WorkDirExtension.class) +public class TestJettyEmbedder +{ + @Test + public void testJettyEmbedderFromDefaults(WorkDir workDir) throws Exception + { + Path basePath = workDir.getEmptyPathDir(); + MavenWebAppContext webApp = new MavenWebAppContext(); + webApp.setBaseResourceAsPath(basePath); + MavenServerConnector connector = new MavenServerConnector(); + connector.setPort(0); + + JettyEmbedder jetty = new JettyEmbedder(); + jetty.setHttpConnector(connector); + jetty.setExitVm(false); + jetty.setServer(null); + jetty.setContextHandlers(null); + jetty.setRequestLog(null); + jetty.setJettyXmlFiles(null); + jetty.setJettyProperties(null); + jetty.setLoginServices(null); + jetty.setContextXml(MavenTestingUtils.getTestResourceFile("embedder-context.xml").getAbsolutePath()); + jetty.setWebApp(webApp); + + try + { + jetty.start(); + assertEquals("/embedder", webApp.getContextPath()); + assertTrue(webApp.isAvailable()); + assertNotNull(jetty.getServer()); + assertTrue(jetty.getServer().isStarted()); + assertNotNull(jetty.getServer().getConnectors()); + assertNotNull(ServerSupport.findContextHandlerCollection(jetty.getServer())); + } + finally + { + jetty.stop(); + } + } + + @Test + public void testJettyEmbedder(WorkDir workDir) + throws Exception + { + Path basePath = workDir.getPath(); + MavenWebAppContext webApp = new MavenWebAppContext(); + webApp.setBaseResourceAsPath(basePath); + Server server = new Server(); + Map jettyProperties = new HashMap<>(); + jettyProperties.put("jetty.server.dumpAfterStart", "false"); + + ContextHandler otherHandler = new ContextHandler(); + otherHandler.setContextPath("/other"); + otherHandler.setBaseResourceAsPath(MavenTestingUtils.getTestResourcePathDir("root")); + + MavenServerConnector connector = new MavenServerConnector(); + connector.setPort(0); + + JettyEmbedder jetty = new JettyEmbedder(); + jetty.setHttpConnector(connector); + jetty.setExitVm(false); + jetty.setServer(server); + jetty.setContextHandlers(List.of(otherHandler)); + jetty.setRequestLog(null); + jetty.setJettyXmlFiles(List.of(MavenTestingUtils.getTestResourceFile("embedder-jetty.xml"))); + jetty.setJettyProperties(jettyProperties); + jetty.setLoginServices(null); + jetty.setContextXml(MavenTestingUtils.getTestResourceFile("embedder-context.xml").getAbsolutePath()); + jetty.setWebApp(webApp); + + try + { + jetty.start(); + assertEquals("/embedder", webApp.getContextPath()); + assertTrue(webApp.isAvailable()); + assertNotNull(jetty.getServer()); + assertTrue(jetty.getServer().isStarted()); + assertNotNull(jetty.getServer().getConnectors()); + ContextHandlerCollection contexts = ServerSupport.findContextHandlerCollection(jetty.getServer()); + assertNotNull(contexts); + assertTrue(contexts.contains(otherHandler)); + assertTrue(contexts.contains(webApp)); + + //stop the webapp and check durable listener retained + jetty.stopWebApp(); + boolean someListener = false; + for (ListenerHolder h : webApp.getServletHandler().getListeners()) + { + if (h.getHeldClass() != null && "org.eclipse.jetty.ee11.maven.plugin.SomeListener".equalsIgnoreCase(h.getHeldClass().getName())) + { + if (someListener) + fail("Duplicate listeners"); + else + someListener = true; + } + } + + + //restart the webapp + jetty.redeployWebApp(); + someListener = false; + + //ensure still only 1 listener + for (ListenerHolder h : webApp.getServletHandler().getListeners()) + { + if (h.getHeldClass() != null && "org.eclipse.jetty.ee11.maven.plugin.SomeListener".equalsIgnoreCase(h.getHeldClass().getName())) + { + if (someListener) + fail("Duplicate listeners"); + else + someListener = true; + } + } + } + finally + { + jetty.stop(); + } + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/test/java/org/eclipse/jetty/ee11/maven/plugin/TestJettyStopMojo.java b/jetty-ee11/jetty-ee11-maven-plugin/src/test/java/org/eclipse/jetty/ee11/maven/plugin/TestJettyStopMojo.java new file mode 100644 index 00000000000..3deacd5d59f --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/test/java/org/eclipse/jetty/ee11/maven/plugin/TestJettyStopMojo.java @@ -0,0 +1,299 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.maven.plugin; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import org.awaitility.Awaitility; +import org.eclipse.jetty.server.ShutdownMonitor; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +public class TestJettyStopMojo +{ + /** + * ShutdownMonitorMain + * Kick off the ShutdownMonitor and wait for it to exit. + */ + public static final class ShutdownMonitorMain + { + public static void main(String[] args) + { + try + { + ShutdownMonitor monitor = ShutdownMonitor.getInstance(); + monitor.setPort(0); + monitor.start(); + monitor.await(); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + } + + public static class TestLog implements org.apache.maven.plugin.logging.Log + { + List sink = new ArrayList<>(); + + @Override + public boolean isDebugEnabled() + { + return true; + } + + @Override + public void debug(CharSequence content) + { + sink.add(content.toString()); + } + + @Override + public void debug(CharSequence content, Throwable error) + { + sink.add(content.toString()); + } + + @Override + public void debug(Throwable error) + { + } + + @Override + public boolean isInfoEnabled() + { + return true; + } + + @Override + public void info(CharSequence content) + { + sink.add(content.toString()); + } + + @Override + public void info(CharSequence content, Throwable error) + { + sink.add(content.toString()); + } + + @Override + public void info(Throwable error) + { + } + + @Override + public boolean isWarnEnabled() + { + return true; + } + + @Override + public void warn(CharSequence content) + { + sink.add(content.toString()); + } + + @Override + public void warn(CharSequence content, Throwable error) + { + sink.add(content.toString()); + } + + @Override + public void warn(Throwable error) + { + } + + @Override + public boolean isErrorEnabled() + { + return true; + } + + @Override + public void error(CharSequence content) + { + sink.add(content.toString()); + } + + @Override + public void error(CharSequence content, Throwable error) + { + sink.add(content.toString()); + } + + @Override + public void error(Throwable error) + { + } + + public void assertContains(String str) + { + assertThat(sink, Matchers.hasItem(str)); + } + + public void dumpStdErr() + { + for (String s : sink) + { + System.err.println(s); + } + } + } + + @Test + public void testStopNoWait() throws Exception + { + //send a stop message and don't wait for the reply or the process to shutdown + String stopKey = "foo"; + MockShutdownMonitorRunnable runnable = new MockShutdownMonitorRunnable(); + runnable.setPidResponse("abcd"); + MockShutdownMonitor monitor = new MockShutdownMonitor(stopKey, runnable); + monitor.start(); + + TestLog log = new TestLog(); + JettyStopMojo mojo = new JettyStopMojo(); + mojo.stopKey = stopKey; + mojo.stopPort = monitor.getPort(); + mojo.setLog(log); + + mojo.execute(); + + log.assertContains("Stopping jetty"); + } + + @Test + public void testStopWaitBadPid() throws Exception + { + //test that even if we receive a bad pid, we still send the stop command and wait to + //receive acknowledgement, but we don't wait for the process to exit + String stopKey = "foo"; + MockShutdownMonitorRunnable runnable = new MockShutdownMonitorRunnable(); + runnable.setPidResponse("abcd"); + MockShutdownMonitor monitor = new MockShutdownMonitor(stopKey, runnable); + monitor.start(); + + TestLog log = new TestLog(); + JettyStopMojo mojo = new JettyStopMojo(); + mojo.stopWait = 5; + mojo.stopKey = stopKey; + mojo.stopPort = monitor.getPort(); + mojo.setLog(log); + + mojo.execute(); + + log.assertContains("Server returned bad pid"); + log.assertContains("Server reports itself as stopped"); + } + + @Test + public void testStopSameProcess() throws Exception + { + //test that if we need to stop a jetty in the same process as us + //we will wait for it to exit + String stopKey = "foo"; + long thisPid = ProcessHandle.current().pid(); + MockShutdownMonitorRunnable runnable = new MockShutdownMonitorRunnable(); + runnable.setPidResponse(Long.toString(thisPid)); + MockShutdownMonitor monitor = new MockShutdownMonitor(stopKey, runnable); + monitor.start(); + + TestLog log = new TestLog(); + JettyStopMojo mojo = new JettyStopMojo(); + mojo.stopWait = 5; + mojo.stopKey = stopKey; + mojo.stopPort = monitor.getPort(); + mojo.setLog(log); + + mojo.execute(); + + log.assertContains("Waiting 5 seconds for jetty " + thisPid + " to stop"); + } + + @Test + public void testStopWait() throws Exception + { + //test that we will communicate with a remote process and wait for it to exit + String stopKey = "foo"; + List cmd = new ArrayList<>(); + String java = "java"; + String[] javaexes = new String[]{"java", "java.exe"}; + File javaHomeDir = new File(System.getProperty("java.home")); + Path javaHomePath = javaHomeDir.toPath(); + for (String javaexe : javaexes) + { + Path javaBinPath = javaHomePath.resolve(Paths.get("bin", javaexe)); + if (Files.exists(javaBinPath) && !Files.isDirectory(javaBinPath)) + java = javaBinPath.toFile().getAbsolutePath(); + } + + cmd.add(java); + cmd.add("-DSTOP.KEY=" + stopKey); + cmd.add("-DDEBUG=true"); + cmd.add("-cp"); + cmd.add(System.getProperty("java.class.path")); + cmd.add(ShutdownMonitorMain.class.getName()); + + ProcessBuilder command = new ProcessBuilder(cmd); + File file = MavenTestingUtils.getTargetFile("tester.out"); + command.redirectOutput(file); + command.redirectErrorStream(true); + command.directory(MavenTestingUtils.getTargetDir()); + Process fork = command.start(); + + Awaitility.await().atMost(Duration.ofSeconds(5)).until(file::exists); + final String[] port = {null}; + Awaitility.await().atMost(Duration.ofSeconds(5)).until(() -> + { + Optional tmp = Files.readAllLines(file.toPath()).stream() + .filter(s -> s.startsWith("STOP.PORT=")).findFirst(); + if (tmp.isPresent()) + { + // TODO validate it's an integer + port[0] = tmp.get().substring(10); + return true; + } + return false; + + }); + + assertNotNull(port[0]); + + TestLog log = new TestLog(); + JettyStopMojo mojo = new JettyStopMojo(); + mojo.stopWait = 5; + mojo.stopKey = stopKey; + mojo.stopPort = Integer.parseInt(port[0]); + mojo.setLog(log); + + mojo.execute(); + + log.dumpStdErr(); + log.assertContains("Waiting " + mojo.stopWait + " seconds for jetty " + fork.pid() + " to stop"); + log.assertContains("Server process stopped"); + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/test/java/org/eclipse/jetty/ee11/maven/plugin/TestQuickStartGenerator.java b/jetty-ee11/jetty-ee11-maven-plugin/src/test/java/org/eclipse/jetty/ee11/maven/plugin/TestQuickStartGenerator.java new file mode 100644 index 00000000000..045307d5468 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/test/java/org/eclipse/jetty/ee11/maven/plugin/TestQuickStartGenerator.java @@ -0,0 +1,63 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.maven.plugin; + +import java.nio.file.Files; +import java.nio.file.Path; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; +import org.eclipse.jetty.util.resource.ResourceFactory; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.greaterThan; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * + * + */ +@ExtendWith(WorkDirExtension.class) +public class TestQuickStartGenerator +{ + @Test + public void testGenerator(WorkDir workDir) throws Exception + { + Path tmpDir = workDir.getEmptyPathDir(); + MavenWebAppContext webApp = new MavenWebAppContext(); + webApp.setContextPath("/shouldbeoverridden"); + Path rootDir = MavenTestingUtils.getTargetPath("test-classes/root"); + assertTrue(Files.exists(rootDir)); + assertTrue(Files.isDirectory(rootDir)); + webApp.setBaseResource(ResourceFactory.root().newResource(rootDir)); + + Path quickstartFile = tmpDir.resolve("quickstart-web.xml"); + QuickStartGenerator generator = new QuickStartGenerator(quickstartFile, webApp); + generator.setContextXml(MavenTestingUtils.getTargetFile("test-classes/embedder-context.xml").getAbsolutePath()); + generator.setServer(new Server()); + + Path propsFile = tmpDir.resolve("webapp.props"); + Files.createFile(propsFile); + generator.setWebAppProps(propsFile); + generator.generate(); + assertTrue(Files.exists(propsFile)); + assertThat(Files.size(propsFile), greaterThan(0L)); + assertTrue(Files.exists(quickstartFile)); + assertThat(Files.size(quickstartFile), greaterThan(0L)); + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/test/java/org/eclipse/jetty/ee11/maven/plugin/TestSelectiveJarResource.java b/jetty-ee11/jetty-ee11-maven-plugin/src/test/java/org/eclipse/jetty/ee11/maven/plugin/TestSelectiveJarResource.java new file mode 100644 index 00000000000..03ef98dc2d0 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/test/java/org/eclipse/jetty/ee11/maven/plugin/TestSelectiveJarResource.java @@ -0,0 +1,114 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.maven.plugin; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jetty.maven.SelectiveJarResource; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.util.resource.ResourceFactory; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@ExtendWith(WorkDirExtension.class) +public class TestSelectiveJarResource +{ + + @Test + public void testIncludesNoExcludes(WorkDir workDir) throws Exception + { + Path unpackDir = workDir.getPath(); + Path testJar = MavenTestingUtils.getTestResourcePathFile("selective-jar-test.jar"); + try (ResourceFactory.Closeable resourceFactory = ResourceFactory.closeable()) + { + Resource resource = resourceFactory.newJarFileResource(testJar.toUri()); + SelectiveJarResource sjr = new SelectiveJarResource(resource); + sjr.setCaseSensitive(false); + List includes = new ArrayList<>(); + includes.add("**/*.html"); + sjr.setIncludes(includes); + sjr.copyTo(unpackDir); + assertTrue(Files.exists(unpackDir.resolve("top.html"))); + assertTrue(Files.exists(unpackDir.resolve("aa/a1.html"))); + assertTrue(Files.exists(unpackDir.resolve("aa/a2.html"))); + assertTrue(Files.exists(unpackDir.resolve("aa/deep/a3.html"))); + assertTrue(Files.exists(unpackDir.resolve("bb/b1.html"))); + assertTrue(Files.exists(unpackDir.resolve("bb/b2.html"))); + assertTrue(Files.exists(unpackDir.resolve("cc/c1.html"))); + assertTrue(Files.exists(unpackDir.resolve("cc/c2.html"))); + } + } + + @Test + public void testExcludesNoIncludes(WorkDir workDir) throws Exception + { + Path unpackDir = workDir.getPath(); + Path testJar = MavenTestingUtils.getTestResourcePathFile("selective-jar-test.jar"); + try (ResourceFactory.Closeable resourceFactory = ResourceFactory.closeable()) + { + Resource resource = resourceFactory.newJarFileResource(testJar.toUri()); + SelectiveJarResource sjr = new SelectiveJarResource(resource); + sjr.setCaseSensitive(false); + List excludes = new ArrayList<>(); + excludes.add("**/*"); + sjr.setExcludes(excludes); + sjr.copyTo(unpackDir); + assertFalse(Files.exists(unpackDir.resolve("top.html"))); + assertFalse(Files.exists(unpackDir.resolve("aa/a1.html"))); + assertFalse(Files.exists(unpackDir.resolve("aa/a2.html"))); + assertFalse(Files.exists(unpackDir.resolve("aa/deep/a3.html"))); + assertFalse(Files.exists(unpackDir.resolve("bb/b1.html"))); + assertFalse(Files.exists(unpackDir.resolve("bb/b2.html"))); + assertFalse(Files.exists(unpackDir.resolve("cc/c1.html"))); + assertFalse(Files.exists(unpackDir.resolve("cc/c2.html"))); + } + } + + @Test + public void testIncludesExcludes(WorkDir workDir) throws Exception + { + Path unpackDir = workDir.getPath(); + Path testJar = MavenTestingUtils.getTestResourcePathFile("selective-jar-test.jar"); + try (ResourceFactory.Closeable resourceFactory = ResourceFactory.closeable()) + { + Resource resource = resourceFactory.newJarFileResource(testJar.toUri()); + SelectiveJarResource sjr = new SelectiveJarResource(resource); + sjr.setCaseSensitive(false); + List excludes = new ArrayList<>(); + excludes.add("**/deep/*"); + sjr.setExcludes(excludes); + List includes = new ArrayList<>(); + includes.add("bb/*"); + sjr.setIncludes(includes); + sjr.copyTo(unpackDir); + assertFalse(Files.exists(unpackDir.resolve("top.html"))); + assertFalse(Files.exists(unpackDir.resolve("aa/a1.html"))); + assertFalse(Files.exists(unpackDir.resolve("aa/a2.html"))); + assertFalse(Files.exists(unpackDir.resolve("aa/deep/a3.html"))); + assertTrue(Files.exists(unpackDir.resolve("bb/b1.html"))); + assertTrue(Files.exists(unpackDir.resolve("bb/b2.html"))); + assertFalse(Files.exists(unpackDir.resolve("cc/c1.html"))); + assertFalse(Files.exists(unpackDir.resolve("cc/c2.html"))); + } + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/test/java/org/eclipse/jetty/ee11/maven/plugin/TestWebAppPropertyConverter.java b/jetty-ee11/jetty-ee11-maven-plugin/src/test/java/org/eclipse/jetty/ee11/maven/plugin/TestWebAppPropertyConverter.java new file mode 100644 index 00000000000..ee27db0c9b8 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/test/java/org/eclipse/jetty/ee11/maven/plugin/TestWebAppPropertyConverter.java @@ -0,0 +1,165 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.maven.plugin; + +import java.io.File; +import java.io.FileInputStream; +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Properties; + +import org.eclipse.jetty.ee11.webapp.WebAppContext; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.util.IO; +import org.eclipse.jetty.util.resource.CombinedResource; +import org.eclipse.jetty.util.resource.Resource; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.instanceOf; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class TestWebAppPropertyConverter +{ + static File testDir; + static String contextXml; + static File tmpDir; + static File classesDir; + static File testClassesDir; + static File jar1; + static File jar2; + static File war; + static File override1; + static File override2; + static File webXml; + + @BeforeAll + public static void setUp() throws Exception + { + testDir = MavenTestingUtils.getTargetTestingDir("TestWebApPropertyConverter"); + testDir.mkdirs(); + contextXml = MavenTestingUtils.getTestResourceFile("embedder-context.xml").getAbsolutePath(); + tmpDir = new File(testDir, "testToProperties"); + tmpDir.mkdirs(); + classesDir = new File(testDir, "imaginaryClasses"); + classesDir.mkdirs(); + testClassesDir = new File(testDir, "imaginaryTestClasses"); + testClassesDir.mkdirs(); + jar1 = new File(testDir, "imaginary1.jar"); + jar1.createNewFile(); + jar2 = new File(testDir, "imaginary2.jar"); + jar2.createNewFile(); + war = new File(testDir, "imaginary.war"); + war.createNewFile(); + override1 = new File(testDir, "override-web1.xml"); + override1.createNewFile(); + override2 = new File(testDir, "override-web2.xml"); + override2.createNewFile(); + webXml = new File(testDir, "web.xml"); + webXml.createNewFile(); + } + + @AfterAll + public static void tearDown() throws Exception + { + IO.delete(testDir); + } + + @Test + public void testToProperties() throws Exception + { + File propsFile = new File(testDir, "webapp.props"); + if (propsFile.exists()) + propsFile.delete(); + propsFile.createNewFile(); + + MavenWebAppContext webApp = new MavenWebAppContext(); + webApp.setContextPath("/foo"); + webApp.setBaseResourceAsPath(MavenTestingUtils.getTestResourcePathDir("root")); + webApp.setTempDirectory(tmpDir); + webApp.setTempDirectoryPersistent(false); + webApp.setClasses(classesDir); + webApp.setTestClasses(testClassesDir); + webApp.setWebInfLib(Arrays.asList(jar1, jar2)); + webApp.setWar(war.getAbsolutePath()); + webApp.addOverrideDescriptor(override1.getAbsolutePath()); + webApp.addOverrideDescriptor(override2.getAbsolutePath()); + WebAppPropertyConverter.toProperties(webApp, propsFile, contextXml); + + assertTrue(propsFile.exists()); + Properties props = new Properties(); + props.load(new FileInputStream(propsFile)); + assertEquals("/foo", props.get(WebAppPropertyConverter.CONTEXT_PATH)); + assertEquals(contextXml, props.get(WebAppPropertyConverter.CONTEXT_XML)); + assertEquals(tmpDir.getAbsolutePath(), props.get(WebAppPropertyConverter.TMP_DIR)); + assertEquals("false", props.get(WebAppPropertyConverter.TMP_DIR_PERSIST)); + assertEquals(classesDir.getAbsolutePath(), props.get(WebAppPropertyConverter.CLASSES_DIR)); + assertEquals(testClassesDir.getAbsolutePath(), props.get(WebAppPropertyConverter.TEST_CLASSES_DIR)); + assertEquals(String.join(",", jar1.getAbsolutePath(), jar2.getAbsolutePath()), props.get(WebAppPropertyConverter.LIB_JARS)); + assertEquals(war.getAbsolutePath(), props.get(WebAppPropertyConverter.WAR_FILE)); + assertEquals(WebAppContext.WEB_DEFAULTS_XML, props.get(WebAppPropertyConverter.DEFAULTS_DESCRIPTOR)); + assertEquals(String.join(",", override1.getAbsolutePath(), override2.getAbsolutePath()), props.get(WebAppPropertyConverter.OVERRIDE_DESCRIPTORS)); + } + + @Test + public void testFromProperties() throws Exception + { + File base1 = new File(testDir, "base1"); + base1.mkdirs(); + File base2 = new File(testDir, "base2"); + base2.mkdirs(); + MavenWebAppContext webApp = new MavenWebAppContext(); + Properties props = new Properties(); + props.setProperty(WebAppPropertyConverter.BASE_DIRS, String.join(",", base1.getAbsolutePath(), base2.getAbsolutePath())); + props.setProperty(WebAppPropertyConverter.CLASSES_DIR, classesDir.getAbsolutePath()); + props.setProperty(WebAppPropertyConverter.CONTEXT_PATH, "/foo"); + props.setProperty(WebAppPropertyConverter.CONTEXT_XML, contextXml); + props.setProperty(WebAppPropertyConverter.LIB_JARS, String.join(",", jar1.getAbsolutePath(), jar2.getAbsolutePath())); + props.setProperty(WebAppPropertyConverter.OVERRIDE_DESCRIPTORS, String.join(",", override1.getAbsolutePath(), override2.getAbsolutePath())); + //props.setProperty(WebAppPropertyConverter.QUICKSTART_WEB_XML, value); + props.setProperty(WebAppPropertyConverter.TEST_CLASSES_DIR, testClassesDir.getAbsolutePath()); + props.setProperty(WebAppPropertyConverter.TMP_DIR, tmpDir.getAbsolutePath()); + props.setProperty(WebAppPropertyConverter.TMP_DIR_PERSIST, "true"); + props.setProperty(WebAppPropertyConverter.WAR_FILE, war.getAbsolutePath()); + props.setProperty(WebAppPropertyConverter.WEB_XML, webXml.getAbsolutePath()); + WebAppPropertyConverter.fromProperties(webApp, props, new Server(), null); + + assertEquals("/embedder", webApp.getContextPath()); //the embedder-context file changes the context path + assertEquals(classesDir, webApp.getClasses()); + assertEquals(testClassesDir, webApp.getTestClasses()); + assertThat(webApp.getWebInfLib(), Matchers.contains(jar1, jar2)); + assertThat(webApp.getOverrideDescriptors(), Matchers.contains(override1.getAbsolutePath(), override2.getAbsolutePath())); + assertEquals(tmpDir, webApp.getTempDirectory()); + assertEquals(true, webApp.isTempDirectoryPersistent()); + assertEquals(war.getAbsolutePath(), webApp.getWar()); + assertEquals(webXml.getAbsolutePath(), webApp.getDescriptor()); + assertThat(webApp.getBaseResource(), instanceOf(CombinedResource.class)); + + Resource combinedResource = webApp.getBaseResource(); + List actual = new ArrayList<>(); + for (Resource r : combinedResource) + if (r != null) + actual.add(r.getURI()); + URI[] expected = new URI[]{base1.toURI(), base2.toURI()}; + assertThat(actual, containsInAnyOrder(expected)); + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/test/java/org/eclipse/jetty/ee11/maven/plugin/it/IntegrationTestGetContent.java b/jetty-ee11/jetty-ee11-maven-plugin/src/test/java/org/eclipse/jetty/ee11/maven/plugin/it/IntegrationTestGetContent.java new file mode 100644 index 00000000000..d115e1a7734 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/test/java/org/eclipse/jetty/ee11/maven/plugin/it/IntegrationTestGetContent.java @@ -0,0 +1,120 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.maven.plugin.it; + +import java.io.LineNumberReader; +import java.io.Reader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.concurrent.TimeUnit; + +import org.awaitility.Awaitility; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.util.StringUtil; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class IntegrationTestGetContent +{ + @Test + public void getContentResponse() + throws Exception + { + int port = getPort(); + assertTrue(port > 0); + String contextPath = getContextPath(); + if (contextPath.endsWith("/")) + contextPath = contextPath.substring(0, contextPath.lastIndexOf('/')); + + HttpClient httpClient = new HttpClient(); + try + { + httpClient.start(); + + if (Boolean.getBoolean("helloServlet")) + { + String response = httpClient.GET("http://localhost:" + port + contextPath + "/hello?name=beer").getContentAsString(); + assertEquals("Hello beer", response.trim(), "it test " + System.getProperty("maven.it.name")); + response = httpClient.GET("http://localhost:" + port + contextPath + "/hello?name=foo").getContentAsString(); + assertEquals("Hello foo", response.trim(), "it test " + System.getProperty("maven.it.name")); + System.out.println("helloServlet"); + } + if (Boolean.getBoolean("pingServlet")) + { + System.out.println("pingServlet"); + String response = httpClient.GET("http://localhost:" + port + contextPath + "/ping?name=beer").getContentAsString(); + assertEquals("pong beer", response.trim(), "it test " + System.getProperty("maven.it.name")); + System.out.println("pingServlet ok"); + } + String contentCheck = System.getProperty("contentCheck"); + String pathToCheck = System.getProperty("pathToCheck"); + if (StringUtil.isNotBlank(contentCheck)) + { + String url = "http://localhost:" + port + contextPath; + if (pathToCheck != null) + { + url += pathToCheck; + } + String response = httpClient.GET(url).getContentAsString(); + assertTrue(response.contains(contentCheck), "it test " + System.getProperty("maven.it.name") + + ", response not contentCheck: " + contentCheck + ", response:" + response); + System.out.println("contentCheck"); + } + if (Boolean.getBoolean("helloTestServlet")) + { + String response = httpClient.GET("http://localhost:" + port + contextPath + "/testhello?name=beer").getContentAsString(); + assertEquals("Hello from test beer", response.trim(), "it test " + System.getProperty("maven.it.name")); + response = httpClient.GET("http://localhost:" + port + contextPath + "/testhello?name=foo").getContentAsString(); + assertEquals("Hello from test foo", response.trim(), "it test " + System.getProperty("maven.it.name")); + System.out.println("helloServlet"); + } + } + finally + { + httpClient.stop(); + } + } + + public static String getContextPath() + { + return System.getProperty("context.path", "/"); + } + + public static int getPort() + throws Exception + { + String s = System.getProperty("jetty.port.file"); + assertNotNull(s, "jetty.port.file System property"); + Path p = Paths.get(s); + + System.err.println("Looking for port file: " + p); + + Awaitility.await() + .pollInterval(1, TimeUnit.MILLISECONDS) + .atMost(30, TimeUnit.SECONDS) + .until(() -> Files.exists(p)); + + try (Reader r = Files.newBufferedReader(p); + LineNumberReader lnr = new LineNumberReader(r)) + { + s = lnr.readLine(); + assertNotNull(s); + return Integer.parseInt(s.trim()); + } + } +} diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/test/resources/embedder-context.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/test/resources/embedder-context.xml new file mode 100644 index 00000000000..4263daeb576 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/test/resources/embedder-context.xml @@ -0,0 +1,17 @@ + + + + + /embedder + + + + + + + + + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/test/resources/embedder-jetty.xml b/jetty-ee11/jetty-ee11-maven-plugin/src/test/resources/embedder-jetty.xml new file mode 100644 index 00000000000..08157651885 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/test/resources/embedder-jetty.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/test/resources/jetty-logging.properties b/jetty-ee11/jetty-ee11-maven-plugin/src/test/resources/jetty-logging.properties new file mode 100644 index 00000000000..1ab206d22d5 --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/test/resources/jetty-logging.properties @@ -0,0 +1,6 @@ +# Jetty Logging using jetty-slf4j-impl +#org.eclipse.jetty.maven.plugin.LEVEL=DEBUG +#org.eclipse.jetty.LEVEL=DEBUG +#org.eclipse.jetty.server.LEVEL=DEBUG +#org.eclipse.jetty.http.LEVEL=DEBUG +#org.eclipse.jetty.http.pathmap.LEVEL=DEBUG diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/test/resources/root/index.html b/jetty-ee11/jetty-ee11-maven-plugin/src/test/resources/root/index.html new file mode 100644 index 00000000000..e8d4c65dd0d --- /dev/null +++ b/jetty-ee11/jetty-ee11-maven-plugin/src/test/resources/root/index.html @@ -0,0 +1 @@ +

ROOT

\ No newline at end of file diff --git a/jetty-ee11/jetty-ee11-maven-plugin/src/test/resources/selective-jar-test.jar b/jetty-ee11/jetty-ee11-maven-plugin/src/test/resources/selective-jar-test.jar new file mode 100644 index 00000000000..dc7dc4b596c Binary files /dev/null and b/jetty-ee11/jetty-ee11-maven-plugin/src/test/resources/selective-jar-test.jar differ diff --git a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-alpn/pom.xml b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-alpn/pom.xml new file mode 100644 index 00000000000..6a7cab1cd61 --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-alpn/pom.xml @@ -0,0 +1,34 @@ + + + + + + 4.0.0 + + org.eclipse.jetty.ee11.osgi + jetty-ee11-osgi + 12.1.0-SNAPSHOT + + jetty-ee11-osgi-alpn + jar + EE11 :: OSGi :: ALPN Fragment + + ${project.groupId}.alpn.fragment + + + + + org.apache.felix + maven-bundle-plugin + + + ${bundle-symbolic-name};singleton:=true + Jetty OSGi ALPN Fragment + !javax.*;!org.eclipse.jetty.* + system.bundle;extension:=framework + + + + + + diff --git a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot-jsp/pom.xml b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot-jsp/pom.xml new file mode 100644 index 00000000000..a7a2b996717 --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot-jsp/pom.xml @@ -0,0 +1,149 @@ + + + 4.0.0 + + org.eclipse.jetty.ee11.osgi + jetty-ee11-osgi + 12.1.0-SNAPSHOT + + jetty-ee11-osgi-boot-jsp + EE11 :: OSGi :: Boot JSP + Jetty OSGi Boot JSP bundle + + ${project.groupId}.boot.jsp + org.eclipse.jetty.ee11.osgi.boot.jsp.* + + + + + jakarta.servlet + jakarta.servlet-api + + + org.eclipse.jetty + jetty-deploy + + + + org.eclipse.jetty.ee11 + jetty-ee11-apache-jsp + + + org.eclipse.platform + org.eclipse.osgi + + + org.eclipse.platform + org.eclipse.osgi.services + + + org.eclipse.jetty.ee11.osgi + jetty-ee11-osgi-boot + provided + + + + + + + + org.apache.felix + maven-bundle-plugin + true + + + Jetty-OSGi-Jasper Integration + + org.eclipse.jetty.ee11.osgi.boot + ${osgi.slf4j.import.packages}, + org.eclipse.jdt.*;resolution:=optional, + org.eclipse.jdt.core.compiler.*;resolution:=optional, + com.sun.el;resolution:=optional, + com.sun.el.lang;resolution:=optional, + com.sun.el.parser;resolution:=optional, + com.sun.el.util;resolution:=optional, + jakarta.el;version="[5.0,6.0)", + jakarta.servlet;version="[$(version;==;${jakarta.servlet.api.version}),$(version;+;${jakarta.servlet.api.version}))", + jakarta.servlet.resources;version="[$(version;==;${jakarta.servlet.api.version}),$(version;+;${jakarta.servlet.api.version}))", + jakarta.servlet.jsp.resources;version="[$(version;==;${jakarta.servlet.jsp.api.version}),$(version;+;${jakarta.servlet.jsp.api.version}))", + jakarta.servlet.jsp;version="[$(version;==;${jakarta.servlet.jsp.api.version}),$(version;+;${jakarta.servlet.jsp.api.version}))", + jakarta.servlet.jsp.el;version="[$(version;==;${jakarta.servlet.jsp.api.version}),$(version;+;${jakarta.servlet.jsp.api.version}))", + jakarta.servlet.jsp.tagext;version="[$(version;==;${jakarta.servlet.jsp.api.version}),$(version;+;${jakarta.servlet.jsp.api.version}))", + jakarta.servlet.jsp.jstl.core;version="2.0";resolution:=optional, + jakarta.servlet.jsp.jstl.fmt;version="2.0";resolution:=optional, + jakarta.servlet.jsp.jstl.sql;version="2.0";resolution:=optional, + jakarta.servlet.jsp.jstl.tlv;version="2.0";resolution:=optional, + org.apache.el;version="[$(version;==;${jspImpl.osgiVersion}),$(version;+;${jspImpl.osgiVersion}))";resolution:=optional, + org.apache.el.lang;version="[$(version;==;${jspImpl.osgiVersion}),$(version;+;${jspImpl.osgiVersion}))";resolution:=optional, + org.apache.el.stream;version="[$(version;==;${jspImpl.osgiVersion}),$(version;+;${jspImpl.osgiVersion}))";resolution:=optional, + org.apache.el.util;version="[$(version;==;${jspImpl.osgiVersion}),$(version;+;${jspImpl.osgiVersion}))";resolution:=optional, + org.apache.el.parser;version="[$(version;==;${jspImpl.osgiVersion}),$(version;+;${jspImpl.osgiVersion}))";resolution:=optional, + org.apache.jasper;version="[$(version;==;${jspImpl.osgiVersion}),$(version;+;${jspImpl.osgiVersion}))";resolution:=optional, + org.apache.jasper.compiler;version="[$(version;==;${jspImpl.osgiVersion}),$(version;+;${jspImpl.osgiVersion}))";resolution:=optional, + org.apache.jasper.compiler.util;version="[$(version;==;${jspImpl.osgiVersion}),$(version;+;${jspImpl.osgiVersion}))";resolution:=optional, + org.apache.jasper.compiler.tagplugin;version="[$(version;==;${jspImpl.osgiVersion}),$(version;+;${jspImpl.osgiVersion}))";resolution:=optional, + org.apache.jasper.runtime;version="[$(version;==;${jspImpl.osgiVersion}),$(version;+;${jspImpl.osgiVersion}))";resolution:=optional, + org.apache.jasper.security;version="[$(version;==;${jspImpl.osgiVersion}),$(version;+;${jspImpl.osgiVersion}))";resolution:=optional, + org.apache.jasper.servlet;version="[$(version;==;${jspImpl.osgiVersion}),$(version;+;${jspImpl.osgiVersion}))";resolution:=optional, + org.apache.jasper.tagplugins.jstl;version="[$(version;==;${jspImpl.osgiVersion}),$(version;+;${jspImpl.osgiVersion}))";resolution:=optional, + org.apache.jasper.util;version="[$(version;===;${jspImpl.osgiVersion}),$(version;+;${jspImpl.osgiVersion}))";resolution:=optional, + org.apache.jasper.xmlparser;version="[$(version;==;${jspImpl.osgiVersion}),$(version;+;${jspImpl.osgiVersion}))";resolution:=optional, + org.apache.taglibs.standard;version="2.0";resolution:=optional, + org.apache.taglibs.standard.extra.spath;version="2.0";resolution:=optional, + org.apache.taglibs.standard.functions;version="2.0";resolution:=optional, + org.apache.taglibs.standard.lang.jstl;version="2.0";resolution:=optional, + org.apache.taglibs.standard.lang.jstl.parser;version="2.0";resolution:=optional, + org.apache.taglibs.standard.lang.jstl.test;version="2.0";resolution:=optional, + org.apache.taglibs.standard.lang.jstl.test.beans;version="2.0";resolution:=optional, + org.apache.taglibs.standard.lang.support;version="2.0";resolution:=optional, + org.apache.taglibs.standard.resources;version="2.0";resolution:=optional, + org.apache.taglibs.standard.tag.common.core;version="2.0";resolution:=optional, + org.apache.taglibs.standard.tag.common.fmt;version="2.0";resolution:=optional, + org.apache.taglibs.standard.tag.common.sql;version="2.0";resolution:=optional, + org.apache.taglibs.standard.tag.common.xml;version="2.0";resolution:=optional, + org.apache.taglibs.standard.tag.el.core;version="2.0";resolution:=optional, + org.apache.taglibs.standard.tag.el.fmt;version="2.0";resolution:=optional, + org.apache.taglibs.standard.tag.el.sql;version="2.0";resolution:=optional, + org.apache.taglibs.standard.tag.el.xml;version="2.0";resolution:=optional, + org.apache.taglibs.standard.tag.rt.core;version="2.0";resolution:=optional, + org.apache.taglibs.standard.tag.rt.fmt;version="2.0";resolution:=optional, + org.apache.taglibs.standard.tag.rt.sql;version="2.0";resolution:=optional, + org.apache.taglibs.standard.tag.rt.xml;version="2.0";resolution:=optional, + org.apache.taglibs.standard.tei;version="2.0";resolution:=optional, + org.apache.taglibs.standard.tlv;version="2.0";resolution:=optional, + org.apache.tomcat;version="[10,11)";resolution:=optional, + org.eclipse.jetty.ee11.jsp;version="[$(version;===;${parsedVersion.osgiVersion}),$(version;==+;${parsedVersion.osgiVersion}))";resolution:=optional, + org.osgi.*, + org.xml.*;resolution:=optional, + org.xml.sax.*;resolution:=optional, + javax.xml.*;resolution:=optional, + org.w3c.dom;resolution:=optional, + org.w3c.dom.ls;resolution:=optional, + javax.xml.parser;resolution:=optional + org.eclipse.jetty.ee11.jsp.*;version="[$(version;===;${parsedVersion.osgiVersion}),$(version;==+;${parsedVersion.osgiVersion}))", + org.apache.jasper.*;version="[$(version;===;${jspImpl.osgiVersion}),$(version;+;${jspImpl.osgiVersion}))", + org.apache.el.*;version="[$(version;===;${jspImpl.osgiVersion}),$(version;+;${jspImpl.osgiVersion}))" + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + set-jsp-api-version + + parse-version + + validate + + ${jsp.impl.version} + jspImpl + + + + + + + diff --git a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot-jsp/src/main/java/org/eclipse/jetty/ee11/osgi/boot/jsp/FragmentActivator.java b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot-jsp/src/main/java/org/eclipse/jetty/ee11/osgi/boot/jsp/FragmentActivator.java new file mode 100644 index 00000000000..d75597d4b8d --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot-jsp/src/main/java/org/eclipse/jetty/ee11/osgi/boot/jsp/FragmentActivator.java @@ -0,0 +1,55 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.osgi.boot.jsp; + +import org.eclipse.jetty.ee11.osgi.boot.EE11Activator; +import org.eclipse.jetty.osgi.util.ServerClasspathContributor; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; + +/** + * FragmentActivator + * + * Sets up support for jsp and jstl. All relevant jsp jars must also be installed + * into the osgi environment. + *

+ * NOTE that as this is part of a bundle fragment, this activator is NOT + * called by the OSGi environment. Instead, the org.eclipse.jetty.osgi.util.internal.PackageAdminTracker + * simulates fragment activation and causes this class's start() method to be called. + *

+ *

+ * The package of this class MUST match the Bundle-SymbolicName of this fragment + * in order for the PackageAdminTracker to find it. + *

+ */ +public class FragmentActivator implements BundleActivator +{ + ServerClasspathContributor _tldClasspathContributor; + + @Override + public void start(BundleContext context) throws Exception + { + //Register a class that will provide the identity of bundles that + //contain TLDs and therefore need to be scanned. + _tldClasspathContributor = new TLDServerClasspathContributor(); + EE11Activator.registerServerClasspathContributor(_tldClasspathContributor); + } + + @Override + public void stop(BundleContext context) throws Exception + { + EE11Activator.unregisterServerClasspathContributor(_tldClasspathContributor); + _tldClasspathContributor = null; + } +} diff --git a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot-jsp/src/main/java/org/eclipse/jetty/ee11/osgi/boot/jsp/TLDServerClasspathContributor.java b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot-jsp/src/main/java/org/eclipse/jetty/ee11/osgi/boot/jsp/TLDServerClasspathContributor.java new file mode 100644 index 00000000000..12498cbced9 --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot-jsp/src/main/java/org/eclipse/jetty/ee11/osgi/boot/jsp/TLDServerClasspathContributor.java @@ -0,0 +1,112 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.osgi.boot.jsp; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.eclipse.jetty.ee11.osgi.boot.OSGiMetaInfConfiguration; +import org.eclipse.jetty.osgi.util.ServerClasspathContributor; +import org.osgi.framework.Bundle; +import org.osgi.framework.FrameworkUtil; + +/** + * @author janb + * + */ +public class TLDServerClasspathContributor implements ServerClasspathContributor +{ + + /** + * Name of a class that belongs to the jstl bundle. From that class + * we locate the corresponding bundle. + */ + private static String JSTL_BUNDLE_CLASS = "org.apache.taglibs.standard.tag.el.core.WhenTag"; + + @Override + public List getScannableBundles() + { + if (!isJspAvailable()) + { + return Collections.emptyList(); + } + + List scannableBundles = new ArrayList<>(); + List bundleNames = Collections.emptyList(); + + String tmp = System.getProperty(OSGiMetaInfConfiguration.SYS_PROP_TLD_BUNDLES); //comma separated exact names + + if (tmp != null) + { + String[] names = tmp.split(", \n\r\t"); + bundleNames = Arrays.asList(names); + } + + Bundle jstlBundle = findJstlBundle(); + if (jstlBundle != null) + scannableBundles.add(jstlBundle); + + final Bundle[] bundles = FrameworkUtil.getBundle(getClass()).getBundleContext().getBundles(); + for (Bundle bundle : bundles) + { + if (bundleNames.contains(bundle.getSymbolicName())) + scannableBundles.add(bundle); + } + + return scannableBundles; + } + + /** + * Check that jsp is on the classpath + * + * @return true if jsp is available in the environment + */ + public boolean isJspAvailable() + { + try + { + getClass().getClassLoader().loadClass("org.apache.jasper.servlet.JspServlet"); + return true; + } + catch (Exception e) + { + return false; + } + } + + /** + * Find the bundle that contains a jstl implementation class, which assumes that + * the jstl taglibs will be inside the same bundle. + * + * @return Bundle contains the jstl implementation class + */ + public Bundle findJstlBundle() + { + Class jstlClass = null; + + try + { + jstlClass = getClass().getClassLoader().loadClass(JSTL_BUNDLE_CLASS); + return FrameworkUtil.getBundle(jstlClass); + } + catch (ClassNotFoundException e) + { + //no jstl do nothing + } + + return null; + } +} diff --git a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot-warurl/pom.xml b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot-warurl/pom.xml new file mode 100644 index 00000000000..9d6cf4bbf1e --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot-warurl/pom.xml @@ -0,0 +1,41 @@ + + + org.eclipse.jetty.ee11.osgi + jetty-ee11-osgi + 12.0.0-SNAPSHOT + + 4.0.0 + jetty-ee11-osgi-boot-warurl + EE11 :: OSGi :: Boot :: Warurl + Jetty OSGi Boot-Warurl bundle + + ${project.groupId}.boot.warurl + org.eclipse.jetty.ee11.osgi.boot.warurl.* + + + + org.eclipse.jetty + jetty-util + + + org.eclipse.platform + org.eclipse.osgi + + + + + + + org.apache.felix + maven-bundle-plugin + true + + + RFC66 War URL + org.eclipse.jetty.ee11.osgi.boot.warurl.WarUrlActivator + + + + + + diff --git a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot-warurl/src/main/java/org/eclipse/jetty/ee11/osgi/boot/warurl/WarUrlActivator.java b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot-warurl/src/main/java/org/eclipse/jetty/ee11/osgi/boot/warurl/WarUrlActivator.java new file mode 100644 index 00000000000..a20b66ba97b --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot-warurl/src/main/java/org/eclipse/jetty/ee11/osgi/boot/warurl/WarUrlActivator.java @@ -0,0 +1,69 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.osgi.boot.warurl; + +import java.util.Dictionary; +import java.util.Hashtable; + +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceRegistration; +import org.osgi.service.url.URLConstants; +import org.osgi.service.url.URLStreamHandlerService; + +/** + * Register the factory to handle the war scheme specified by rfc66 + * when the bundle is activated. + */ +public class WarUrlActivator implements BundleActivator +{ + + private ServiceRegistration _reg; + + /** + * Register the url stream handler factory. + * + * @param context the {@link BundleContext} to use + */ + @SuppressWarnings("unchecked") + @Override + public void start(BundleContext context) throws Exception + { + Dictionary props = new Hashtable(); + props.put(URLConstants.URL_HANDLER_PROTOCOL, new String[]{"war"}); + context.registerService(URLStreamHandlerService.class.getName(), + new WarUrlStreamHandler(), props); + } + + /** + * Remove the url stream handler. (probably not required, + * as osgi might shutdown every registered service + * by default: need test) + */ + @Override + public void stop(BundleContext context) throws Exception + { + try + { + if (_reg != null) + { + _reg.unregister(); + } + } + catch (Exception e) + { + e.printStackTrace(); + } + } +} diff --git a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot-warurl/src/main/java/org/eclipse/jetty/ee11/osgi/boot/warurl/WarUrlStreamHandler.java b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot-warurl/src/main/java/org/eclipse/jetty/ee11/osgi/boot/warurl/WarUrlStreamHandler.java new file mode 100644 index 00000000000..96c3300fc2f --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot-warurl/src/main/java/org/eclipse/jetty/ee11/osgi/boot/warurl/WarUrlStreamHandler.java @@ -0,0 +1,98 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.osgi.boot.warurl; + +import java.io.File; +import java.io.IOException; +import java.net.JarURLConnection; +import java.net.URL; +import java.net.URLConnection; +import java.util.jar.Manifest; + +import org.eclipse.jetty.ee11.osgi.boot.warurl.internal.WarBundleManifestGenerator; +import org.eclipse.jetty.ee11.osgi.boot.warurl.internal.WarURLConnection; +import org.eclipse.jetty.util.URIUtil; +import org.eclipse.jetty.util.resource.Resource; +import org.osgi.service.url.AbstractURLStreamHandlerService; + +/** + * RFC-66: support for the "war" protocol We are reusing the parsing of the + * query string from jetty. If we wanted to not depend on jetty at all we could + * duplicate that method here + */ +public class WarUrlStreamHandler extends AbstractURLStreamHandlerService +{ + + /** + * @param url The url with a war scheme + */ + @Override + public URLConnection openConnection(URL url) throws IOException + { + // remove the war scheme. + URL actual = new URL(url.toString().substring("war:".length())); + + // let's do some basic tests: see if this is a folder or not. + // if it is a folder. we will try to support it. + if (actual.getProtocol().equals("file")) + { + File file = new File(URIUtil.encodePath(actual.getPath())); + if (file.exists()) + { + if (file.isDirectory()) + { + // TODO (not mandatory for rfc66 though) + } + } + } + + // if (actual.toString().startsWith("file:/") && ! actual.to) + URLConnection ori = (URLConnection)actual.openConnection(); + ori.setDefaultUseCaches(Resource.getDefaultUseCaches()); + JarURLConnection jarOri = null; + try + { + if (ori instanceof JarURLConnection) + { + jarOri = (JarURLConnection)ori; + } + else + { + jarOri = (JarURLConnection)new URL("jar:" + actual.toString() + "!/").openConnection(); + jarOri.setDefaultUseCaches(Resource.getDefaultUseCaches()); + } + Manifest mf = WarBundleManifestGenerator.createBundleManifest(jarOri.getManifest(), url, jarOri.getJarFile()); + try + { + jarOri.getJarFile().close(); + jarOri = null; + } + catch (Throwable ignored) + { + } + return new WarURLConnection(actual, mf); + } + finally + { + if (jarOri != null) + try + { + jarOri.getJarFile().close(); + } + catch (Throwable ignored) + { + } + } + } +} diff --git a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot-warurl/src/main/java/org/eclipse/jetty/ee11/osgi/boot/warurl/internal/WarBundleManifestGenerator.java b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot-warurl/src/main/java/org/eclipse/jetty/ee11/osgi/boot/warurl/internal/WarBundleManifestGenerator.java new file mode 100644 index 00000000000..4496e9a693d --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot-warurl/src/main/java/org/eclipse/jetty/ee11/osgi/boot/warurl/internal/WarBundleManifestGenerator.java @@ -0,0 +1,276 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.osgi.boot.warurl.internal; + +import java.net.URL; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map.Entry; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.Manifest; + +import org.eclipse.jetty.util.MultiMap; +import org.eclipse.jetty.util.UrlEncoded; +import org.osgi.framework.Constants; + +public class WarBundleManifestGenerator +{ + /** + * missing version in the url and in the manifest + * use this one. + */ + private static final String MISSING_VERSION = "0.0.1.unknown"; + private static final String MISSING_MANIFEST_VERSION = "2"; + + public static Manifest createBundleManifest(Manifest originalManifest, URL url, JarFile jarFile) + { + Manifest res = new Manifest(); + res.getMainAttributes().putAll( + createBundleManifest(originalManifest.getMainAttributes(), + url.toString(), jarFile)); + return res; + } + + private static Attributes createBundleManifest(Attributes originalManifest, String url, JarFile jarFile) + { + HashMap res = new HashMap(); + for (Entry entries : originalManifest.entrySet()) + { + res.put(entries.getKey().toString(), String.valueOf(entries.getValue())); + } + MultiMap params = parseQueryString(url); + //follow RFC66 documentation: + //#1 Bundle-Version + String version = params.getString(Constants.BUNDLE_VERSION); + if (version != null) + { + res.put(Constants.BUNDLE_VERSION, version); + } + else + { + String versionInManifest = (String)res.get(Constants.BUNDLE_VERSION); + if (versionInManifest == null) + { + res.put(Constants.BUNDLE_VERSION, MISSING_VERSION); + } + } + + //#2 Bundle_ManifestVersion + String manversion = params.getString(Constants.BUNDLE_MANIFESTVERSION); + if (manversion != null) + { + res.put(Constants.BUNDLE_MANIFESTVERSION, manversion); + } + else + { + int manv = 2; + try + { + String versionInManifest = (String)res.get(Constants.BUNDLE_MANIFESTVERSION); + if (versionInManifest != null) + { + manv = Integer.parseInt(versionInManifest.trim()); + } + } + catch (NumberFormatException ignored) + { + } + res.put(Constants.BUNDLE_MANIFESTVERSION, String.valueOf(manv < 2 ? 2 : manv)); + } + + //#3 Bundle-SymbolicName + String symbname = params.getString(Constants.BUNDLE_SYMBOLICNAME); + if (symbname != null) + { + res.put(Constants.BUNDLE_SYMBOLICNAME, symbname); + } + else + { + symbname = (String)res.get(Constants.BUNDLE_SYMBOLICNAME); + if (symbname == null) + { + //derive the symbolic name from the url. + int lastSlash = url.lastIndexOf('/'); + int beforeQueryString = url.indexOf(lastSlash, '?'); + if (beforeQueryString == -1) + { + beforeQueryString = url.indexOf(lastSlash, '#'); + if (beforeQueryString == -1) + { + beforeQueryString = url.length(); + } + } + symbname = url.substring(lastSlash + 1, beforeQueryString); + //todo: something better probably. + res.put(Constants.BUNDLE_SYMBOLICNAME, symbname); + } + } + + //#4 Bundle-Classpath + String extraBundleClasspath = params.getString(Constants.BUNDLE_CLASSPATH); + String alreadyBundleClasspath = res.get(Constants.BUNDLE_CLASSPATH); + if (alreadyBundleClasspath == null) + { + StringBuilder bundleClasspath = new StringBuilder(); + if (jarFile == null || jarFile.getJarEntry("WEB-INF/classes/") != null) + { + bundleClasspath.append("WEB-INF/classes"); + } + if (jarFile != null) + { + List libs = getJarsInWebInfLib(jarFile); + if (extraBundleClasspath != null) + { + libs.add(extraBundleClasspath); + } + for (String lib : libs) + { + if (bundleClasspath.length() != 0) + { + bundleClasspath.append(","); + } + bundleClasspath.append(lib); + } + } + alreadyBundleClasspath = bundleClasspath.toString(); + } + + //if there is already a manifest and it specifies the Bundle-Classpath. + //for now let's trust that one. + //please note that the draft of the spec implies that we should be parsing the existing + //header and merge it with the missing stuff so this does not follow the spec yet. + + res.put(Constants.BUNDLE_CLASSPATH, + alreadyBundleClasspath + (extraBundleClasspath == null ? "" : "," + extraBundleClasspath)); + + //#5 Import-Package + String extraImportPackage = params.getString(Constants.IMPORT_PACKAGE); + String alreadyImportPackage = res.get(Constants.IMPORT_PACKAGE); + if (alreadyImportPackage == null) + { + //The spec does not specify that the jsp imports are optional + //kind of nice to have them optional so we can run simple wars in + //simple environments. + alreadyImportPackage = "jakarta.servlet; version=\"2.5\"," + + "jakarta.servlet.http;version=\"2.5\"," + + "javax.el;version=\"1.0\"" + + "javax.jsp;version=\"2.1\";resolution:=optional," + + "javax.jsp.tagext;version=\"2.1\";resolution:=optional"; + } + if (extraImportPackage != null) + { //if there is already a manifest and it specifies the Bundle-Classpath. + //for now let's trust that one. + //please note that the draft of the spec implies that we should be parsing the existing + //header and merge it with the missing stuff so this does not follow the spec yet. + + res.put(Constants.IMPORT_PACKAGE, + (alreadyImportPackage == null ? "" : alreadyImportPackage + ",") + + extraImportPackage); + } + + //#6 Export-Package + String extraExportPackage = params.getString(Constants.EXPORT_PACKAGE); + String alreadyExportPackage = res.get(Constants.EXPORT_PACKAGE); + if (extraExportPackage != null) + { //if there is already a manifest and it specifies the Bundle-Classpath. + //for now let's trust that one. + //please note that the draft of the spec implies that we should be parsing the existing + //header and merge it with the missing stuff so this does not follow the spec yet. + res.put(Constants.EXPORT_PACKAGE, + (alreadyExportPackage == null ? "" : alreadyExportPackage + ",") + + extraImportPackage); + } + + //#7 Web-ContextPath + String webContextPath = params.getString("Web-ContextPath"); + if (webContextPath != null) + { + res.put("Web-ContextPath", webContextPath); + } + else + { + webContextPath = res.get("Web-ContextPath"); + if (webContextPath == null) + { + //we choose to use the symbolic name as the default context path. + if (symbname.endsWith(".war")) + { + webContextPath = "/" + symbname.substring(0, symbname.length() - ".war".length()); + } + else + { + webContextPath = "/" + symbname; + } + res.put("Web-ContextPath", webContextPath); + } + } + + //#8 Web-JSPExtractLocation + String jspExtractLocation = params.getString("Web-JSPExtractLocation"); + if (jspExtractLocation != null) + { + res.put("Web-JSPExtractLocation", jspExtractLocation); + } + else + { + //nothing to do. + } + Attributes newAttrs = new Attributes(); + for (Entry e : res.entrySet()) + { + newAttrs.putValue(e.getKey(), e.getValue()); + } + return newAttrs; + } + + /** + * @return The key values pairs that are in the query string of this url. + */ + private static MultiMap parseQueryString(String url) + { + MultiMap res = new MultiMap(); + int questionMarkIndex = url.indexOf('?'); + if (questionMarkIndex == -1) + { + return res; + } + int poundIndex = url.indexOf('#'); + if (poundIndex == -1) + { + poundIndex = url.length(); + } + UrlEncoded.decodeUtf8To(url, questionMarkIndex + 1, + poundIndex - questionMarkIndex - 1, res); + return res; + } + + private static List getJarsInWebInfLib(JarFile jarFile) + { + List res = new ArrayList(); + Enumeration en = jarFile.entries(); + while (en.hasMoreElements()) + { + JarEntry e = en.nextElement(); + if (e.getName().startsWith("WEB-INF/lib/") && FileID.isJavaArchive(e.getName())) + { + res.add(e.getName()); + } + } + return res; + } +} diff --git a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot-warurl/src/main/java/org/eclipse/jetty/ee11/osgi/boot/warurl/internal/WarURLConnection.java b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot-warurl/src/main/java/org/eclipse/jetty/ee11/osgi/boot/warurl/internal/WarURLConnection.java new file mode 100644 index 00000000000..69fa3601398 --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot-warurl/src/main/java/org/eclipse/jetty/ee11/osgi/boot/warurl/internal/WarURLConnection.java @@ -0,0 +1,360 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.osgi.boot.warurl.internal; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; +import java.net.URL; +import java.net.URLConnection; +import java.security.Permission; +import java.util.List; +import java.util.Map; +import java.util.jar.JarFile; +import java.util.jar.JarInputStream; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; +import java.util.zip.ZipEntry; + +import org.eclipse.jetty.util.IO; +import org.eclipse.jetty.util.resource.Resource; + +/** + * Facade for a URLConnection that will read a jar and substitute its + * manifest by the manifest provided here. + *

+ * Use Piped streams to avoid having to create temporary files. + *

+ */ +public class WarURLConnection extends URLConnection +{ + + /** + * Use PipedOuputStream and PipedInputStream to do the transformation without making + * a new temporary file ust to replace the manifest. + * + * @param newmanifest The new manifest + * @param rawIn The file input stream or equivalent. not the jar input stream. + * @return InputStream of the replaced manifest file + * @throws IOException if an I/O error occurs. + */ + public static InputStream substitueManifest(final Manifest newmanifest, + final InputStream rawIn) throws IOException + { + final PipedOutputStream pOut = new PipedOutputStream(); + PipedInputStream pIn = new PipedInputStream(pOut); + Runnable run = new Runnable() + { + @Override + public void run() + { + JarInputStream jin = null; + JarOutputStream dest = null; + try + { + jin = new JarInputStream(rawIn, false); + dest = new JarOutputStream(pOut, newmanifest); + ZipEntry next = jin.getNextEntry(); + while (next != null) + { + if (next.getName().equalsIgnoreCase(JarFile.MANIFEST_NAME)) + { + continue; + } + dest.putNextEntry(next); + if (next.getSize() > 0) + { + IO.copy(jin, dest, next.getSize()); + } + next = jin.getNextJarEntry(); + } + } + catch (IOException ioe) + { + ioe.printStackTrace(); + } + finally + { + if (dest != null) + IO.close(dest); + if (jin != null) + IO.close(jin); + IO.close(pOut); + } + } + }; + Thread th = new Thread(run); + th.start(); + return pIn; + } + + private Manifest _mf; + private URLConnection _conn; + + /** + * @param url The file url (for example) + * @param mf The manifest to use as a replacement to the jar file inside + * the file url. + * @throws IOException if an I/O error occurs. + */ + public WarURLConnection(URL url, Manifest mf) throws IOException + { + super(url); + _conn = url.openConnection(); + _conn.setDefaultUseCaches(Resource.getDefaultUseCaches()); + _mf = mf; + } + + @Override + public void connect() throws IOException + { + _conn.connect(); + } + + @Override + public InputStream getInputStream() throws IOException + { + return substitueManifest(_mf, _conn.getInputStream()); + } + + @Override + public void addRequestProperty(String key, String value) + { + _conn.addRequestProperty(key, value); + } + + @Override + public boolean equals(Object obj) + { + return _conn.equals(obj); + } + + @Override + public boolean getAllowUserInteraction() + { + return _conn.getAllowUserInteraction(); + } + + @Override + public int getConnectTimeout() + { + return _conn.getConnectTimeout(); + } + + @Override + public Object getContent() throws IOException + { + return _conn.getContent(); + } + + @Override + public Object getContent(Class[] classes) throws IOException + { + return _conn.getContent(classes); + } + + @Override + public String getContentEncoding() + { + return _conn.getContentEncoding(); + } + + @Override + public int getContentLength() + { + return _conn.getContentLength(); + } + + @Override + public String getContentType() + { + return _conn.getContentType(); + } + + @Override + public long getDate() + { + return _conn.getDate(); + } + + @Override + public boolean getDefaultUseCaches() + { + return _conn.getDefaultUseCaches(); + } + + @Override + public boolean getDoInput() + { + return _conn.getDoInput(); + } + + @Override + public boolean getDoOutput() + { + return _conn.getDoOutput(); + } + + @Override + public long getExpiration() + { + return _conn.getExpiration(); + } + + @Override + public String getHeaderField(int n) + { + return _conn.getHeaderField(n); + } + + @Override + public String getHeaderField(String name) + { + return _conn.getHeaderField(name); + } + + @Override + public long getHeaderFieldDate(String name, long defaultVal) + { + return _conn.getHeaderFieldDate(name, defaultVal); + } + + @Override + public int getHeaderFieldInt(String name, int defaultVal) + { + return _conn.getHeaderFieldInt(name, defaultVal); + } + + @Override + public String getHeaderFieldKey(int n) + { + return _conn.getHeaderFieldKey(n); + } + + @Override + public Map> getHeaderFields() + { + return _conn.getHeaderFields(); + } + + @Override + public long getIfModifiedSince() + { + return _conn.getIfModifiedSince(); + } + + @Override + public long getLastModified() + { + return _conn.getLastModified(); + } + + @Override + public OutputStream getOutputStream() throws IOException + { + return _conn.getOutputStream(); + } + + @Override + public Permission getPermission() throws IOException + { + return _conn.getPermission(); + } + + @Override + public int getReadTimeout() + { + return _conn.getReadTimeout(); + } + + @Override + public Map> getRequestProperties() + { + return _conn.getRequestProperties(); + } + + @Override + public String getRequestProperty(String key) + { + return _conn.getRequestProperty(key); + } + + @Override + public URL getURL() + { + return _conn.getURL(); + } + + @Override + public boolean getUseCaches() + { + return _conn.getUseCaches(); + } + + @Override + public void setAllowUserInteraction(boolean allowuserinteraction) + { + _conn.setAllowUserInteraction(allowuserinteraction); + } + + @Override + public void setConnectTimeout(int timeout) + { + _conn.setConnectTimeout(timeout); + } + + @Override + public void setDefaultUseCaches(boolean defaultusecaches) + { + _conn.setDefaultUseCaches(defaultusecaches); + } + + @Override + public void setDoInput(boolean doinput) + { + _conn.setDoInput(doinput); + } + + @Override + public void setDoOutput(boolean dooutput) + { + _conn.setDoOutput(dooutput); + } + + @Override + public void setIfModifiedSince(long ifmodifiedsince) + { + _conn.setIfModifiedSince(ifmodifiedsince); + } + + @Override + public void setReadTimeout(int timeout) + { + _conn.setReadTimeout(timeout); + } + + @Override + public void setRequestProperty(String key, String value) + { + _conn.setRequestProperty(key, value); + } + + @Override + public void setUseCaches(boolean usecaches) + { + _conn.setUseCaches(usecaches); + } +} diff --git a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/jettyhome/contexts/README b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/jettyhome/contexts/README new file mode 100644 index 00000000000..91f4a49ead3 --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/jettyhome/contexts/README @@ -0,0 +1,2 @@ +Default locations for standard context definitions. +Those applications are unlikely to have access to the OSGi framework currently. \ No newline at end of file diff --git a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/jettyhome/etc/README b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/jettyhome/etc/README new file mode 100644 index 00000000000..dbc9fa71564 --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/jettyhome/etc/README @@ -0,0 +1,2 @@ +This folder contains the default jetty configurations file for the server. +In production, it is likely to be a different folder outside of the jetty's bootstrap plugin. \ No newline at end of file diff --git a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/jettyhome/etc/jetty-deploy.xml b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/jettyhome/etc/jetty-deploy.xml new file mode 100644 index 00000000000..e630c9a766e --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/jettyhome/etc/jetty-deploy.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/jettyhome/etc/jetty-http.xml b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/jettyhome/etc/jetty-http.xml new file mode 100644 index 00000000000..cbf909a9518 --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/jettyhome/etc/jetty-http.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/jettyhome/etc/jetty.xml b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/jettyhome/etc/jetty.xml new file mode 100644 index 00000000000..ed1e4f3433e --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/jettyhome/etc/jetty.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + 10 + 200 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + java.naming.factory.initial + + + + java.naming.factory.url.pkgs + + + + diff --git a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/jettyhome/lib/ext/README b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/jettyhome/lib/ext/README new file mode 100644 index 00000000000..921d5f41bd4 --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/jettyhome/lib/ext/README @@ -0,0 +1,2 @@ +Place here the jars that are inserted in the jetty classloader. +As similar as possible as what this folder was for in the classical jetty installation. diff --git a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/jettyhome/logs/README b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/jettyhome/logs/README new file mode 100644 index 00000000000..ef503747e9d --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/jettyhome/logs/README @@ -0,0 +1,3 @@ +This folder contains the logs by default during development time. +In production or outside the eclipse PDE, it is likely that a different jetty.home +was set or jetty.log so it won't be here. \ No newline at end of file diff --git a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/jettyhome/resources/README b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/jettyhome/resources/README new file mode 100644 index 00000000000..f2baef21eff --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/jettyhome/resources/README @@ -0,0 +1,2 @@ +This folder is part of the class-loader shared by the webapps run in jetty. +Typically it contains log4j configuration files. \ No newline at end of file diff --git a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/jettyhome/webapps/README b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/jettyhome/webapps/README new file mode 100644 index 00000000000..08a57edf6ef --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/jettyhome/webapps/README @@ -0,0 +1,2 @@ +Default locations for standard web-applications. +Those applications are unlikely to have access to the OSGi framework currently. \ No newline at end of file diff --git a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/pom.xml b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/pom.xml new file mode 100644 index 00000000000..57bf6aa8c74 --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/pom.xml @@ -0,0 +1,124 @@ + + + 4.0.0 + + org.eclipse.jetty.ee11.osgi + jetty-ee11-osgi + 12.1.0-SNAPSHOT + + jetty-ee11-osgi-boot + EE11 :: OSGi :: Boot + Jetty OSGi Boot bundle + + ${project.groupId}.boot + org.eclipse.jetty.ee11.osgi.boot.* + + + + org.eclipse.jetty + jetty-jmx + + + org.eclipse.jetty + jetty-osgi + + + org.eclipse.jetty.ee11 + jetty-ee11-annotations + + + org.eclipse.jetty.ee11 + jetty-ee11-webapp + + + org.eclipse.platform + org.eclipse.osgi + + + org.eclipse.platform + org.eclipse.osgi.services + + + org.osgi + org.osgi.service.cm + + + org.osgi + org.osgi.service.event + + + org.osgi + org.osgi.util.tracker + + + + + + + src/main/resources + + + + + org.apache.felix + maven-bundle-plugin + true + + + org.eclipse.jetty.ee11.osgi.boot;singleton:=true + org.eclipse.jetty.ee11.osgi.boot.EE11Activator + org.eclipse.jetty.*;version="[$(version;===;${parsedVersion.osgiVersion}),$(version;==+;${parsedVersion.osgiVersion}))", org.eclipse.jetty.ee11.*;version="[$(version;===;${parsedVersion.osgiVersion}),$(version;==+;${parsedVersion.osgiVersion}))" + ${osgi.slf4j.import.packages}, + jakarta.mail;version="2.0";resolution:=optional, + jakarta.mail.event;version="2.0";resolution:=optional, + jakarta.mail.internet;version="2.0";resolution:=optional, + jakarta.mail.search;version="2.0";resolution:=optional, + jakarta.mail.util;version="2.0";resolution:=optional, + jakarta.servlet;version="[$(version;==;${jakarta.servlet.api.version}),$(version;+;${jakarta.servlet.api.version}))", + jakarta.servlet.http;version="[$(version;==;${jakarta.servlet.api.version}),$(version;+;${jakarta.servlet.api.version}))", + jakarta.transaction;version="2.0.0";resolution:=optional, + jakarta.transaction.xa;version="2.0.0";resolution:=optional, + org.objectweb.asm;version="$(version;=;${asm.version})";resolution:=optional, + org.osgi.framework, + org.osgi.service.cm;version="${osgi-service-cm-version}", + org.osgi.service.component;version="${osgi-service-component-version}", + org.osgi.service.event;version="${osgi-service-event-version}", + org.osgi.service.packageadmin, + org.osgi.service.startlevel;version="1.0.0", + org.osgi.service.url;version="1.0.0", + org.osgi.util.tracker;version="${osgi-util-tracker-version}", + org.xml.sax, + org.xml.sax.helpers, + org.eclipse.jetty.ee11.annotations;resolution:=optional, + * + osgi.extender; filter:="(osgi.extender=osgi.serviceloader.registrar)" + osgi.serviceloader; osgi.serviceloader=org.eclipse.jetty.ee11.webapp.Configuration + <_nouses>true + + + + + maven-antrun-plugin + + + + run + + process-resources + + + + + + + + + + + + + + + + + diff --git a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/annotations/AnnotationConfiguration.java b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/annotations/AnnotationConfiguration.java new file mode 100644 index 00000000000..f1bb7a1b9fc --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/annotations/AnnotationConfiguration.java @@ -0,0 +1,243 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.osgi.annotations; + +import java.util.HashSet; +import java.util.Set; + +import jakarta.servlet.ServletContainerInitializer; +import org.eclipse.jetty.ee11.annotations.AnnotationParser.Handler; +import org.eclipse.jetty.ee11.osgi.boot.OSGiMetaInfConfiguration; +import org.eclipse.jetty.ee11.webapp.Configuration; +import org.eclipse.jetty.ee11.webapp.WebAppContext; +import org.eclipse.jetty.osgi.OSGiWebappConstants; +import org.eclipse.jetty.util.FileID; +import org.eclipse.jetty.util.StringUtil; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.util.resource.ResourceFactory; +import org.eclipse.jetty.util.statistic.CounterStatistic; +import org.osgi.framework.Bundle; +import org.osgi.framework.Constants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Extend the AnnotationConfiguration to support OSGi: + * Look for annotations inside WEB-INF/lib and also in the fragments and required bundles. + * Discover them using a scanner adapted to OSGi instead of the jarscanner. + */ +public class AnnotationConfiguration extends org.eclipse.jetty.ee11.annotations.AnnotationConfiguration +{ + private static final Logger LOG = LoggerFactory.getLogger(org.eclipse.jetty.ee11.annotations.AnnotationConfiguration.class); + + public class BundleParserTask extends ParserTask + { + + public BundleParserTask(AnnotationParser parser, Set handlers, Resource resource) + { + super(parser, handlers, resource); + } + + @Override + public Void call() throws Exception + { + if (_parser != null) + { + org.eclipse.jetty.ee11.osgi.annotations.AnnotationParser osgiAnnotationParser = (org.eclipse.jetty.ee11.osgi.annotations.AnnotationParser)_parser; + Bundle bundle = osgiAnnotationParser.getBundle(_resource); + if (_stat != null) + _stat.start(); + osgiAnnotationParser.parse(_handlers, bundle); + if (_stat != null) + _stat.end(); + } + return null; + } + } + + public AnnotationConfiguration() + { + } + + @Override + public Class replaces() + { + return org.eclipse.jetty.ee11.annotations.AnnotationConfiguration.class; + } + + /** + * This parser scans the bundles using the OSGi APIs instead of assuming a jar. + */ + @Override + protected org.eclipse.jetty.ee11.annotations.AnnotationParser createAnnotationParser(int platform) + { + return new AnnotationParser(platform); + } + + @Override + protected Resource getJarFor(WebAppContext context, ServletContainerInitializer service) + { + Resource resource = super.getJarFor(context, service); + // TODO This is not correct, but implemented like this to be bug for bug compatible + // with previous implementation that could only handle actual jars and not bundles. + if (resource != null && !FileID.isJavaArchive(resource.getURI())) + return null; + return resource; + } + + /** + * Here is the order in which jars and osgi artifacts are scanned for discoverable annotations. + *
    + *
  1. The container jars are scanned.
  2. + *
  3. The WEB-INF/classes are scanned
  4. + *
  5. The osgi fragment to the web bundle are parsed.
  6. + *
  7. The WEB-INF/lib are scanned
  8. + *
  9. The required bundles are parsed
  10. + *
+ */ + @Override + public void parseWebInfLib(State state, org.eclipse.jetty.ee11.annotations.AnnotationParser parser) + throws Exception + { + AnnotationParser oparser = (AnnotationParser)parser; + + if (state._webInfLibStats == null) + state._webInfLibStats = new CounterStatistic(); + + WebAppContext context = state._context; + Bundle webbundle = (Bundle)context.getAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE); + @SuppressWarnings("unchecked") + Set fragAndRequiredBundles = (Set)context.getAttribute(OSGiMetaInfConfiguration.FRAGMENT_AND_REQUIRED_BUNDLES); + if (fragAndRequiredBundles != null) + { + //index and scan fragments + for (Bundle bundle : fragAndRequiredBundles) + { + //skip bundles that have been uninstalled since we discovered them + if (bundle.getState() == Bundle.UNINSTALLED) + continue; + + Resource bundleRes = oparser.indexBundle(ResourceFactory.of(context), bundle); + if (!context.getMetaData().getWebInfResources(false).contains(bundleRes)) + { + context.getMetaData().addWebInfResource(bundleRes); + } + + if (bundle.getHeaders().get(Constants.FRAGMENT_HOST) != null) + { + //a fragment indeed: + parseFragmentBundle(state, oparser, webbundle, bundle); + state._webInfLibStats.increment(); + } + } + } + //scan ourselves + oparser.indexBundle(ResourceFactory.of(context), webbundle); + parseWebBundle(state, oparser, webbundle); + state._webInfLibStats.increment(); + + //scan the WEB-INF/lib + super.parseWebInfLib(state, parser); + if (fragAndRequiredBundles != null) + { + //scan the required bundles + for (Bundle requiredBundle : fragAndRequiredBundles) + { + //skip bundles that have been uninstalled since we discovered them + if (requiredBundle.getState() == Bundle.UNINSTALLED) + continue; + + if (requiredBundle.getHeaders().get(Constants.FRAGMENT_HOST) == null) + { + //a bundle indeed: + parseRequiredBundle(state, oparser, webbundle, requiredBundle); + state._webInfLibStats.increment(); + } + } + } + } + + /** + * Scan a fragment bundle for servlet annotations + * + * @param state The webapp context state + * @param parser The parser + * @param webbundle The current webbundle + * @param fragmentBundle The OSGi fragment bundle to scan + * @throws Exception if unable to parse fragment bundle + */ + protected void parseFragmentBundle(State state, AnnotationParser parser, + Bundle webbundle, Bundle fragmentBundle) throws Exception + { + parseBundle(state, parser, webbundle, fragmentBundle); + } + + /** + * Scan a bundle required by the webbundle for servlet annotations + * + * @param state The webapp context state + * @param parser The parser + * @param webbundle The current webbundle + * @throws Exception if unable to parse the web bundle + */ + protected void parseWebBundle(State state, AnnotationParser parser, Bundle webbundle) + throws Exception + { + parseBundle(state, parser, webbundle, webbundle); + } + + @Override + public void parseWebInfClasses(State state, org.eclipse.jetty.ee11.annotations.AnnotationParser parser) + { + WebAppContext context = state._context; + Bundle webbundle = (Bundle)context.getAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE); + String bundleClasspath = webbundle.getHeaders().get(Constants.BUNDLE_CLASSPATH); + //only scan WEB-INF/classes if we didn't already scan it with parseWebBundle + if (StringUtil.isBlank(bundleClasspath) || !bundleClasspath.contains("WEB-INF/classes")) + super.parseWebInfClasses(state, parser); + } + + /** + * Scan a bundle required by the webbundle for servlet annotations + * + * @param state The webapp annotation parse state + * @param parser The parser + * @param webbundle The current webbundle + * @param requiredBundle The OSGi required bundle to scan + * @throws Exception if unable to parse the required bundle + */ + protected void parseRequiredBundle(State state, AnnotationParser parser, + Bundle webbundle, Bundle requiredBundle) throws Exception + { + parseBundle(state, parser, webbundle, requiredBundle); + } + + protected void parseBundle(State state, AnnotationParser parser, Bundle webbundle, Bundle bundle) + { + + Resource bundleRes = parser.getResource(bundle); + Set handlers = new HashSet<>(state._discoverableAnnotationHandlers); + if (state._classInheritanceHandler != null) + handlers.add(state._classInheritanceHandler); + handlers.addAll(state._containerInitializerAnnotationHandlers); + + if (state._parserTasks != null) + { + BundleParserTask task = new BundleParserTask(parser, handlers, bundleRes); + state._parserTasks.add(task); + if (LOG.isDebugEnabled()) + task.setStatistic(new TimeStatistic()); + } + } +} diff --git a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/annotations/AnnotationParser.java b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/annotations/AnnotationParser.java new file mode 100644 index 00000000000..00188932f3e --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/annotations/AnnotationParser.java @@ -0,0 +1,141 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.osgi.annotations; + +import java.io.File; +import java.net.URI; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import org.eclipse.jetty.osgi.util.BundleFileLocatorHelperFactory; +import org.eclipse.jetty.util.FileID; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.util.resource.ResourceFactory; +import org.osgi.framework.Bundle; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + */ +public class AnnotationParser extends org.eclipse.jetty.ee11.annotations.AnnotationParser +{ + private static final Logger LOG = LoggerFactory.getLogger(AnnotationParser.class); + + private Set _parsed = ConcurrentHashMap.newKeySet(); + + private ConcurrentHashMap _uriToBundle = new ConcurrentHashMap<>(); + private ConcurrentHashMap _bundleToResource = new ConcurrentHashMap<>(); + private ConcurrentHashMap _resourceToBundle = new ConcurrentHashMap<>(); + private ConcurrentHashMap _bundleToUri = new ConcurrentHashMap<>(); + + public AnnotationParser() + { + super(); + } + + public AnnotationParser(int platform) + { + super(platform); + } + + /** + * Keep track of a jetty URI Resource and its associated OSGi bundle. + * + *@param resourceFactory the ResourceFactory to convert bundle location + * @param bundle the bundle to index + * @return the resource for the bundle + * @throws Exception if unable to create the resource reference + */ + public Resource indexBundle(ResourceFactory resourceFactory, Bundle bundle) throws Exception + { + File bundleFile = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(bundle); + Resource resource = resourceFactory.newResource(bundleFile.toURI()); + URI uri = resource.getURI(); + _uriToBundle.putIfAbsent(uri, bundle); + _bundleToUri.putIfAbsent(bundle, uri); + _bundleToResource.putIfAbsent(bundle, resource); + _resourceToBundle.putIfAbsent(resource, bundle); + return resource; + } + + protected URI getURI(Bundle bundle) + { + return _bundleToUri.get(bundle); + } + + protected Resource getResource(Bundle bundle) + { + return _bundleToResource.get(bundle); + } + + protected Bundle getBundle(Resource resource) + { + return _resourceToBundle.get(resource); + } + + public void parse(Set handlers, Bundle bundle) + throws Exception + { + + Resource bundleResource = _bundleToResource.get(bundle); + if (bundleResource == null) + return; + + //if already added, it is already parsed + if (!_parsed.add(_bundleToUri.get(bundle))) + return; + + parse(handlers, bundleResource); + } + + @Override + public void parse(final Set handlers, Resource r) throws Exception + { + if (r == null) + return; + + if (!r.exists()) + return; + + if (FileID.isJavaArchive(r.getPath())) + { + parseJar(handlers, r); + return; + } + + if (r.isDirectory()) + { + parseDir(handlers, r); + return; + } + + if (FileID.isClassFile(r.getPath())) + { + parseClass(handlers, null, r.getPath()); + } + + //Not already parsed, it could be a file that actually is compressed but does not have + //.jar/.zip etc extension, such as equinox urls, so try to parse it + try + { + parseJar(handlers, r); + } + catch (Exception e) + { + if (LOG.isDebugEnabled()) + LOG.warn("Resource not able to be scanned for classes: {}", r); + } + } +} diff --git a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/EE11Activator.java b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/EE11Activator.java new file mode 100644 index 00000000000..93bf311400f --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/EE11Activator.java @@ -0,0 +1,615 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.osgi.boot; + +import java.io.File; +import java.net.URI; +import java.net.URL; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.eclipse.jetty.deploy.App; +import org.eclipse.jetty.deploy.AppProvider; +import org.eclipse.jetty.deploy.DeploymentManager; +import org.eclipse.jetty.ee11.webapp.Configuration; +import org.eclipse.jetty.ee11.webapp.Configurations; +import org.eclipse.jetty.ee11.webapp.WebAppClassLoader; +import org.eclipse.jetty.ee11.webapp.WebAppContext; +import org.eclipse.jetty.osgi.AbstractContextProvider; +import org.eclipse.jetty.osgi.BundleContextProvider; +import org.eclipse.jetty.osgi.BundleWebAppProvider; +import org.eclipse.jetty.osgi.ContextFactory; +import org.eclipse.jetty.osgi.OSGiApp; +import org.eclipse.jetty.osgi.OSGiServerConstants; +import org.eclipse.jetty.osgi.OSGiWebappConstants; +import org.eclipse.jetty.osgi.util.BundleFileLocatorHelperFactory; +import org.eclipse.jetty.osgi.util.FakeURLClassLoader; +import org.eclipse.jetty.osgi.util.OSGiClassLoader; +import org.eclipse.jetty.osgi.util.ServerClasspathContributor; +import org.eclipse.jetty.osgi.util.Util; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.util.FileID; +import org.eclipse.jetty.util.StringUtil; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.util.resource.ResourceFactory; +import org.eclipse.jetty.util.resource.URLResourceFactory; +import org.eclipse.jetty.xml.XmlConfiguration; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.osgi.util.tracker.ServiceTracker; +import org.osgi.util.tracker.ServiceTrackerCustomizer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * EE11Activator + *

+ * Enable deployment of webapps/contexts to E10E + */ +public class EE11Activator implements BundleActivator +{ + private static final Logger LOG = LoggerFactory.getLogger(EE11Activator.class); + + public static final String ENVIRONMENT = "ee11"; + + private static Collection __serverClasspathContributors = new ArrayList<>(); + + public static void registerServerClasspathContributor(ServerClasspathContributor contributor) + { + __serverClasspathContributors.add(contributor); + } + + public static void unregisterServerClasspathContributor(ServerClasspathContributor contributor) + { + __serverClasspathContributors.remove(contributor); + } + + public static Collection getServerClasspathContributors() + { + return __serverClasspathContributors; + } + + /** + * ServerTracker + * + * Tracks appearance of Server instances as OSGi services, and then configures them + * for deployment of EE11 contexts and webapps. + * + */ + public static class ServerTracker implements ServiceTrackerCustomizer + { + private Bundle _myBundle = null; + + public ServerTracker(Bundle bundle) + { + _myBundle = bundle; + } + + @Override + public Object addingService(ServiceReference sr) + { + Bundle contributor = sr.getBundle(); + Server server = contributor.getBundleContext().getService(sr); + //find bundles that should be on the container classpath and convert to URLs + List contributedURLs = new ArrayList<>(); + List contributedBundles = new ArrayList<>(); + Collection serverClasspathContributors = getServerClasspathContributors(); + serverClasspathContributors.stream().forEach(c -> contributedBundles.addAll(c.getScannableBundles())); + contributedBundles.stream().forEach(b -> contributedURLs.addAll(convertBundleToURL(b))); + + if (!contributedURLs.isEmpty()) + { + //There should already be a default set up by the JettyServerFactory + ClassLoader serverClassLoader = (ClassLoader)server.getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); + if (serverClassLoader != null) + { + server.setAttribute(OSGiServerConstants.SERVER_CLASSLOADER, + new FakeURLClassLoader(serverClassLoader, contributedURLs.toArray(new URL[contributedURLs.size()]))); + + if (LOG.isDebugEnabled()) + LOG.debug("Server classloader for contexts = {}", server.getAttribute(OSGiServerConstants.SERVER_CLASSLOADER)); + } + server.setAttribute(OSGiServerConstants.SERVER_CLASSPATH_BUNDLES, contributedBundles); + } + + Optional deployer = getDeploymentManager(server); + BundleWebAppProvider webAppProvider = null; + BundleContextProvider contextProvider = null; + + String containerScanBundlePattern = null; + if (contributedBundles != null) + { + StringBuffer strbuff = new StringBuffer(); + contributedBundles.stream().forEach(b -> strbuff.append(b.getSymbolicName()).append("|")); + + if (strbuff.length() > 0) + containerScanBundlePattern = strbuff.toString().substring(0, strbuff.length() - 1); + } + + if (deployer.isPresent()) + { + for (AppProvider provider : deployer.get().getAppProviders()) + { + if (BundleContextProvider.class.isInstance(provider) && ENVIRONMENT.equalsIgnoreCase(provider.getEnvironmentName())) + contextProvider = BundleContextProvider.class.cast(provider); + if (BundleWebAppProvider.class.isInstance(provider) && ENVIRONMENT.equalsIgnoreCase(provider.getEnvironmentName())) + webAppProvider = BundleWebAppProvider.class.cast(provider); + } + if (contextProvider == null) + { + contextProvider = new BundleContextProvider(ENVIRONMENT, server, new EE11ContextFactory(_myBundle)); + deployer.get().addAppProvider(contextProvider); + } + + if (webAppProvider == null) + { + webAppProvider = new BundleWebAppProvider(ENVIRONMENT, server, new EE11WebAppFactory(_myBundle)); + deployer.get().addAppProvider(webAppProvider); + } + + //ensure the providers are configured with the extra bundles that must be scanned from the container classpath + if (containerScanBundlePattern != null) + { + contextProvider.getProperties().put(OSGiMetaInfConfiguration.CONTAINER_BUNDLE_PATTERN, containerScanBundlePattern); + webAppProvider.getProperties().put(OSGiMetaInfConfiguration.CONTAINER_BUNDLE_PATTERN, containerScanBundlePattern); + } + } + else + LOG.info("No DeploymentManager for Server {}", server); + + try + { + if (!server.isStarted()) + server.start(); + } + catch (Exception e) + { + LOG.warn("Failed to start server {}", server); + } + return server; + } + + @Override + public void modifiedService(ServiceReference reference, Object service) + { + removedService(reference, service); + addingService(reference); + } + + @Override + public void removedService(ServiceReference reference, Object service) + { + } + + private Optional getDeploymentManager(Server server) + { + Collection deployers = server.getBeans(DeploymentManager.class); + return deployers.stream().findFirst(); + } + + private List convertBundleToURL(Bundle bundle) + { + List urls = new ArrayList<>(); + try + { + File file = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(bundle); + + if (file.isDirectory()) + { + for (File f : file.listFiles()) + { + if (FileID.isJavaArchive(f.getName()) && f.isFile()) + { + urls.add(f.toURI().toURL()); + } + else if (f.isDirectory() && f.getName().equals("lib")) + { + for (File f2 : file.listFiles()) + { + if (FileID.isJavaArchive(f2.getName()) && f2.isFile()) + { + urls.add(f2.toURI().toURL()); + } + } + } + } + urls.add(file.toURI().toURL()); + } + else + { + urls.add(file.toURI().toURL()); + } + } + catch (Exception e) + { + LOG.warn("Unable to convert bundle {} to url", bundle, e); + } + + return urls; + } + } + + public static class EE11ContextFactory implements ContextFactory + { + private Bundle _myBundle; + + public EE11ContextFactory(Bundle bundle) + { + _myBundle = bundle; + } + + @Override + public ContextHandler createContextHandler(AbstractContextProvider provider, App app) + throws Exception + { + OSGiApp osgiApp = OSGiApp.class.cast(app); + String jettyHome = (String)app.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME); + Path jettyHomePath = (StringUtil.isBlank(jettyHome) ? null : Paths.get(jettyHome)); + + ContextHandler contextHandler = new ContextHandler(); + + //Make base resource that of the bundle + contextHandler.setBaseResource(osgiApp.getBundleResource()); + + // provides access to core classes + ClassLoader coreLoader = (ClassLoader)osgiApp.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); + if (LOG.isDebugEnabled()) + LOG.debug("Core classloader = {}", coreLoader.getClass()); + + //provide access to all ee11 classes + ClassLoader environmentLoader = new OSGiClassLoader(coreLoader, _myBundle); + + //Use a classloader that knows about the common jetty parent loader, and also the bundle + OSGiClassLoader classLoader = new OSGiClassLoader(environmentLoader, osgiApp.getBundle()); + contextHandler.setClassLoader(classLoader); + + //Apply any context xml file + String tmp = osgiApp.getProperties().get(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH); + final URI contextXmlURI = Util.resolvePathAsLocalizedURI(tmp, osgiApp.getBundle(), jettyHomePath); + + if (contextXmlURI != null) + { + ClassLoader oldLoader = Thread.currentThread().getContextClassLoader(); + try + { + Thread.currentThread().setContextClassLoader(contextHandler.getClassLoader()); + WebAppClassLoader.runWithServerClassAccess(() -> + { + XmlConfiguration xmlConfiguration = new XmlConfiguration(ResourceFactory.of(contextHandler).newResource(contextXmlURI)); + WebAppClassLoader.runWithServerClassAccess(() -> + { + Map properties = new HashMap<>(); + xmlConfiguration.getIdMap().put("Server", osgiApp.getDeploymentManager().getServer()); + properties.put(OSGiWebappConstants.JETTY_BUNDLE_ROOT, osgiApp.getPath().toUri().toString()); + properties.put(OSGiServerConstants.JETTY_HOME, (String)osgiApp.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME)); + xmlConfiguration.getProperties().putAll(properties); + xmlConfiguration.configure(contextHandler); + return null; + }); + return null; + }); + } + catch (Exception e) + { + LOG.warn("Error applying context xml", e); + throw e; + } + finally + { + Thread.currentThread().setContextClassLoader(oldLoader); + } + } + + //osgi Enterprise Spec r4 p.427 + contextHandler.setAttribute(OSGiWebappConstants.OSGI_BUNDLECONTEXT, osgiApp.getBundle().getBundleContext()); + + //make sure we protect also the osgi dirs specified by OSGi Enterprise spec + String[] targets = contextHandler.getProtectedTargets(); + int length = (targets == null ? 0 : targets.length); + + String[] updatedTargets = null; + if (targets != null) + { + updatedTargets = new String[length + OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length]; + System.arraycopy(targets, 0, updatedTargets, 0, length); + } + else + updatedTargets = new String[OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length]; + System.arraycopy(OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS, 0, updatedTargets, length, OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length); + contextHandler.setProtectedTargets(updatedTargets); + + return contextHandler; + } + } + + public static class EE11WebAppFactory implements ContextFactory + { + private Bundle _myBundle; + + public EE11WebAppFactory(Bundle bundle) + { + _myBundle = bundle; + } + + @Override + public ContextHandler createContextHandler(AbstractContextProvider provider, App app) + throws Exception + { + if (!(app instanceof OSGiApp osgiApp)) + throw new IllegalArgumentException("App is not OSGi"); + + String jettyHome = (String)app.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME); + Path jettyHomePath = StringUtil.isBlank(jettyHome) ? null : ResourceFactory.of(provider.getServer()).newResource(jettyHome).getPath(); + + WebAppContext webApp = new WebAppContext(); + + //Apply defaults from the deployer providers + webApp.initializeDefaults(provider.getProperties()); + + // provides access to core classes + ClassLoader coreLoader = (ClassLoader)osgiApp.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); + if (LOG.isDebugEnabled()) + LOG.debug("Core classloader = {}", coreLoader); + + //provide access to all ee11 classes + ClassLoader environmentLoader = new OSGiClassLoader(coreLoader, _myBundle); + if (LOG.isDebugEnabled()) + LOG.debug("Environment classloader = {}", environmentLoader); + + //Ensure Configurations.getKnown is called with a classloader that can see all of the ee11 and core classes + + ClassLoader old = Thread.currentThread().getContextClassLoader(); + try + { + Thread.currentThread().setContextClassLoader(environmentLoader); + WebAppClassLoader.runWithServerClassAccess(() -> + { + Configurations.getKnown(); + return null; + }); + } + finally + { + Thread.currentThread().setContextClassLoader(old); + } + + webApp.setConfigurations(Configurations.getKnown().stream() + .filter(c -> c.isEnabledByDefault()) + .toArray(Configuration[]::new)); + + //Make a webapp classloader + OSGiWebappClassLoader webAppLoader = new OSGiWebappClassLoader(environmentLoader, webApp, osgiApp.getBundle()); + + //Handle Require-TldBundle + //This is a comma separated list of names of bundles that contain tlds that this webapp uses. + //We add them to the webapp classloader. + String requireTldBundles = (String)osgiApp.getProperties().get(OSGiWebappConstants.REQUIRE_TLD_BUNDLE); + + List pathsToTldBundles = Util.getPathsToBundlesBySymbolicNames(requireTldBundles, osgiApp.getBundle().getBundleContext()); + for (Path p : pathsToTldBundles) + webAppLoader.addClassPath(p.toUri().toString()); + + //Set up configuration from manifest headers + //extra classpath + String extraClasspath = osgiApp.getProperties().get(OSGiWebappConstants.JETTY_EXTRA_CLASSPATH); + if (extraClasspath != null) + webApp.setExtraClasspath(extraClasspath); + + webApp.setClassLoader(webAppLoader); + + //Take care of extra provider properties + webApp.setAttribute(OSGiMetaInfConfiguration.CONTAINER_BUNDLE_PATTERN, provider.getProperties().get(OSGiMetaInfConfiguration.CONTAINER_BUNDLE_PATTERN)); + + //TODO needed? + webApp.setAttribute(OSGiWebappConstants.REQUIRE_TLD_BUNDLE, requireTldBundles); + + //Set up some attributes + // rfc66 + webApp.setAttribute(OSGiWebappConstants.RFC66_OSGI_BUNDLE_CONTEXT, osgiApp.getBundle().getBundleContext()); + + // spring-dm-1.2.1 looks for the BundleContext as a different attribute. + // not a spec... but if we want to support + // org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext + // then we need to do this to: + webApp.setAttribute("org.springframework.osgi.web." + BundleContext.class.getName(), osgiApp.getBundle().getBundleContext()); + + // also pass the bundle directly. sometimes a bundle does not have a + // bundlecontext. + // it is still useful to have access to the Bundle from the servlet + // context. + webApp.setAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE, osgiApp.getBundle()); + + + // apply any META-INF/context.xml file that is found to configure + // the webapp first + //First try looking for one in /META-INF + URI tmpUri = null; + + URL contextXmlURL = Util.getLocalizedEntry("/META-INF/jetty-webapp-context.xml", osgiApp.getBundle()); + if (contextXmlURL != null) + tmpUri = contextXmlURL.toURI(); + + //Then look in the property OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH and apply the first one + if (contextXmlURL == null) + { + String tmp = osgiApp.getProperties().get(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH); + if (tmp != null) + { + String[] filenames = tmp.split("[,;]"); + tmpUri = Util.resolvePathAsLocalizedURI(filenames[0], osgiApp.getBundle(), jettyHomePath); + } + } + + //apply a context xml if there is one + if (tmpUri != null) + { + final URI contextXmlUri = tmpUri; + ClassLoader oldLoader = Thread.currentThread().getContextClassLoader(); + try + { + Thread.currentThread().setContextClassLoader(webApp.getClassLoader()); + WebAppClassLoader.runWithServerClassAccess(() -> + { + XmlConfiguration xmlConfiguration = new XmlConfiguration(ResourceFactory.of(webApp).newResource(contextXmlUri)); + WebAppClassLoader.runWithServerClassAccess(() -> + { + Map properties = new HashMap<>(); + xmlConfiguration.getIdMap().put("Server", osgiApp.getDeploymentManager().getServer()); + properties.put(OSGiWebappConstants.JETTY_BUNDLE_ROOT, osgiApp.getPath().toUri().toString()); + properties.put(OSGiServerConstants.JETTY_HOME, (String)osgiApp.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME)); + xmlConfiguration.getProperties().putAll(properties); + xmlConfiguration.configure(webApp); + return null; + }); + return null; + }); + } + catch (Exception e) + { + LOG.warn("Error applying context xml", e); + throw e; + } + finally + { + Thread.currentThread().setContextClassLoader(oldLoader); + } + } + + //ensure the context path is set + webApp.setContextPath(osgiApp.getContextPath()); + + //osgi Enterprise Spec r4 p.427 + webApp.setAttribute(OSGiWebappConstants.OSGI_BUNDLECONTEXT, osgiApp.getBundle().getBundleContext()); + + //Indicate the webapp has been deployed, so that we don't try and redeploy again + webApp.setAttribute(OSGiWebappConstants.WATERMARK, OSGiWebappConstants.WATERMARK); + + //make sure we protect also the osgi dirs specified by OSGi Enterprise spec + String[] targets = webApp.getProtectedTargets(); + String[] updatedTargets = null; + if (targets != null) + { + updatedTargets = new String[targets.length + OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length]; + System.arraycopy(targets, 0, updatedTargets, 0, targets.length); + } + else + updatedTargets = new String[OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length]; + System.arraycopy(OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS, 0, updatedTargets, targets.length, OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length); + webApp.setProtectedTargets(updatedTargets); + + + Path bundlePath = osgiApp.getPath(); + + Resource bundleResource = osgiApp.getBundleResource(); + + String pathToResourceBase = osgiApp.getPathToResourceBase(); + + //if the path wasn't set or it was ., then it is the root of the bundle's installed location + if (StringUtil.isBlank(pathToResourceBase) || ".".equals(pathToResourceBase)) + { + if (LOG.isDebugEnabled()) + LOG.debug("Webapp base using bundle install location: {}", bundleResource); + webApp.setWarResource(bundleResource); + } + else + { + if (pathToResourceBase.startsWith("/") || pathToResourceBase.startsWith("file:")) + { + //The baseResource is outside of the bundle + Path p = Paths.get(pathToResourceBase); + webApp.setWar(p.toUri().toString()); + if (LOG.isDebugEnabled()) + LOG.debug("Webapp base using absolute location: {}", p); + } + else + { + //The baseResource is relative to the root of the bundle + Resource r = bundleResource.resolve(pathToResourceBase); + webApp.setWarResource(r); + if (LOG.isDebugEnabled()) + LOG.debug("Webapp base using path relative to bundle unpacked install location: {}", r); + } + } + + //web.xml + String tmp = osgiApp.getProperties().get(OSGiWebappConstants.JETTY_WEB_XML_PATH); + if (!StringUtil.isBlank(tmp)) + { + URI webXml = Util.resolvePathAsLocalizedURI(tmp, osgiApp.getBundle(), jettyHomePath); + if (webXml != null) + webApp.setDescriptor(webXml.toString()); + } + + // webdefault-ee11.xml + tmp = osgiApp.getProperties().get(OSGiWebappConstants.JETTY_DEFAULT_WEB_XML_PATH); + if (tmp != null) + { + URI defaultWebXml = Util.resolvePathAsLocalizedURI(tmp, osgiApp.getBundle(), jettyHomePath); + if (defaultWebXml != null) + { + webApp.setDefaultsDescriptor(defaultWebXml.toString()); + } + } + + return webApp; + } + } + + private PackageAdminServiceTracker _packageAdminServiceTracker; + private ServiceTracker _tracker; + + /** + * Track jetty Server instances and add ability to deploy EE11 contexts/webapps + * + * @param context the bundle context + */ + @Override + public void start(final BundleContext context) throws Exception + { + // track other bundles and fragments attached to this bundle that we + // should activate. + _packageAdminServiceTracker = new PackageAdminServiceTracker(context); + + //track jetty Server instances + _tracker = new ServiceTracker(context, context.createFilter("(objectclass=" + Server.class.getName() + ")"), new ServerTracker(context.getBundle())); + _tracker.open(); + + //register for bundleresource: url resource handling + ResourceFactory.registerResourceFactory("bundleresource", new URLResourceFactory()); + } + + /** + * Stop the activator. + * + * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext) + */ + @Override + public void stop(BundleContext context) throws Exception + { + if (_tracker != null) + { + _tracker.close(); + _tracker = null; + } + } +} diff --git a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/OSGiMetaInfConfiguration.java b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/OSGiMetaInfConfiguration.java new file mode 100644 index 00000000000..d5e112fa34e --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/OSGiMetaInfConfiguration.java @@ -0,0 +1,343 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.osgi.boot; + +import java.io.File; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.StringTokenizer; +import java.util.TreeMap; +import java.util.regex.Pattern; + +import org.eclipse.jetty.ee11.webapp.Configuration; +import org.eclipse.jetty.ee11.webapp.MetaInfConfiguration; +import org.eclipse.jetty.ee11.webapp.WebAppContext; +import org.eclipse.jetty.osgi.OSGiWebappConstants; +import org.eclipse.jetty.osgi.util.BundleFileLocatorHelperFactory; +import org.eclipse.jetty.osgi.util.Util; +import org.eclipse.jetty.util.FileID; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.util.resource.ResourceFactory; +import org.osgi.framework.Bundle; +import org.osgi.framework.FrameworkUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * OSGiMetaInfConfiguration + * + * Handle adding resources found in bundles. + */ +public class OSGiMetaInfConfiguration extends MetaInfConfiguration +{ + private static final Logger LOG = LoggerFactory.getLogger(OSGiMetaInfConfiguration.class); + + /** + * Comma separated list of symbolic names of bundles that contain tlds that should be considered + * as on the container classpath + */ + public static final String SYS_PROP_TLD_BUNDLES = "org.eclipse.jetty.osgi.tldbundles"; + /** + * Regex of symbolic names of bundles that should be considered to be on the container classpath + */ + public static final String CONTAINER_BUNDLE_PATTERN = "org.eclipse.jetty.server.webapp.containerIncludeBundlePattern"; + public static final String FRAGMENT_AND_REQUIRED_BUNDLES = "org.eclipse.jetty.ee11.osgi.fragmentAndRequiredBundles"; + public static final String FRAGMENT_AND_REQUIRED_RESOURCES = "org.eclipse.jetty.ee11.osgi.fragmentAndRequiredResources"; + + @Override + public Class replaces() + { + return MetaInfConfiguration.class; + } + + /** + * Check to see if there have been any bundle symbolic names added of bundles that should be + * regarded as being on the container classpath, and scanned for fragments, tlds etc etc. + * This can be defined in: + *

    + *
  1. SystemProperty SYS_PROP_TLD_BUNDLES
  2. + *
  3. DeployerManager.setContextAttribute CONTAINER_BUNDLE_PATTERN
  4. + *
+ * + * We also allow individual bundles to specify particular bundles that might include TLDs via the Require-Tlds + * MANIFEST.MF header. + * + * @see org.eclipse.jetty.ee11.webapp.WebInfConfiguration#preConfigure(org.eclipse.jetty.ee11.webapp.WebAppContext) + */ + @Override + public void preConfigure(final WebAppContext context) throws Exception + { + super.preConfigure(context); + } + + @Override + protected void scanJars(final WebAppContext context) throws Exception + { + //Check to see if there have been any bundle symbolic names added of bundles that should be + //regarded as being on the container classpath, and scanned for fragments, tlds etc etc. + //This can be defined in: + // 1. SystemProperty SYS_PROP_TLD_BUNDLES + // 2. DeployerManager.setContextAttribute CONTAINER_BUNDLE_PATTERN + String tmp = (String)context.getAttribute(CONTAINER_BUNDLE_PATTERN); + Pattern pattern = (tmp == null ? null : Pattern.compile(tmp)); + List names = new ArrayList(); + tmp = System.getProperty(SYS_PROP_TLD_BUNDLES); + if (tmp != null) + { + StringTokenizer tokenizer = new StringTokenizer(tmp, ", \n\r\t", false); + while (tokenizer.hasMoreTokens()) + { + names.add(tokenizer.nextToken()); + } + } + + HashSet matchingResources = new HashSet<>(); + if (!names.isEmpty() || pattern != null) + { + Bundle[] bundles = FrameworkUtil.getBundle(OSGiMetaInfConfiguration.class).getBundleContext().getBundles(); + + for (Bundle bundle : bundles) + { + if (LOG.isDebugEnabled()) + LOG.debug("Checking bundle {}:{}", bundle.getBundleId(), bundle.getSymbolicName()); + if (pattern != null) + { + // if bundle symbolic name matches the pattern + if (pattern.matcher(bundle.getSymbolicName()).matches()) + { + //get the file location of the jar and put it into the list of container jars that will be scanned for stuff (including tlds) + matchingResources.addAll(getBundleAsResource(ResourceFactory.of(context), bundle)); + } + } + if (names != null) + { + //if there is an explicit bundle name, then check if it matches + if (names.contains(bundle.getSymbolicName())) + matchingResources.addAll(getBundleAsResource(ResourceFactory.of(context), bundle)); + } + } + } + for (Resource r : matchingResources) + { + context.getMetaData().addContainerResource(r); + } + + super.scanJars(context); + } + + @Override + public void postConfigure(WebAppContext context) throws Exception + { + context.setAttribute(FRAGMENT_AND_REQUIRED_BUNDLES, null); + context.setAttribute(FRAGMENT_AND_REQUIRED_RESOURCES, null); + super.postConfigure(context); + } + + /** + * Consider the fragment bundles associated with the bundle of the webapp being deployed. + * + * @see org.eclipse.jetty.ee11.webapp.MetaInfConfiguration#findJars(org.eclipse.jetty.ee11.webapp.WebAppContext) + */ + @Override + protected List findJars(WebAppContext context) + throws Exception + { + List mergedResources = new ArrayList(); + //get jars from WEB-INF/lib if there are any + List webInfJars = super.findJars(context); + if (webInfJars != null) + mergedResources.addAll(webInfJars); + + //add fragment jars and any Required-Bundles as if in WEB-INF/lib of the associated webapp + Bundle[] bundles = PackageAdminServiceTracker.INSTANCE.getFragmentsAndRequiredBundles((Bundle)context.getAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE)); + if (bundles != null && bundles.length > 0) + { + @SuppressWarnings("unchecked") + Set fragsAndReqsBundles = (Set)context.getAttribute(FRAGMENT_AND_REQUIRED_BUNDLES); + if (fragsAndReqsBundles == null) + { + fragsAndReqsBundles = new HashSet(); + context.setAttribute(FRAGMENT_AND_REQUIRED_BUNDLES, fragsAndReqsBundles); + } + + @SuppressWarnings("unchecked") + Set fragsAndReqsResources = (Set)context.getAttribute(FRAGMENT_AND_REQUIRED_RESOURCES); + if (fragsAndReqsResources == null) + { + fragsAndReqsResources = new HashSet(); + context.setAttribute(FRAGMENT_AND_REQUIRED_RESOURCES, fragsAndReqsResources); + } + + for (Bundle b : bundles) + { + //skip bundles that are not installed + if (b.getState() == Bundle.UNINSTALLED) + continue; + + //add to context attribute storing associated fragments and required bundles + fragsAndReqsBundles.add(b); + File f = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(b); + Resource r = ResourceFactory.of(context).newResource(f.toURI()); + //add to convenience context attribute storing fragments and required bundles as Resources + fragsAndReqsResources.add(r); + mergedResources.add(r); + } + } + + return mergedResources; + } + + /** + * Allow fragments to supply some resources that are added to the baseResource of the webapp. + * + * The resources can be either prepended or appended to the baseResource. + * + * @see org.eclipse.jetty.ee11.webapp.WebInfConfiguration#configure(org.eclipse.jetty.ee11.webapp.WebAppContext) + */ + @Override + public void configure(WebAppContext context) throws Exception + { + TreeMap prependedResourcesPath = new TreeMap(); + TreeMap appendedResourcesPath = new TreeMap(); + + Bundle bundle = (Bundle)context.getAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE); + if (bundle != null) + { + @SuppressWarnings("unchecked") + Set fragments = (Set)context.getAttribute(FRAGMENT_AND_REQUIRED_BUNDLES); + if (fragments != null && !fragments.isEmpty()) + { + // sorted extra resource base found in the fragments. + // the resources are either overriding the resourcebase found in the + // web-bundle + // or appended. + // amongst each resource we sort them according to the alphabetical + // order + // of the name of the internal folder and the symbolic name of the + // fragment. + // this is useful to make sure that the lookup path of those + // resource base defined by fragments is always the same. + // This natural order could be abused to define the order in which + // the base resources are + // looked up. + for (Bundle frag : fragments) + { + String path = Util.getManifestHeaderValue(OSGiWebappConstants.JETTY_WAR_FRAGMENT_RESOURCE_PATH, frag.getHeaders()); + convertFragmentPathToResource(ResourceFactory.of(context), path, frag, appendedResourcesPath); + path = Util.getManifestHeaderValue(OSGiWebappConstants.JETTY_WAR_PREPEND_FRAGMENT_RESOURCE_PATH, frag.getHeaders()); + convertFragmentPathToResource(ResourceFactory.of(context), path, frag, prependedResourcesPath); + } + if (!appendedResourcesPath.isEmpty()) + { + LinkedHashSet resources = new LinkedHashSet<>(); + //Add in any existing setting of extra resource dirs + @SuppressWarnings("unchecked") + Set resourceDirs = (Set)context.getAttribute(MetaInfConfiguration.RESOURCE_DIRS); + if (resourceDirs != null && !resourceDirs.isEmpty()) + resources.addAll(resourceDirs); + //Then append the values from JETTY_WAR_FRAGMENT_FOLDER_PATH + resources.addAll(appendedResourcesPath.values()); + + context.setAttribute(MetaInfConfiguration.RESOURCE_DIRS, resources); + } + } + } + + super.configure(context); + + // place the prepended resources at the beginning of the contexts's resource base + if (!prependedResourcesPath.isEmpty()) + { + Resource[] resources = new Resource[1 + prependedResourcesPath.size()]; + System.arraycopy(prependedResourcesPath.values().toArray(new Resource[prependedResourcesPath.size()]), 0, resources, 0, prependedResourcesPath.size()); + resources[resources.length - 1] = context.getBaseResource(); + context.setBaseResource(ResourceFactory.combine(resources)); + } + } + + /** + * Resolves the bundle. Usually that would be a single URL per bundle. But we do some more work if there are jars + * embedded in the bundle. + */ + private List getBundleAsResource(ResourceFactory resourceFactory, Bundle bundle) + throws Exception + { + List resources = new ArrayList<>(); + + File file = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(bundle); + if (file.isDirectory()) + { + for (File f : file.listFiles()) + { + if (FileID.isJavaArchive(f.getName()) && f.isFile()) + { + resources.add(resourceFactory.newResource(f.toPath())); + } + else if (f.isDirectory() && f.getName().equals("lib")) + { + for (File f2 : file.listFiles()) + { + if (FileID.isJavaArchive(f2.getName()) && f2.isFile()) + { + resources.add(resourceFactory.newResource(f.toPath())); + } + } + } + } + resources.add(resourceFactory.newResource(file.toPath())); //TODO really??? + } + else + { + resources.add(resourceFactory.newResource(file.toPath())); + } + + return resources; + } + + /** + * Convert a path inside a fragment into a Resource + */ + private void convertFragmentPathToResource(ResourceFactory resourceFactory, String resourcePath, Bundle fragment, Map resourceMap) + throws Exception + { + if (resourcePath == null) + return; + + URL url = fragment.getEntry(resourcePath); + if (url == null) + { + throw new IllegalArgumentException("Unable to locate " + resourcePath + " inside the fragment '" + fragment.getSymbolicName() + "'"); + } + url = BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(url); + String key = resourcePath.startsWith("/") ? resourcePath.substring(1) : resourcePath; + + URI uri; + try + { + uri = url.toURI(); + } + catch (URISyntaxException e) + { + uri = new URI(url.toString().replaceAll(" ", "%20")); + } + resourceMap.put(key + ";" + fragment.getSymbolicName(), resourceFactory.newResource(uri)); + } +} diff --git a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/OSGiWebappClassLoader.java b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/OSGiWebappClassLoader.java new file mode 100644 index 00000000000..0665078accf --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/OSGiWebappClassLoader.java @@ -0,0 +1,143 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.osgi.boot; + +import java.io.IOException; +import java.net.URL; +import java.util.Enumeration; + +import org.eclipse.jetty.ee11.webapp.WebAppClassLoader; +import org.eclipse.jetty.ee11.webapp.WebAppContext; +import org.eclipse.jetty.osgi.util.BundleClassLoaderHelperFactory; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleReference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * OSGiWebappClassLoader + * + * + * Extends the webapp classloader to also use the classloader of the Bundle defining the webapp. + */ +public class OSGiWebappClassLoader extends WebAppClassLoader implements BundleReference +{ + private static final Logger LOG = LoggerFactory.getLogger(OSGiWebappClassLoader.class.getName()); + + private ClassLoader _osgiBundleClassLoader; + + private Bundle _contributor; + + /** + * @param parent The parent classloader. + * @param context The WebAppContext + * @param contributor The bundle that defines this web-application. + * @throws IOException if unable to cerate the OSGiWebappClassLoader + */ + public OSGiWebappClassLoader(ClassLoader parent, WebAppContext context, Bundle contributor) + throws IOException + { + super(parent, context); + _contributor = contributor; + _osgiBundleClassLoader = BundleClassLoaderHelperFactory.getFactory().getHelper().getBundleClassLoader(contributor); + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException + { + try + { + return _osgiBundleClassLoader.loadClass(name); + } + catch (ClassNotFoundException cne) + { + try + { + + return super.findClass(name); + } + catch (ClassNotFoundException cne2) + { + throw cne; + } + } + } + + /** + * Returns the Bundle that defined this web-application. + * + * @return The Bundle object associated with this + * BundleReference. + */ + @Override + public Bundle getBundle() + { + return _contributor; + } + + @Override + public Enumeration getResources(String name) throws IOException + { + Enumeration osgiUrls = _osgiBundleClassLoader.getResources(name); + if (osgiUrls != null && osgiUrls.hasMoreElements()) + return osgiUrls; + + Enumeration urls = super.getResources(name); + return urls; + } + + @Override + public URL getResource(String name) + { + URL url = _osgiBundleClassLoader.getResource(name); + return url != null ? url : super.getResource(name); + } + + @Override + public URL findResource(String name) + { + URL url = _osgiBundleClassLoader.getResource(name); + return url != null ? url : super.findResource(name); + } + + /** + * Try to load the class from the bundle classloader. + * We do NOT load it as a resource as the WebAppClassLoader does because the + * url that is returned is an osgi-special url that does not play + * properly with WebAppClassLoader's method of extracting the class + * from the resource. This implementation directly asks the osgi + * bundle classloader to load the given class name. + * + * @see org.eclipse.jetty.ee11.webapp.WebAppClassLoader#loadAsResource(java.lang.String, boolean) + */ + @Override + protected Class loadAsResource(String name, boolean checkSystemResource) throws ClassNotFoundException + { + try + { + return _osgiBundleClassLoader.loadClass(name); + } + catch (ClassNotFoundException cne) + { + try + { + return super.loadAsResource(name, checkSystemResource); + } + catch (ClassNotFoundException cne2) + { + throw cne; + } + } + } +} diff --git a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/PackageAdminServiceTracker.java b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/PackageAdminServiceTracker.java new file mode 100644 index 00000000000..d48a1bc8bdf --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/PackageAdminServiceTracker.java @@ -0,0 +1,389 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.osgi.boot; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; + +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceEvent; +import org.osgi.framework.ServiceListener; +import org.osgi.framework.ServiceReference; +import org.osgi.service.packageadmin.PackageAdmin; +import org.osgi.service.startlevel.StartLevel; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * PackageAdminServiceTracker + *

+ * When the PackageAdmin service is activated we can look for the fragments + * attached to this bundle and do a fake "activate" on them. + *

+ * See particularly the jetty-ee9-osgi-boot-jsp fragment bundle that uses this + * facility. + */ +public class PackageAdminServiceTracker implements ServiceListener +{ + private static Logger LOG = LoggerFactory.getLogger(PackageAdminServiceTracker.class); + private BundleContext _context; + + private List _activatedFragments = new ArrayList<>(); + + private boolean _fragmentsWereActivated = false; + + // Use the deprecated StartLevel to stay compatible with older versions of + // OSGi. + private StartLevel _startLevel; + + private int _maxStartLevel = 6; + + public static PackageAdminServiceTracker INSTANCE = null; + + public PackageAdminServiceTracker(BundleContext context) + throws Exception + { + INSTANCE = this; + _context = context; + if (!setup()) + { + _context.addServiceListener(this, "(objectclass=" + PackageAdmin.class.getName() + ")"); + } + } + + /** + * @return true if the fragments were activated by this method. + */ + private boolean setup() + throws Exception + { + ServiceReference sr = _context.getServiceReference(PackageAdmin.class.getName()); + _fragmentsWereActivated = sr != null; + if (sr != null) + invokeFragmentActivators(sr); + + sr = _context.getServiceReference(StartLevel.class.getName()); + if (sr != null) + { + _startLevel = (StartLevel)_context.getService(sr); + try + { + _maxStartLevel = Integer.parseInt(System.getProperty("osgi.startLevel", "6")); + } + catch (Exception e) + { + // nevermind default on the usual. + _maxStartLevel = 6; + } + } + return _fragmentsWereActivated; + } + + /** + * Invokes the optional BundleActivator in each fragment. By convention the + * bundle activator for a fragment must be in the package that is defined by + * the symbolic name of the fragment and the name of the class must be + * 'FragmentActivator'. + * + * @param event The ServiceEvent object. + */ + @Override + public void serviceChanged(ServiceEvent event) + { + if (event.getType() == ServiceEvent.REGISTERED) + { + try + { + invokeFragmentActivators(event.getServiceReference()); + } + catch (Exception e) + { + LOG.warn("Error invoking fragment activators", e); + } + } + } + + /** + * Helper to access the PackageAdmin and return the fragments hosted by a + * bundle. when we drop the support for the older versions of OSGi, we will + * stop using the PackageAdmin service. + * + * @param bundle the bundle + * @return the bundle fragment list + */ + public Bundle[] getFragments(Bundle bundle) + { + ServiceReference sr = _context.getServiceReference(PackageAdmin.class.getName()); + if (sr == null) + { + // we should never be here really. + return null; + } + PackageAdmin admin = (PackageAdmin)_context.getService(sr); + return admin.getFragments(bundle); + } + + /** + * Returns the fragments and the required-bundles of a bundle. Recursively + * collect the required-bundles and fragment when the directive + * visibility:=reexport is added to a required-bundle. + * + * @param bundle the bundle + * @return the bundle fragment and required list + */ + public Bundle[] getFragmentsAndRequiredBundles(Bundle bundle) + { + ServiceReference sr = _context.getServiceReference(PackageAdmin.class.getName()); + if (sr == null) + { + // we should never be here really. + return null; + } + PackageAdmin admin = (PackageAdmin)_context.getService(sr); + LinkedHashMap deps = new LinkedHashMap<>(); + collectFragmentsAndRequiredBundles(bundle, admin, deps, false); + return deps.values().toArray(new Bundle[deps.size()]); + } + + /** + * Returns the fragments and the required-bundles. Collects them + * transitively when the directive 'visibility:=reexport' is added to a + * required-bundle. + * + * @param bundle the bundle + * @param admin the admin package + * @param deps The map of fragment and required bundles associated to the value of the + * jetty-web attribute. + * @param onlyReexport true to collect resources and web-fragments + * transitively if and only if the directive visibility is + * reexport. + */ + protected void collectFragmentsAndRequiredBundles(Bundle bundle, PackageAdmin admin, Map deps, boolean onlyReexport) + { + Bundle[] fragments = admin.getFragments(bundle); + if (fragments != null) + { + // Also add the bundles required by the fragments. + // this way we can inject onto an existing web-bundle a set of + // bundles that extend it + for (Bundle f : fragments) + { + if (!deps.keySet().contains(f.getSymbolicName())) + { + deps.put(f.getSymbolicName(), f); + collectRequiredBundles(f, admin, deps, onlyReexport); + } + } + } + collectRequiredBundles(bundle, admin, deps, onlyReexport); + } + + /** + * A simplistic but good enough parser for the Require-Bundle header. Parses + * the version range attribute and the visibility directive. + * + * @param bundle the bundle + * @param admin the admin package + * @param deps The map of required bundles associated to the value of the + * jetty-web attribute. + * @param onlyReexport true to collect resources and web-fragments + * transitively if and only if the directive visibility is + * reexport. + */ + protected void collectRequiredBundles(Bundle bundle, PackageAdmin admin, Map deps, boolean onlyReexport) + { + String requiredBundleHeader = (String)bundle.getHeaders().get("Require-Bundle"); + if (requiredBundleHeader == null) + { + return; + } + StringTokenizer tokenizer = new ManifestTokenizer(requiredBundleHeader); + while (tokenizer.hasMoreTokens()) + { + String tok = tokenizer.nextToken().trim(); + StringTokenizer tokenizer2 = new StringTokenizer(tok, ";"); + String symbolicName = tokenizer2.nextToken().trim(); + if (deps.keySet().contains(symbolicName)) + { + // was already added. 2 dependencies pointing at the same + // bundle. + continue; + } + String versionRange = null; + boolean reexport = false; + while (tokenizer2.hasMoreTokens()) + { + String next = tokenizer2.nextToken().trim(); + if (next.startsWith("bundle-version=")) + { + if (next.startsWith("bundle-version=\"") || next.startsWith("bundle-version='")) + { + versionRange = next.substring("bundle-version=\"".length(), next.length() - 1); + } + else + { + versionRange = next.substring("bundle-version=".length()); + } + } + else if (next.equals("visibility:=reexport")) + { + reexport = true; + } + } + if (!reexport && onlyReexport) + { + return; + } + Bundle[] reqBundles = admin.getBundles(symbolicName, versionRange); + if (reqBundles != null && reqBundles.length != 0) + { + Bundle reqBundle = null; + for (Bundle b : reqBundles) + { + if (b.getState() == Bundle.ACTIVE || b.getState() == Bundle.STARTING) + { + reqBundle = b; + break; + } + } + if (reqBundle == null) + { + // strange? in OSGi with Require-Bundle, + // the dependent bundle is supposed to be active already + reqBundle = reqBundles[0]; + } + deps.put(reqBundle.getSymbolicName(), reqBundle); + collectFragmentsAndRequiredBundles(reqBundle, admin, deps, true); + } + } + } + + private void invokeFragmentActivators(ServiceReference sr) + throws Exception + { + PackageAdmin admin = (PackageAdmin)_context.getService(sr); + Bundle[] fragments = admin.getFragments(_context.getBundle()); + if (fragments == null) + { + return; + } + for (Bundle frag : fragments) + { + // find a convention to look for a class inside the fragment. + String fragmentActivator = frag.getSymbolicName() + ".FragmentActivator"; + Class c = Class.forName(fragmentActivator); + if (c != null) + { + BundleActivator bActivator = (BundleActivator)c.getDeclaredConstructor().newInstance(); + bActivator.start(_context); + _activatedFragments.add(bActivator); + } + } + } + + public void stop() + { + INSTANCE = null; + for (BundleActivator fragAct : _activatedFragments) + { + try + { + fragAct.stop(_context); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + } + + /** + * @return true if the framework has completed all the start levels. + */ + public boolean frameworkHasCompletedAutostarts() + { + return _startLevel == null ? true : _startLevel.getStartLevel() >= _maxStartLevel; + } + + private static class ManifestTokenizer extends StringTokenizer + { + + public ManifestTokenizer(String header) + { + super(header, ","); + } + + @Override + public String nextToken() + { + String token = super.nextToken(); + + while (hasOpenQuote(token) && hasMoreTokens()) + { + token += "," + super.nextToken(); + } + return token; + } + + private boolean hasOpenQuote(String token) + { + int i = -1; + do + { + int quote = getQuote(token, i + 1); + if (quote < 0) + { + return false; + } + + i = token.indexOf(quote, i + 1); + i = token.indexOf(quote, i + 1); + } + while (i >= 0); + return true; + } + + private int getQuote(String token, int offset) + { + int i = token.indexOf('"', offset); + int j = token.indexOf('\'', offset); + if (i < 0) + { + if (j < 0) + { + return -1; + } + else + { + return '\''; + } + } + if (j < 0) + { + return '"'; + } + if (i < j) + { + return '"'; + } + return '\''; + } + } +} + diff --git a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/resources/META-INF/services/org.eclipse.jetty.ee11.webapp.Configuration b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/resources/META-INF/services/org.eclipse.jetty.ee11.webapp.Configuration new file mode 100644 index 00000000000..3032448a08f --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/resources/META-INF/services/org.eclipse.jetty.ee11.webapp.Configuration @@ -0,0 +1,2 @@ +org.eclipse.jetty.ee11.osgi.annotations.AnnotationConfiguration +org.eclipse.jetty.ee11.osgi.boot.OSGiMetaInfConfiguration diff --git a/jetty-ee11/jetty-ee11-osgi/pom.xml b/jetty-ee11/jetty-ee11-osgi/pom.xml new file mode 100644 index 00000000000..359212ca5d9 --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/pom.xml @@ -0,0 +1,73 @@ + + + + 4.0.0 + + org.eclipse.jetty.ee11 + jetty-ee11 + 12.1.0-SNAPSHOT + + org.eclipse.jetty.ee11.osgi + jetty-ee11-osgi + pom + EE11 :: OSGi + + + jetty-ee11-osgi-alpn + jetty-ee11-osgi-boot + jetty-ee11-osgi-boot-jsp + test-jetty-ee11-osgi + test-jetty-ee11-osgi-fragment + test-jetty-ee11-osgi-server + test-jetty-ee11-osgi-webapp-resources + + + + true + + + + + + org.eclipse.jetty.ee11.osgi + jetty-ee11-osgi-boot + ${project.version} + + + org.eclipse.jetty.ee11.osgi + jetty-ee11-osgi-boot-jsp + ${project.version} + + + + + + + + true + META-INF/.. + + META-INF/**/* + + + **/.* + **/*.jar + .settings/**/* + pom.xml + + jettyhome/**/* + src/**/* + target/**/* + build.properties + + + + src/main/java + + **/*.java + + + + + + diff --git a/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi-fragment/pom.xml b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi-fragment/pom.xml new file mode 100644 index 00000000000..60d20cbe971 --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi-fragment/pom.xml @@ -0,0 +1,41 @@ + + + 4.0.0 + + org.eclipse.jetty.ee11.osgi + jetty-ee11-osgi + 12.1.0-SNAPSHOT + + test-jetty-ee11-osgi-fragment + EE11 :: OSGi :: WebApp Fragment + Test Jetty OSGi Webapp Fragment bundle + + ${project.groupId}.webapp.fragment + true + true + + + + + src/main/resources + + + + + org.apache.felix + maven-bundle-plugin + true + + + ${bundle-symbolic-name} + Jetty OSGi Test WebApp Fragment + J2SE-1.5 + org.eclipse.jetty.ee11.demos.spec.webapp + / + + + + + + + diff --git a/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi-fragment/src/main/resources/frag.html b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi-fragment/src/main/resources/frag.html new file mode 100644 index 00000000000..9dd6187f75e --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi-fragment/src/main/resources/frag.html @@ -0,0 +1,5 @@ + + +

FRAGMENT

+ + diff --git a/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi-server/pom.xml b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi-server/pom.xml new file mode 100644 index 00000000000..e0803a25216 --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi-server/pom.xml @@ -0,0 +1,66 @@ + + + 4.0.0 + + org.eclipse.jetty.ee11.osgi + jetty-ee11-osgi + 12.1.0-SNAPSHOT + + test-jetty-ee11-osgi-server + EE11 :: OSGi :: Server + Test Jetty OSGi bundle with a Server + + ${project.groupId}.testserver + true + true + + + + org.eclipse.jetty.ee11 + jetty-ee11-webapp + + + org.eclipse.platform + org.eclipse.osgi + provided + + + org.eclipse.platform + org.eclipse.osgi.services + provided + + + + + + + org.apache.felix + maven-bundle-plugin + true + + + ${bundle-symbolic-name} + Jetty OSGi Test Server + com.acme.osgi.Activator + J2SE-1.5 + + <_nouses>true + org.osgi.framework, + org.osgi.service.cm;version="1.2.0", + org.osgi.service.packageadmin, + org.osgi.service.startlevel;version="1.0.0", + org.osgi.service.url;version="1.0.0", + org.osgi.util.tracker;version="1.3.0", + org.xml.sax, + org.xml.sax.helpers, + * + org.eclipse.jetty.*;version="[$(version;==;${parsedVersion.osgiVersion}),$(version;+;${parsedVersion.osgiVersion}))", org.eclipse.jetty.ee11.*;version="[$(version;==;${parsedVersion.osgiVersion}),$(version;+;${parsedVersion.osgiVersion}))" + + + + + + + diff --git a/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi-server/src/main/java/com/acme/osgi/Activator.java b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi-server/src/main/java/com/acme/osgi/Activator.java new file mode 100644 index 00000000000..6c01f1dde34 --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi-server/src/main/java/com/acme/osgi/Activator.java @@ -0,0 +1,83 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package com.acme.osgi; + +import java.util.Dictionary; +import java.util.Hashtable; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.handler.ContextHandlerCollection; +import org.eclipse.jetty.util.component.LifeCycle; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceRegistration; + +/** + * Bootstrap a Server + */ +public class Activator implements BundleActivator +{ + + private ServiceRegistration _sr; + + /** + * + */ + @Override + public void start(BundleContext context) throws Exception + { + //For test purposes, use a random port + Server server = new Server(0); + server.getConnectors()[0].addEventListener(new LifeCycle.Listener() + { + + @Override + public void lifeCycleStarted(LifeCycle event) + { + System.setProperty("bundle.server.port", String.valueOf(((ServerConnector)event).getLocalPort())); + } + }); + ContextHandlerCollection contexts = new ContextHandlerCollection(); + server.setHandler(contexts); + // server.setDumpAfterStart(true); + + String[] list = new String[]{ + "org.eclipse.jetty.ee11.osgi.boot.OSGiWebInfConfiguration", + "org.eclipse.jetty.ee11.webapp.WebXmlConfiguration", + "org.eclipse.jetty.ee11.webapp.MetaInfConfiguration", + "org.eclipse.jetty.ee11.webapp.FragmentConfiguration", + "org.eclipse.jetty.ee11.webapp.JettyWebXmlConfiguration" + }; + server.setAttribute("org.eclipse.jetty.ee11.webapp.configuration", list); + + Dictionary serverProps = new Hashtable(); + //define the unique name of the server instance + serverProps.put("managedServerName", "fooServer"); + //Could also instead call serverProps.put("jetty.http.port", "9999"); + //register as an OSGi Service for Jetty to find + _sr = context.registerService(Server.class.getName(), server, serverProps); + } + + /** + * Stop the activator. + * + * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext) + */ + @Override + public void stop(BundleContext context) throws Exception + { + _sr.unregister(); + } +} diff --git a/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi-server/src/main/resources/index.html b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi-server/src/main/resources/index.html new file mode 100644 index 00000000000..9e62c04bb91 --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi-server/src/main/resources/index.html @@ -0,0 +1,6 @@ + + +

Test OSGi WebApp

+

Webapp registered by bundle as service successfully deployed.

+ + diff --git a/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi-webapp-resources/pom.xml b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi-webapp-resources/pom.xml new file mode 100644 index 00000000000..ddc266a0c1c --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi-webapp-resources/pom.xml @@ -0,0 +1,73 @@ + + + 4.0.0 + + org.eclipse.jetty.ee11.osgi + jetty-ee11-osgi + 12.1.0-SNAPSHOT + + test-jetty-ee11-osgi-webapp-resources + war + EE11 :: OSGi :: WebApp With Resources + + ${project.groupId}.webapp.resources + true + true + + + + jakarta.servlet + jakarta.servlet-api + provided + + + + + + org.apache.felix + maven-bundle-plugin + true + + + war + + + !com.acme* + /test-webapp-resources + ee11 + + + + + org.apache.maven.plugins + maven-resources-plugin + + + copy-resources + + copy-resources + + validate + + ${basedir}/target/classes + + + src/main/resources + + + + + + + + + maven-war-plugin + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + + diff --git a/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi-webapp-resources/src/main/java/com/acme/HelloWorld.java b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi-webapp-resources/src/main/java/com/acme/HelloWorld.java new file mode 100644 index 00000000000..16957814003 --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi-webapp-resources/src/main/java/com/acme/HelloWorld.java @@ -0,0 +1,62 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package com.acme; + +import java.io.IOException; +import java.net.URL; +import java.util.Enumeration; + +import jakarta.servlet.ServletConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +/** + * Dump Servlet Request. + */ +@SuppressWarnings("serial") +public class HelloWorld extends HttpServlet +{ + + @Override + public void init(ServletConfig config) throws ServletException + { + super.init(config); + } + + @Override + public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + doGet(request, response); + } + + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + response.setContentType("text/html"); + ServletOutputStream out = response.getOutputStream(); + out.println(""); + out.println("

Hello World

"); + + Enumeration resources = Thread.currentThread().getContextClassLoader().getResources("fake.properties"); + + while (resources.hasMoreElements()) + out.println(resources.nextElement().toString()); + + out.println(""); + out.flush(); + } +} diff --git a/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi-webapp-resources/src/main/resources/fake.properties b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi-webapp-resources/src/main/resources/fake.properties new file mode 100644 index 00000000000..e69de29bb2d diff --git a/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi-webapp-resources/src/main/webapp/WEB-INF/web.xml b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi-webapp-resources/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000000..26e52b31e19 --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi-webapp-resources/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,24 @@ + + + + WebApp With Resources + + + Hello + com.acme.HelloWorld + 1 + + + + Hello + /hello/* + + + + + diff --git a/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/README.txt b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/README.txt new file mode 100644 index 00000000000..0313d46df09 --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/README.txt @@ -0,0 +1,220 @@ +Unit Tests with OSGi +==================== + +The unit tests use PaxExam https://ops4j1.jira.com/wiki/spaces/PAXEXAM4/overview +to fork a jvm to start an OSGi container (currently eclipse) and deploy the jetty +jars as osgi bundles, along with the jetty-ee11-osgi infrastructure (like jetty-ee11-osgi-boot). + +To run all the tests: + mvn test + +To run a particular test: + mvn test -Dtest=[name of test] + + +At the time of writing, PaxExam only works with junit-4, so you may not be +able to invoke them easily from your IDE. + + +Logging +------- + +By default, very little log info comes out of the tests. If you wish to see more +logging information, you can control this from the command line. + +There are 2 sources of logging information: 1) the pax environment and 2) jetty logs. + +To set the logging level for the pax environment use the following system property: + + mvn -Dpax.exam.LEVEL=[log level] + +INFO, WARN and TRACE are known to work. + +To set the logging level for the jetty logs edit the src/test/resources/jetty-logging.properties +to set the logging level you want and rerun your tests. The usual jetty logging levels apply. + + + +General Test Diagnostics +------------------------ + +There are generally only ever 2 things wrong with the jetty/osgi interworking: + +1. you've added or changed an existing jetty-whatever subproject to use the java ServiceLoader, but you haven't put the right entries into the jar's manifest to allow ServiceLoader to work in osgi + +2. you've upgraded the jvm version and the version of Aries SpiFly that we use to provide ServiceLoader functionality in osgi does not support parsing java classes compiled with the new version + + +* Diagnosing problem 1: + +Can be an obvious failure, because the osgi test that exercises your feature fails. Worst case is that your code substitutes the missing service with some default that isn't your new feature and it's never detected because the test still works. That's a problem with the design of the test, c'est la vie. + +Other problems with misconfigured manifests are usually to do with missing or incorrect Import-Package/Export-Package statements. This usually isn't a problem because we mostly automatically generate these using the mvn bnd tool during assembly of the jar, but can become a problem if the manifest has been manually cobbled together in the pom.xml. You'll notice these failures because an osgi test will fail with messages something like the following: + +Bundle [id:24, url:mvn:org.eclipse.jetty/jetty-util/11.0.0-SNAPSHOT] is not resolved + +To diagnose that further, you can rerun the test, and ask it to spit out a list of the status of every jetty jar that is deployed. To do that, supply the following system property at the command line: +mvn -Dbundle.debug=true + +You'll see several lines of output like the following. All of the bundles should be state 32 (active) or state 4 (resolved): + +ACTIVE 32 +RESOLVED 4 +INSTALLED 2 +0 org.eclipse.osgi System Bundle 3.15.100.v20191114-1701 32 +1 org.ops4j.pax.exam file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.ops4j.pax.exam_4.13.1.jar 4.13.1 32 +2 org.ops4j.pax.exam.inject file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.ops4j.pax.exam.inject_4.13.1.jar 4.13.1 32 +3 org.ops4j.pax.exam.extender.service file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.ops4j.pax.exam.extender.service_4.13.1.jar 4.13.1 32 +4 osgi.cmpn file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/osgi.cmpn_4.3.1.201210102024.jar 4.3.1.201210102024 32 +5 org.ops4j.pax.logging.pax-logging-api file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.ops4j.pax.logging.pax-logging-api_1.10.1.jar 1.10.1 32 +6 org.ops4j.base file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.ops4j.base_1.5.0.jar 1.5.0 32 +7 org.ops4j.pax.swissbox.core file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.ops4j.pax.swissbox.core_1.8.2.jar 1.8.2 32 +8 org.ops4j.pax.swissbox.extender file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.ops4j.pax.swissbox.extender_1.8.2.jar 1.8.2 32 +9 org.ops4j.pax.swissbox.framework file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.ops4j.pax.swissbox.framework_1.8.2.jar 1.8.2 32 +10 org.ops4j.pax.swissbox.lifecycle file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.ops4j.pax.swissbox.lifecycle_1.8.2.jar 1.8.2 32 +11 org.ops4j.pax.swissbox.tracker file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.ops4j.pax.swissbox.tracker_1.8.2.jar 1.8.2 32 +12 org.apache.geronimo.specs.geronimo-atinject_1.0_spec file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.apache.geronimo.specs.geronimo-atinject_1.0_spec_1.0.jar 1.0.0 32 +13 org.ops4j.pax.tipi.junit file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.ops4j.pax.tipi.junit_4.12.0.1.jar 4.12.0.1 32 +14 org.ops4j.pax.tipi.hamcrest.core file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.ops4j.pax.tipi.hamcrest.core_1.3.0.1.jar 1.3.0.1 32 +15 org.ops4j.pax.exam.invoker.junit file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.ops4j.pax.exam.invoker.junit_4.13.1.jar 4.13.1 32 +16 org.eclipse.jetty.servlet-api file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.eclipse.jetty.servlet-api_4.0.3.jar 4.0.3 32 +17 org.objectweb.asm file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.objectweb.asm_7.2.0.jar 7.2.0 32 +18 org.objectweb.asm.commons file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.objectweb.asm.commons_7.2.0.jar 7.2.0 32 +19 org.objectweb.asm.tree file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.objectweb.asm.tree_7.2.0.jar 7.2.0 32 +20 org.apache.aries.spifly.dynamic.bundle file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.apache.aries.spifly.dynamic.bundle_1.2.3.jar 1.2.3 32 +21 jakarta.annotation-api file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/jakarta.annotation-api_1.3.4.jar 1.3.4 32 +22 org.apache.geronimo.specs.geronimo-jta_1.1_spec file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.apache.geronimo.specs.geronimo-jta_1.1_spec_1.1.1.jar 1.1.1 32 +23 slf4j.api file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/slf4j.api_2.0.0.alpha1.jar 2.0.0.alpha1 4 +24 slf4j.log4j12 file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/slf4j.log4j12_2.0.0.alpha1.jar 2.0.0.alpha1 4 +25 org.eclipse.jetty.util file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.eclipse.jetty.util_10.0.0.SNAPSHOT.jar 10.0.0.SNAPSHOT 32 +26 org.eclipse.jetty.deploy file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.eclipse.jetty.deploy_10.0.0.SNAPSHOT.jar 10.0.0.SNAPSHOT 32 +27 org.eclipse.jetty.server file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.eclipse.jetty.server_10.0.0.SNAPSHOT.jar 10.0.0.SNAPSHOT 32 +28 org.eclipse.jetty.servlet file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.eclipse.jetty.servlet_10.0.0.SNAPSHOT.jar 10.0.0.SNAPSHOT 32 +29 org.eclipse.jetty.http file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.eclipse.jetty.http_10.0.0.SNAPSHOT.jar 10.0.0.SNAPSHOT 32 +30 org.eclipse.jetty.xml file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.eclipse.jetty.xml_10.0.0.SNAPSHOT.jar 10.0.0.SNAPSHOT 32 +31 org.eclipse.jetty.webapp file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.eclipse.jetty.webapp_10.0.0.SNAPSHOT.jar 10.0.0.SNAPSHOT 32 +32 org.eclipse.jetty.io file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.eclipse.jetty.io_10.0.0.SNAPSHOT.jar 10.0.0.SNAPSHOT 32 +33 org.eclipse.jetty.security file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.eclipse.jetty.security_10.0.0.SNAPSHOT.jar 10.0.0.SNAPSHOT 32 +34 org.eclipse.jetty.servlets file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.eclipse.jetty.servlets_10.0.0.SNAPSHOT.jar 10.0.0.SNAPSHOT 32 +35 org.eclipse.jetty.client file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.eclipse.jetty.client_10.0.0.SNAPSHOT.jar 10.0.0.SNAPSHOT 32 +36 org.eclipse.jetty.jndi file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.eclipse.jetty.jndi_10.0.0.SNAPSHOT.jar 10.0.0.SNAPSHOT 32 +37 org.eclipse.jetty.plus file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.eclipse.jetty.plus_10.0.0.SNAPSHOT.jar 10.0.0.SNAPSHOT 32 +38 org.eclipse.jetty.annotations file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.eclipse.jetty.annotations_10.0.0.SNAPSHOT.jar 10.0.0.SNAPSHOT 32 +39 org.eclipse.jetty.websocket.core file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.eclipse.jetty.websocket.core_10.0.0.SNAPSHOT.jar 10.0.0.SNAPSHOT 32 +40 org.eclipse.jetty.websocket.servlet file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.eclipse.jetty.websocket.servlet_10.0.0.SNAPSHOT.jar 10.0.0.SNAPSHOT 32 +41 org.eclipse.jetty.websocket.util file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.eclipse.jetty.websocket.util_10.0.0.SNAPSHOT.jar 10.0.0.SNAPSHOT 32 +42 org.eclipse.jetty.ee11.websocket.api file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.eclipse.jetty.ee11.websocket.api_10.0.0.SNAPSHOT.jar 10.0.0.SNAPSHOT 32 +43 org.eclipse.jetty.websocket.server file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.eclipse.jetty.websocket.server_10.0.0.SNAPSHOT.jar 10.0.0.SNAPSHOT 32 +44 org.eclipse.jetty.ee11.websocket.client file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.eclipse.jetty.ee11.websocket.client_10.0.0.SNAPSHOT.jar 10.0.0.SNAPSHOT 32 +45 org.eclipse.jetty.websocket.common file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.eclipse.jetty.websocket.common_10.0.0.SNAPSHOT.jar 10.0.0.SNAPSHOT 32 +46 org.eclipse.jetty.websocket-api file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.eclipse.jetty.websocket-api_1.1.2.jar 1.1.2 4 +47 org.eclipse.jetty.websocket.javax.server file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.eclipse.jetty.websocket.javax.server_10.0.0.SNAPSHOT.jar 10.0.0.SNAPSHOT 4 +48 org.eclipse.jetty.websocket.javax.client file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.eclipse.jetty.websocket.javax.client_10.0.0.SNAPSHOT.jar 10.0.0.SNAPSHOT 4 +49 org.eclipse.jetty.websocket.javax.common file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.eclipse.jetty.websocket.javax.common_10.0.0.SNAPSHOT.jar 10.0.0.SNAPSHOT 4 +50 org.eclipse.jetty.ee11.osgi.boot file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.eclipse.jetty.ee11.osgi.boot_10.0.0.SNAPSHOT.jar 10.0.0.SNAPSHOT 32 +51 org.eclipse.jetty.alpn.java.client file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.eclipse.jetty.alpn.java.client_10.0.0.SNAPSHOT.jar 10.0.0.SNAPSHOT 32 +52 org.eclipse.jetty.alpn.client file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.eclipse.jetty.alpn.client_10.0.0.SNAPSHOT.jar 10.0.0.SNAPSHOT 32 +53 javax.servlet.jsp.jstl file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/javax.servlet.jsp.jstl_1.2.0.v201105211821.jar 1.2.0.v201105211821 32 +54 org.mortbay.jasper.apache-el file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.mortbay.jasper.apache-el_9.0.29.jar 9.0.29 32 +55 org.mortbay.jasper.apache-jsp file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.mortbay.jasper.apache-jsp_9.0.29.jar 9.0.29 32 +56 org.eclipse.jetty.apache-jsp file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.eclipse.jetty.apache-jsp_10.0.0.SNAPSHOT.jar 10.0.0.SNAPSHOT 32 +57 org.glassfish.web.javax.servlet.jsp.jstl file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.glassfish.web.javax.servlet.jsp.jstl_1.2.2.jar 1.2.2 32 +58 org.eclipse.jdt.core.compiler.batch file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.eclipse.jdt.core.compiler.batch_3.19.0.v20190903-0936.jar 3.19.0.v20190903-0936 32 +59 org.eclipse.jetty.ee11.osgi.boot.jsp file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.eclipse.jetty.ee11.osgi.boot.jsp_10.0.0.SNAPSHOT.jar 10.0.0.SNAPSHOT 4 +60 org.eclipse.jetty.tests.webapp file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee11-osgi/test-jetty-ee11-osgi/target/1584628869418-0/pax-exam-downloads/org.eclipse.jetty.tests.webapp_10.0.0.SNAPSHOT.jar 10.0.0.SNAPSHOT 32 +61 PAXEXAM-PROBE-d9c5a341-5c98-4084-b814-8303880cb447 local 0.0.0 32 + + +If things didn't go so well, and some bundle that should be in state ACTIVE (32) isn't, then you'll see diagnosis like this: + +Trying to start the bundle org.glassfish.web.javax.servlet.jsp.jstl that was supposed to be active or resolved. +org.glassfish.web.javax.servlet.jsp.jstl failed to start +org.osgi.framework.BundleException: Could not resolve module: org.glassfish.web.javax.servlet.jsp.jstl [57] + Unresolved requirement: Import-Package: javax.servlet.jsp.jstl.core + +The "Unresolved requirement" means either that some bundle that exports that package has not been deployed, or that it is deployed, but it's manifest is screwed up, and didn't expose that package. Check the test code for the mavenBundle() statements to ascertain if it has been deployed, and then check the manifest inside the jar for the Export-Package statements to verify the correct packages are exported. + + + +* Diagnosing Problem 2 + +If you've upgraded the jetty jar and that's all you've changed, then more than likely it's a SpiFly versioning problem. SpiFly internally uses asm to parse classes to find services for the ServiceLoader, so it can be that the version of asm used by SpiFly doesn't support the higher jvm version. At the time of writing SpiFly is shipping an older version of asm baked into their jars, so very likely it won't support anything above jdk13. + +Also, if you don't see any test failures with unresolved jar messages, then it's also a good indicator that the problem is with SpiFly versioning. Unfortunately, when the problem is a jvm/SpiFly versioning mismatch, the osgi paxexam environment doesn't output any good log messages. There is a java.lang.Error that is thrown from inside asm that the environment doesn't pass on in any useful fashion. + +To try and catch this error, you can modify the ServletInstanceWrapper at line 163 to catch Throwable instead of Exception: +https://github.com/eclipse/jetty.project/blob/jetty-10.0.x/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/ServerInstanceWrapper.java#L163 + +When you do this, you get output like the following: + +java.lang.ClassFormatError: Unexpected error from weaving hook. +at org.eclipse.osgi.internal.weaving.WeavingHookConfigurator.processClass(WeavingHookConfigurator.java:77) +at org.eclipse.osgi.internal.loader.classpath.ClasspathManager.processClass(ClasspathManager.java:736) +at org.eclipse.osgi.internal.loader.classpath.ClasspathManager.defineClass(ClasspathManager.java:707) +at org.eclipse.osgi.internal.loader.classpath.ClasspathManager.findClassImpl(ClasspathManager.java:640) +at org.eclipse.osgi.internal.loader.classpath.ClasspathManager.findLocalClassImpl(ClasspathManager.java:608) +at org.eclipse.osgi.internal.loader.classpath.ClasspathManager.findLocalClassImpl(ClasspathManager.java:588) +at org.eclipse.osgi.internal.loader.classpath.ClasspathManager.findLocalClass(ClasspathManager.java:567) +at org.eclipse.osgi.internal.loader.ModuleClassLoader.findLocalClass(ModuleClassLoader.java:346) +at org.eclipse.osgi.internal.loader.BundleLoader.findLocalClass(BundleLoader.java:398) +at org.eclipse.osgi.internal.loader.sources.SingleSourcePackage.loadClass(SingleSourcePackage.java:41) +at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(BundleLoader.java:460) +at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:425) +at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:417) +at org.eclipse.osgi.internal.loader.ModuleClassLoader.loadClass(ModuleClassLoader.java:171) +at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522) +at org.eclipse.jetty.ee11.osgi.boot.internal.serverfactory.ServerInstanceWrapper.configure(ServerInstanceWrapper.java:143) +at org.eclipse.jetty.ee11.osgi.boot.internal.serverfactory.DefaultJettyAtJettyHomeHelper.startJettyAtJettyHome(DefaultJettyAtJettyHomeHelper.java:211) +at org.eclipse.jetty.ee11.osgi.boot.JettyBootstrapActivator.start(JettyBootstrapActivator.java:98) +at org.eclipse.osgi.internal.framework.BundleContextImpl$3.run(BundleContextImpl.java:842) +at org.eclipse.osgi.internal.framework.BundleContextImpl$3.run(BundleContextImpl.java:1) +at java.base/java.security.AccessController.doPrivileged(AccessController.java:554) +at org.eclipse.osgi.internal.framework.BundleContextImpl.startActivator(BundleContextImpl.java:834) +at org.eclipse.osgi.internal.framework.BundleContextImpl.start(BundleContextImpl.java:791) +at org.eclipse.osgi.internal.framework.EquinoxBundle.startWorker0(EquinoxBundle.java:1015) +at org.eclipse.osgi.internal.framework.EquinoxBundle$EquinoxModule.startWorker(EquinoxBundle.java:365) +at org.eclipse.osgi.container.Module.doStart(Module.java:603) +at org.eclipse.osgi.container.Module.start(Module.java:467) +at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel$2.run(ModuleContainer.java:1844) +at org.eclipse.osgi.internal.framework.EquinoxContainerAdaptor$1$1.execute(EquinoxContainerAdaptor.java:136) +at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.incStartLevel(ModuleContainer.java:1837) +at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.incStartLevel(ModuleContainer.java:1780) +at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.doContainerStartLevel(ModuleContainer.java:1742) +at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.dispatchEvent(ModuleContainer.java:1664) +at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.dispatchEvent(ModuleContainer.java:1) +at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:234) +at org.eclipse.osgi.framework.eventmgr.EventManager$EventThread.run(EventManager.java:345) +Caused by: java.lang.IllegalArgumentException: Unsupported class file major version 58 +at org.objectweb.asm.ClassReader.(ClassReader.java:195) +at org.objectweb.asm.ClassReader.(ClassReader.java:176) +at org.objectweb.asm.ClassReader.(ClassReader.java:162) +at org.objectweb.asm.ClassReader.(ClassReader.java:283) +at org.apache.aries.spifly.dynamic.OSGiFriendlyClassWriter.getCommonSuperClass(OSGiFriendlyClassWriter.java:81) +at org.objectweb.asm.SymbolTable.addMergedType(SymbolTable.java:1200) +at org.objectweb.asm.Frame.merge(Frame.java:1299) +at org.objectweb.asm.Frame.merge(Frame.java:1207) +at org.objectweb.asm.MethodWriter.computeAllFrames(MethodWriter.java:1607) +at org.objectweb.asm.MethodWriter.visitMaxs(MethodWriter.java:1543) +at org.objectweb.asm.MethodVisitor.visitMaxs(MethodVisitor.java:762) +at org.objectweb.asm.commons.LocalVariablesSorter.visitMaxs(LocalVariablesSorter.java:147) +at org.objectweb.asm.ClassReader.readCode(ClassReader.java:2431) +at org.objectweb.asm.ClassReader.readMethod(ClassReader.java:1283) +at org.objectweb.asm.ClassReader.accept(ClassReader.java:688) +at org.objectweb.asm.ClassReader.accept(ClassReader.java:400) +at org.apache.aries.spifly.dynamic.ClientWeavingHook.weave(ClientWeavingHook.java:60) +at org.eclipse.osgi.internal.weaving.WovenClassImpl.call(WovenClassImpl.java:175) +at org.eclipse.osgi.internal.serviceregistry.ServiceRegistry.notifyHookPrivileged(ServiceRegistry.java:1343) +at org.eclipse.osgi.internal.serviceregistry.ServiceRegistry.notifyHooksPrivileged(ServiceRegistry.java:1323) +at org.eclipse.osgi.internal.weaving.WovenClassImpl.callHooks(WovenClassImpl.java:270) +at org.eclipse.osgi.internal.weaving.WeavingHookConfigurator.processClass(WeavingHookConfigurator.java:71) +... 35 more + + + + +Other Useful Things +------------------- + +If you have an ordinary jar with no osgi manifest headers in it, or one with incorrect/incomplete osgi manifest headers in it, you can use the Pax Wrapped Bundle facility to add in/replace the headers so that the test will work. + +See https://ops4j1.jira.com/wiki/spaces/PAXEXAM4/pages/54263890/Configuration+Options#ConfigurationOptions-wrappedBundle for more information. The wrappedBundle() itself uses an underlying Wrap Protocol mechanism, which has more details on configuration options, so also look at https://ops4j1.jira.com/wiki/spaces/paxurl/pages/3833898/Wrap+Protocol. For an example of it in use in the tests, look at TestOSGiUtil.java line 179. diff --git a/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/pom.xml b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/pom.xml new file mode 100644 index 00000000000..19021d964ab --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/pom.xml @@ -0,0 +1,631 @@ + + + 4.0.0 + + org.eclipse.jetty.ee11.osgi + jetty-ee11-osgi + 12.1.0-SNAPSHOT + + test-jetty-ee11-osgi + EE11 :: OSGi :: Test + Jetty OSGi Integration test + + target/distribution + ${project.groupId}.boot.test.osgi + + true + true + true + + + + biz.aQute.bnd + biz.aQute.bndlib + + + org.osgi + org.osgi.core + + + + + jakarta.servlet + jakarta.servlet-api + + + jakarta.servlet.jsp.jstl + jakarta.servlet.jsp.jstl-api + + + jakarta.el + jakarta.el-api + + + + + org.eclipse.jetty.ee11.demos + jetty-ee11-demo-mock-resources + ${project.version} + + + org.eclipse.jetty.http2 + jetty-http2-hpack + + + org.eclipse.jetty.http2 + jetty-http2-server + + + org.glassfish.web + jakarta.servlet.jsp.jstl + + + org.mortbay.jasper + apache-el + + + org.mortbay.jasper + apache-jsp + + + org.ops4j.pax.tinybundles + tinybundles + + + jakarta.websocket + jakarta.websocket-api + runtime + + + org.eclipse.jetty + jetty-client + runtime + + + org.eclipse.jetty + jetty-deploy + runtime + + + org.eclipse.jetty + jetty-jmx + runtime + + + org.eclipse.jetty + jetty-jndi + runtime + + + org.eclipse.jetty + jetty-plus + runtime + + + org.eclipse.jetty + jetty-server + runtime + + + org.eclipse.jetty + jetty-util + runtime + + + org.eclipse.jetty + jetty-xml + runtime + + + org.eclipse.jetty.ee11 + jetty-ee11-annotations + runtime + + + + org.eclipse.jetty.ee11 + jetty-ee11-jndi + runtime + + + org.eclipse.jetty.ee11 + jetty-ee11-plus + runtime + + + org.eclipse.jetty.ee11 + jetty-ee11-servlet + runtime + + + org.eclipse.jetty.ee11 + jetty-ee11-servlets + runtime + + + org.eclipse.jetty.ee11 + jetty-ee11-webapp + runtime + + + org.eclipse.jetty.ee11.websocket + jetty-ee11-websocket-jakarta-client + runtime + + + org.eclipse.jetty.ee11.websocket + jetty-ee11-websocket-jakarta-server + runtime + + + org.eclipse.jetty.ee11.websocket + jetty-ee11-websocket-jetty-server + runtime + + + org.eclipse.jetty.ee11.websocket + jetty-ee11-websocket-servlet + runtime + + + org.eclipse.jetty.websocket + jetty-websocket-jetty-api + runtime + + + org.eclipse.jetty.websocket + jetty-websocket-jetty-client + runtime + + + org.eclipse.jetty.websocket + jetty-websocket-jetty-common + runtime + + + org.eclipse.jetty.websocket + jetty-websocket-jetty-server + runtime + + + jakarta.activation + jakarta.activation-api + test + + + jakarta.el + jakarta.el-api + test + + + jakarta.enterprise + jakarta.enterprise.cdi-api + test + + + jakarta.inject + jakarta.inject-api + test + + + jakarta.interceptor + jakarta.interceptor-api + test + + + jakarta.transaction + jakarta.transaction-api + test + + + org.apache.aries.spifly + org.apache.aries.spifly.dynamic.bundle + test + + + org.apache.felix + org.apache.felix.framework + + + + + org.apache.geronimo.specs + geronimo-atinject_1.0_spec + test + + + org.conscrypt + conscrypt-openjdk-uber + test + + + org.eclipse.jetty + jetty-alpn-conscrypt-client + test + + + org.eclipse.jetty + jetty-alpn-conscrypt-server + test + + + org.eclipse.jetty + jetty-alpn-java-client + test + + + org.eclipse.jetty + jetty-alpn-java-server + test + + + org.eclipse.jetty + jetty-alpn-server + test + + + + org.eclipse.jetty + jetty-slf4j-impl + test + + + org.eclipse.jetty.ee11.demos + jetty-ee11-demo-container-initializer + ${project.version} + test + + + org.eclipse.jetty.ee11.demos + jetty-ee11-demo-jetty-webapp + ${project.version} + war + test + + + org.eclipse.jetty.ee11.demos + jetty-ee11-demo-jetty-webapp + ${project.version} + webbundle + test + + + + + org.eclipse.jetty.ee11.demos + jetty-ee11-demo-jsp-webapp + ${project.version} + webbundle + test + + + org.eclipse.jetty.ee11.demos + jetty-ee11-demo-spec-webapp + ${project.version} + war + test + + + org.eclipse.jetty.ee11.osgi + jetty-ee11-osgi-alpn + ${project.version} + test + + + org.eclipse.jetty.ee11.osgi + jetty-ee11-osgi-boot + test + + + org.eclipse.platform + org.eclipse.osgi + + + org.eclipse.platform + org.eclipse.osgi.services + + + + + org.eclipse.jetty.ee11.osgi + jetty-ee11-osgi-boot-jsp + test + + + org.eclipse.platform + org.eclipse.osgi + + + org.eclipse.platform + org.eclipse.osgi.services + + + + + org.eclipse.jetty.ee11.osgi + test-jetty-ee11-osgi-fragment + ${project.version} + test + + + org.eclipse.jetty.ee11.osgi + test-jetty-ee11-osgi-server + ${project.version} + test + + + org.eclipse.jetty.ee11.osgi + test-jetty-ee11-osgi-webapp-resources + ${project.version} + war + test + + + org.eclipse.jetty.http2 + jetty-http2-client + test + + + org.eclipse.jetty.http2 + jetty-http2-client-transport + test + + + org.eclipse.jetty.toolchain + jetty-test-helper + test + + + org.eclipse.platform + org.eclipse.osgi + test + + + org.eclipse.platform + org.eclipse.osgi.services + test + + + org.eclipse.platform + org.eclipse.osgi.util + test + + + + org.ops4j.pax.exam + pax-exam + test + + + + org.ops4j.pax.exam + pax-exam-container-forked + test + + + biz.aQute.bnd + bndlib + + + org.ops4j.pax.tinybundles + tinybundles + + + + + org.ops4j.pax.exam + pax-exam-inject + test + + + org.ops4j.pax.exam + pax-exam-junit4 + test + + + org.hamcrest + hamcrest-core + + + + + org.ops4j.pax.exam + pax-exam-link-mvn + test + + + org.ops4j.pax.swissbox + pax-swissbox-framework + test + + + org.ops4j.base + ops4j-base-monitors + + + + + org.ops4j.pax.swissbox + pax-swissbox-tracker + test + + + org.ops4j.pax.url + pax-url-aether + test + + + javax.annotation + javax.annotation-api + + + + + org.ops4j.pax.url + pax-url-wrap + test + + + biz.aQute.bnd + bndlib + + + + + org.osgi + org.osgi.util.measurement + test + + + org.osgi + org.osgi.util.position + test + + + org.osgi + org.osgi.util.promise + test + + + org.osgi + org.osgi.util.xml + test + + + org.ow2.asm + asm + test + + + org.ow2.asm + asm-analysis + test + + + org.ow2.asm + asm-commons + test + + + org.ow2.asm + asm-tree + test + + + org.ow2.asm + asm-util + test + + + + org.slf4j + slf4j-api + test + + + + + + + maven-surefire-plugin + + ${skipTests} + + ${session.repositorySession.localRepository.basedir.absolutePath} + ${env.GLOBAL_MVN_SETTINGS} + + -Dconscrypt-version=${conscrypt.version} + + + **/TestJettyOSGiBootHTTP2Conscrypt.java + **/TestJettyOSGiBootHTTP2JDK9.java + **/TestJettyOSGiBootWithJakartaWebSocket.java + + + + + + org.apache.maven.surefire + surefire-junit47 + ${maven.surefire.plugin.version} + + + + + org.apache.servicemix.tooling + depends-maven-plugin + + + generate-depends-file + + generate-depends-file + + + + + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + org.apache.servicemix.tooling + depends-maven-plugin + [1.2,) + + generate-depends-file + + + + + + + + + + + + + + + maven-dependency-plugin + + test-jetty-ee11-osgi-webapp-resources + target + true + + + + copy + + copy-dependencies + + process-test-resources + + + + + org.apache.servicemix.tooling + depends-maven-plugin + + + generate-depends-file + + generate-depends-file + + + + + + + diff --git a/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-alpn.xml b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-alpn.xml new file mode 100644 index 00000000000..22eacf1cf38 --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-alpn.xml @@ -0,0 +1,23 @@ + + + + + + + + + alpn + + + + + + + + + + + + + + diff --git a/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-deploy.xml b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-deploy.xml new file mode 100644 index 00000000000..e630c9a766e --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-deploy.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-http-boot-with-annotations.xml b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-http-boot-with-annotations.xml new file mode 100644 index 00000000000..030a2e46832 --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-http-boot-with-annotations.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + boot.annotations.port + + + + + + + + + + + diff --git a/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-http-boot-with-bundle.xml b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-http-boot-with-bundle.xml new file mode 100644 index 00000000000..82f0bb0841f --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-http-boot-with-bundle.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + boot.bundle.port + + + + + + + + + + + diff --git a/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-http-boot-with-jakarta-websocket.xml b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-http-boot-with-jakarta-websocket.xml new file mode 100644 index 00000000000..e21afb24057 --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-http-boot-with-jakarta-websocket.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + boot.jakarta.websocket.port + + + + + + + + + + diff --git a/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-http-boot-with-jsp.xml b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-http-boot-with-jsp.xml new file mode 100644 index 00000000000..1b1f7b8dbb1 --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-http-boot-with-jsp.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + boot.jsp.port + + + + + + + + + + + diff --git a/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-http-boot-with-resources.xml b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-http-boot-with-resources.xml new file mode 100644 index 00000000000..a1b677bc6ec --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-http-boot-with-resources.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + boot.resources.port + + + + + + + + + + + diff --git a/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-http-boot-with-websocket.xml b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-http-boot-with-websocket.xml new file mode 100644 index 00000000000..d5230f18f43 --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-http-boot-with-websocket.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + boot.websocket.port + + + + + + + + + + diff --git a/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-http-connector-listener.xml b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-http-connector-listener.xml new file mode 100644 index 00000000000..de169be5ef0 --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-http-connector-listener.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + boot.http.port + + + + + + diff --git a/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-http2-jdk9.xml b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-http2-jdk9.xml new file mode 100644 index 00000000000..ad51713fd9c --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-http2-jdk9.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + true + + + diff --git a/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-http2.xml b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-http2.xml new file mode 100644 index 00000000000..bd0226dd58d --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-http2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + diff --git a/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-https.xml b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-https.xml new file mode 100644 index 00000000000..a71de579531 --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-https.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + http/1.1 + + + + + + + + + + + + + + diff --git a/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-ssl-context.xml b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-ssl-context.xml new file mode 100644 index 00000000000..84dc9a80836 --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-ssl-context.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-ssl.xml b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-ssl.xml new file mode 100644 index 00000000000..f58ee8c3573 --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-ssl.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-testrealm.xml b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-testrealm.xml new file mode 100644 index 00000000000..2ce036e69ec --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-testrealm.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + /etc/realm.properties + + + + + + + Test Realm + + false + + + + + diff --git a/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-with-custom-class.xml b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-with-custom-class.xml new file mode 100644 index 00000000000..7bb1e3b0bfd --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty-with-custom-class.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + 10 + 200 + + + + + + + + + + + + + + https + + 32768 + 8192 + 8192 + true + false + 512 + + + + + + + true + 1000 + false + false + + + + + + + + + org.eclipse.jetty.ee11.webapp.FragmentConfiguration + org.eclipse.jetty.ee11.webapp.JettyWebXmlConfiguration + org.eclipse.jetty.ee11.webapp.WebXmlConfiguration + org.eclipse.jetty.ee11.webapp.WebAppConfiguration + org.eclipse.jetty.ee11.webapp.ServletsConfiguration + org.eclipse.jetty.ee11.webapp.JspConfiguration + org.eclipse.jetty.ee11.webapp.JaasConfiguration + org.eclipse.jetty.ee11.webapp.JndiConfiguration + org.eclipse.jetty.ee11.plus.webapp.PlusConfiguration + org.eclipse.jetty.ee11.plus.webapp.EnvConfiguration + org.eclipse.jetty.ee11.webapp.JmxConfiguration + config.org.eclipse.jetty.ee11.websocket.server.JettyWebSocketConfiguration + org.eclipse.jetty.websocket.jakarta.server.config.JakartaWebSocketConfiguration + org.eclipse.jetty.ee11.osgi.annotations.AnnotationConfiguration + org.eclipse.jetty.ee11.osgi.boot.OSGiWebInfConfiguration + org.eclipse.jetty.ee11.osgi.boot.OSGiMetaInfConfiguration + + + + + + java.naming.factory.initial + + + + java.naming.factory.url.pkgs + + + + diff --git a/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty.xml b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty.xml new file mode 100644 index 00000000000..ed1e4f3433e --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/jetty.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + 10 + 200 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + java.naming.factory.initial + + + + java.naming.factory.url.pkgs + + + + diff --git a/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/keystore.p12 b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/keystore.p12 new file mode 100644 index 00000000000..7196dcdadc0 Binary files /dev/null and b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/keystore.p12 differ diff --git a/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/realm.properties b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/realm.properties new file mode 100644 index 00000000000..cbf905de9fb --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/realm.properties @@ -0,0 +1,21 @@ +# +# This file defines users passwords and roles for a HashUserRealm +# +# The format is +# : [, ...] +# +# Passwords may be clear text, obfuscated or checksummed. The class +# org.eclipse.util.Password should be used to generate obfuscated +# passwords or password checksums +# +# If DIGEST Authentication is used, the password must be in a recoverable +# format, either plain text or OBF:. +# +jetty: MD5:164c88b302622e17050af52c89945d44,user +admin: CRYPT:adpexzg3FUZAk,server-administrator,content-administrator,admin +other: OBF:1xmk1w261u9r1w1c1xmq,user +plain: plain,user +user: password,user + +# This entry is for digest auth. The credential is a MD5 hash of username:realmname:password +digest: MD5:6e120743ad67abfbc385bc2bb754e297,user diff --git a/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/webdefault-ee11.xml b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/webdefault-ee11.xml new file mode 100644 index 00000000000..d1649148f75 --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/config/etc/webdefault-ee11.xml @@ -0,0 +1,532 @@ + + + + + + + + + + + + + + + + + + + + + + + Default web.xml file. + This file is applied to a Web application before its own WEB_INF/web.xml file + + + + + + + + org.eclipse.jetty.ee11.servlet.listener.ELContextCleaner + + + + + + + + org.eclipse.jetty.ee11.servlet.listener.IntrospectorCleaner + + + + + + + + + + + + + + + + + default + org.eclipse.jetty.ee11.servlet.DefaultServlet + + aliases + false + + + acceptRanges + true + + + dirAllowed + true + + + welcomeServlets + false + + + redirectWelcome + false + + + maxCacheSize + 256000000 + + + maxCachedFileSize + 200000000 + + + maxCachedFiles + 2048 + + + gzip + false + + + etags + false + + + useFileMappedBuffer + true + + + + 0 + + + + default + / + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + jsp + org.eclipse.jetty.jsp.JettyJspServlet + + logVerbosityLevel + DEBUG + + + fork + false + + + xpoweredBy + false + + + compilerTargetVM + 1.7 + + + compilerSourceVM + 1.7 + + + 0 + + + + jsp + *.jsp + *.jspf + *.jspx + *.xsp + *.JSP + *.JSPF + *.JSPX + *.XSP + + + + + + + + 30 + + + + + + + + + + + + + + + index.html + index.htm + index.jsp + + + + + + + + ar + ISO-8859-6 + + + be + ISO-8859-5 + + + bg + ISO-8859-5 + + + ca + ISO-8859-1 + + + cs + ISO-8859-2 + + + da + ISO-8859-1 + + + de + ISO-8859-1 + + + el + ISO-8859-7 + + + en + ISO-8859-1 + + + es + ISO-8859-1 + + + et + ISO-8859-1 + + + fi + ISO-8859-1 + + + fr + ISO-8859-1 + + + hr + ISO-8859-2 + + + hu + ISO-8859-2 + + + is + ISO-8859-1 + + + it + ISO-8859-1 + + + iw + ISO-8859-8 + + + ja + Shift_JIS + + + ko + EUC-KR + + + lt + ISO-8859-2 + + + lv + ISO-8859-2 + + + mk + ISO-8859-5 + + + nl + ISO-8859-1 + + + no + ISO-8859-1 + + + pl + ISO-8859-2 + + + pt + ISO-8859-1 + + + ro + ISO-8859-2 + + + ru + ISO-8859-5 + + + sh + ISO-8859-5 + + + sk + ISO-8859-2 + + + sl + ISO-8859-2 + + + sq + ISO-8859-2 + + + sr + ISO-8859-5 + + + sv + ISO-8859-1 + + + tr + ISO-8859-9 + + + uk + ISO-8859-5 + + + zh + GB2312 + + + zh_TW + Big5 + + + + + + + + + Disable TRACE + / + TRACE + + + + + + Enable everything but TRACE + / + TRACE + + + + + diff --git a/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/java/org/eclipse/jetty/ee11/osgi/test/SimpleEchoSocket.java b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/java/org/eclipse/jetty/ee11/osgi/test/SimpleEchoSocket.java new file mode 100644 index 00000000000..c79e1f80ad4 --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/java/org/eclipse/jetty/ee11/osgi/test/SimpleEchoSocket.java @@ -0,0 +1,70 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.osgi.test; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jetty.websocket.api.Callback; +import org.eclipse.jetty.websocket.api.Session; +import org.eclipse.jetty.websocket.api.StatusCode; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketOpen; +import org.eclipse.jetty.websocket.api.annotations.WebSocket; + +/** + * Basic Echo Client Socket + */ +@WebSocket +public class SimpleEchoSocket +{ + private final CountDownLatch closeLatch; + + public SimpleEchoSocket() + { + this.closeLatch = new CountDownLatch(1); + } + + public boolean awaitClose(int duration, TimeUnit unit) throws InterruptedException + { + return this.closeLatch.await(duration, unit); + } + + @OnWebSocketClose + public void onClose(int statusCode, String reason) + { + this.closeLatch.countDown(); // trigger latch + } + + @OnWebSocketOpen + public void onOpen(Session session) + { + session.setMaxTextMessageSize(64 * 1024); + try + { + session.sendText("Foo", Callback.NOOP); + session.close(StatusCode.NORMAL, "I'm done", Callback.NOOP); + } + catch (Throwable t) + { + t.printStackTrace(); + } + } + + @OnWebSocketMessage + public void onMessage(String msg) + { + } +} diff --git a/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/java/org/eclipse/jetty/ee11/osgi/test/SimpleJakartaWebSocket.java b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/java/org/eclipse/jetty/ee11/osgi/test/SimpleJakartaWebSocket.java new file mode 100644 index 00000000000..37cb57e2a68 --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/java/org/eclipse/jetty/ee11/osgi/test/SimpleJakartaWebSocket.java @@ -0,0 +1,66 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.osgi.test; + +import java.util.concurrent.CountDownLatch; +import java.util.logging.Level; +import java.util.logging.Logger; + +import jakarta.websocket.ClientEndpoint; +import jakarta.websocket.CloseReason; +import jakarta.websocket.OnClose; +import jakarta.websocket.OnError; +import jakarta.websocket.OnMessage; +import jakarta.websocket.OnOpen; +import jakarta.websocket.Session; + +import static org.junit.Assert.fail; + +@ClientEndpoint( + subprotocols = {"chat"}) +public class SimpleJakartaWebSocket +{ + private static final Logger LOG = Logger.getLogger(SimpleJakartaWebSocket.class.getName()); + private Session session; + public CountDownLatch messageLatch = new CountDownLatch(1); + public CountDownLatch closeLatch = new CountDownLatch(1); + + @OnError + public void onError(Throwable t) + { + LOG.log(Level.WARNING, "onError", t); + fail(t.getMessage()); + } + + @OnClose + public void onClose(CloseReason close) + { + LOG.info(String.format("Closed: %d, \"%s\"", close.getCloseCode().getCode(), close.getReasonPhrase())); + closeLatch.countDown(); + } + + @OnMessage + public void onMessage(String message) + { + LOG.info(String.format("Received: \"%s\"", message)); + messageLatch.countDown(); + } + + @OnOpen + public void onOpen(Session session) + { + LOG.info("Opened"); + this.session = session; + } +} diff --git a/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/java/org/eclipse/jetty/ee11/osgi/test/SomeCustomBean.java b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/java/org/eclipse/jetty/ee11/osgi/test/SomeCustomBean.java new file mode 100644 index 00000000000..ea43da47ce3 --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/java/org/eclipse/jetty/ee11/osgi/test/SomeCustomBean.java @@ -0,0 +1,24 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.osgi.test; + +/** + * Just a simple bean + * + * @author laeubi + */ +public class SomeCustomBean +{ + +} diff --git a/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/java/org/eclipse/jetty/ee11/osgi/test/TestJettyOSGiAnnotationParser.java b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/java/org/eclipse/jetty/ee11/osgi/test/TestJettyOSGiAnnotationParser.java new file mode 100644 index 00000000000..e88e1801dcf --- /dev/null +++ b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/java/org/eclipse/jetty/ee11/osgi/test/TestJettyOSGiAnnotationParser.java @@ -0,0 +1,94 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee11.osgi.test; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collections; +import java.util.concurrent.ConcurrentHashMap; +import javax.inject.Inject; + +import aQute.bnd.osgi.Constants; +import org.eclipse.jetty.ee11.annotations.ClassInheritanceHandler; +import org.eclipse.jetty.ee11.osgi.annotations.AnnotationParser; +import org.eclipse.jetty.util.resource.ResourceFactory; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.ops4j.pax.exam.Configuration; +import org.ops4j.pax.exam.CoreOptions; +import org.ops4j.pax.exam.Option; +import org.ops4j.pax.exam.junit.PaxExam; +import org.ops4j.pax.tinybundles.core.TinyBundle; +import org.ops4j.pax.tinybundles.core.TinyBundles; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; + +import static org.junit.Assert.assertTrue; +import static org.ops4j.pax.exam.CoreOptions.mavenBundle; + +/** + * TestJettyOSGiAnnotationParser + * + */ +@RunWith(PaxExam.class) +public class TestJettyOSGiAnnotationParser +{ + @Inject + BundleContext bundleContext = null; + + @Configuration + public static Option[] configure() throws IOException + { + ArrayList