From 0a32147e89a6ff8377c589ed1dc0f55142f02cdc Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Tue, 3 May 2022 15:52:44 +0200 Subject: [PATCH] Jetty-12 Restructure Copied ee10 from hackathon branch Co-authored-by: Greg Wilkins Co-authored-by: Jan Bartel Co-authored-by: Joakim Erdfelt Co-authored-by: Lachlan Roberts Co-authored-by: Ludovic Orban Co-authored-by: Olivier Lamy Co-authored-by: Simone Bordet --- jetty-ee10/jetty-ee10-annotations/pom.xml | 95 + .../src/main/config/modules/annotations.mod | 15 + .../src/main/java/module-info.java | 32 + ...AbstractDiscoverableAnnotationHandler.java | 37 + .../annotations/AnnotationConfiguration.java | 1191 ++ .../ee10/annotations/AnnotationDecorator.java | 84 + .../annotations/AnnotationIntrospector.java | 223 + .../ee10/annotations/AnnotationParser.java | 978 ++ .../annotations/ClassInheritanceHandler.java | 81 + ...ContainerInitializerAnnotationHandler.java | 104 + .../DeclareRolesAnnotationHandler.java | 64 + .../MultiPartConfigAnnotationHandler.java | 64 + .../PostConstructAnnotationHandler.java | 101 + .../PreDestroyAnnotationHandler.java | 103 + .../ResourceAnnotationHandler.java | 394 + .../ResourcesAnnotationHandler.java | 72 + .../annotations/RunAsAnnotationHandler.java | 75 + .../ServletContainerInitializersStarter.java | 65 + .../ServletSecurityAnnotationHandler.java | 184 + .../ee10/annotations/WebFilterAnnotation.java | 208 + .../WebFilterAnnotationHandler.java | 57 + .../annotations/WebListenerAnnotation.java | 91 + .../WebListenerAnnotationHandler.java | 54 + .../annotations/WebServletAnnotation.java | 269 + .../WebServletAnnotationHandler.java | 64 + .../jetty/ee10/annotations/package-info.java | 17 + ...rg.eclipse.jetty.ee10.webapp.Configuration | 1 + .../test/jar/test-sci-for-container-path.jar | Bin 0 -> 3256 bytes .../src/test/jar/test-sci-for-webinf.jar | Bin 0 -> 3000 bytes .../src/test/jar/test-sci-with-ordering.jar | Bin 0 -> 3409 bytes .../src/test/jar/test-sci.jar | Bin 0 -> 7749 bytes .../src/test/java/org/acme/ClassOne.java | 25 + .../jetty/ee10/annotations/ClassA.java | 91 + .../jetty/ee10/annotations/ClassB.java | 50 + .../jetty/ee10/annotations/FilterC.java | 78 + .../jetty/ee10/annotations/InterfaceD.java | 22 + .../jetty/ee10/annotations/ListenerC.java | 35 + .../eclipse/jetty/ee10/annotations/Multi.java | 26 + .../jetty/ee10/annotations/Sample.java | 26 + .../jetty/ee10/annotations/ServletC.java | 68 + .../jetty/ee10/annotations/ServletD.java | 26 + .../jetty/ee10/annotations/ServletE.java | 25 + .../TestAnnotationConfiguration.java | 400 + .../annotations/TestAnnotationDecorator.java | 122 + .../TestAnnotationInheritance.java | 177 + .../TestAnnotationIntrospector.java | 93 + .../annotations/TestAnnotationParser.java | 298 + ...eredServletContainerInitializerHolder.java | 98 + .../ee10/annotations/TestRunAsAnnotation.java | 58 + .../TestSecurityAnnotationConversions.java | 340 + .../annotations/TestServletAnnotations.java | 290 + .../ee10/annotations/resources/ResourceA.java | 115 + .../ee10/annotations/resources/ResourceB.java | 39 + .../resources/TestResourceAnnotations.java | 168 + .../src/test/resources/bad-classes.jar | Bin 0 -> 1977 bytes .../test/resources/jdk10/multirelease-10.jar | Bin 0 -> 4059 bytes .../test/resources/jdk9/log4j-api-2.9.0.jar | Bin 0 -> 238875 bytes .../resources/jdk9/slf4j-api-1.8.0-alpha2.jar | Bin 0 -> 43917 bytes .../test/resources/jetty-logging.properties | 3 + .../src/test/resources/tinytest.jar | Bin 0 -> 1568 bytes .../src/test/resources/tinytest_copy.jar | Bin 0 -> 1568 bytes .../src/test/resources/web-fragment4false.xml | 7 + .../src/test/resources/web-fragment4true.xml | 9 + .../src/test/resources/web25.xml | 10 + .../src/test/resources/web31false.xml | 11 + .../src/test/resources/web31true.xml | 11 + jetty-ee10/jetty-ee10-ant/pom.xml | 96 + .../ee10/ant/AntMetaInfConfiguration.java | 81 + .../jetty/ee10/ant/AntWebAppContext.java | 691 ++ .../ee10/ant/AntWebInfConfiguration.java | 59 + .../ee10/ant/AntWebXmlConfiguration.java | 64 + .../eclipse/jetty/ee10/ant/JettyRunTask.java | 310 + .../eclipse/jetty/ee10/ant/JettyStopTask.java | 114 + .../jetty/ee10/ant/ServerProxyImpl.java | 491 + .../eclipse/jetty/ee10/ant/package-info.java | 18 + .../jetty/ee10/ant/types/Attribute.java | 42 + .../jetty/ee10/ant/types/Attributes.java | 33 + .../jetty/ee10/ant/types/Connector.java | 54 + .../jetty/ee10/ant/types/Connectors.java | 76 + .../jetty/ee10/ant/types/ContextHandlers.java | 37 + .../ant/types/FileMatchingConfiguration.java | 91 + .../jetty/ee10/ant/types/LoginServices.java | 37 + .../ee10/ant/types/SystemProperties.java | 59 + .../jetty/ee10/ant/types/package-info.java | 18 + .../jetty/ee10/ant/utils/ServerProxy.java | 34 + .../eclipse/jetty/ee10/ant/utils/TaskLog.java | 52 + .../jetty/ee10/ant/utils/package-info.java | 18 + .../org.eclipse.jetty.webapp.Configuration | 2 + .../src/main/resources/tasks.properties | 2 + .../jetty-ee10-ant/src/test/config/build.xml | 39 + .../org/eclipse/jetty/ee10/ant/AntBuild.java | 289 + .../jetty/ee10/ant/JettyAntTaskTest.java | 66 + .../src/test/resources/connector-test.xml | 19 + .../resources/foo/WEB-INF/acme-taglib.tld | 28 + .../resources/foo/WEB-INF/acme-taglib2.tld | 35 + .../test/resources/foo/WEB-INF/tags/panel.tag | 17 + .../src/test/resources/foo/WEB-INF/web.xml | 28 + .../src/test/resources/foo/index.html | 5 + .../src/test/resources/foo/jsp/bean1.jsp | 15 + .../src/test/resources/foo/jsp/bean2.jsp | 15 + .../src/test/resources/foo/jsp/dump.jsp | 23 + .../src/test/resources/foo/jsp/expr.jsp | 23 + .../src/test/resources/foo/jsp/foo/foo.jsp | 15 + .../src/test/resources/foo/jsp/index.html | 20 + .../src/test/resources/foo/jsp/jstl.jsp | 15 + .../src/test/resources/foo/jsp/tag.jsp | 16 + .../src/test/resources/foo/jsp/tag2.jsp | 19 + .../src/test/resources/foo/jsp/tagfile.jsp | 37 + .../src/test/resources/webapp-test.xml | 17 + jetty-ee10/jetty-ee10-apache-jsp/pom.xml | 105 + .../src/main/config/modules/apache-jsp.mod | 8 + .../src/main/java/module-info.java | 30 + .../apache/jsp/JettyJasperInitializer.java | 98 + .../ee10/apache/jsp/JettyTldPreScanned.java | 87 + .../jetty/ee10/apache/jsp/JuliLog.java | 182 + .../jetty/ee10/jsp/JettyJspServlet.java | 119 + ...akarta.servlet.ServletContainerInitializer | 1 + .../services/org.apache.juli.logging.Log | 1 + .../jetty/ee10/jsp/TestJettyJspServlet.java | 122 + .../ee10/jsp/TestJettyTldPreScanned.java | 67 + .../ee10/jsp/TestJspFileNameToClass.java | 52 + .../test/resources/META-INF/foo-taglib.tld | 25 + .../src/test/resources/base/dir/empty.txt | 0 .../src/test/resources/base/foo.jsp | 23 + .../src/test/resources/taglib.jar | Bin 0 -> 3322 bytes jetty-ee10/jetty-ee10-bom/pom.xml | 206 + jetty-ee10/jetty-ee10-cdi/pom.xml | 87 + .../src/main/config/etc/cdi/jetty-cdi.xml | 8 + .../src/main/config/modules/cdi-decorate.mod | 17 + .../src/main/config/modules/cdi-spi.mod | 17 + .../src/main/config/modules/cdi.mod | 29 + .../src/main/java/module-info.java | 22 + .../jetty/ee10/cdi/CdiConfiguration.java | 34 + .../jetty/ee10/cdi/CdiDecoratingListener.java | 32 + .../cdi/CdiServletContainerInitializer.java | 92 + .../jetty/ee10/cdi/CdiSpiDecorator.java | 218 + ...akarta.servlet.ServletContainerInitializer | 1 + ...rg.eclipse.jetty.ee10.webapp.Configuration | 2 + .../demo-async-rest-jar/pom.xml | 32 + .../jetty/ee10/demos/AbstractRestServlet.java | 137 + .../jetty/ee10/demos/AsyncRestServlet.java | 207 + .../jetty/ee10/demos/SerialRestServlet.java | 96 + .../META-INF/resources/asyncrest.html | 38 + .../META-INF/resources/asyncrest/green.png | Bin 0 -> 166 bytes .../META-INF/resources/asyncrest/red.png | Bin 0 -> 164 bytes .../main/resources/META-INF/web-fragment.xml | 28 + .../demo-async-rest-server/pom.xml | 23 + .../jetty/ee10/demos/AsyncRestServer.java | 47 + .../demo-async-rest-webapp/pom.xml | 33 + .../main/config/modules/demo-async-rest.mod | 15 + .../src/main/webapp/META-INF/MANIFEST.MF | 3 + .../src/main/webapp/WEB-INF/jetty-web.xml | 15 + .../src/main/webapp/WEB-INF/web.xml | 9 + .../src/main/webapp/demo.css | 83 + .../src/main/webapp/index.html | 62 + .../src/main/webapp/small_powered_by.gif | Bin 0 -> 4787 bytes .../jetty-ee10-demos/demo-async-rest/pom.xml | 19 + .../jetty-ee10-demos/demo-jaas-webapp/pom.xml | 54 + .../src/main/config/modules/demo-jaas.mod | 26 + .../main/config/modules/demo.d/demo-jaas.xml | 26 + .../config/modules/demo.d/demo-login.conf | 5 + .../modules/demo.d/demo-login.properties | 1 + .../src/main/webapp/WEB-INF/jetty-web.xml | 8 + .../src/main/webapp/WEB-INF/web.xml | 41 + .../src/main/webapp/auth.html | 17 + .../src/main/webapp/authfail.html | 10 + .../demo-jaas-webapp/src/main/webapp/demo.css | 83 + .../src/main/webapp/index.html | 50 + .../src/main/webapp/login.html | 35 + .../src/main/webapp/logout.jsp | 18 + .../src/main/webapp/small_powered_by.gif | Bin 0 -> 4787 bytes .../src/main/webapp/stylesheet.css | 7 + .../demo-jetty-webapp/jetty-chat.jmx | 318 + .../demo-jetty-webapp/pom.xml | 200 + .../embedded-jetty-web-for-webbundle.xml | 93 + .../src/main/assembly/web-bundle.xml | 38 + .../src/main/config/modules/demo-jetty.mod | 26 + .../config/modules/demo-moved-context.mod | 14 + .../src/main/config/modules/demo-rewrite.mod | 17 + .../demo.d/demo-jetty-override-web.xml | 64 + .../main/config/modules/demo.d/demo-jetty.xml | 41 + .../modules/demo.d/demo-moved-context.xml | 12 + .../modules/demo.d/demo-rewrite-rules.xml | 104 + .../AddListServletRequestListener.java | 45 + .../main/java/org/example/ChatServlet.java | 218 + .../src/main/java/org/example/CookieDump.java | 130 + .../java/org/example/DispatchServlet.java | 254 + .../src/main/java/org/example/Dump.java | 1082 ++ .../src/main/java/org/example/HelloWorld.java | 54 + .../org/example/JakartaWebSocketChat.java | 83 + .../main/java/org/example/LoginServlet.java | 71 + .../src/main/java/org/example/RegTest.java | 171 + .../main/java/org/example/RewriteServlet.java | 70 + .../java/org/example/SecureModeServlet.java | 366 + .../main/java/org/example/SessionDump.java | 191 + .../src/main/java/org/example/TestFilter.java | 105 + .../main/java/org/example/TestListener.java | 232 + .../main/java/org/example/TestServlet.java | 34 + .../org/example/WebSocketChatServlet.java | 116 + .../src/main/webapp/WEB-INF/jetty-web.xml | 26 + .../src/main/webapp/WEB-INF/web.xml | 304 + .../src/main/webapp/auth.html | 47 + .../src/main/webapp/auth/file.txt | 10 + .../src/main/webapp/auth/relax.txt | 10 + .../src/main/webapp/auth2/index.html | 6 + .../src/main/webapp/cgi-bin/hello.sh | 4 + .../src/main/webapp/chat/index.html | 165 + .../demo-jetty-webapp/src/main/webapp/d.txt | 10 + .../demo-jetty-webapp/src/main/webapp/da.txt | 1000 ++ .../src/main/webapp/da.txt.gz | Bin 0 -> 2565 bytes .../demo-jetty-webapp/src/main/webapp/dat.txt | 4000 +++++++ .../src/main/webapp/data.txt | 10000 ++++++++++++++++ .../src/main/webapp/data.txt.gz | Bin 0 -> 25691 bytes .../src/main/webapp/demo.css | 83 + .../src/main/webapp/error404.html | 4 + .../src/main/webapp/favicon.ico | Bin 0 -> 1150 bytes .../src/main/webapp/index.html | 72 + .../main/webapp/jakarta.websocket/index.html | 112 + .../src/main/webapp/logon.html | 20 + .../src/main/webapp/logonError.html | 4 + .../src/main/webapp/remote.html | 35 + .../src/main/webapp/rewrite/index.html | 13 + .../src/main/webapp/rewrite/info.html | 59 + .../src/main/webapp/small_powered_by.gif | Bin 0 -> 4787 bytes .../src/main/webapp/ws/index.html | 112 + .../eclipse/jetty/ee10/ChatServletTest.java | 87 + .../jetty/ee10/DispatchServletTest.java | 147 + .../org/eclipse/jetty/ee10/TestServer.java | 174 + .../test/resources/jetty-logging.properties | 3 + .../src/test/resources/test-realm.properties | 20 + .../jetty-ee10-demos/demo-jndi-webapp/pom.xml | 103 + .../src/main/config/modules/demo-jndi.mod | 20 + .../main/config/modules/demo.d/demo-jndi.xml | 74 + .../src/main/java/org/example/JNDITest.java | 136 + .../src/main/templates/env-definitions.xml | 27 + .../main/templates/jetty-test-jndi-header.xml | 20 + .../main/templates/plugin-context-header.xml | 15 + .../src/main/webapp/WEB-INF/jetty-env.xml | 45 + .../src/main/webapp/WEB-INF/jetty-web.xml | 8 + .../src/main/webapp/WEB-INF/web.xml | 54 + .../demo-jndi-webapp/src/main/webapp/demo.css | 83 + .../src/main/webapp/index.html | 56 + .../src/main/webapp/small_powered_by.gif | Bin 0 -> 4787 bytes .../src/main/webapp/stylesheet.css | 7 + .../jetty-ee10-demos/demo-jsp-webapp/pom.xml | 124 + .../src/main/assembly/web-bundle.xml | 20 + .../src/main/config/modules/demo-jsp.mod | 14 + .../src/main/java/org/example/Counter.java | 38 + .../src/main/java/org/example/Date2Tag.java | 51 + .../src/main/java/org/example/DateTag.java | 102 + .../main/java/org/example/TagListener.java | 133 + .../src/main/webapp/WEB-INF/acme-taglib.tld | 28 + .../src/main/webapp/WEB-INF/acme-taglib2.tld | 37 + .../src/main/webapp/WEB-INF/jetty-web.xml | 8 + .../src/main/webapp/WEB-INF/tags/panel.tag | 17 + .../src/main/webapp/WEB-INF/web.xml | 18 + .../demo-jsp-webapp/src/main/webapp/bean1.jsp | 15 + .../demo-jsp-webapp/src/main/webapp/bean2.jsp | 15 + .../demo-jsp-webapp/src/main/webapp/demo.css | 84 + .../demo-jsp-webapp/src/main/webapp/dump.jsp | 24 + .../demo-jsp-webapp/src/main/webapp/expr.jsp | 23 + .../src/main/webapp/foo/foo.jsp | 15 + .../demo-jsp-webapp/src/main/webapp/index.jsp | 45 + .../demo-jsp-webapp/src/main/webapp/jstl.jsp | 15 + .../src/main/webapp/small_powered_by.gif | Bin 0 -> 4787 bytes .../demo-jsp-webapp/src/main/webapp/tag.jsp | 16 + .../demo-jsp-webapp/src/main/webapp/tag2.jsp | 19 + .../src/main/webapp/tagfile.jsp | 37 + .../demo-mock-resources/pom.xml | 60 + .../config/modules/demo-mock-resources.mod | 14 + .../main/java/org/example/MockDataSource.java | 83 + .../main/java/org/example/MockTransport.java | 41 + .../java/org/example/MockUserTransaction.java | 62 + .../resources/META-INF/javaxmail.providers | 1 + .../demo-proxy-webapp/pom.xml | 91 + .../src/main/config/modules/demo-proxy.mod | 14 + .../src/main/webapp/META-INF/MANIFEST.MF | 21 + .../src/main/webapp/WEB-INF/jetty-web.xml | 9 + .../src/main/webapp/WEB-INF/web.xml | 31 + .../jetty/ee10/demos/ProxyWebAppTest.java | 93 + .../test/resources/jetty-logging.properties | 5 + .../demo-simple-webapp/pom.xml | 18 + .../src/main/config/modules/demo-simple.mod | 12 + .../src/main/webapp/WEB-INF/web.xml | 15 + .../src/main/webapp/index.html | 6 + .../src/main/webapp/jetty.icon | Bin 0 -> 6586 bytes .../src/main/webapp/jetty.png | Bin 0 -> 5465 bytes .../src/main/webapp/jetty.webp | Bin 0 -> 3534 bytes .../demo-container-initializer/pom.xml | 41 + .../java/org/example/initializer/Foo.java | 27 + .../example/initializer/FooInitializer.java | 96 + ...akarta.servlet.ServletContainerInitializer | 1 + .../demo-spec/demo-spec-webapp/pom.xml | 226 + .../demo-spec-webapp/src/etc/realm.properties | 21 + .../src/main/assembly/web-bundle.xml | 18 + .../src/main/config/modules/demo-spec.mod | 21 + .../main/config/modules/demo.d/demo-spec.xml | 33 + .../org/example/test/AnnotatedListener.java | 175 + .../java/org/example/test/AnnotationTest.java | 349 + .../example/test/AsyncListenerServlet.java | 114 + .../src/main/java/org/example/test/Bar.java | 22 + .../org/example/test/ClassLoaderServlet.java | 125 + .../java/org/example/test/MultiPartTest.java | 165 + .../org/example/test/RoleAnnotationTest.java | 84 + .../java/org/example/test/SecuredServlet.java | 53 + .../java/org/example/test/TestListener.java | 221 + .../templates/annotations-context-header.xml | 29 + .../src/main/templates/env-definitions.xml | 19 + .../main/templates/plugin-context-header.xml | 17 + .../src/main/webapp/WEB-INF/jetty-env.xml | 17 + .../src/main/webapp/WEB-INF/jetty-web.xml | 17 + .../src/main/webapp/WEB-INF/web.xml | 106 + .../src/main/webapp/authfail.html | 10 + .../demo-spec-webapp/src/main/webapp/demo.css | 83 + .../src/main/webapp/dynamic.jsp | 13 + .../src/main/webapp/index.html | 86 + .../src/main/webapp/login.html | 19 + .../src/main/webapp/logout.jsp | 19 + .../src/main/webapp/small_powered_by.gif | Bin 0 -> 4787 bytes .../src/main/webapp/stylesheet.css | 7 + .../src/test/jetty-plugin-env.xml | 43 + .../demo-spec/demo-web-fragment/pom.xml | 24 + .../org/example/fragment/FragmentServlet.java | 67 + .../META-INF/resources/fragmentA/index.html | 8 + .../main/resources/META-INF/web-fragment.xml | 42 + jetty-ee10/jetty-ee10-demos/demo-spec/pom.xml | 18 + .../jetty-ee10-demos/demo-template/pom.xml | 28 + .../demo-template/src/main/resources/demo.css | 83 + .../src/main/resources/index.html | 35 + .../src/main/resources/small_powered_by.gif | Bin 0 -> 4787 bytes jetty-ee10/jetty-ee10-demos/embedded/pom.xml | 135 + .../embedded/prodDb.properties | 17 + .../jetty-ee10-demos/embedded/prodDb.script | 4 + .../jetty/ee10/demos/AsyncEchoServlet.java | 116 + .../eclipse/jetty/ee10/demos/DumpServlet.java | 70 + .../jetty/ee10/demos/ExampleServer.java | 50 + .../jetty/ee10/demos/ExampleServerXml.java | 43 + .../eclipse/jetty/ee10/demos/ExampleUtil.java | 81 + .../jetty/ee10/demos/FastFileServer.java | 216 + .../eclipse/jetty/ee10/demos/FileServer.java | 69 + .../jetty/ee10/demos/FileServerXml.java | 51 + .../jetty/ee10/demos/HelloHandler.java | 51 + .../jetty/ee10/demos/HelloServlet.java | 48 + .../jetty/ee10/demos/HelloSessionServlet.java | 79 + .../eclipse/jetty/ee10/demos/HelloWorld.java | 48 + .../eclipse/jetty/ee10/demos/Http2Server.java | 194 + .../eclipse/jetty/ee10/demos/JarServer.java | 60 + .../eclipse/jetty/ee10/demos/JettyDemos.java | 159 + .../jetty/ee10/demos/LikeJettyXml.java | 188 + .../jetty/ee10/demos/ManyConnectors.java | 134 + .../jetty/ee10/demos/ManyContexts.java | 58 + .../jetty/ee10/demos/ManyHandlers.java | 165 + .../jetty/ee10/demos/ManyServletContexts.java | 66 + .../jetty/ee10/demos/MinimalServlets.java | 76 + .../jetty/ee10/demos/OneConnector.java | 52 + .../eclipse/jetty/ee10/demos/OneContext.java | 45 + .../eclipse/jetty/ee10/demos/OneHandler.java | 34 + .../jetty/ee10/demos/OneServletContext.java | 140 + .../ee10/demos/OneServletContextJmxStats.java | 55 + .../demos/OneServletContextWithSession.java | 72 + .../eclipse/jetty/ee10/demos/OneWebApp.java | 67 + .../jetty/ee10/demos/OneWebAppWithJsp.java | 106 + .../eclipse/jetty/ee10/demos/ProxyServer.java | 56 + .../jetty/ee10/demos/RewriteServer.java | 57 + .../jetty/ee10/demos/SecuredHelloHandler.java | 114 + .../ee10/demos/ServerWithAnnotations.java | 93 + .../jetty/ee10/demos/ServerWithJMX.java | 58 + .../jetty/ee10/demos/ServerWithJNDI.java | 96 + .../jetty/ee10/demos/SimplestServer.java | 40 + .../jetty/ee10/demos/SplitFileServer.java | 97 + .../jetty/ee10/demos/WebSocketServer.java | 84 + .../embedded/src/main/other/content.jar | Bin 0 -> 767 bytes .../main/resources/demo/demo-realm.properties | 21 + .../src/main/resources/demo/webdefault.xml | 443 + .../src/main/resources/docroot/push.html | 100 + .../main/resources/docroot/pushed/tile00.jpg | Bin 0 -> 15228 bytes .../main/resources/docroot/pushed/tile01.jpg | Bin 0 -> 15168 bytes .../main/resources/docroot/pushed/tile02.jpg | Bin 0 -> 15217 bytes .../main/resources/docroot/pushed/tile03.jpg | Bin 0 -> 15323 bytes .../main/resources/docroot/pushed/tile04.jpg | Bin 0 -> 15321 bytes .../main/resources/docroot/pushed/tile05.jpg | Bin 0 -> 15276 bytes .../main/resources/docroot/pushed/tile06.jpg | Bin 0 -> 15302 bytes .../main/resources/docroot/pushed/tile07.jpg | Bin 0 -> 15246 bytes .../main/resources/docroot/pushed/tile08.jpg | Bin 0 -> 15281 bytes .../main/resources/docroot/pushed/tile09.jpg | Bin 0 -> 15539 bytes .../main/resources/docroot/pushed/tile10.jpg | Bin 0 -> 16093 bytes .../main/resources/docroot/pushed/tile11.jpg | Bin 0 -> 16024 bytes .../main/resources/docroot/pushed/tile12.jpg | Bin 0 -> 15972 bytes .../main/resources/docroot/pushed/tile13.jpg | Bin 0 -> 16012 bytes .../main/resources/docroot/pushed/tile14.jpg | Bin 0 -> 16069 bytes .../main/resources/docroot/pushed/tile15.jpg | Bin 0 -> 15916 bytes .../main/resources/docroot/pushed/tile16.jpg | Bin 0 -> 15792 bytes .../main/resources/docroot/pushed/tile17.jpg | Bin 0 -> 15813 bytes .../main/resources/docroot/pushed/tile18.jpg | Bin 0 -> 15868 bytes .../main/resources/docroot/pushed/tile19.jpg | Bin 0 -> 16299 bytes .../main/resources/docroot/pushed/tile20.jpg | Bin 0 -> 15852 bytes .../main/resources/docroot/pushed/tile21.jpg | Bin 0 -> 15775 bytes .../main/resources/docroot/pushed/tile22.jpg | Bin 0 -> 15740 bytes .../main/resources/docroot/pushed/tile23.jpg | Bin 0 -> 15950 bytes .../main/resources/docroot/pushed/tile24.jpg | Bin 0 -> 16441 bytes .../main/resources/docroot/pushed/tile25.jpg | Bin 0 -> 16426 bytes .../main/resources/docroot/pushed/tile26.jpg | Bin 0 -> 15749 bytes .../main/resources/docroot/pushed/tile27.jpg | Bin 0 -> 15647 bytes .../main/resources/docroot/pushed/tile28.jpg | Bin 0 -> 15744 bytes .../main/resources/docroot/pushed/tile29.jpg | Bin 0 -> 15849 bytes .../main/resources/docroot/pushed/tile30.jpg | Bin 0 -> 15638 bytes .../main/resources/docroot/pushed/tile31.jpg | Bin 0 -> 15567 bytes .../main/resources/docroot/pushed/tile32.jpg | Bin 0 -> 15660 bytes .../main/resources/docroot/pushed/tile33.jpg | Bin 0 -> 16331 bytes .../main/resources/docroot/pushed/tile34.jpg | Bin 0 -> 16060 bytes .../main/resources/docroot/pushed/tile35.jpg | Bin 0 -> 16062 bytes .../main/resources/docroot/pushed/tile36.jpg | Bin 0 -> 16395 bytes .../main/resources/docroot/pushed/tile37.jpg | Bin 0 -> 15823 bytes .../main/resources/docroot/pushed/tile38.jpg | Bin 0 -> 15664 bytes .../main/resources/docroot/pushed/tile39.jpg | Bin 0 -> 15609 bytes .../main/resources/docroot/pushed/tile40.jpg | Bin 0 -> 15601 bytes .../main/resources/docroot/pushed/tile41.jpg | Bin 0 -> 15664 bytes .../main/resources/docroot/pushed/tile42.jpg | Bin 0 -> 16299 bytes .../main/resources/docroot/pushed/tile43.jpg | Bin 0 -> 15958 bytes .../main/resources/docroot/pushed/tile44.jpg | Bin 0 -> 15915 bytes .../main/resources/docroot/pushed/tile45.jpg | Bin 0 -> 15903 bytes .../main/resources/docroot/pushed/tile46.jpg | Bin 0 -> 15927 bytes .../main/resources/docroot/pushed/tile47.jpg | Bin 0 -> 16327 bytes .../main/resources/docroot/pushed/tile48.jpg | Bin 0 -> 15690 bytes .../main/resources/docroot/pushed/tile49.jpg | Bin 0 -> 15612 bytes .../src/main/resources/docroot/readme.txt | 1 + .../main/resources/docroot/tiles/tile00.jpg | Bin 0 -> 15089 bytes .../main/resources/docroot/tiles/tile01.jpg | Bin 0 -> 15085 bytes .../main/resources/docroot/tiles/tile02.jpg | Bin 0 -> 15140 bytes .../main/resources/docroot/tiles/tile03.jpg | Bin 0 -> 15276 bytes .../main/resources/docroot/tiles/tile04.jpg | Bin 0 -> 15235 bytes .../main/resources/docroot/tiles/tile05.jpg | Bin 0 -> 15254 bytes .../main/resources/docroot/tiles/tile06.jpg | Bin 0 -> 15292 bytes .../main/resources/docroot/tiles/tile07.jpg | Bin 0 -> 15227 bytes .../main/resources/docroot/tiles/tile08.jpg | Bin 0 -> 15282 bytes .../main/resources/docroot/tiles/tile09.jpg | Bin 0 -> 15532 bytes .../main/resources/docroot/tiles/tile10.jpg | Bin 0 -> 15677 bytes .../main/resources/docroot/tiles/tile11.jpg | Bin 0 -> 15673 bytes .../main/resources/docroot/tiles/tile12.jpg | Bin 0 -> 15605 bytes .../main/resources/docroot/tiles/tile13.jpg | Bin 0 -> 15664 bytes .../main/resources/docroot/tiles/tile14.jpg | Bin 0 -> 15730 bytes .../main/resources/docroot/tiles/tile15.jpg | Bin 0 -> 15614 bytes .../main/resources/docroot/tiles/tile16.jpg | Bin 0 -> 15507 bytes .../main/resources/docroot/tiles/tile17.jpg | Bin 0 -> 15533 bytes .../main/resources/docroot/tiles/tile18.jpg | Bin 0 -> 15615 bytes .../main/resources/docroot/tiles/tile19.jpg | Bin 0 -> 16092 bytes .../main/resources/docroot/tiles/tile20.jpg | Bin 0 -> 15844 bytes .../main/resources/docroot/tiles/tile21.jpg | Bin 0 -> 15815 bytes .../main/resources/docroot/tiles/tile22.jpg | Bin 0 -> 15788 bytes .../main/resources/docroot/tiles/tile23.jpg | Bin 0 -> 15994 bytes .../main/resources/docroot/tiles/tile24.jpg | Bin 0 -> 16491 bytes .../main/resources/docroot/tiles/tile25.jpg | Bin 0 -> 16403 bytes .../main/resources/docroot/tiles/tile26.jpg | Bin 0 -> 15743 bytes .../main/resources/docroot/tiles/tile27.jpg | Bin 0 -> 15603 bytes .../main/resources/docroot/tiles/tile28.jpg | Bin 0 -> 15748 bytes .../main/resources/docroot/tiles/tile29.jpg | Bin 0 -> 15876 bytes .../main/resources/docroot/tiles/tile30.jpg | Bin 0 -> 15476 bytes .../main/resources/docroot/tiles/tile31.jpg | Bin 0 -> 15438 bytes .../main/resources/docroot/tiles/tile32.jpg | Bin 0 -> 15509 bytes .../main/resources/docroot/tiles/tile33.jpg | Bin 0 -> 16272 bytes .../main/resources/docroot/tiles/tile34.jpg | Bin 0 -> 16062 bytes .../main/resources/docroot/tiles/tile35.jpg | Bin 0 -> 16064 bytes .../main/resources/docroot/tiles/tile36.jpg | Bin 0 -> 16259 bytes .../main/resources/docroot/tiles/tile37.jpg | Bin 0 -> 15679 bytes .../main/resources/docroot/tiles/tile38.jpg | Bin 0 -> 15502 bytes .../main/resources/docroot/tiles/tile39.jpg | Bin 0 -> 15439 bytes .../main/resources/docroot/tiles/tile40.jpg | Bin 0 -> 15390 bytes .../main/resources/docroot/tiles/tile41.jpg | Bin 0 -> 15455 bytes .../main/resources/docroot/tiles/tile42.jpg | Bin 0 -> 16162 bytes .../main/resources/docroot/tiles/tile43.jpg | Bin 0 -> 15896 bytes .../main/resources/docroot/tiles/tile44.jpg | Bin 0 -> 15861 bytes .../main/resources/docroot/tiles/tile45.jpg | Bin 0 -> 15843 bytes .../main/resources/docroot/tiles/tile46.jpg | Bin 0 -> 15848 bytes .../main/resources/docroot/tiles/tile47.jpg | Bin 0 -> 16214 bytes .../main/resources/docroot/tiles/tile48.jpg | Bin 0 -> 15564 bytes .../main/resources/docroot/tiles/tile49.jpg | Bin 0 -> 15517 bytes .../src/main/resources/etc/keystore.p12 | Bin 0 -> 2565 bytes .../src/main/resources/etc/realm.properties | 20 + .../src/main/resources/exampleserver.xml | 41 + .../src/main/resources/fileserver.xml | 44 + .../resources/java-util-logging.properties | 9 + .../main/resources/jetty-logging.properties | 12 + .../src/main/resources/logback-access.xml | 16 + .../ee10/demos/AbstractEmbeddedTest.java | 59 + .../jetty/ee10/demos/ExampleServerTest.java | 85 + .../ee10/demos/ExampleServerXmlTest.java | 62 + .../jetty/ee10/demos/FastFileServerTest.java | 90 + .../jetty/ee10/demos/FileServerTest.java | 88 + .../jetty/ee10/demos/FileServerXmlTest.java | 87 + .../jetty/ee10/demos/JarServerTest.java | 81 + .../jetty/ee10/demos/LikeJettyXmlTest.java | 93 + .../jetty/ee10/demos/ManyConnectorsTest.java | 90 + .../jetty/ee10/demos/ManyContextsTest.java | 112 + .../jetty/ee10/demos/ManyHandlersTest.java | 105 + .../ee10/demos/ManyServletContextsTest.java | 110 + .../jetty/ee10/demos/MinimalServletsTest.java | 62 + .../jetty/ee10/demos/OneConnectorTest.java | 62 + .../jetty/ee10/demos/OneContextTest.java | 62 + .../jetty/ee10/demos/OneHandlerTest.java | 62 + .../demos/OneServletContextJmxStatsTest.java | 96 + .../ee10/demos/OneServletContextTest.java | 153 + .../OneServletContextWithSessionTest.java | 90 + .../jetty/ee10/demos/OneWebAppTest.java | 68 + .../ee10/demos/OneWebAppWithJspTest.java | 106 + .../jetty/ee10/demos/ProxyServerTest.java | 71 + .../jetty/ee10/demos/RewriteServerTest.java | 78 + .../ee10/demos/SecuredHelloHandlerTest.java | 80 + .../eclipse/jetty/ee10/demos/ServerUtil.java | 83 + .../ee10/demos/ServerWithAnnotationsTest.java | 64 + .../jetty/ee10/demos/ServerWithJMXTest.java | 59 + .../jetty/ee10/demos/ServerWithJNDITest.java | 72 + .../jetty/ee10/demos/SimplestServerTest.java | 52 + .../jetty/ee10/demos/SplitFileServerTest.java | 92 + .../jetty/ee10/demos/WebSocketServerTest.java | 106 + .../src/test/resources/dir0/test0.txt | 1 + .../src/test/resources/dir1/test1.txt | 1 + .../test/resources/jetty-logging.properties | 11 + .../src/test/resources/realm.properties | 21 + jetty-ee10/jetty-ee10-demos/pom.xml | 45 + jetty-ee10/jetty-ee10-glassfish-jstl/pom.xml | 79 + .../main/config/modules/glassfish-jstl.mod | 7 + .../src/main/resources/readme.txt | 4 + .../eclipse/jetty/ee10/jstl/JspConfig.java | 36 + .../jetty/ee10/jstl/JspIncludeTest.java | 167 + .../org/eclipse/jetty/ee10/jstl/JstlTest.java | 139 + .../test/resources/jetty-logging.properties | 3 + .../src/test/taglibjar/META-INF/etag.tld | 16 + .../taglibjar/META-INF/tags/errorhandler.tag | 12 + .../test/webapp/WEB-INF/lib/testtaglib.jar | Bin 0 -> 791 bytes .../src/test/webapp/WEB-INF/web.xml | 7 + .../src/test/webapp/catch-basic.jsp | 16 + .../src/test/webapp/catch-taglib.jsp | 11 + .../src/test/webapp/included.jsp | 8 + .../src/test/webapp/ref.jsp | 2 + .../src/test/webapp/top.jsp | 5 + .../src/test/webapp/urls.jsp | 6 + jetty-ee10/jetty-ee10-http-spi/pom.xml | 114 + .../src/main/java/module-info.java | 23 + .../ee10/http/spi/DelegatingThreadPool.java | 130 + .../ee10/http/spi/HttpSpiContextHandler.java | 159 + .../jetty/ee10/http/spi/JettyExchange.java | 27 + .../jetty/ee10/http/spi/JettyHttpContext.java | 104 + .../ee10/http/spi/JettyHttpExchange.java | 164 + .../http/spi/JettyHttpExchangeDelegate.java | 226 + .../jetty/ee10/http/spi/JettyHttpServer.java | 314 + .../http/spi/JettyHttpServerProvider.java | 71 + .../ee10/http/spi/JettyHttpsExchange.java | 174 + ....sun.net.httpserver.spi.HttpServerProvider | 1 + .../jetty/ee10/http/spi/LoggingUtil.java | 29 + .../jetty/ee10/http/spi/SPIServerTest.java | 138 + .../TestEndpointMultiplePublishProblem.java | 160 + .../jetty/ee10/http/spi/TestSPIServer.java | 202 + .../jetty/ee10/http/spi/util/Pool.java | 43 + .../jetty/ee10/http/spi/util/PrintTask.java | 27 + .../ee10/http/spi/util/SpiConstants.java | 78 + .../test/resources/jetty-logging.properties | 5 + jetty-ee10/jetty-ee10-jaas/pom.xml | 169 + .../src/main/config/etc/jetty-jaas.xml | 21 + .../src/main/config/modules/jaas.mod | 18 + .../src/main/java/module-info.java | 27 + .../jetty/ee10/jaas/JAASLoginService.java | 310 + .../jetty/ee10/jaas/JAASPrincipal.java | 63 + .../org/eclipse/jetty/ee10/jaas/JAASRole.java | 33 + .../jetty/ee10/jaas/JAASUserPrincipal.java | 68 + .../ee10/jaas/PropertyUserStoreManager.java | 94 + .../callback/AbstractCallbackHandler.java | 51 + .../jaas/callback/DefaultCallbackHandler.java | 77 + .../ee10/jaas/callback/ObjectCallback.java | 44 + .../callback/RequestParameterCallback.java | 50 + .../jaas/callback/ServletRequestCallback.java | 38 + .../ee10/jaas/callback/package-info.java | 18 + .../eclipse/jetty/ee10/jaas/package-info.java | 18 + .../jaas/spi/AbstractDatabaseLoginModule.java | 156 + .../ee10/jaas/spi/AbstractLoginModule.java | 291 + .../ee10/jaas/spi/DataSourceLoginModule.java | 83 + .../jetty/ee10/jaas/spi/JDBCLoginModule.java | 102 + .../jetty/ee10/jaas/spi/LdapLoginModule.java | 756 ++ .../jaas/spi/PropertyFileLoginModule.java | 138 + .../jetty/ee10/jaas/spi/package-info.java | 18 + .../ee10/jaas/JAASLdapLoginServiceTest.java | 314 + .../jetty/ee10/jaas/JAASLoginServiceTest.java | 245 + .../jetty/ee10/jaas/TestLoginModule.java | 59 + .../eclipse/jetty/ee10/jaas/TestServlet.java | 91 + .../jaas/spi/PropertyFileLoginModuleTest.java | 122 + .../test/resources/jetty-logging.properties | 3 + .../src/test/resources/login.properties | 1 + jetty-ee10/jetty-ee10-jaspi/pom.xml | 74 + .../etc/jaspi/jaspi-authmoduleconfig.xml | 8 + .../main/config/etc/jaspi/jaspi-default.xml | 20 + .../src/main/config/etc/jaspi/jaspi-demo.xml | 48 + .../jaspi-default-auth-config-factory.mod | 16 + .../src/main/config/modules/jaspi-demo.mod | 16 + .../src/main/config/modules/jaspi.mod | 22 + .../src/main/java/module-info.java | 29 + .../jaspi/DefaultAuthConfigFactory.java | 248 + .../security/jaspi/JaspiAuthenticator.java | 304 + .../jaspi/JaspiAuthenticatorFactory.java | 155 + .../ee10/security/jaspi/JaspiMessageInfo.java | 274 + .../jaspi/ServletCallbackHandler.java | 134 + .../ee10/security/jaspi/SimpleAuthConfig.java | 77 + .../CredentialValidationCallback.java | 70 + .../security/jaspi/callback/package-info.java | 18 + .../jaspi/modules/BaseAuthModule.java | 139 + .../BasicAuthenticationAuthModule.java | 101 + .../security/jaspi/modules/package-info.java | 18 + .../ee10/security/jaspi/package-info.java | 18 + .../provider/JaspiAuthConfigProvider.java | 132 + .../jaspi/provider/SimpleAuthConfig.java | 84 + .../provider/SimpleServerAuthContext.java | 59 + ...lipse.jetty.security.Authenticator$Factory | 1 + .../jaspi/DefaultAuthConfigFactoryTest.java | 115 + .../security/jaspi/HttpHeaderAuthModule.java | 118 + .../jetty/ee10/security/jaspi/JaspiTest.java | 225 + .../jetty-ee10-jspc-maven-plugin/pom.xml | 169 + .../src/it/package-root/invoker.properties | 2 + .../src/it/package-root/pom.xml | 42 + .../src/it/package-root/postbuild.groovy | 9 + .../it/package-root/src/main/webapp/foo.jsp | 23 + .../src/it/settings.xml | 36 + .../src/it/simple-jsp-fail/invoker.properties | 3 + .../src/it/simple-jsp-fail/pom.xml | 41 + .../src/it/simple-jsp-fail/postbuild.groovy | 1 + .../it/simple-jsp-fail/src/main/jsp/foo.jsp | 23 + .../src/it/simple-jsp/invoker.properties | 2 + .../src/it/simple-jsp/pom.xml | 37 + .../src/it/simple-jsp/postbuild.groovy | 18 + .../src/it/simple-jsp/src/main/webapp/foo.jsp | 23 + .../jetty/ee10/jspc/plugin/JspcMojo.java | 627 + .../jetty/ee10/jspc/plugin/package-info.java | 18 + .../m2e/lifecycle-mapping-metadata.xml | 30 + .../src/site/markdown/index.md | 4 + .../src/site/site.xml | 44 + .../README_INTEGRATION_TEST.md | 26 + jetty-ee10/jetty-ee10-maven-plugin/pom.xml | 416 + .../src/it/it-parent-pom/invoker.properties | 1 + .../src/it/it-parent-pom/pom.xml | 157 + .../jetty-cdi-start-forked/invoker.properties | 1 + .../src/it/jetty-cdi-start-forked/pom.xml | 94 + .../jetty-cdi-start-forked/postbuild.groovy | 17 + .../src/main/java/test/Greeter.java | 37 + .../src/main/jetty/jetty-context.xml | 22 + .../src/main/jetty/jetty.xml | 40 + .../api/pom.xml | 11 + .../api/src/main/java/test/Api.java | 22 + .../invoker.properties | 2 + .../pom.xml | 20 + .../postbuild.groovy | 21 + .../web/pom.xml | 62 + .../web/src/config/jetty.xml | 48 + ...sLoadingTestingServletContextListener.java | 70 + .../MyLibrary/pom.xml | 21 + .../main/java/jettyissue/MyAnnotation.java | 24 + .../MyServletContainerInitializer.java | 28 + ...akarta.servlet.ServletContainerInitializer | 1 + .../MyWebApp/pom.xml | 66 + .../MyWebApp/src/config/context.xml | 7 + .../MyWebApp/src/config/jetty.xml | 39 + .../src/main/java/jettyissue/NormalClass.java | 19 + .../MyWebApp/src/main/webapp/index.html | 10 + .../invoker.properties | 1 + .../src/it/jetty-run-mojo-jar-scan-it/pom.xml | 32 + .../postbuild.groovy | 3 + .../invoker.properties | 2 + .../jetty-simple-base/pom.xml | 41 + .../HelloServlet.java | 40 + .../PingServlet.java | 35 + .../main/resources/META-INF/web-fragment.xml | 33 + .../jetty-simple-webapp/pom.xml | 114 + .../src/base/etc/test-jetty.xml | 14 + .../src/base/modules/testmod.mod | 12 + .../src/main/webapp/WEB-INF/web.xml | 7 + .../src/it/jetty-start-distro-mojo-it/pom.xml | 40 + .../postbuild.groovy | 18 + .../invoker.properties | 1 + .../jetty-simple-base/pom.xml | 41 + .../jetty/its/jetty_start_forked/Counter.java | 38 + .../its/jetty_start_forked/HelloServlet.java | 40 + .../its/jetty_start_forked/PingServlet.java | 35 + .../main/resources/META-INF/web-fragment.xml | 32 + .../jetty-simple-webapp/pom.xml | 114 + .../jetty-simple-webapp/src/config/jetty.xml | 40 + .../src/main/webapp/WEB-INF/web.xml | 7 + .../src/main/webapp/jsp/bean1.jsp | 13 + .../src/it/jetty-start-forked-mojo-it/pom.xml | 41 + .../postbuild.groovy | 19 + .../it/jetty-start-gwt-it/beer-client/pom.xml | 48 + .../src/main/java/org/olamy/App.java | 184 + .../beer-client/src/main/module.gwt.xml | 12 + .../it/jetty-start-gwt-it/beer-server/pom.xml | 113 + .../beer-server/src/config/jetty.xml | 40 + .../java/org/olamy/GreetingServiceImpl.java | 46 + .../src/main/jettyconf/context.xml | 8 + .../src/main/webapp/WEB-INF/web.xml | 20 + .../beer-server/src/main/webapp/beer.css | 34 + .../beer-server/src/main/webapp/favicon.ico | Bin 0 -> 1082 bytes .../beer-server/src/main/webapp/index.html | 59 + .../it/jetty-start-gwt-it/beer-shared/pom.xml | 29 + .../main/java/org/olamy/FieldVerifier.java | 58 + .../main/java/org/olamy/GreetingResponse.java | 54 + .../main/java/org/olamy/GreetingService.java | 26 + .../java/org/olamy/GreetingServiceAsync.java | 25 + .../it/jetty-start-gwt-it/invoker.properties | 1 + .../src/it/jetty-start-gwt-it/pom.xml | 82 + .../it/jetty-start-gwt-it/postbuild.groovy | 22 + .../it/jetty-start-mojo-it/invoker.properties | 1 + .../jetty-simple-base/pom.xml | 39 + .../its/jetty_start_mojo_it/Counter.java | 38 + .../its/jetty_start_mojo_it/HelloServlet.java | 40 + .../its/jetty_start_mojo_it/PingServlet.java | 35 + .../main/resources/META-INF/web-fragment.xml | 32 + .../jetty-simple-webapp/pom.xml | 110 + .../src/config/context.xml | 7 + .../jetty-simple-webapp/src/config/jetty.xml | 40 + .../src/main/webapp/WEB-INF/web.xml | 7 + .../src/main/webapp/jsp/bean1.jsp | 13 + .../src/it/jetty-start-mojo-it/pom.xml | 40 + .../it/jetty-start-mojo-it/postbuild.groovy | 23 + .../common/pom.xml | 11 + .../main/java/mca/common/CommonService.java | 19 + .../invoker.properties | 1 + .../module/module-api/pom.xml | 11 + .../src/main/java/mca/module/ModuleApi.java | 19 + .../module/module-impl/pom.xml | 17 + .../src/main/java/mca/module/ModuleImpl.java | 22 + .../module/pom.xml | 24 + .../pom.xml | 51 + .../postbuild.groovy | 36 + .../webapp-war/pom.xml | 60 + .../webapp-war/src/config/jetty.xml | 40 + .../mca/webapp/WebAppServletListener.java | 51 + .../src/main/webapp/WEB-INF/web.xml | 11 + .../invoker.properties | 2 + .../jetty-simple-base/pom.xml | 39 + .../its/jetty_run_mojo_it/HelloServlet.java | 40 + .../its/jetty_run_mojo_it/PingServlet.java | 35 + .../main/resources/META-INF/web-fragment.xml | 32 + .../jetty-simple-webapp/pom.xml | 130 + .../src/base/etc/test-jetty.xml | 14 + .../src/base/modules/testmod.mod | 11 + .../src/main/webapp/WEB-INF/web.xml | 7 + .../it/jetty-start-war-distro-mojo-it/pom.xml | 40 + .../postbuild.groovy | 35 + .../invoker.properties | 2 + .../jetty-simple-base/pom.xml | 40 + .../HelloServlet.java | 40 + .../PingServlet.java | 35 + .../main/resources/META-INF/web-fragment.xml | 32 + .../jetty-simple-webapp/pom.xml | 128 + .../jetty-simple-webapp/src/config/jetty.xml | 40 + .../src/main/webapp/WEB-INF/web.xml | 7 + .../it/jetty-start-war-forked-mojo-it/pom.xml | 41 + .../postbuild.groovy | 34 + .../invoker.properties | 2 + .../src/it/jetty-start-war-mojo-it/pom.xml | 139 + .../jetty-start-war-mojo-it/postbuild.groovy | 22 + .../src/config/jetty.xml | 40 + .../src/it/settings.xml | 36 + .../ee10/maven/plugin/AbstractForker.java | 250 + .../plugin/AbstractUnassembledWebAppMojo.java | 243 + .../ee10/maven/plugin/AbstractWebAppMojo.java | 878 ++ .../ee10/maven/plugin/ConsoleReader.java | 66 + .../maven/plugin/JettyEffectiveWebXml.java | 87 + .../ee10/maven/plugin/JettyEmbedder.java | 320 + .../ee10/maven/plugin/JettyForkedChild.java | 218 + .../jetty/ee10/maven/plugin/JettyForker.java | 279 + .../ee10/maven/plugin/JettyHomeForker.java | 420 + .../jetty/ee10/maven/plugin/JettyRunMojo.java | 417 + .../ee10/maven/plugin/JettyRunWarMojo.java | 294 + .../ee10/maven/plugin/JettyStartMojo.java | 105 + .../ee10/maven/plugin/JettyStartWarMojo.java | 144 + .../ee10/maven/plugin/JettyStopMojo.java | 231 + .../plugin/MavenMetaInfConfiguration.java | 119 + .../plugin/MavenQuickStartConfiguration.java | 62 + .../maven/plugin/MavenServerConnector.java | 221 + .../ee10/maven/plugin/MavenWebAppContext.java | 507 + .../plugin/MavenWebInfConfiguration.java | 68 + .../jetty/ee10/maven/plugin/Overlay.java | 89 + .../ee10/maven/plugin/OverlayConfig.java | 337 + .../ee10/maven/plugin/OverlayManager.java | 156 + .../jetty/ee10/maven/plugin/PluginLog.java | 37 + .../maven/plugin/QuickStartGenerator.java | 187 + .../jetty/ee10/maven/plugin/ScanPattern.java | 48 + .../ee10/maven/plugin/ScanTargetPattern.java | 106 + .../maven/plugin/SelectiveJarResource.java | 214 + .../maven/plugin/ServerConnectorListener.java | 107 + .../ee10/maven/plugin/ServerListener.java | 56 + .../ee10/maven/plugin/ServerSupport.java | 244 + .../ee10/maven/plugin/WarPluginInfo.java | 238 + .../maven/plugin/WebAppPropertyConverter.java | 341 + .../jetty/ee10/maven/plugin/package-info.java | 18 + .../plugin/utils/MavenProjectHelper.java | 192 + ...rg.eclipse.jetty.ee10.webapp.Configuration | 4 + .../src/main/resources/jetty-maven.xml | 13 + .../src/main/resources/maven.mod | 15 + .../src/main/resources/maven.xml | 17 + .../src/site/markdown/index.md | 10 + .../jetty-ee10-maven-plugin/src/site/site.xml | 44 + .../maven/plugin/MockShutdownMonitor.java | 74 + .../plugin/MockShutdownMonitorRunnable.java | 111 + .../ee10/maven/plugin/TestForkedChild.java | 178 + .../ee10/maven/plugin/TestJettyEmbedder.java | 126 + .../ee10/maven/plugin/TestJettyStopMojo.java | 306 + .../maven/plugin/TestQuickStartGenerator.java | 50 + .../plugin/TestSelectiveJarResource.java | 110 + .../plugin/TestWebAppPropertyConverter.java | 157 + .../plugin/it/IntegrationTestGetContent.java | 133 + .../src/test/resources/embedder-context.xml | 6 + .../src/test/resources/embedder-jetty.xml | 25 + .../test/resources/jetty-logging.properties | 5 + .../src/test/resources/root/index.html | 1 + .../src/test/resources/selective-jar-test.jar | Bin 0 -> 1577 bytes jetty-ee10/jetty-ee10-openid/pom.xml | 73 + .../src/main/config/etc/jetty-openid.xml | 51 + .../src/main/config/modules/openid.mod | 47 + .../openid/openid-baseloginservice.xml | 10 + .../src/main/java/module-info.java | 28 + .../ee10/security/openid/JwtDecoder.java | 101 + .../openid/OpenIdAuthConfiguration.java | 52 + .../security/openid/OpenIdAuthenticator.java | 671 ++ .../openid/OpenIdAuthenticatorFactory.java | 57 + .../security/openid/OpenIdConfiguration.java | 236 + .../security/openid/OpenIdCredentials.java | 213 + .../security/openid/OpenIdLoginService.java | 167 + .../security/openid/OpenIdUserIdentity.java | 51 + .../security/openid/OpenIdUserPrincipal.java | 45 + ...e.jetty.ee9.security.Authenticator$Factory | 1 + ...lipse.jetty.security.Authenticator$Factory | 1 + .../ee10/security/openid/JwtDecoderTest.java | 117 + .../ee10/security/openid/JwtEncoder.java | 53 + .../openid/OpenIdAuthenticationTest.java | 224 + .../openid/OpenIdCredentialsTest.java | 40 + .../ee10/security/openid/OpenIdProvider.java | 332 + .../security/openid/OpenIdReamNameTest.java | 184 + .../test/resources/jetty-logging.properties | 3 + .../jetty-ee10-osgi-alpn/pom.xml | 33 + .../jetty-ee10-osgi-boot-jsp/pom.xml | 152 + .../jasper/ContainerTldBundleDiscoverer.java | 249 + .../boot/jasper/JSTLBundleDiscoverer.java | 178 + .../ee10/osgi/boot/jsp/FragmentActivator.java | 59 + .../jetty-ee10-osgi-boot-warurl/pom.xml | 42 + .../osgi/boot/warurl/WarUrlActivator.java | 69 + .../osgi/boot/warurl/WarUrlStreamHandler.java | 98 + .../internal/WarBundleManifestGenerator.java | 276 + .../warurl/internal/WarURLConnection.java | 360 + .../jettyhome/contexts/README | 2 + .../jetty-ee10-osgi-boot/jettyhome/etc/README | 2 + .../jettyhome/etc/jetty-deploy.xml | 25 + .../jettyhome/etc/jetty-http.xml | 41 + .../jettyhome/etc/jetty.xml | 114 + .../jettyhome/lib/ext/README | 2 + .../jettyhome/logs/README | 3 + .../jettyhome/resources/README | 2 + .../jettyhome/webapps/README | 2 + .../jetty-ee10-osgi-boot/pom.xml | 115 + .../annotations/AnnotationConfiguration.java | 244 + .../osgi/annotations/AnnotationParser.java | 225 + .../osgi/boot/AbstractContextProvider.java | 235 + .../jetty/ee10/osgi/boot/AbstractOSGiApp.java | 195 + .../osgi/boot/AbstractWebAppProvider.java | 553 + .../ee10/osgi/boot/BundleContextProvider.java | 199 + .../jetty/ee10/osgi/boot/BundleProvider.java | 28 + .../ee10/osgi/boot/BundleWebAppProvider.java | 257 + .../osgi/boot/JettyBootstrapActivator.java | 126 + .../jetty/ee10/osgi/boot/OSGiDeployer.java | 77 + .../osgi/boot/OSGiMetaInfConfiguration.java | 338 + .../ee10/osgi/boot/OSGiServerConstants.java | 83 + .../jetty/ee10/osgi/boot/OSGiUndeployer.java | 54 + .../osgi/boot/OSGiWebInfConfiguration.java | 31 + .../ee10/osgi/boot/OSGiWebappConstants.java | 138 + .../osgi/boot/ServiceContextProvider.java | 224 + .../jetty/ee10/osgi/boot/ServiceProvider.java | 29 + .../ee10/osgi/boot/ServiceWebAppProvider.java | 256 + .../DefaultJettyAtJettyHomeHelper.java | 334 + .../JettyServerServiceTracker.java | 92 + .../serverfactory/ServerInstanceWrapper.java | 441 + .../webapp/LibExtClassLoaderHelper.java | 196 + .../webapp/OSGiWebappClassLoader.java | 261 + .../boot/utils/BundleClassLoaderHelper.java | 53 + .../utils/BundleClassLoaderHelperFactory.java | 56 + .../boot/utils/BundleFileLocatorHelper.java | 118 + .../utils/BundleFileLocatorHelperFactory.java | 54 + .../ee10/osgi/boot/utils/EventSender.java | 84 + .../osgi/boot/utils/FakeURLClassLoader.java | 66 + .../ee10/osgi/boot/utils/OSGiClassLoader.java | 160 + .../boot/utils/ServerConnectorListener.java | 91 + .../osgi/boot/utils/TldBundleDiscoverer.java | 36 + .../jetty/ee10/osgi/boot/utils/Util.java | 166 + .../DefaultBundleClassLoaderHelper.java | 428 + .../internal/DefaultFileLocatorHelper.java | 385 + .../internal/PackageAdminServiceTracker.java | 391 + ...rg.eclipse.jetty.ee10.webapp.Configuration | 2 + jetty-ee10/jetty-ee10-osgi/pom.xml | 134 + .../test-jetty-ee10-osgi-context/pom.xml | 97 + .../src/main/context/acme.xml | 36 + .../main/java/com/acme/osgi/Activator.java | 72 + .../src/main/resources/static/index.html | 6 + .../test-jetty-ee10-osgi-fragment/pom.xml | 59 + .../src/main/resources/frag.html | 5 + .../test-jetty-ee10-osgi-server/pom.xml | 85 + .../main/java/com/acme/osgi/Activator.java | 83 + .../src/main/resources/index.html | 6 + .../pom.xml | 90 + .../src/main/java/com/acme/HelloWorld.java | 62 + .../src/main/resources/fake.properties | 0 .../src/main/webapp/WEB-INF/web.xml | 24 + .../test-jetty-ee10-osgi-webapp/pom.xml | 79 + .../main/java/com/acme/osgi/Activator.java | 91 + .../src/main/resources/index.html | 6 + .../src/main/resources/webappA/index.html | 6 + .../src/main/resources/webappB/index.html | 6 + .../test-jetty-ee10-osgi/README.txt | 220 + .../test-jetty-ee10-osgi/pom.xml | 616 + .../src/test/config/etc/jetty-alpn.xml | 23 + .../src/test/config/etc/jetty-deploy.xml | 23 + .../jetty-http-boot-context-as-service.xml | 48 + .../etc/jetty-http-boot-webapp-as-service.xml | 48 + .../etc/jetty-http-boot-with-annotations.xml | 48 + .../etc/jetty-http-boot-with-bundle.xml | 48 + ...jetty-http-boot-with-jakarta-websocket.xml | 47 + .../config/etc/jetty-http-boot-with-jsp.xml | 48 + .../etc/jetty-http-boot-with-resources.xml | 47 + .../etc/jetty-http-boot-with-websocket.xml | 47 + .../src/test/config/etc/jetty-http.xml | 48 + .../src/test/config/etc/jetty-http2-jdk9.xml | 26 + .../src/test/config/etc/jetty-http2.xml | 33 + .../src/test/config/etc/jetty-https.xml | 34 + .../src/test/config/etc/jetty-ssl.xml | 55 + .../src/test/config/etc/jetty-testrealm.xml | 19 + .../config/etc/jetty-with-custom-class.xml | 99 + .../src/test/config/etc/jetty.xml | 102 + .../src/test/config/etc/keystore.p12 | Bin 0 -> 2565 bytes .../src/test/config/etc/realm.properties | 21 + .../src/test/config/etc/webdefault.xml | 534 + .../ee10/osgi/test/SimpleEchoSocket.java | 72 + .../osgi/test/SimpleJakartaWebSocket.java | 66 + .../jetty/ee10/osgi/test/SomeCustomBean.java | 24 + .../test/TestJettyOSGiAnnotationParser.java | 97 + .../TestJettyOSGiBootContextAsService.java | 116 + .../test/TestJettyOSGiBootHTTP2Conscrypt.java | 164 + .../osgi/test/TestJettyOSGiBootHTTP2JDK9.java | 149 + .../TestJettyOSGiBootWebAppAsService.java | 142 + .../TestJettyOSGiBootWithAnnotations.java | 128 + .../test/TestJettyOSGiBootWithBundle.java | 119 + ...TestJettyOSGiBootWithJakartaWebSocket.java | 137 + .../osgi/test/TestJettyOSGiBootWithJsp.java | 95 + .../test/TestJettyOSGiBootWithWebSocket.java | 108 + .../test/TestJettyOSGiClasspathResources.java | 146 + .../jetty/ee10/osgi/test/TestOSGiUtil.java | 330 + .../test/resources/jetty-logging.properties | 1 + .../src/test/resources/module-info.clazz | Bin 0 -> 158 bytes .../src/test/resources/module-info.java | 16 + .../test/resources/simplelogger.properties | 1 + jetty-ee10/jetty-ee10-plus/pom.xml | 68 + .../src/main/config/etc/jetty-plus.xml | 29 + .../src/main/config/modules/plus.mod | 12 + .../src/main/java/module-info.java | 39 + .../plus/annotation/ContainerInitializer.java | 212 + .../jetty/ee10/plus/annotation/Injection.java | 229 + .../plus/annotation/InjectionCollection.java | 139 + .../plus/annotation/LifeCycleCallback.java | 169 + .../LifeCycleCallbackCollection.java | 197 + .../annotation/PostConstructCallback.java | 80 + .../plus/annotation/PreDestroyCallback.java | 90 + .../jetty/ee10/plus/annotation/RunAs.java | 61 + .../ee10/plus/annotation/RunAsCollection.java | 69 + .../ee10/plus/annotation/package-info.java | 17 + .../jetty/ee10/plus/jndi/EnvEntry.java | 57 + .../eclipse/jetty/ee10/plus/jndi/Link.java | 54 + .../jetty/ee10/plus/jndi/NamingDump.java | 66 + .../jetty/ee10/plus/jndi/NamingEntry.java | 215 + .../jetty/ee10/plus/jndi/NamingEntryUtil.java | 229 + .../jetty/ee10/plus/jndi/Resource.java | 36 + .../jetty/ee10/plus/jndi/Transaction.java | 110 + .../jetty/ee10/plus/jndi/package-info.java | 17 + .../plus/security/DataSourceLoginService.java | 465 + .../ee10/plus/security/package-info.java | 17 + .../ee10/plus/webapp/EnvConfiguration.java | 286 + .../ee10/plus/webapp/PlusConfiguration.java | 138 + .../jetty/ee10/plus/webapp/PlusDecorator.java | 75 + .../plus/webapp/PlusDescriptorProcessor.java | 894 ++ .../jetty/ee10/plus/webapp/package-info.java | 17 + ...rg.eclipse.jetty.ee10.webapp.Configuration | 2 + .../LifeCycleCallbackCollectionTest.java | 319 + .../ee10/plus/jndi/NamingEntryUtilTest.java | 41 + .../ee10/plus/jndi/TestNamingEntries.java | 312 + .../ee10/plus/jndi/TestNamingEntryUtil.java | 134 + .../webapp/PlusDescriptorProcessorTest.java | 348 + .../src/test/resources/web-fragment-1.xml | 27 + .../src/test/resources/web-fragment-2.xml | 27 + .../src/test/resources/web-fragment-3.xml | 27 + .../src/test/resources/web-fragment-4.xml | 16 + .../src/test/resources/web.xml | 73 + jetty-ee10/jetty-ee10-proxy/pom.xml | 81 + .../src/main/config/etc/jetty-proxy.xml | 35 + .../src/main/config/modules/proxy.mod | 24 + .../src/main/java/module-info.java | 22 + .../ee10/proxy/AbstractProxyServlet.java | 859 ++ .../ee10/proxy/AfterContentTransformer.java | 488 + .../ee10/proxy/AsyncMiddleManServlet.java | 866 ++ .../jetty/ee10/proxy/AsyncProxyServlet.java | 291 + .../jetty/ee10/proxy/BalancerServlet.java | 310 + .../jetty/ee10/proxy/ConnectHandler.java | 678 ++ .../jetty/ee10/proxy/ProxyConnection.java | 161 + .../jetty/ee10/proxy/ProxyServlet.java | 328 + .../jetty/ee10/proxy/package-info.java | 18 + .../proxy/AbstractConnectHandlerTest.java | 64 + .../ee10/proxy/AsyncMiddleManServletTest.java | 1788 +++ .../jetty/ee10/proxy/BalancerServletTest.java | 238 + .../jetty/ee10/proxy/CachingProxyServlet.java | 79 + .../jetty/ee10/proxy/ClientAuthProxyTest.java | 566 + .../ee10/proxy/ConnectHandlerSSLTest.java | 185 + .../jetty/ee10/proxy/ConnectHandlerTest.java | 863 ++ .../jetty/ee10/proxy/EchoHttpServlet.java | 31 + .../jetty/ee10/proxy/EmptyHttpServlet.java | 29 + .../jetty/ee10/proxy/EmptyServerHandler.java | 27 + .../ee10/proxy/ForwardProxyServerTest.java | 307 + .../ee10/proxy/ForwardProxyTLSServerTest.java | 906 ++ .../eclipse/jetty/ee10/proxy/ProxyServer.java | 45 + .../ee10/proxy/ProxyServletFailureTest.java | 425 + .../ee10/proxy/ProxyServletLoadTest.java | 229 + .../jetty/ee10/proxy/ProxyServletTest.java | 1638 +++ .../jetty/ee10/proxy/ReverseProxyTest.java | 149 + .../resources/client_auth/client_keystore.p12 | Bin 0 -> 7953 bytes .../resources/client_auth/proxy_keystore.p12 | Bin 0 -> 7933 bytes .../resources/client_auth/server_keystore.p12 | Bin 0 -> 2565 bytes .../src/test/resources/client_keystore.p12 | Bin 0 -> 7031 bytes .../test/resources/client_proxy_keystore.p12 | Bin 0 -> 3625 bytes .../test/resources/client_server_keystore.p12 | Bin 0 -> 3627 bytes .../test/resources/jetty-logging.properties | 6 + .../src/test/resources/proxy_keystore.p12 | Bin 0 -> 3589 bytes .../src/test/resources/readme_keystores.txt | 22 + .../src/test/resources/server_keystore.p12 | Bin 0 -> 3589 bytes jetty-ee10/jetty-ee10-quickstart/pom.xml | 66 + .../src/main/config/etc/jetty-quickstart.xml | 31 + .../jetty-quickstart.d/quickstart-webapp.xml | 28 + .../src/main/config/modules/quickstart.mod | 25 + .../src/main/java/module-info.java | 22 + .../ee10/quickstart/AttributeNormalizer.java | 469 + .../ExtraXmlDescriptorProcessor.java | 87 + .../quickstart/PreconfigureQuickStartWar.java | 144 + .../quickstart/QuickStartConfiguration.java | 258 + .../QuickStartDescriptorProcessor.java | 281 + .../QuickStartGeneratorConfiguration.java | 830 ++ ...rg.eclipse.jetty.ee10.webapp.Configuration | 2 + .../ee10/quickstart/FooContextListener.java | 60 + .../jetty/ee10/quickstart/FooFilter.java | 43 + .../jetty/ee10/quickstart/FooServlet.java | 32 + .../jetty/ee10/quickstart/TestQuickStart.java | 288 + .../src/test/resources/context.xml | 57 + .../src/test/resources/web.xml | 18 + jetty-ee10/jetty-ee10-runner/pom.xml | 172 + .../invoker.properties | 1 + .../pom.xml | 139 + .../invoker.properties | 1 + .../src/it/demo-simple-webapp-runner/pom.xml | 139 + .../jetty-ee10-runner/src/it/settings.xml | 36 + .../it/test-jar-manifest/invoker.properties | 1 + .../src/it/test-jar-manifest/pom.xml | 59 + .../src/it/test-jar-manifest/postbuild.groovy | 11 + .../org/eclipse/jetty/ee10/runner/Runner.java | 608 + .../jetty/ee10/runner/package-info.java | 18 + .../src/main/resources/META-INF/MANIFEST.MF | 0 .../main/resources/jetty-logging.properties | 2 + .../it/IntegrationTestJettyRunner.java | 66 + jetty-ee10/jetty-ee10-servlet/pom.xml | 79 + .../src/main/config/modules/security.mod | 10 + .../src/main/config/modules/servlet.mod | 10 + .../src/main/java/module-info.java | 47 + .../ee10/servlet/AsyncContentProducer.java | 566 + .../jetty/ee10/servlet/AsyncContextEvent.java | 157 + .../jetty/ee10/servlet/AsyncContextState.java | 200 + .../jetty/ee10/servlet/BaseHolder.java | 300 + .../ee10/servlet/BlockingContentProducer.java | 181 + .../jetty/ee10/servlet/ContentProducer.java | 147 + .../jetty/ee10/servlet/DebugListener.java | 331 + .../ee10/servlet/DecoratingListener.java | 169 + .../jetty/ee10/servlet/DefaultServlet.java | 20 + .../jetty/ee10/servlet/Dispatcher.java | 657 + .../jetty/ee10/servlet/ErrorHandler.java | 611 + .../ee10/servlet/ErrorPageErrorHandler.java | 275 + .../jetty/ee10/servlet/FilterHolder.java | 398 + .../jetty/ee10/servlet/FilterMapping.java | 326 + .../eclipse/jetty/ee10/servlet/Holder.java | 295 + .../eclipse/jetty/ee10/servlet/HttpInput.java | 536 + .../jetty/ee10/servlet/HttpOutput.java | 1852 +++ .../eclipse/jetty/ee10/servlet/Invoker.java | 295 + .../ee10/servlet/JspPropertyGroupServlet.java | 135 + .../jetty/ee10/servlet/ListenerHolder.java | 178 + .../servlet/ManagedAttributeListener.java | 98 + .../servlet/MultiPartFormInputStream.java | 32 + .../jetty/ee10/servlet/NoJspServlet.java | 36 + .../ee10/servlet/QuietServletException.java | 48 + .../jetty/ee10/servlet/ServletChannel.java | 1029 ++ .../ServletContainerInitializerHolder.java | 262 + .../ee10/servlet/ServletContextHandler.java | 3389 ++++++ .../ee10/servlet/ServletContextRequest.java | 1288 ++ .../ee10/servlet/ServletContextResponse.java | 1020 ++ .../jetty/ee10/servlet/ServletHandler.java | 1561 +++ .../jetty/ee10/servlet/ServletHolder.java | 1371 +++ .../jetty/ee10/servlet/ServletMapping.java | 127 + .../ee10/servlet/ServletPathMapping.java | 160 + .../servlet/ServletRequestHttpWrapper.java | 230 + .../ee10/servlet/ServletRequestState.java | 1397 +++ .../servlet/ServletResponseHttpWrapper.java | 132 + .../jetty/ee10/servlet/ServletTester.java | 296 + .../jetty/ee10/servlet/SessionHandler.java | 644 + .../eclipse/jetty/ee10/servlet/Source.java | 69 + .../jetty/ee10/servlet/StatisticsServlet.java | 590 + .../ee10/servlet/jmx/FilterMappingMBean.java | 40 + .../jetty/ee10/servlet/jmx/HolderMBean.java | 38 + .../ee10/servlet/jmx/ServletMappingMBean.java | 40 + .../jetty/ee10/servlet/jmx/package-info.java | 18 + .../listener/ContainerInitializer.java | 180 + .../servlet/listener/ELContextCleaner.java | 116 + .../servlet/listener/IntrospectorCleaner.java | 41 + .../ee10/servlet/listener/package-info.java | 18 + .../jetty/ee10/servlet/package-info.java | 18 + .../security/AbstractLoginService.java | 157 + .../security/AbstractUserAuthentication.java | 104 + .../ee10/servlet/security/Authentication.java | 217 + .../ee10/servlet/security/Authenticator.java | 131 + .../ConfigurableSpnegoLoginService.java | 324 + .../servlet/security/ConstraintAware.java | 68 + .../servlet/security/ConstraintMapping.java | 87 + .../security/ConstraintSecurityHandler.java | 874 ++ .../security/DefaultAuthenticatorFactory.java | 117 + .../security/DefaultIdentityService.java | 78 + .../servlet/security/DefaultUserIdentity.java | 67 + .../servlet/security/EmptyLoginService.java | 56 + .../servlet/security/HashLoginService.java | 168 + .../servlet/security/IdentityService.java | 84 + .../servlet/security/JDBCLoginService.java | 260 + .../security/LoggedOutAuthentication.java | 51 + .../ee10/servlet/security/LoginService.java | 68 + .../servlet/security/PropertyUserStore.java | 372 + .../jetty/ee10/servlet/security/RoleInfo.java | 162 + .../ee10/servlet/security/RolePrincipal.java | 47 + .../ee10/servlet/security/RoleRunAsToken.java | 38 + .../ee10/servlet/security/RunAsToken.java | 23 + .../servlet/security/SecurityHandler.java | 687 ++ .../servlet/security/ServerAuthException.java | 42 + .../servlet/security/SpnegoUserIdentity.java | 54 + .../servlet/security/SpnegoUserPrincipal.java | 56 + .../servlet/security/UserAuthentication.java | 32 + .../servlet/security/UserDataConstraint.java | 38 + .../ee10/servlet/security/UserIdentity.java | 79 + .../ee10/servlet/security/UserPrincipal.java | 87 + .../ee10/servlet/security/UserStore.java | 87 + .../security/WrappedAuthConfiguration.java | 74 + .../authentication/AuthorizationService.java | 43 + .../authentication/BasicAuthenticator.java | 104 + .../ClientCertAuthenticator.java | 367 + .../ConfigurableSpnegoAuthenticator.java | 237 + .../DeferredAuthentication.java | 194 + .../authentication/DigestAuthenticator.java | 383 + .../authentication/FormAuthenticator.java | 509 + .../authentication/LoginAuthenticator.java | 143 + .../authentication/LoginCallback.java | 46 + .../authentication/LoginCallbackImpl.java | 112 + .../authentication/SessionAuthentication.java | 112 + .../SslClientCertAuthenticator.java | 152 + .../security/authentication/package-info.java | 18 + .../ee10/servlet/security/package-info.java | 18 + .../util/ServletOutputStreamWrapper.java | 167 + .../servlet/writer/EncodingHttpWriter.java | 60 + .../jetty/ee10/servlet/writer/HttpWriter.java | 77 + .../servlet/writer/Iso88591HttpWriter.java | 67 + .../ee10/servlet/writer/ResponseWriter.java | 505 + .../ee10/servlet/writer/Utf8HttpWriter.java | 179 + .../AsyncContextDispatchWithQueryStrings.java | 110 + .../servlet/AsyncContextListenersTest.java | 278 + .../jetty/ee10/servlet/AsyncContextTest.java | 720 ++ .../jetty/ee10/servlet/AsyncListenerTest.java | 479 + .../ee10/servlet/AsyncServletIOTest.java | 972 ++ .../servlet/AsyncServletLongPollTest.java | 154 + .../jetty/ee10/servlet/AsyncServletTest.java | 1046 ++ .../ee10/servlet/CacheControlHeaderTest.java | 201 + .../servlet/ComplianceViolations2616Test.java | 183 + .../jetty/ee10/servlet/ComponentWrapTest.java | 287 + .../servlet/CustomRequestLogServletTest.java | 141 + .../servlet/DefaultServletRangesTest.java | 289 + .../ee10/servlet/DefaultServletTest.java | 2326 ++++ .../ee10/servlet/DispatcherForwardTest.java | 553 + .../jetty/ee10/servlet/DispatcherTest.java | 1126 ++ .../jetty/ee10/servlet/EncodedURITest.java | 198 + .../jetty/ee10/servlet/ErrorPageTest.java | 801 ++ .../jetty/ee10/servlet/FilterHolderTest.java | 130 + .../eclipse/jetty/ee10/servlet/FormTest.java | 199 + .../servlet/GzipHandlerBreakEvenSizeTest.java | 116 + .../ee10/servlet/GzipHandlerCommitTest.java | 125 + .../jetty/ee10/servlet/GzipHandlerTest.java | 893 ++ .../ee10/servlet/IncludedServletTest.java | 268 + .../jetty/ee10/servlet/InitServletTest.java | 127 + .../jetty/ee10/servlet/InvokerTest.java | 92 + .../ee10/servlet/ListenerHolderTest.java | 52 + .../ee10/servlet/MultiPartServletTest.java | 217 + .../jetty/ee10/servlet/PostServletTest.java | 243 + .../jetty/ee10/servlet/RegexServletTest.java | 153 + .../ee10/servlet/RequestHeadersTest.java | 119 + .../jetty/ee10/servlet/RequestURITest.java | 269 + .../ee10/servlet/ResponseHeadersTest.java | 272 + .../ee10/servlet/SSLAsyncIOServletTest.java | 247 + ...ServletContainerInitializerHolderTest.java | 121 + .../servlet/ServletContextHandlerTest.java | 2325 ++++ .../servlet/ServletContextResourcesTest.java | 128 + .../ee10/servlet/ServletHandlerTest.java | 927 ++ .../jetty/ee10/servlet/ServletHolderTest.java | 219 + .../ee10/servlet/ServletLifeCycleTest.java | 242 + .../ee10/servlet/ServletRequestLogTest.java | 604 + .../ee10/servlet/ServletUpgradeTest.java | 356 + .../ee10/servlet/ServletWrapperTest.java | 142 + .../ee10/servlet/SessionHandlerTest.java | 490 + .../ee10/servlet/StatisticsServletTest.java | 390 + .../security/AliasedConstraintTest.java | 167 + .../security/ClientCertAuthenticatorTest.java | 191 + .../ee10/servlet/security/ConstraintTest.java | 2079 ++++ .../servlet/security/DataConstraintsTest.java | 470 + .../security/DefaultIdentityServiceTest.java | 88 + .../security/HashLoginServiceTest.java | 63 + .../security/PropertyUserStoreTest.java | 305 + .../security/SessionAuthenticationTest.java | 85 + .../security/SpecExampleConstraintTest.java | 344 + .../servlet/security/TestLoginService.java | 49 + .../servlet/security/UnauthenticatedTest.java | 154 + .../ee10/servlet/security/UserStoreTest.java | 56 + .../SpnegoAuthenticatorTest.java | 202 + .../src/test/resources/Foo.clazz | Bin 0 -> 495 bytes .../src/test/resources/Foo.java | 36 + .../src/test/resources/cacerts.jks | Bin 0 -> 668 bytes .../src/test/resources/clientcert.jks | Bin 0 -> 1375 bytes .../resources/contextResources/content.txt | 1 + .../dispatchResourceTest/content.txt | 1 + .../test/resources/dispatchTest/dispatch.txt | 1 + .../src/test/resources/docroot/all/index.txt | 1 + .../test/resources/docroot/forbid/index.txt | 1 + .../src/test/resources/foo.properties | 2 + .../src/test/resources/jar-resource-odd.jar | Bin 0 -> 2863 bytes .../test/resources/jetty-logging.properties | 7 + .../src/test/resources/keystore.p12 | Bin 0 -> 2565 bytes .../src/test/resources/realm.properties | 27 + jetty-ee10/jetty-ee10-servlets/pom.xml | 82 + .../src/main/config/modules/servlets.mod | 13 + .../src/main/java/module-info.java | 32 + .../org/eclipse/jetty/ee10/servlets/CGI.java | 567 + .../ee10/servlets/CloseableDoSFilter.java | 35 + .../ee10/servlets/CrossOriginFilter.java | 511 + .../jetty/ee10/servlets/DoSFilter.java | 1520 +++ .../jetty/ee10/servlets/EventSource.java | 103 + .../ee10/servlets/EventSourceServlet.java | 235 + .../jetty/ee10/servlets/HeaderFilter.java | 193 + .../servlets/IncludeExcludeBasedFilter.java | 179 + .../jetty/ee10/servlets/PushCacheFilter.java | 318 + .../ee10/servlets/PushSessionCacheFilter.java | 198 + .../jetty/ee10/servlets/QoSFilter.java | 389 + .../jetty/ee10/servlets/package-info.java | 18 + .../ee10/servlets/AbstractDoSFilterTest.java | 363 + .../servlets/AbstractFileContentServlet.java | 42 + .../jetty/ee10/servlets/AbstractGzipTest.java | 154 + .../jetty/ee10/servlets/AsyncManipFilter.java | 97 + .../servlets/AsyncScheduledDispatchWrite.java | 117 + .../servlets/AsyncTimeoutCompleteWrite.java | 132 + .../servlets/AsyncTimeoutDispatchWrite.java | 116 + .../BlockingServletLengthStreamTypeWrite.java | 60 + .../BlockingServletLengthTypeStreamWrite.java | 59 + .../BlockingServletStreamLengthTypeWrite.java | 60 + ...ServletStreamLengthTypeWriteWithFlush.java | 66 + .../BlockingServletStreamTypeLengthWrite.java | 60 + .../BlockingServletTypeLengthStreamWrite.java | 59 + .../BlockingServletTypeStreamLengthWrite.java | 60 + .../ee10/servlets/CloseableDoSFilterTest.java | 31 + .../ee10/servlets/CrossOriginFilterTest.java | 650 + .../jetty/ee10/servlets/DoSFilterJMXTest.java | 93 + .../jetty/ee10/servlets/DoSFilterTest.java | 165 + .../ee10/servlets/EventSourceServletTest.java | 358 + .../ee10/servlets/GzipContentLengthTest.java | 222 + ...DefaultServletDeferredContentTypeTest.java | 134 + .../ee10/servlets/GzipDefaultServletTest.java | 1000 ++ .../servlets/GzipHandlerNoReCompressTest.java | 143 + .../jetty/ee10/servlets/GzipHandlerTest.java | 222 + .../jetty/ee10/servlets/HeaderFilterTest.java | 140 + .../HttpOutputWriteFileContentServlet.java | 62 + .../IncludeExcludeBasedFilterTest.java | 347 + .../jetty/ee10/servlets/NoOpOutputStream.java | 53 + .../ee10/servlets/PassThruInputStream.java | 31 + .../jetty/ee10/servlets/QoSFilterTest.java | 275 + .../ee10/servlets/TestMinGzipSizeServlet.java | 62 + .../servlets/TestStaticMimeTypeServlet.java | 76 + .../ee10/servlets/ThreadStarvationTest.java | 405 + .../src/test/resources/big_script.js | 792 ++ .../src/test/resources/big_script.js.sha1 | 1 + .../test/resources/jetty-logging.properties | 5 + .../src/test/resources/jetty_logo.bmp | Bin 0 -> 90682 bytes .../src/test/resources/jetty_logo.bmp.sha1 | 1 + .../src/test/resources/jetty_logo.gif | Bin 0 -> 3443 bytes .../src/test/resources/jetty_logo.gif.sha1 | 1 + .../src/test/resources/jetty_logo.jp2 | Bin 0 -> 19897 bytes .../src/test/resources/jetty_logo.jp2.sha1 | 1 + .../src/test/resources/jetty_logo.jpeg | Bin 0 -> 6722 bytes .../src/test/resources/jetty_logo.jpeg.sha1 | 1 + .../src/test/resources/jetty_logo.jpg | Bin 0 -> 6722 bytes .../src/test/resources/jetty_logo.jpg.sha1 | 1 + .../src/test/resources/jetty_logo.png | Bin 0 -> 13002 bytes .../src/test/resources/jetty_logo.png.sha1 | 1 + .../src/test/resources/jetty_logo.tga | Bin 0 -> 90578 bytes .../src/test/resources/jetty_logo.tga.sha1 | 1 + .../src/test/resources/jetty_logo.tif | Bin 0 -> 13284 bytes .../src/test/resources/jetty_logo.tif.sha1 | 1 + .../src/test/resources/jetty_logo.tiff | Bin 0 -> 13284 bytes .../src/test/resources/jetty_logo.tiff.sha1 | 1 + .../src/test/resources/jetty_logo.xcf | Bin 0 -> 11447 bytes .../src/test/resources/jetty_logo.xcf.sha1 | 1 + .../src/test/resources/small_script.js | 29 + .../src/test/resources/small_script.js.sha1 | 1 + .../src/test/resources/test.svg | 2069 ++++ .../src/test/resources/test.svgz | Bin 0 -> 6916 bytes .../src/test/resources/test.svgz.sha1 | 1 + .../src/test/resources/test_quotes.br | Bin 0 -> 445 bytes .../src/test/resources/test_quotes.br.sha1 | 1 + .../src/test/resources/test_quotes.bz2 | Bin 0 -> 594 bytes .../src/test/resources/test_quotes.bz2.sha1 | 1 + .../src/test/resources/test_quotes.gz | Bin 0 -> 577 bytes .../src/test/resources/test_quotes.gz.sha1 | 1 + .../src/test/resources/test_quotes.rar | Bin 0 -> 679 bytes .../src/test/resources/test_quotes.rar.sha1 | 1 + .../src/test/resources/test_quotes.txt | 19 + .../src/test/resources/test_quotes.txt.sha1 | 1 + .../src/test/resources/test_quotes.zip | Bin 0 -> 739 bytes .../src/test/resources/test_quotes.zip.sha1 | 1 + jetty-ee10/jetty-ee10-tests/pom.xml | 52 + .../test-bad-websocket-webapp/pom.xml | 34 + .../bad/BadOnCloseServerEndpoint.java | 49 + .../bad/BadOnOpenServerEndpoint.java | 49 + .../webapp/websocket/bad/StringSequence.java | 53 + .../src/main/webapp/WEB-INF/web.xml | 10 + .../src/main/webapp/index.jsp | 5 + jetty-ee10/jetty-ee10-tests/test-cdi/pom.xml | 91 + .../ee10/cdi/tests/EmbeddedWeldTest.java | 252 + .../ee10/cdi/tests/FriendlyGreetings.java | 35 + .../jetty/ee10/cdi/tests/Greetings.java | 19 + .../ee10/cdi/tests/GreetingsServlet.java | 51 + .../ee10/cdi/tests/MyContextListener.java | 37 + .../jetty/ee10/cdi/tests/MyFilter.java | 52 + .../websocket/JavaxWebSocketCdiTest.java | 404 + .../websocket/JettyWebSocketCdiTest.java | 153 + .../ee10/cdi/tests/websocket/LogFactory.java | 30 + .../src/test/resources/META-INF/beans.xml | 4 + .../test/resources/jetty-logging.properties | 4 + .../test-cdi/src/test/weldtest/.donotdelete | 0 .../test-http-client-transport/pom.xml | 132 + .../jetty/ee10/http/client/AbstractTest.java | 38 + .../ee10/http/client/AsyncIOServletTest.java | 1875 +++ .../http/client/AsyncRequestContentTest.java | 240 + .../jetty/ee10/http/client/BlockedIOTest.java | 140 + .../http/client/ConnectionStatisticsTest.java | 118 + .../ee10/http/client/EmptyServerHandler.java | 36 + .../client/HttpChannelAssociationTest.java | 284 + .../client/HttpClientConnectTimeoutTest.java | 162 + .../http/client/HttpClientContinueTest.java | 871 ++ .../http/client/HttpClientDemandTest.java | 528 + .../client/HttpClientIdleTimeoutTest.java | 153 + .../ee10/http/client/HttpClientLoadTest.java | 440 + .../http/client/HttpClientStreamTest.java | 1250 ++ .../ee10/http/client/HttpClientTest.java | 855 ++ .../http/client/HttpClientTimeoutTest.java | 620 + .../HttpClientTransportDynamicTest.java | 758 ++ .../ee10/http/client/HttpTrailersTest.java | 356 + .../client/ProxyWithDynamicTransportTest.java | 575 + .../ee10/http/client/RequestReaderTest.java | 80 + .../client/RoundRobinConnectionPoolTest.java | 270 + .../ee10/http/client/ServerTimeoutsTest.java | 689 ++ .../jetty/ee10/http/client/Transport.java | 34 + .../ee10/http/client/TransportProvider.java | 41 + .../ee10/http/client/TransportScenario.java | 443 + .../test/resources/jetty-logging.properties | 12 + .../src/test/resources/keystore.p12 | Bin 0 -> 2597 bytes .../test-http2-webapp/pom.xml | 111 + .../jetty/test/webapp/HTTP1Servlet.java | 132 + .../jetty/test/webapp/HTTP2Servlet.java | 30 + .../src/main/webapp/WEB-INF/web.xml | 28 + .../jetty/test/webapp/HTTP2FromWebAppIT.java | 95 + .../test/resources/jetty-logging.properties | 3 + .../src/test/resources/keystore.p12 | Bin 0 -> 2565 bytes .../jetty-ee10-tests/test-integration/pom.xml | 167 + .../ee10/test/AliasCheckerSymlinkTest.java | 224 + .../test/AllowedResourceAliasCheckerTest.java | 150 + .../ee10/test/AnnotatedAsyncListenerTest.java | 156 + .../jetty/ee10/test/CustomRequestLogTest.java | 685 ++ .../jetty/ee10/test/DefaultHandlerTest.java | 132 + .../ee10/test/DeploymentErrorInitializer.java | 34 + .../jetty/ee10/test/DeploymentErrorTest.java | 398 + .../jetty/ee10/test/DigestPostTest.java | 374 + .../jetty/ee10/test/FailedSelectorTest.java | 386 + .../ee10/test/GzipWithSendErrorTest.java | 393 + .../ee10/test/HttpInputIntegrationTest.java | 679 ++ .../ee10/test/HttpInputInterceptorTest.java | 340 + .../jetty/ee10/test/KeyStoreScannerTest.java | 319 + .../ee10/test/RecoverFailedSelectorTest.java | 374 + .../jetty/ee10/test/jsp/FakeJspServlet.java | 55 + .../jsp/JspAndDefaultWithAliasesTest.java | 181 + .../jsp/JspAndDefaultWithoutAliasesTest.java | 211 + .../jetty/ee10/test/rfcs/RFC2616BaseTest.java | 1765 +++ .../ee10/test/rfcs/RFC2616NIOHttpTest.java | 44 + .../ee10/test/rfcs/RFC2616NIOHttpsTest.java | 45 + .../jetty/ee10/test/support/StringUtil.java | 181 + .../test/support/XmlBasedJettyServer.java | 209 + .../rawhttp/HttpRequestTesterTest.java | 70 + .../rawhttp/HttpResponseTesterTest.java | 124 + .../ee10/test/support/rawhttp/HttpSocket.java | 26 + .../test/support/rawhttp/HttpSocketImpl.java | 30 + .../test/support/rawhttp/HttpTesting.java | 360 + .../test/support/rawhttp/HttpsSocketImpl.java | 62 + .../websocket/JakartaSimpleEchoSocket.java | 66 + .../test/websocket/JakartaWebSocketTest.java | 83 + .../test/websocket/JettySimpleEchoSocket.java | 78 + .../test/websocket/JettyWebSocketTest.java | 79 + .../src/test/resources/DefaultHandler.xml | 51 + .../src/test/resources/NIOHttp.xml | 29 + .../src/test/resources/NIOHttps.xml | 31 + .../src/test/resources/RFC2616Base.xml | 111 + .../src/test/resources/RFC2616_Filters.xml | 6 + .../src/test/resources/RFC2616_Redirects.xml | 52 + .../test/resources/add-jetty-test-webapp.xml | 18 + .../src/test/resources/badKeystore | Bin 0 -> 2457 bytes .../src/test/resources/basic-server.xml | 42 + .../src/test/resources/deploy.xml | 32 + .../test/resources/docroots/default/R1.txt | 2 + .../test/resources/docroots/default/R2.txt | 2 + .../test/resources/docroots/default/R3.txt | 2 + .../test/resources/docroots/default/alpha.txt | 1 + .../resources/docroots/default/index.html | 8 + .../resources/docroots/default/quotes.txt | 14 + .../deployerror/badapp-unavailable-false.xml | 13 + .../resources/docroots/deployerror/badapp.xml | 13 + ...akarta.servlet.ServletContainerInitializer | 1 + .../deployerror/badapp/WEB-INF/web.xml | 7 + .../src/test/resources/docroots/jsp/dump.jsp | 23 + .../resources/docroots/virtualhost/R1.txt | 2 + .../resources/docroots/virtualhost/R2.txt | 2 + .../resources/docroots/virtualhost/R3.txt | 2 + .../resources/docroots/virtualhost/index.html | 8 + .../test-integration/src/test/resources/file | 1 + .../test/resources/jetty-logging.properties | 4 + .../src/test/resources/keystore.p12 | Bin 0 -> 2565 bytes .../src/test/resources/logback.xml | 19 + .../src/test/resources/login-service.xml | 21 + .../src/test/resources/message.txt | 12 + .../src/test/resources/newKeystore | Bin 0 -> 2071 bytes .../src/test/resources/oldKeystore | Bin 0 -> 2303 bytes .../src/test/resources/realm.properties | 21 + .../src/test/resources/sibling/file | 1 + .../src/test/resources/ssl.xml | 26 + .../RFC2616/rfc2616-webapp.xml | 21 + .../src/test/resources/webdefault.xml | 534 + .../test/resources/webroot/WEB-INF/web.xml | 1 + .../src/test/resources/webroot/documents/file | 1 + .../src/test/resources/webroot/file | 1 + .../src/test/resources/webroot/index.html | 4 + .../src/test/resources/zeros.gz.gz | Bin 0 -> 2773 bytes .../test-jmx/jmx-webapp-it/pom.xml | 66 + .../eclipse/jetty/ee10/test/jmx/JmxIT.java | 209 + .../test-jmx/jmx-webapp/pom.xml | 49 + .../jetty/ee10/test/jmx/CommonComponent.java | 44 + .../eclipse/jetty/ee10/test/jmx/Echoer.java | 38 + .../ee10/test/jmx/MyContainerInitializer.java | 40 + .../jetty/ee10/test/jmx/PingServlet.java | 58 + .../eclipse/jetty/ee10/test/jmx/Pinger.java | 35 + .../jetty/ee10/test/jmx/jmx/EchoerMBean.java | 33 + .../jetty/ee10/test/jmx/jmx/PingerMBean.java | 46 + ...akarta.servlet.ServletContainerInitializer | 1 + .../src/main/webapp/WEB-INF/web.xml | 34 + .../jmx-webapp/src/main/webapp/index.html | 2 + jetty-ee10/jetty-ee10-tests/test-jmx/pom.xml | 16 + jetty-ee10/jetty-ee10-tests/test-jndi/pom.xml | 36 + .../factories/TestMailSessionReference.java | 72 + .../test-loginservice/pom.xml | 67 + .../DataSourceLoginServiceTest.java | 169 + .../DatabaseLoginServiceTestServer.java | 236 + .../loginservice/JdbcLoginServiceTest.java | 190 + .../src/test/resources/createdb.sql | 42 + .../src/test/resources/droptables.sql | 6 + .../src/test/resources/jdbcrealm.properties | 11 + .../test/resources/jetty-logging.properties | 3 + .../jetty-ee10-tests/test-quickstart/pom.xml | 187 + .../quickstart/AttributeNormalizerTest.java | 338 + ...AttributeNormalizerToCanonicalUriTest.java | 64 + .../jetty/ee10/quickstart/EnvUtils.java | 71 + .../ee10/quickstart/PreconfigureJNDIWar.java | 42 + .../ee10/quickstart/PreconfigureSpecWar.java | 61 + .../PreconfigureStandardTestWar.java | 54 + .../ee10/quickstart/QuickStartJNDIWar.java | 24 + .../ee10/quickstart/QuickStartSpecWar.java | 24 + .../quickstart/QuickStartStandardTestWar.java | 25 + .../jetty/ee10/quickstart/QuickStartTest.java | 200 + .../jetty/ee10/quickstart/Quickstart.java | 72 + .../test/resources/jetty-logging.properties | 3 + .../src/test/resources/realm.properties | 21 + .../src/test/resources/test-jndi.xml | 40 + .../src/test/resources/test-spec.xml | 39 + .../src/test/resources/test.xml | 47 + .../pom.xml | 41 + .../tests/webapp/websocket/EchoEndpoint.java | 27 + .../websocket/WebSocketClientServlet.java | 128 + .../resources/jetty-websocket-httpclient.xml | 20 + .../src/main/webapp/WEB-INF/web.xml | 8 + .../test-websocket-client-webapp/pom.xml | 40 + .../tests/webapp/websocket/EchoEndpoint.java | 27 + .../websocket/WebSocketClientServlet.java | 132 + .../src/main/webapp/WEB-INF/web.xml | 8 + .../test-websocket-webapp/pom.xml | 42 + .../tests/webapp/websocket/EchoEndpoint.java | 39 + .../webapp/websocket/StringSequence.java | 49 + .../websocket/StringSequenceDecoder.java | 45 + .../src/main/webapp/WEB-INF/web.xml | 10 + .../src/main/webapp/index.jsp | 5 + jetty-ee10/jetty-ee10-webapp/pom.xml | 106 + .../src/main/config/etc/jetty-webapp.xml | 23 + .../src/main/config/etc/webdefault.xml | 443 + .../src/main/config/modules/webapp.mod | 35 + .../src/main/java/module-info.java | 54 + .../jetty/ee10/webapp/AbsoluteOrdering.java | 89 + .../ee10/webapp/AbstractConfiguration.java | 208 + .../ee10/webapp/CachingWebAppClassLoader.java | 120 + .../jetty/ee10/webapp/ClassMatcher.java | 789 ++ .../jetty/ee10/webapp/Configuration.java | 247 + .../jetty/ee10/webapp/Configurations.java | 544 + .../jetty/ee10/webapp/DecoratingListener.java | 57 + .../jetty/ee10/webapp/DefaultsDescriptor.java | 27 + .../eclipse/jetty/ee10/webapp/Descriptor.java | 71 + .../ee10/webapp/DescriptorProcessor.java | 22 + .../ee10/webapp/DiscoveredAnnotation.java | 93 + .../ee10/webapp/FragmentConfiguration.java | 66 + .../jetty/ee10/webapp/FragmentDescriptor.java | 168 + .../webapp/IterativeDescriptorProcessor.java | 83 + .../jetty/ee10/webapp/JaasConfiguration.java | 52 + .../jetty/ee10/webapp/JaspiConfiguration.java | 50 + .../ee10/webapp/JettyWebXmlConfiguration.java | 108 + .../jetty/ee10/webapp/JmxConfiguration.java | 52 + .../jetty/ee10/webapp/JndiConfiguration.java | 53 + .../jetty/ee10/webapp/JspConfiguration.java | 55 + .../eclipse/jetty/ee10/webapp/MetaData.java | 769 ++ .../ee10/webapp/MetaInfConfiguration.java | 900 ++ .../eclipse/jetty/ee10/webapp/Ordering.java | 26 + .../org/eclipse/jetty/ee10/webapp/Origin.java | 37 + .../jetty/ee10/webapp/OverrideDescriptor.java | 27 + .../jetty/ee10/webapp/RelativeOrdering.java | 143 + .../ee10/webapp/ServletsConfiguration.java | 54 + .../webapp/StandardDescriptorProcessor.java | 2046 ++++ .../jetty/ee10/webapp/WebAppClassLoader.java | 658 + .../ee10/webapp/WebAppConfiguration.java | 35 + .../jetty/ee10/webapp/WebAppContext.java | 1514 +++ .../jetty/ee10/webapp/WebDescriptor.java | 388 + .../ee10/webapp/WebInfConfiguration.java | 599 + .../ee10/webapp/WebXmlConfiguration.java | 123 + .../jetty/ee10/webapp/package-info.java | 18 + ...rg.eclipse.jetty.ee10.webapp.Configuration | 12 + .../java/org/acme/webapp/ClassInJarA.java | 19 + .../java/org/acme/webapp/TestAnnotation.java | 44 + .../jetty/ee10/webapp/ClassMatcherTest.java | 311 + .../jetty/ee10/webapp/ConfigurationsTest.java | 322 + .../jetty/ee10/webapp/ForcedServletTest.java | 267 + .../jetty/ee10/webapp/HugeResourceTest.java | 389 + .../ee10/webapp/MetaInfConfigurationTest.java | 149 + .../jetty/ee10/webapp/OrderingTest.java | 946 ++ .../StandardDescriptorProcessorTest.java | 41 + .../jetty/ee10/webapp/TempDirTest.java | 388 + .../jetty/ee10/webapp/TestMetaData.java | 198 + .../ee10/webapp/URLStreamHandlerUtil.java | 73 + .../ee10/webapp/WebAppClassLoaderTest.java | 389 + .../WebAppClassLoaderUrlStreamTest.java | 109 + .../jetty/ee10/webapp/WebAppContextTest.java | 598 + .../ee10/webapp/WebAppDefaultServletTest.java | 143 + .../ee10/webapp/WebInfConfigurationTest.java | 97 + .../test/resources/ext/org-acme-ext-one.jar | Bin 0 -> 2605 bytes .../test/resources/ext/org-acme-ext-two.jar | Bin 0 -> 2491 bytes .../resources/ext/sub/org-acme-ext-three.jar | Bin 0 -> 2519 bytes .../src/test/resources/fragments/sigma.jar | Bin 0 -> 432 bytes .../src/test/resources/fragments/zeta.jar | Bin 0 -> 1750 bytes .../test/resources/jetty-logging.properties | 5 + .../src/test/resources/mods/com-acme-janb.jar | Bin 0 -> 1772 bytes .../src/test/resources/mods/foo-bar-janb.jar | Bin 0 -> 1246 bytes .../src/test/resources/org/acme/resource.txt | 1 + ...override-web-with-default-context-path.xml | 10 + .../src/test/resources/wars/dump.war | Bin 0 -> 471247 bytes .../web-default-with-default-context-path.xml | 10 + .../src/test/resources/web-session-config.xml | 12 + .../web-with-default-context-path.xml | 10 + .../web-with-empty-default-context-path.xml | 10 + .../src/test/resources/web25.xml | 10 + .../src/test/resources/web31.xml | 11 + .../src/test/resources/web31false.xml | 11 + .../src/test/webapp-alt-jsp/WEB-INF/web.xml | 27 + .../src/test/webapp-alt-jsp/hello.jsp | 1 + .../org/acme/other/ClassInClassesC.class | Bin 0 -> 300 bytes .../WEB-INF/classes/org/acme/resource.txt | 1 + .../src/test/webapp/WEB-INF/lib/acme.jar | Bin 0 -> 2341 bytes .../src/test/webapp/WEB-INF/lib/alpha.jar | Bin 0 -> 577 bytes .../src/test/webapp/WEB-INF/lib/omega.jar | Bin 0 -> 577 bytes .../src/test/webapp/WEB-INF/test.xml | 1 + .../src/test/webapp/test.xml | 1 + .../pom.xml | 87 + .../src/main/java/module-info.java | 32 + ...kartaWebSocketClientContainerProvider.java | 69 + .../JakartaWebSocketShutdownContainer.java | 73 + .../AnnotatedClientEndpointConfig.java | 54 + .../internal/BasicClientEndpointConfig.java | 24 + .../client/internal/EmptyConfigurator.java | 37 + .../internal/JakartaClientUpgradeRequest.java | 59 + .../JakartaWebSocketClientContainer.java | 407 + ...rtaWebSocketClientFrameHandlerFactory.java | 53 + .../client/internal/JsrUpgradeListener.java | 84 + ...akarta.servlet.ServletContainerInitializer | 1 + .../jakarta.websocket.ContainerProvider | 1 + .../src/test/java/examples/EchoEndpoint.java | 86 + .../examples/OriginServerConfigurator.java | 40 + .../SecureClientContainerExample.java | 90 + .../SecureWebSocketContainerExample.java | 88 + .../test/resources/jetty-logging.properties | 9 + .../resources/jetty-websocket-httpclient.xml | 22 + .../pom.xml | 61 + .../src/main/java/module-info.java | 25 + .../common/ClientEndpointConfigWrapper.java | 57 + .../jakarta/common/ConfiguredEndpoint.java | 52 + .../jakarta/common/EndpointConfigWrapper.java | 71 + .../jakarta/common/InitException.java | 27 + .../common/JakartaWebSocketAsyncRemote.java | 200 + .../common/JakartaWebSocketBasicRemote.java | 161 + .../common/JakartaWebSocketContainer.java | 209 + .../common/JakartaWebSocketExtension.java | 128 + .../JakartaWebSocketExtensionConfig.java | 29 + .../common/JakartaWebSocketFrameHandler.java | 688 ++ .../JakartaWebSocketFrameHandlerFactory.java | 645 + .../JakartaWebSocketFrameHandlerMetadata.java | 207 + .../JakartaWebSocketMessageMetadata.java | 90 + .../common/JakartaWebSocketPongMessage.java | 39 + .../JakartaWebSocketRemoteEndpoint.java | 263 + .../common/JakartaWebSocketSession.java | 574 + .../JakartaWebSocketSessionListener.java | 29 + .../jakarta/common/PathParamProvider.java | 29 + .../jakarta/common/PutListenerMap.java | 113 + .../common/RegisteredMessageHandler.java | 45 + .../jakarta/common/SendHandlerCallback.java | 43 + .../common/ServerEndpointConfigWrapper.java | 69 + .../jakarta/common/SessionTracker.java | 99 + .../jakarta/common/UpgradeRequest.java | 39 + .../jakarta/common/UpgradeRequestAdapter.java | 53 + .../common/decoders/AbstractDecoder.java | 30 + .../common/decoders/AvailableDecoders.java | 221 + .../common/decoders/BooleanDecoder.java | 39 + .../common/decoders/ByteArrayDecoder.java | 37 + .../common/decoders/ByteBufferDecoder.java | 36 + .../jakarta/common/decoders/ByteDecoder.java | 56 + .../common/decoders/CharacterDecoder.java | 46 + .../common/decoders/DoubleDecoder.java | 56 + .../jakarta/common/decoders/FloatDecoder.java | 61 + .../common/decoders/InputStreamDecoder.java | 40 + .../common/decoders/IntegerDecoder.java | 57 + .../jakarta/common/decoders/LongDecoder.java | 56 + .../common/decoders/ReaderDecoder.java | 40 + .../common/decoders/RegisteredDecoder.java | 116 + .../jakarta/common/decoders/ShortDecoder.java | 56 + .../common/decoders/StringDecoder.java | 37 + .../common/encoders/AbstractEncoder.java | 30 + .../common/encoders/AvailableEncoders.java | 254 + .../common/encoders/BooleanEncoder.java | 33 + .../common/encoders/ByteArrayEncoder.java | 41 + .../common/encoders/ByteBufferEncoder.java | 41 + .../jakarta/common/encoders/ByteEncoder.java | 33 + .../common/encoders/CharacterEncoder.java | 33 + .../common/encoders/DoubleEncoder.java | 33 + .../common/encoders/EncodeFailedFuture.java | 66 + .../jakarta/common/encoders/FloatEncoder.java | 33 + .../common/encoders/IntegerEncoder.java | 33 + .../jakarta/common/encoders/LongEncoder.java | 33 + .../common/encoders/RegisteredEncoder.java | 85 + .../jakarta/common/encoders/ShortEncoder.java | 33 + .../common/encoders/StringEncoder.java | 29 + .../messages/AbstractDecodedMessageSink.java | 110 + .../messages/DecodedBinaryMessageSink.java | 72 + .../DecodedBinaryStreamMessageSink.java | 60 + .../messages/DecodedTextMessageSink.java | 71 + .../DecodedTextStreamMessageSink.java | 60 + ...tractJakartaWebSocketFrameHandlerTest.java | 78 + .../jakarta/common/AbstractSessionTest.java | 91 + .../websocket/jakarta/common/Defaults.java | 23 + .../jakarta/common/DummyContainer.java | 130 + .../common/DummyFrameHandlerFactory.java | 50 + ...ebSocketFrameHandlerBadSignaturesTest.java | 236 + ...kartaWebSocketFrameHandlerOnCloseTest.java | 148 + ...kartaWebSocketFrameHandlerOnErrorTest.java | 106 + ...FrameHandlerOnMessageBinaryStreamTest.java | 86 + ...SocketFrameHandlerOnMessageBinaryTest.java | 134 + ...etFrameHandlerOnMessageTextStreamTest.java | 84 + ...ebSocketFrameHandlerOnMessageTextTest.java | 136 + ...akartaWebSocketFrameHandlerOnOpenTest.java | 77 + .../common/coders/tests/BadDualDecoder.java | 114 + .../common/coders/tests/BadDualEncoder.java | 49 + .../common/coders/tests/ExtDecoder.java | 26 + .../jakarta/common/coders/tests/Fruit.java | 20 + .../coders/tests/FruitBinaryEncoder.java | 62 + .../common/coders/tests/FruitDecoder.java | 77 + .../common/coders/tests/FruitTextEncoder.java | 37 + .../endpoints/AbstractStringEndpoint.java | 86 + .../common/endpoints/DummyEndpoint.java | 27 + .../common/endpoints/EchoStringEndpoint.java | 32 + .../common/handlers/BaseMessageHandler.java | 25 + .../handlers/ByteArrayPartialHandler.java | 25 + .../handlers/ByteArrayWholeHandler.java | 25 + .../handlers/ByteBufferPartialHandler.java | 26 + .../handlers/ByteBufferWholeHandler.java | 27 + .../common/handlers/ComboMessageHandler.java | 36 + .../handlers/ExtendedMessageHandler.java | 27 + .../handlers/InputStreamWholeHandler.java | 27 + .../common/handlers/LongMessageHandler.java | 24 + .../common/handlers/ReaderWholeHandler.java | 27 + .../common/handlers/StringPartialHandler.java | 25 + .../common/handlers/StringWholeHandler.java | 25 + .../messages/AbstractMessageSinkTest.java | 61 + .../DecodedBinaryMessageSinkTest.java | 158 + .../DecodedBinaryStreamMessageSinkTest.java | 165 + .../messages/DecodedTextMessageSinkTest.java | 146 + .../DecodedTextStreamMessageSinkTest.java | 154 + .../messages/InputStreamMessageSinkTest.java | 172 + .../common/messages/MessageWriterTest.java | 180 + .../messages/ReaderMessageSinkTest.java | 105 + .../common/sockets/TrackingSocket.java | 40 + .../util/InvokerUtilsStaticParamsTest.java | 150 + .../jakarta/common/util/InvokerUtilsTest.java | 545 + .../common/util/NameParamIdentifier.java | 46 + .../jakarta/common/util/ReflectUtilsTest.java | 134 + .../test/resources/jetty-logging.properties | 30 + .../src/test/resources/quotes-ben.txt | 4 + .../src/test/resources/quotes-twain.txt | 5 + .../pom.xml | 81 + .../main/config/modules/websocket-jakarta.mod | 24 + .../src/main/java/module-info.java | 34 + .../config/ContainerDefaultConfigurator.java | 135 + .../config/JakartaWebSocketConfiguration.java | 40 + ...aWebSocketServletContainerInitializer.java | 302 + .../AnnotatedServerEndpointConfig.java | 131 + .../internal/BasicServerEndpointConfig.java | 41 + .../internal/JakartaServerUpgradeRequest.java | 49 + .../internal/JakartaWebSocketCreator.java | 199 + .../JakartaWebSocketServerContainer.java | 348 + ...rtaWebSocketServerFrameHandlerFactory.java | 55 + .../server/internal/JsrHandshakeRequest.java | 107 + .../server/internal/JsrHandshakeResponse.java | 46 + .../server/internal/PathParamIdentifier.java | 73 + .../PathParamServerEndpointConfig.java | 46 + ...akarta.servlet.ServletContainerInitializer | 1 + ...t.server.ServerEndpointConfig$Configurator | 1 + ...rg.eclipse.jetty.ee10.webapp.Configuration | 1 + .../browser/JsrBrowserConfigurator.java | 46 + .../server/browser/JsrBrowserDebugTool.java | 101 + .../server/browser/JsrBrowserSocket.java | 232 + .../test/resources/jetty-logging.properties | 10 + .../jsr-browser-debug-tool/index.html | 64 + .../resources/jsr-browser-debug-tool/main.css | 33 + .../jsr-browser-debug-tool/websocket.js | 140 + .../fuzzingclient.json | 18 + .../fuzzingserver.json | 10 + .../pom.xml | 98 + .../websocket/jakarta/tests/BadFrame.java | 42 + .../tests/BiConsumerServiceServlet.java | 41 + .../tests/CompletableFutureMethodHandle.java | 34 + .../websocket/jakarta/tests/CoreServer.java | 131 + .../websocket/jakarta/tests/DataUtils.java | 54 + .../jakarta/tests/DummyEndpoint.java | 26 + .../websocket/jakarta/tests/EchoSocket.java | 39 + .../websocket/jakarta/tests/EventSocket.java | 96 + .../jakarta/tests/FunctionMethod.java | 59 + .../ee10/websocket/jakarta/tests/Fuzzer.java | 189 + .../websocket/jakarta/tests/LocalFuzzer.java | 292 + .../websocket/jakarta/tests/LocalServer.java | 296 + .../websocket/jakarta/tests/MessageType.java | 21 + .../jakarta/tests/NetworkFuzzer.java | 272 + .../jakarta/tests/ParserCapture.java | 54 + .../jakarta/tests/RawFrameBuilder.java | 104 + .../jakarta/tests/SessionMatchers.java | 33 + .../ee10/websocket/jakarta/tests/Sha1Sum.java | 106 + .../websocket/jakarta/tests/Timeouts.java | 23 + .../jakarta/tests/UnitGenerator.java | 94 + .../websocket/jakarta/tests/UpgradeUtils.java | 31 + .../jakarta/tests/WSEndpointTracker.java | 144 + .../jakarta/tests/WSEventTracker.java | 185 + .../websocket/jakarta/tests/WSServer.java | 176 + .../ee10/websocket/jakarta/tests/WSURI.java | 128 + .../tests/framehandlers/FrameEcho.java | 60 + .../framehandlers/FrameHandlerTracker.java | 78 + .../tests/framehandlers/StaticText.java | 33 + .../tests/framehandlers/WholeMessageEcho.java | 34 + .../tests/matchers/IsMessageHandlerType.java | 100 + .../IsMessageHandlerTypeRegistered.java | 145 + .../com/acme/websocket/BasicEchoEndpoint.java | 41 + ...asicEchoEndpointConfigContextListener.java | 49 + .../websocket/IdleTimeoutContextListener.java | 49 + .../websocket/IdleTimeoutOnOpenEndpoint.java | 39 + .../websocket/IdleTimeoutOnOpenSocket.java | 44 + .../websocket/LargeEchoContextListener.java | 34 + .../websocket/LargeEchoDefaultSocket.java | 28 + .../websocket/OnOpenIdleTimeoutEndpoint.java | 39 + .../acme/websocket/PongContextListener.java | 56 + .../acme/websocket/PongMessageEndpoint.java | 62 + .../java/com/acme/websocket/PongSocket.java | 63 + .../jakarta/tests/CloseInOnOpenTest.java | 97 + .../jakarta/tests/GracefulCloseTest.java | 130 + .../tests/JakartaClientClassLoaderTest.java | 220 + ...aClientShutdownWithServerEmbeddedTest.java | 111 + ...rtaClientShutdownWithServerWebAppTest.java | 194 + .../jakarta/tests/JakartaOnCloseTest.java | 228 + .../tests/JakartaWebSocketRestartTest.java | 120 + .../tests/JettySpecificConfigTest.java | 147 + .../jakarta/tests/PathParamTest.java | 165 + .../ProgrammaticWebSocketUpgradeTest.java | 170 + .../jakarta/tests/RestartContextTest.java | 183 + .../jakarta/tests/ServerConfigTest.java | 132 + .../tests/SingleMessageHandlerTest.java | 139 + .../jakarta/tests/SyntheticOnMessageTest.java | 107 + .../tests/UpgradeRequestResponseTest.java | 104 + .../tests/autobahn/JakartaAutobahnClient.java | 196 + .../tests/autobahn/JakartaAutobahnServer.java | 77 + .../tests/autobahn/JakartaAutobahnSocket.java | 71 + .../client/AbstractClientSessionTest.java | 61 + .../client/AnnotatedClientEndpointTest.java | 174 + .../tests/client/AnnotatedEchoClient.java | 57 + .../tests/client/AnnotatedEchoTest.java | 180 + .../tests/client/ConfiguratorTest.java | 138 + .../jakarta/tests/client/CookiesTest.java | 167 + .../client/DecoderReaderManySmallTest.java | 167 + .../tests/client/DelayedStartClientTest.java | 105 + .../tests/client/EndpointEchoTest.java | 150 + .../client/JsrClientEchoTrackingSocket.java | 59 + .../tests/client/JsrClientTrackingSocket.java | 57 + .../tests/client/MessageReceivingTest.java | 510 + .../jakarta/tests/client/OnCloseTest.java | 114 + .../client/SessionAddMessageHandlerTest.java | 267 + .../tests/client/WriteTimeoutTest.java | 95 + .../misbehaving/AnnotatedRuntimeOnOpen.java | 55 + .../misbehaving/EndpointRuntimeOnOpen.java | 53 + .../misbehaving/MisbehavingClassTest.java | 81 + .../samples/CloseEndpointConfigSocket.java | 45 + .../samples/CloseReasonSessionSocket.java | 45 + .../client/samples/CloseReasonSocket.java | 45 + .../samples/CloseSessionReasonSocket.java | 45 + .../client/samples/CloseSessionSocket.java | 44 + .../tests/client/samples/CloseSocket.java | 44 + .../tests/client/samples/IntSocket.java | 39 + .../tests/coders/AvailableDecodersTest.java | 338 + .../tests/coders/AvailableEncodersTest.java | 295 + .../jakarta/tests/coders/BadDualDecoder.java | 114 + .../jakarta/tests/coders/BadDualEncoder.java | 49 + .../tests/coders/CoderEventTracking.java | 83 + .../jakarta/tests/coders/DateDecoder.java | 65 + .../jakarta/tests/coders/DateEncoder.java | 50 + .../jakarta/tests/coders/DateTimeDecoder.java | 60 + .../jakarta/tests/coders/DateTimeEncoder.java | 50 + .../jakarta/tests/coders/DecoderListTest.java | 417 + .../tests/coders/DecoderTextStreamTest.java | 127 + .../jakarta/tests/coders/DecoderTextTest.java | 41 + .../tests/coders/EncoderLifeCycleTest.java | 181 + .../jakarta/tests/coders/EncoderTextTest.java | 35 + .../jakarta/tests/coders/ExtDecoder.java | 26 + .../tests/coders/FloatDecoderTest.java | 61 + .../websocket/jakarta/tests/coders/Fruit.java | 20 + .../tests/coders/FruitBinaryEncoder.java | 62 + .../jakarta/tests/coders/FruitDecoder.java | 77 + .../tests/coders/FruitTextEncoder.java | 37 + .../tests/coders/IntegerDecoderTest.java | 52 + .../jakarta/tests/coders/LongDecoderTest.java | 47 + .../jakarta/tests/coders/Quotes.java | 43 + .../jakarta/tests/coders/QuotesDecoder.java | 67 + .../jakarta/tests/coders/QuotesEncoder.java | 45 + .../jakarta/tests/coders/QuotesUtil.java | 90 + .../tests/coders/ShortDecoderTest.java | 47 + .../jakarta/tests/coders/TimeDecoder.java | 60 + .../jakarta/tests/coders/TimeEncoder.java | 50 + .../tests/coders/ValidDualDecoder.java | 91 + .../tests/coders/ValidDualEncoder.java | 54 + .../handlers/AbstractAnnotatedHandler.java | 65 + .../tests/handlers/AbstractHandler.java | 64 + .../tests/handlers/BaseMessageHandler.java | 25 + .../tests/handlers/BinaryHandlers.java | 164 + .../tests/handlers/ComboMessageHandler.java | 36 + .../handlers/ExtendedMessageHandler.java | 27 + .../tests/handlers/LongMessageHandler.java | 25 + .../tests/handlers/MessageHandlerTest.java | 162 + .../jakarta/tests/handlers/TextHandlers.java | 120 + .../tests/pathparam/BooleanClassSocket.java | 30 + .../tests/pathparam/BooleanTypeSocket.java | 30 + .../tests/pathparam/ByteClassSocket.java | 30 + .../tests/pathparam/ByteTypeSocket.java | 30 + .../tests/pathparam/CharacterClassSocket.java | 30 + .../tests/pathparam/CharacterTypeSocket.java | 30 + .../tests/pathparam/DoubleClassSocket.java | 30 + .../tests/pathparam/DoubleTypeSocket.java | 30 + .../tests/pathparam/FloatClassSocket.java | 30 + .../tests/pathparam/FloatTypeSocket.java | 30 + .../tests/pathparam/IntegerClassSocket.java | 30 + .../tests/pathparam/IntegerTypeSocket.java | 30 + .../tests/pathparam/LongClassSocket.java | 30 + .../tests/pathparam/LongTypeSocket.java | 30 + .../tests/pathparam/ShortClassSocket.java | 30 + .../tests/pathparam/ShortTypeSocket.java | 30 + .../tests/pathparam/StringClassSocket.java | 30 + .../jakarta/tests/quotes/Quotes.java | 49 + .../jakarta/tests/quotes/QuotesDecoder.java | 66 + .../tests/quotes/QuotesDecoderTest.java | 153 + .../quotes/QuotesDecoderTextStreamTest.java | 133 + .../jakarta/tests/quotes/QuotesEncoder.java | 44 + .../tests/quotes/QuotesEncoderTest.java | 216 + .../jakarta/tests/quotes/QuotesSocket.java | 62 + .../jakarta/tests/quotes/QuotesUtil.java | 90 + ...akartaWebSocketServerFrameHandlerTest.java | 67 + .../jakarta/tests/server/AddEndpointTest.java | 311 + .../jakarta/tests/server/AltFilterTest.java | 86 + .../server/AnnotatedServerEndpointTest.java | 134 + ...asicEchoEndpointConfigContextListener.java | 53 + .../BasicEchoEndpointContextListener.java | 51 + .../BasicEchoSocketConfigContextListener.java | 51 + .../BasicEchoSocketContextListener.java | 46 + .../tests/server/BinaryStreamTest.java | 281 + .../tests/server/ConfiguratorTest.java | 696 ++ .../server/ContainerProviderServerTest.java | 86 + .../tests/server/DeploymentExceptionTest.java | 109 + .../jakarta/tests/server/DeploymentTest.java | 198 + .../tests/server/EndpointViaConfigTest.java | 100 + .../server/IdleTimeoutContextListener.java | 50 + .../jakarta/tests/server/IdleTimeoutTest.java | 102 + .../tests/server/InputStreamEchoTest.java | 137 + ...etFrameHandlerOnMessageTextStreamTest.java | 151 + .../JettyServerEndpointConfiguratorTest.java | 46 + .../tests/server/JsrBatchModeTest.java | 178 + .../jakarta/tests/server/JsrEchoTest.java | 204 + .../tests/server/LargeAnnotatedTest.java | 112 + .../tests/server/LargeContainerTest.java | 101 + .../server/LargeEchoContextListener.java | 37 + .../jakarta/tests/server/MemoryUsageTest.java | 146 + .../tests/server/OnMessageReturnTest.java | 131 + .../jakarta/tests/server/PartialEchoTest.java | 160 + .../jakarta/tests/server/PingPongTest.java | 128 + .../tests/server/PongContextListener.java | 58 + .../jakarta/tests/server/PongSocket.java | 51 + .../server/PrimitivesBinaryEchoTest.java | 146 + .../tests/server/PrimitivesTextEchoTest.java | 398 + .../jakarta/tests/server/ReaderEchoTest.java | 137 + .../tests/server/ServerDecoderTest.java | 165 + .../jakarta/tests/server/SessionTest.java | 378 + .../tests/server/SessionTrackingTest.java | 164 + .../jakarta/tests/server/StreamTest.java | 271 + .../jakarta/tests/server/TextStreamTest.java | 350 + .../server/UriTemplateParameterTest.java | 90 + .../tests/server/WebAppClassLoaderTest.java | 139 + .../WebSocketServerContainerExecutorTest.java | 225 + .../configs/EchoSocketConfigurator.java | 30 + .../examples/GetHttpSessionConfigurator.java | 29 + .../server/examples/GetHttpSessionSocket.java | 44 + .../server/examples/MyAuthedConfigurator.java | 43 + .../tests/server/examples/MyAuthedSocket.java | 28 + .../server/examples/StreamingEchoSocket.java | 40 + .../examples/WebSocketServerExamplesTest.java | 200 + .../BasicBinaryMessageByteBufferSocket.java | 30 + .../BasicCloseReasonSessionSocket.java | 30 + .../sockets/BasicCloseReasonSocket.java | 29 + .../BasicCloseSessionReasonSocket.java | 30 + .../server/sockets/BasicCloseSocket.java | 28 + .../tests/server/sockets/BasicEchoSocket.java | 32 + .../sockets/BasicErrorSessionSocket.java | 28 + .../BasicErrorSessionThrowableSocket.java | 29 + .../server/sockets/BasicErrorSocket.java | 27 + .../BasicErrorThrowableSessionSocket.java | 29 + .../sockets/BasicErrorThrowableSocket.java | 28 + .../sockets/BasicOpenCloseSessionSocket.java | 39 + .../server/sockets/BasicOpenCloseSocket.java | 36 + .../sockets/BasicOpenSessionSocket.java | 28 + .../tests/server/sockets/BasicOpenSocket.java | 27 + .../sockets/BasicPongMessageSocket.java | 29 + .../sockets/BasicTextMessageStringSocket.java | 28 + .../server/sockets/ByteBufferSocket.java | 45 + .../server/sockets/ConfiguredEchoSocket.java | 107 + .../tests/server/sockets/DateTextSocket.java | 64 + .../sockets/IdleTimeoutOnOpenEndpoint.java | 39 + .../sockets/IdleTimeoutOnOpenSocket.java | 44 + .../server/sockets/InvalidCloseIntSocket.java | 32 + .../sockets/InvalidErrorErrorSocket.java | 32 + .../server/sockets/InvalidErrorIntSocket.java | 32 + .../sockets/InvalidOpenCloseReasonSocket.java | 33 + .../server/sockets/InvalidOpenIntSocket.java | 32 + .../sockets/InvalidOpenSessionIntSocket.java | 34 + .../StatelessTextMessageStringSocket.java | 29 + .../tests/server/sockets/TrackingSocket.java | 40 + .../sockets/binary/ByteBufferSocket.java | 45 + .../sockets/echo/BasicEchoEndpoint.java | 41 + .../server/sockets/echo/BasicEchoSocket.java | 32 + .../sockets/echo/EchoAsyncTextSocket.java | 37 + .../sockets/echo/EchoBasicTextSocket.java | 55 + .../sockets/echo/EchoReturnEndpoint.java | 58 + .../sockets/echo/EchoReturnTextSocket.java | 27 + .../echo/EchoStatelessAsyncTextSocket.java | 28 + .../echo/EchoStatelessBasicTextSocket.java | 46 + .../echo/LargeEchoConfiguredSocket.java | 42 + .../sockets/echo/LargeEchoDefaultSocket.java | 33 + .../OnOpenIdleTimeoutEndpoint.java | 39 + .../idletimeout/OnOpenIdleTimeoutSocket.java | 35 + .../partial/PartialTextSessionSocket.java | 49 + .../sockets/partial/PartialTextSocket.java | 57 + .../partial/PartialTrackingSocket.java | 30 + .../sockets/pong/PongMessageEndpoint.java | 45 + .../BooleanObjectTextParamSocket.java | 61 + .../primitives/BooleanObjectTextSocket.java | 60 + .../primitives/BooleanTextParamSocket.java | 54 + .../sockets/primitives/BooleanTextSocket.java | 53 + .../primitives/ByteObjectTextSocket.java | 60 + .../sockets/primitives/ByteTextSocket.java | 53 + .../sockets/primitives/CharTextSocket.java | 53 + .../primitives/CharacterObjectTextSocket.java | 60 + .../primitives/DoubleObjectTextSocket.java | 61 + .../sockets/primitives/DoubleTextSocket.java | 54 + .../primitives/FloatObjectTextSocket.java | 61 + .../sockets/primitives/FloatTextSocket.java | 54 + .../primitives/IntParamTextSocket.java | 54 + .../sockets/primitives/IntTextSocket.java | 53 + .../IntegerObjectParamTextSocket.java | 61 + .../primitives/IntegerObjectTextSocket.java | 60 + .../primitives/LongObjectTextSocket.java | 60 + .../sockets/primitives/LongTextSocket.java | 53 + .../primitives/ShortObjectTextSocket.java | 60 + .../sockets/primitives/ShortTextSocket.java | 53 + .../sockets/streaming/InputStreamSocket.java | 46 + .../sockets/streaming/ReaderParamSocket.java | 59 + .../sockets/streaming/ReaderSocket.java | 45 + .../StringReturnReaderParamSocket.java | 50 + .../src/test/resources/alt-filter-web.xml | 23 + .../basic-echo-endpoint-config-web.xml | 12 + .../src/test/resources/data/larger.png | Bin 0 -> 134654 bytes .../src/test/resources/data/larger.png.sha | 1 + .../src/test/resources/data/largest.jpg | Bin 0 -> 4443937 bytes .../src/test/resources/data/largest.jpg.sha | 1 + .../src/test/resources/data/medium.png | Bin 0 -> 45443 bytes .../src/test/resources/data/medium.png.sha | 1 + .../src/test/resources/data/small.png | Bin 0 -> 3142 bytes .../src/test/resources/data/small.png.sha | 1 + .../src/test/resources/empty-web.xml | 8 + .../resources/idle-timeout-config-web.xml | 12 + .../test/resources/jetty-logging.properties | 15 + .../resources/jetty-websocket-httpclient.xml | 20 + .../src/test/resources/keystore.p12 | Bin 0 -> 2565 bytes .../test/resources/large-echo-config-web.xml | 12 + .../src/test/resources/logback-test.xml | 34 + .../src/test/resources/pong-config-web.xml | 12 + .../src/test/resources/quotes-ben.txt | 4 + .../src/test/resources/quotes-twain.txt | 5 + .../simple/jetty-websocket-httpclient.xml | 23 + .../wsuf-alt-config-via-listener.xml | 26 + .../resources/wsuf-config-via-listener.xml | 26 + .../wsuf-config-via-servlet-init.xml | 28 + .../src/test/webapp/index.html | 180 + .../jetty-ee10-websocket-jetty-api/pom.xml | 17 + .../src/main/java/module-info.java | 22 + .../jetty/ee10/websocket/api/BatchMode.java | 42 + .../jetty/ee10/websocket/api/CloseStatus.java | 50 + .../ee10/websocket/api/ExtensionConfig.java | 62 + .../jetty/ee10/websocket/api/Frame.java | 104 + .../ee10/websocket/api/RemoteEndpoint.java | 197 + .../jetty/ee10/websocket/api/Session.java | 171 + .../jetty/ee10/websocket/api/StatusCode.java | 174 + .../ee10/websocket/api/SuspendToken.java | 25 + .../ee10/websocket/api/UpgradeRequest.java | 174 + .../ee10/websocket/api/UpgradeResponse.java | 75 + .../ee10/websocket/api/WebSocketAdapter.java | 77 + .../ee10/websocket/api/WebSocketBehavior.java | 26 + .../api/WebSocketConnectionListener.java | 58 + .../websocket/api/WebSocketContainer.java | 58 + .../websocket/api/WebSocketFrameListener.java | 27 + .../ee10/websocket/api/WebSocketListener.java | 40 + .../api/WebSocketPartialListener.java | 53 + .../api/WebSocketPingPongListener.java | 40 + .../ee10/websocket/api/WebSocketPolicy.java | 152 + .../api/WebSocketSessionListener.java | 32 + .../ee10/websocket/api/WriteCallback.java | 59 + .../api/annotations/OnWebSocketClose.java | 41 + .../api/annotations/OnWebSocketConnect.java | 40 + .../api/annotations/OnWebSocketError.java | 41 + .../api/annotations/OnWebSocketFrame.java | 42 + .../api/annotations/OnWebSocketMessage.java | 67 + .../websocket/api/annotations/WebSocket.java | 64 + .../api/annotations/package-info.java | 18 + .../api/exceptions/BadPayloadException.java | 41 + .../api/exceptions/CloseException.java | 43 + .../exceptions/InvalidWebSocketException.java | 42 + .../exceptions/MessageTooLargeException.java | 40 + .../exceptions/PolicyViolationException.java | 40 + .../api/exceptions/ProtocolException.java | 38 + .../api/exceptions/UpgradeException.java | 62 + .../api/exceptions/WebSocketException.java | 41 + .../exceptions/WebSocketTimeoutException.java | 37 + .../api/exceptions/package-info.java | 18 + .../ee10/websocket/api/package-info.java | 18 + .../jetty/ee10/websocket/api/util/WSURI.java | 120 + .../api/util/WebSocketConstants.java | 26 + .../ee10/websocket/api/util/package-info.java | 18 + .../jetty-ee10-websocket-jetty-client/pom.xml | 70 + .../config/modules/websocket-jetty-client.mod | 23 + .../src/main/java/module-info.java | 26 + .../client/ClientUpgradeRequest.java | 428 + .../client/JettyUpgradeListener.java | 39 + .../websocket/client/WebSocketClient.java | 429 + .../JettyWebSocketClientConfiguration.java | 40 + .../DelegatedJettyClientUpgradeRequest.java | 175 + .../DelegatedJettyClientUpgradeResponse.java | 88 + .../impl/JettyClientUpgradeRequest.java | 72 + .../ee10/websocket/client/package-info.java | 18 + ...rg.eclipse.jetty.ee10.webapp.Configuration | 1 + .../src/test/java/examples/ClientDemo.java | 304 + .../test/java/examples/SimpleEchoClient.java | 65 + .../test/java/examples/SimpleEchoSocket.java | 76 + .../websocket/client/HttpClientInitTest.java | 83 + .../client/WebSocketClientBadUriTest.java | 109 + .../client/WebSocketClientInitTest.java | 105 + .../test/resources/jetty-logging.properties | 16 + .../jetty-ee10-websocket-jetty-common/pom.xml | 59 + .../src/main/java/module-info.java | 26 + .../common/ExtensionConfigParser.java | 31 + .../common/JettyExtensionConfig.java | 123 + .../websocket/common/JettyWebSocketFrame.java | 100 + .../common/JettyWebSocketFrameHandler.java | 522 + .../JettyWebSocketFrameHandlerFactory.java | 548 + .../JettyWebSocketFrameHandlerMetadata.java | 177 + .../common/JettyWebSocketRemoteEndpoint.java | 275 + .../ee10/websocket/common/SessionTracker.java | 101 + .../websocket/common/WebSocketSession.java | 261 + .../ee10/websocket/common/package-info.java | 26 + ....ee10.websocket.api.ExtensionConfig$Parser | 1 + .../ee10/websocket/common/DummyContainer.java | 83 + .../ee10/websocket/common/EndPoints.java | 507 + .../ee10/websocket/common/EventQueue.java | 47 + .../JettyWebSocketFrameHandlerTest.java | 339 + .../common/LocalEndpointMetadataTest.java | 381 + .../common/MessageInputStreamTest.java | 282 + .../common/MessageOutputStreamTest.java | 103 + .../common/OutgoingMessageCapture.java | 177 + .../TestableLeakTrackingBufferPool.java | 43 + .../endpoints/adapters/AdapterEchoSocket.java | 42 + .../adapters/AnnotatedEchoSocket.java | 36 + .../adapters/ListenerEchoSocket.java | 60 + .../common/invoke/InvokerUtilsTest.java | 544 + .../common/invoke/NameParamIdentifier.java | 46 + .../test/resources/jetty-logging.properties | 1 + .../jetty-ee10-websocket-jetty-server/pom.xml | 76 + .../main/config/modules/websocket-jetty.mod | 21 + .../src/main/java/module-info.java | 36 + .../server/JettyServerUpgradeRequest.java | 111 + .../server/JettyServerUpgradeResponse.java | 111 + .../server/JettyWebSocketCreator.java | 33 + .../server/JettyWebSocketServerContainer.java | 348 + .../server/JettyWebSocketServlet.java | 301 + .../server/JettyWebSocketServletFactory.java | 78 + .../config/JettyWebSocketConfiguration.java | 46 + ...yWebSocketServletContainerInitializer.java | 109 + .../DelegatedServerUpgradeRequest.java | 279 + .../DelegatedServerUpgradeResponse.java | 148 + .../JettyServerFrameHandlerFactory.java | 48 + ...akarta.servlet.ServletContainerInitializer | 1 + ...rg.eclipse.jetty.ee10.webapp.Configuration | 1 + .../server/browser/BrowserDebugTool.java | 204 + .../server/browser/BrowserSocket.java | 299 + .../resources/browser-debug-tool/index.html | 60 + .../resources/browser-debug-tool/main.css | 29 + .../resources/browser-debug-tool/websocket.js | 142 + .../fuzzingclient.json | 18 + .../fuzzingserver.json | 10 + .../jetty-ee10-websocket-jetty-tests/pom.xml | 127 + .../tests/AnnoMaxMessageEndpoint.java | 31 + .../tests/AnnotatedPartialListenerTest.java | 240 + .../websocket/tests/CloseInOnOpenTest.java | 95 + .../tests/CloseTrackingEndpoint.java | 132 + .../tests/ConcurrentConnectTest.java | 147 + .../tests/ConnectMessageEndpoint.java | 31 + .../websocket/tests/ConnectionHeaderTest.java | 103 + .../ee10/websocket/tests/EchoCreator.java | 32 + .../ee10/websocket/tests/EchoSocket.java | 38 + .../ee10/websocket/tests/ErrorCloseTest.java | 268 + .../ee10/websocket/tests/EventSocket.java | 101 + .../tests/GetAuthHeaderEndpoint.java | 33 + .../websocket/tests/GracefulCloseTest.java | 111 + .../tests/JettyClientClassLoaderTest.java | 245 + .../websocket/tests/JettyOnCloseTest.java | 190 + .../JettyWebSocketExtensionConfigTest.java | 130 + .../tests/JettyWebSocketFilterTest.java | 434 + .../tests/JettyWebSocketNegotiationTest.java | 152 + .../tests/JettyWebSocketRestartTest.java | 122 + .../JettyWebSocketServletAttributeTest.java | 92 + .../tests/JettyWebSocketServletTest.java | 93 + .../websocket/tests/JettyWebSocketWebApp.java | 96 + .../websocket/tests/LargeDeflateTest.java | 101 + .../tests/MaxOutgoingFramesTest.java | 179 + .../ee10/websocket/tests/ParamsEndpoint.java | 44 + .../tests/PermessageDeflateBufferTest.java | 160 + .../ProgrammaticWebSocketUpgradeTest.java | 114 + .../websocket/tests/SimpleStatusServlet.java | 37 + .../websocket/tests/SingleOnMessageTest.java | 170 + .../websocket/tests/SuspendResumeTest.java | 192 + .../tests/UpgradeRequestResponseTest.java | 126 + .../ee10/websocket/tests/WebAppTester.java | 261 + .../tests/WebSocketOverHTTP2Test.java | 363 + .../tests/WebSocketServletExamplesTest.java | 176 + .../websocket/tests/WebSocketStatsTest.java | 150 + .../websocket/tests/WebSocketStopTest.java | 130 + .../tests/autobahn/JettyAutobahnClient.java | 222 + .../tests/autobahn/JettyAutobahnServer.java | 77 + .../tests/autobahn/JettyAutobahnSocket.java | 32 + .../tests/client/BadNetworkTest.java | 194 + .../tests/client/ClientCloseTest.java | 529 + .../tests/client/ClientConfigTest.java | 213 + .../tests/client/ClientConnectTest.java | 428 + .../client/ClientOpenSessionTracker.java | 66 + .../tests/client/ClientSessionsTest.java | 151 + .../tests/client/ClientTimeoutTest.java | 123 + .../tests/client/ClientWriteThread.java | 103 + .../tests/client/ConnectFutureTest.java | 376 + .../tests/client/InvalidUpgradeServlet.java | 62 + .../tests/client/SlowClientTest.java | 134 + .../tests/client/WebSocketClientTest.java | 404 + .../tests/examples/MyAdvancedEchoCreator.java | 52 + .../tests/examples/MyAdvancedEchoServlet.java | 35 + .../tests/examples/MyAuthedCreator.java | 55 + .../tests/examples/MyAuthedServlet.java | 27 + .../tests/examples/MyBinaryEchoSocket.java | 34 + .../tests/examples/MyEchoServlet.java | 35 + .../tests/examples/MyEchoSocket.java | 32 + .../tests/extensions/ExtensionConfigTest.java | 120 + .../listeners/AbstractAnnotatedListener.java | 64 + .../tests/listeners/AbstractListener.java | 61 + .../tests/listeners/BinaryListeners.java | 124 + .../tests/listeners/TextListeners.java | 100 + .../listeners/WebSocketListenerTest.java | 194 + .../websocket/tests/proxy/WebSocketProxy.java | 309 + .../tests/proxy/WebSocketProxyTest.java | 369 + .../tests/server/AbstractCloseEndpoint.java | 82 + .../tests/server/CloseInOnCloseEndpoint.java | 26 + .../CloseInOnCloseEndpointNewThread.java | 42 + .../tests/server/ContainerEndpoint.java | 63 + .../tests/server/FastCloseEndpoint.java | 30 + .../tests/server/FastFailEndpoint.java | 31 + .../tests/server/FrameAnnotationTest.java | 201 + .../tests/server/FrameListenerTest.java | 179 + .../tests/server/PartialListenerTest.java | 294 + .../tests/server/ServerCloseCreator.java | 87 + .../tests/server/ServerCloseTest.java | 321 + .../tests/server/ServerConfigTest.java | 298 + .../tests/server/SlowServerEndpoint.java | 71 + .../tests/server/SlowServerTest.java | 133 + .../tests/util/FutureWriteCallback.java | 45 + .../ee10/websocket/tests/util/WSURITest.java | 83 + .../test/resources/jetty-logging.properties | 11 + .../src/test/resources/keystore.p12 | Bin 0 -> 2533 bytes .../src/test/resources/wsuf-ordering1.xml | 21 + .../src/test/resources/wsuf-ordering2.xml | 32 + .../jetty-ee10-websocket-servlet/pom.xml | 41 + .../src/main/java/module-info.java | 22 + .../servlet/WebSocketUpgradeFilter.java | 236 + jetty-ee10/jetty-ee10-websocket/pom.xml | 44 + jetty-ee10/jetty-examples/pom.xml | 32 + .../src/test/java/org/acme/MyServlet.java | 30 + .../jetty/examples/Jetty12Example.java | 73 + jetty-ee10/pom.xml | 408 + 2264 files changed, 279102 insertions(+) create mode 100644 jetty-ee10/jetty-ee10-annotations/pom.xml create mode 100644 jetty-ee10/jetty-ee10-annotations/src/main/config/modules/annotations.mod create mode 100644 jetty-ee10/jetty-ee10-annotations/src/main/java/module-info.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/AbstractDiscoverableAnnotationHandler.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/AnnotationConfiguration.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/AnnotationDecorator.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/AnnotationIntrospector.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/AnnotationParser.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/ClassInheritanceHandler.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/ContainerInitializerAnnotationHandler.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/DeclareRolesAnnotationHandler.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/MultiPartConfigAnnotationHandler.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/PostConstructAnnotationHandler.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/PreDestroyAnnotationHandler.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/ResourceAnnotationHandler.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/ResourcesAnnotationHandler.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/RunAsAnnotationHandler.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/ServletContainerInitializersStarter.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/ServletSecurityAnnotationHandler.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/WebFilterAnnotation.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/WebFilterAnnotationHandler.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/WebListenerAnnotation.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/WebListenerAnnotationHandler.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/WebServletAnnotation.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/WebServletAnnotationHandler.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/package-info.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/main/resources/META-INF/services/org.eclipse.jetty.ee10.webapp.Configuration create mode 100644 jetty-ee10/jetty-ee10-annotations/src/test/jar/test-sci-for-container-path.jar create mode 100644 jetty-ee10/jetty-ee10-annotations/src/test/jar/test-sci-for-webinf.jar create mode 100644 jetty-ee10/jetty-ee10-annotations/src/test/jar/test-sci-with-ordering.jar create mode 100644 jetty-ee10/jetty-ee10-annotations/src/test/jar/test-sci.jar create mode 100644 jetty-ee10/jetty-ee10-annotations/src/test/java/org/acme/ClassOne.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/ClassA.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/ClassB.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/FilterC.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/InterfaceD.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/ListenerC.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/Multi.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/Sample.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/ServletC.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/ServletD.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/ServletE.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/TestAnnotationConfiguration.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/TestAnnotationDecorator.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/TestAnnotationInheritance.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/TestAnnotationIntrospector.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/TestAnnotationParser.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/TestDiscoveredServletContainerInitializerHolder.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/TestRunAsAnnotation.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/TestSecurityAnnotationConversions.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/TestServletAnnotations.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/resources/ResourceA.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/resources/ResourceB.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/resources/TestResourceAnnotations.java create mode 100644 jetty-ee10/jetty-ee10-annotations/src/test/resources/bad-classes.jar create mode 100644 jetty-ee10/jetty-ee10-annotations/src/test/resources/jdk10/multirelease-10.jar create mode 100644 jetty-ee10/jetty-ee10-annotations/src/test/resources/jdk9/log4j-api-2.9.0.jar create mode 100644 jetty-ee10/jetty-ee10-annotations/src/test/resources/jdk9/slf4j-api-1.8.0-alpha2.jar create mode 100644 jetty-ee10/jetty-ee10-annotations/src/test/resources/jetty-logging.properties create mode 100644 jetty-ee10/jetty-ee10-annotations/src/test/resources/tinytest.jar create mode 100644 jetty-ee10/jetty-ee10-annotations/src/test/resources/tinytest_copy.jar create mode 100644 jetty-ee10/jetty-ee10-annotations/src/test/resources/web-fragment4false.xml create mode 100644 jetty-ee10/jetty-ee10-annotations/src/test/resources/web-fragment4true.xml create mode 100644 jetty-ee10/jetty-ee10-annotations/src/test/resources/web25.xml create mode 100644 jetty-ee10/jetty-ee10-annotations/src/test/resources/web31false.xml create mode 100644 jetty-ee10/jetty-ee10-annotations/src/test/resources/web31true.xml create mode 100644 jetty-ee10/jetty-ee10-ant/pom.xml create mode 100644 jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/AntMetaInfConfiguration.java create mode 100644 jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/AntWebAppContext.java create mode 100644 jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/AntWebInfConfiguration.java create mode 100644 jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/AntWebXmlConfiguration.java create mode 100644 jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/JettyRunTask.java create mode 100644 jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/JettyStopTask.java create mode 100644 jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/ServerProxyImpl.java create mode 100644 jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/package-info.java create mode 100644 jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/types/Attribute.java create mode 100644 jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/types/Attributes.java create mode 100644 jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/types/Connector.java create mode 100644 jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/types/Connectors.java create mode 100644 jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/types/ContextHandlers.java create mode 100644 jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/types/FileMatchingConfiguration.java create mode 100644 jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/types/LoginServices.java create mode 100644 jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/types/SystemProperties.java create mode 100644 jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/types/package-info.java create mode 100644 jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/utils/ServerProxy.java create mode 100644 jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/utils/TaskLog.java create mode 100644 jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/utils/package-info.java create mode 100644 jetty-ee10/jetty-ee10-ant/src/main/resources/META-INF/services/org.eclipse.jetty.webapp.Configuration create mode 100644 jetty-ee10/jetty-ee10-ant/src/main/resources/tasks.properties create mode 100644 jetty-ee10/jetty-ee10-ant/src/test/config/build.xml create mode 100644 jetty-ee10/jetty-ee10-ant/src/test/java/org/eclipse/jetty/ee10/ant/AntBuild.java create mode 100644 jetty-ee10/jetty-ee10-ant/src/test/java/org/eclipse/jetty/ee10/ant/JettyAntTaskTest.java create mode 100644 jetty-ee10/jetty-ee10-ant/src/test/resources/connector-test.xml create mode 100644 jetty-ee10/jetty-ee10-ant/src/test/resources/foo/WEB-INF/acme-taglib.tld create mode 100644 jetty-ee10/jetty-ee10-ant/src/test/resources/foo/WEB-INF/acme-taglib2.tld create mode 100644 jetty-ee10/jetty-ee10-ant/src/test/resources/foo/WEB-INF/tags/panel.tag create mode 100644 jetty-ee10/jetty-ee10-ant/src/test/resources/foo/WEB-INF/web.xml create mode 100644 jetty-ee10/jetty-ee10-ant/src/test/resources/foo/index.html create mode 100644 jetty-ee10/jetty-ee10-ant/src/test/resources/foo/jsp/bean1.jsp create mode 100644 jetty-ee10/jetty-ee10-ant/src/test/resources/foo/jsp/bean2.jsp create mode 100644 jetty-ee10/jetty-ee10-ant/src/test/resources/foo/jsp/dump.jsp create mode 100644 jetty-ee10/jetty-ee10-ant/src/test/resources/foo/jsp/expr.jsp create mode 100644 jetty-ee10/jetty-ee10-ant/src/test/resources/foo/jsp/foo/foo.jsp create mode 100644 jetty-ee10/jetty-ee10-ant/src/test/resources/foo/jsp/index.html create mode 100644 jetty-ee10/jetty-ee10-ant/src/test/resources/foo/jsp/jstl.jsp create mode 100644 jetty-ee10/jetty-ee10-ant/src/test/resources/foo/jsp/tag.jsp create mode 100644 jetty-ee10/jetty-ee10-ant/src/test/resources/foo/jsp/tag2.jsp create mode 100644 jetty-ee10/jetty-ee10-ant/src/test/resources/foo/jsp/tagfile.jsp create mode 100644 jetty-ee10/jetty-ee10-ant/src/test/resources/webapp-test.xml create mode 100644 jetty-ee10/jetty-ee10-apache-jsp/pom.xml create mode 100644 jetty-ee10/jetty-ee10-apache-jsp/src/main/config/modules/apache-jsp.mod create mode 100644 jetty-ee10/jetty-ee10-apache-jsp/src/main/java/module-info.java create mode 100644 jetty-ee10/jetty-ee10-apache-jsp/src/main/java/org/eclipse/jetty/ee10/apache/jsp/JettyJasperInitializer.java create mode 100644 jetty-ee10/jetty-ee10-apache-jsp/src/main/java/org/eclipse/jetty/ee10/apache/jsp/JettyTldPreScanned.java create mode 100644 jetty-ee10/jetty-ee10-apache-jsp/src/main/java/org/eclipse/jetty/ee10/apache/jsp/JuliLog.java create mode 100644 jetty-ee10/jetty-ee10-apache-jsp/src/main/java/org/eclipse/jetty/ee10/jsp/JettyJspServlet.java create mode 100644 jetty-ee10/jetty-ee10-apache-jsp/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer create mode 100644 jetty-ee10/jetty-ee10-apache-jsp/src/main/resources/META-INF/services/org.apache.juli.logging.Log create mode 100644 jetty-ee10/jetty-ee10-apache-jsp/src/test/java/org/eclipse/jetty/ee10/jsp/TestJettyJspServlet.java create mode 100644 jetty-ee10/jetty-ee10-apache-jsp/src/test/java/org/eclipse/jetty/ee10/jsp/TestJettyTldPreScanned.java create mode 100644 jetty-ee10/jetty-ee10-apache-jsp/src/test/java/org/eclipse/jetty/ee10/jsp/TestJspFileNameToClass.java create mode 100644 jetty-ee10/jetty-ee10-apache-jsp/src/test/resources/META-INF/foo-taglib.tld create mode 100644 jetty-ee10/jetty-ee10-apache-jsp/src/test/resources/base/dir/empty.txt create mode 100644 jetty-ee10/jetty-ee10-apache-jsp/src/test/resources/base/foo.jsp create mode 100644 jetty-ee10/jetty-ee10-apache-jsp/src/test/resources/taglib.jar create mode 100644 jetty-ee10/jetty-ee10-bom/pom.xml create mode 100644 jetty-ee10/jetty-ee10-cdi/pom.xml create mode 100644 jetty-ee10/jetty-ee10-cdi/src/main/config/etc/cdi/jetty-cdi.xml create mode 100644 jetty-ee10/jetty-ee10-cdi/src/main/config/modules/cdi-decorate.mod create mode 100644 jetty-ee10/jetty-ee10-cdi/src/main/config/modules/cdi-spi.mod create mode 100644 jetty-ee10/jetty-ee10-cdi/src/main/config/modules/cdi.mod create mode 100644 jetty-ee10/jetty-ee10-cdi/src/main/java/module-info.java create mode 100644 jetty-ee10/jetty-ee10-cdi/src/main/java/org/eclipse/jetty/ee10/cdi/CdiConfiguration.java create mode 100644 jetty-ee10/jetty-ee10-cdi/src/main/java/org/eclipse/jetty/ee10/cdi/CdiDecoratingListener.java create mode 100644 jetty-ee10/jetty-ee10-cdi/src/main/java/org/eclipse/jetty/ee10/cdi/CdiServletContainerInitializer.java create mode 100644 jetty-ee10/jetty-ee10-cdi/src/main/java/org/eclipse/jetty/ee10/cdi/CdiSpiDecorator.java create mode 100644 jetty-ee10/jetty-ee10-cdi/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer create mode 100644 jetty-ee10/jetty-ee10-cdi/src/main/resources/META-INF/services/org.eclipse.jetty.ee10.webapp.Configuration create mode 100644 jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-jar/pom.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-jar/src/main/java/org/eclipse/jetty/ee10/demos/AbstractRestServlet.java create mode 100644 jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-jar/src/main/java/org/eclipse/jetty/ee10/demos/AsyncRestServlet.java create mode 100644 jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-jar/src/main/java/org/eclipse/jetty/ee10/demos/SerialRestServlet.java create mode 100644 jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-jar/src/main/resources/META-INF/resources/asyncrest.html create mode 100644 jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-jar/src/main/resources/META-INF/resources/asyncrest/green.png create mode 100644 jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-jar/src/main/resources/META-INF/resources/asyncrest/red.png create mode 100644 jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-jar/src/main/resources/META-INF/web-fragment.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-server/pom.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-server/src/main/java/org/eclipse/jetty/ee10/demos/AsyncRestServer.java create mode 100644 jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-webapp/pom.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-webapp/src/main/config/modules/demo-async-rest.mod create mode 100644 jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-webapp/src/main/webapp/META-INF/MANIFEST.MF create mode 100644 jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-webapp/src/main/webapp/WEB-INF/jetty-web.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-webapp/src/main/webapp/WEB-INF/web.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-webapp/src/main/webapp/demo.css create mode 100644 jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-webapp/src/main/webapp/index.html create mode 100644 jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-webapp/src/main/webapp/small_powered_by.gif create mode 100644 jetty-ee10/jetty-ee10-demos/demo-async-rest/pom.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jaas-webapp/pom.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jaas-webapp/src/main/config/modules/demo-jaas.mod create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jaas-webapp/src/main/config/modules/demo.d/demo-jaas.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jaas-webapp/src/main/config/modules/demo.d/demo-login.conf create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jaas-webapp/src/main/config/modules/demo.d/demo-login.properties create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jaas-webapp/src/main/webapp/WEB-INF/jetty-web.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jaas-webapp/src/main/webapp/WEB-INF/web.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jaas-webapp/src/main/webapp/auth.html create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jaas-webapp/src/main/webapp/authfail.html create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jaas-webapp/src/main/webapp/demo.css create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jaas-webapp/src/main/webapp/index.html create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jaas-webapp/src/main/webapp/login.html create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jaas-webapp/src/main/webapp/logout.jsp create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jaas-webapp/src/main/webapp/small_powered_by.gif create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jaas-webapp/src/main/webapp/stylesheet.css create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/jetty-chat.jmx create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/pom.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/assembly/embedded-jetty-web-for-webbundle.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/assembly/web-bundle.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/config/modules/demo-jetty.mod create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/config/modules/demo-moved-context.mod create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/config/modules/demo-rewrite.mod create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/config/modules/demo.d/demo-jetty-override-web.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/config/modules/demo.d/demo-jetty.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/config/modules/demo.d/demo-moved-context.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/config/modules/demo.d/demo-rewrite-rules.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/AddListServletRequestListener.java create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/ChatServlet.java create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/CookieDump.java create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/DispatchServlet.java create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/Dump.java create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/HelloWorld.java create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/JakartaWebSocketChat.java create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/LoginServlet.java create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/RegTest.java create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/RewriteServlet.java create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/SecureModeServlet.java create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/SessionDump.java create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/TestFilter.java create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/TestListener.java create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/TestServlet.java create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/WebSocketChatServlet.java create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/WEB-INF/jetty-web.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/WEB-INF/web.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/auth.html create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/auth/file.txt create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/auth/relax.txt create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/auth2/index.html create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/cgi-bin/hello.sh create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/chat/index.html create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/d.txt create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/da.txt create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/da.txt.gz create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/dat.txt create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/data.txt create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/data.txt.gz create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/demo.css create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/error404.html create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/favicon.ico create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/index.html create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/jakarta.websocket/index.html create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/logon.html create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/logonError.html create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/remote.html create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/rewrite/index.html create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/rewrite/info.html create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/small_powered_by.gif create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/ws/index.html create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/test/java/org/eclipse/jetty/ee10/ChatServletTest.java create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/test/java/org/eclipse/jetty/ee10/DispatchServletTest.java create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/test/java/org/eclipse/jetty/ee10/TestServer.java create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/test/resources/jetty-logging.properties create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/test/resources/test-realm.properties create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jndi-webapp/pom.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jndi-webapp/src/main/config/modules/demo-jndi.mod create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jndi-webapp/src/main/config/modules/demo.d/demo-jndi.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jndi-webapp/src/main/java/org/example/JNDITest.java create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jndi-webapp/src/main/templates/env-definitions.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jndi-webapp/src/main/templates/jetty-test-jndi-header.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jndi-webapp/src/main/templates/plugin-context-header.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jndi-webapp/src/main/webapp/WEB-INF/jetty-env.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jndi-webapp/src/main/webapp/WEB-INF/jetty-web.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jndi-webapp/src/main/webapp/WEB-INF/web.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jndi-webapp/src/main/webapp/demo.css create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jndi-webapp/src/main/webapp/index.html create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jndi-webapp/src/main/webapp/small_powered_by.gif create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jndi-webapp/src/main/webapp/stylesheet.css create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/pom.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/assembly/web-bundle.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/config/modules/demo-jsp.mod create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/java/org/example/Counter.java create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/java/org/example/Date2Tag.java create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/java/org/example/DateTag.java create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/java/org/example/TagListener.java create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/WEB-INF/acme-taglib.tld create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/WEB-INF/acme-taglib2.tld create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/WEB-INF/jetty-web.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/WEB-INF/tags/panel.tag create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/WEB-INF/web.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/bean1.jsp create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/bean2.jsp create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/demo.css create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/dump.jsp create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/expr.jsp create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/foo/foo.jsp create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/index.jsp create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/jstl.jsp create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/small_powered_by.gif create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/tag.jsp create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/tag2.jsp create mode 100644 jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/tagfile.jsp create mode 100644 jetty-ee10/jetty-ee10-demos/demo-mock-resources/pom.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-mock-resources/src/main/config/modules/demo-mock-resources.mod create mode 100644 jetty-ee10/jetty-ee10-demos/demo-mock-resources/src/main/java/org/example/MockDataSource.java create mode 100644 jetty-ee10/jetty-ee10-demos/demo-mock-resources/src/main/java/org/example/MockTransport.java create mode 100644 jetty-ee10/jetty-ee10-demos/demo-mock-resources/src/main/java/org/example/MockUserTransaction.java create mode 100644 jetty-ee10/jetty-ee10-demos/demo-mock-resources/src/main/resources/META-INF/javaxmail.providers create mode 100644 jetty-ee10/jetty-ee10-demos/demo-proxy-webapp/pom.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-proxy-webapp/src/main/config/modules/demo-proxy.mod create mode 100644 jetty-ee10/jetty-ee10-demos/demo-proxy-webapp/src/main/webapp/META-INF/MANIFEST.MF create mode 100644 jetty-ee10/jetty-ee10-demos/demo-proxy-webapp/src/main/webapp/WEB-INF/jetty-web.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-proxy-webapp/src/main/webapp/WEB-INF/web.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-proxy-webapp/src/test/java/org/eclipse/jetty/ee10/demos/ProxyWebAppTest.java create mode 100644 jetty-ee10/jetty-ee10-demos/demo-proxy-webapp/src/test/resources/jetty-logging.properties create mode 100644 jetty-ee10/jetty-ee10-demos/demo-simple-webapp/pom.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-simple-webapp/src/main/config/modules/demo-simple.mod create mode 100644 jetty-ee10/jetty-ee10-demos/demo-simple-webapp/src/main/webapp/WEB-INF/web.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-simple-webapp/src/main/webapp/index.html create mode 100644 jetty-ee10/jetty-ee10-demos/demo-simple-webapp/src/main/webapp/jetty.icon create mode 100644 jetty-ee10/jetty-ee10-demos/demo-simple-webapp/src/main/webapp/jetty.png create mode 100644 jetty-ee10/jetty-ee10-demos/demo-simple-webapp/src/main/webapp/jetty.webp create mode 100644 jetty-ee10/jetty-ee10-demos/demo-spec/demo-container-initializer/pom.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-spec/demo-container-initializer/src/main/java/org/example/initializer/Foo.java create mode 100644 jetty-ee10/jetty-ee10-demos/demo-spec/demo-container-initializer/src/main/java/org/example/initializer/FooInitializer.java create mode 100644 jetty-ee10/jetty-ee10-demos/demo-spec/demo-container-initializer/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer create mode 100644 jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/pom.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/etc/realm.properties create mode 100644 jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/assembly/web-bundle.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/config/modules/demo-spec.mod create mode 100644 jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/config/modules/demo.d/demo-spec.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/java/org/example/test/AnnotatedListener.java create mode 100644 jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/java/org/example/test/AnnotationTest.java create mode 100644 jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/java/org/example/test/AsyncListenerServlet.java create mode 100644 jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/java/org/example/test/Bar.java create mode 100644 jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/java/org/example/test/ClassLoaderServlet.java create mode 100644 jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/java/org/example/test/MultiPartTest.java create mode 100644 jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/java/org/example/test/RoleAnnotationTest.java create mode 100644 jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/java/org/example/test/SecuredServlet.java create mode 100644 jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/java/org/example/test/TestListener.java create mode 100644 jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/templates/annotations-context-header.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/templates/env-definitions.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/templates/plugin-context-header.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/webapp/WEB-INF/jetty-env.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/webapp/WEB-INF/jetty-web.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/webapp/WEB-INF/web.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/webapp/authfail.html create mode 100644 jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/webapp/demo.css create mode 100644 jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/webapp/dynamic.jsp create mode 100644 jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/webapp/index.html create mode 100644 jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/webapp/login.html create mode 100644 jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/webapp/logout.jsp create mode 100644 jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/webapp/small_powered_by.gif create mode 100644 jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/webapp/stylesheet.css create mode 100644 jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/test/jetty-plugin-env.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-spec/demo-web-fragment/pom.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-spec/demo-web-fragment/src/main/java/org/example/fragment/FragmentServlet.java create mode 100644 jetty-ee10/jetty-ee10-demos/demo-spec/demo-web-fragment/src/main/resources/META-INF/resources/fragmentA/index.html create mode 100644 jetty-ee10/jetty-ee10-demos/demo-spec/demo-web-fragment/src/main/resources/META-INF/web-fragment.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-spec/pom.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-template/pom.xml create mode 100644 jetty-ee10/jetty-ee10-demos/demo-template/src/main/resources/demo.css create mode 100644 jetty-ee10/jetty-ee10-demos/demo-template/src/main/resources/index.html create mode 100644 jetty-ee10/jetty-ee10-demos/demo-template/src/main/resources/small_powered_by.gif create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/pom.xml create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/prodDb.properties create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/prodDb.script create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/AsyncEchoServlet.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/DumpServlet.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/ExampleServer.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/ExampleServerXml.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/ExampleUtil.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/FastFileServer.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/FileServer.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/FileServerXml.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/HelloHandler.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/HelloServlet.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/HelloSessionServlet.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/HelloWorld.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/Http2Server.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/JarServer.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/JettyDemos.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/LikeJettyXml.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/ManyConnectors.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/ManyContexts.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/ManyHandlers.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/ManyServletContexts.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/MinimalServlets.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/OneConnector.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/OneContext.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/OneHandler.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/OneServletContext.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/OneServletContextJmxStats.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/OneServletContextWithSession.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/OneWebApp.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/OneWebAppWithJsp.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/ProxyServer.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/RewriteServer.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/SecuredHelloHandler.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/ServerWithAnnotations.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/ServerWithJMX.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/ServerWithJNDI.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/SimplestServer.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/SplitFileServer.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/WebSocketServer.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/other/content.jar create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/demo/demo-realm.properties create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/demo/webdefault.xml create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/push.html create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile00.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile01.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile02.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile03.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile04.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile05.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile06.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile07.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile08.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile09.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile10.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile11.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile12.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile13.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile14.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile15.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile16.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile17.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile18.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile19.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile20.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile21.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile22.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile23.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile24.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile25.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile26.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile27.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile28.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile29.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile30.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile31.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile32.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile33.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile34.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile35.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile36.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile37.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile38.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile39.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile40.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile41.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile42.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile43.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile44.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile45.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile46.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile47.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile48.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile49.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/readme.txt create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile00.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile01.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile02.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile03.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile04.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile05.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile06.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile07.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile08.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile09.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile10.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile11.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile12.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile13.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile14.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile15.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile16.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile17.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile18.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile19.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile20.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile21.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile22.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile23.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile24.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile25.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile26.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile27.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile28.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile29.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile30.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile31.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile32.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile33.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile34.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile35.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile36.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile37.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile38.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile39.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile40.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile41.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile42.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile43.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile44.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile45.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile46.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile47.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile48.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile49.jpg create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/etc/keystore.p12 create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/etc/realm.properties create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/exampleserver.xml create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/fileserver.xml create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/java-util-logging.properties create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/jetty-logging.properties create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/logback-access.xml create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/AbstractEmbeddedTest.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/ExampleServerTest.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/ExampleServerXmlTest.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/FastFileServerTest.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/FileServerTest.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/FileServerXmlTest.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/JarServerTest.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/LikeJettyXmlTest.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/ManyConnectorsTest.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/ManyContextsTest.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/ManyHandlersTest.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/ManyServletContextsTest.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/MinimalServletsTest.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/OneConnectorTest.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/OneContextTest.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/OneHandlerTest.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/OneServletContextJmxStatsTest.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/OneServletContextTest.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/OneServletContextWithSessionTest.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/OneWebAppTest.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/OneWebAppWithJspTest.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/ProxyServerTest.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/RewriteServerTest.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/SecuredHelloHandlerTest.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/ServerUtil.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/ServerWithAnnotationsTest.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/ServerWithJMXTest.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/ServerWithJNDITest.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/SimplestServerTest.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/SplitFileServerTest.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/WebSocketServerTest.java create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/test/resources/dir0/test0.txt create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/test/resources/dir1/test1.txt create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/test/resources/jetty-logging.properties create mode 100644 jetty-ee10/jetty-ee10-demos/embedded/src/test/resources/realm.properties create mode 100644 jetty-ee10/jetty-ee10-demos/pom.xml create mode 100644 jetty-ee10/jetty-ee10-glassfish-jstl/pom.xml create mode 100644 jetty-ee10/jetty-ee10-glassfish-jstl/src/main/config/modules/glassfish-jstl.mod create mode 100644 jetty-ee10/jetty-ee10-glassfish-jstl/src/main/resources/readme.txt create mode 100644 jetty-ee10/jetty-ee10-glassfish-jstl/src/test/java/org/eclipse/jetty/ee10/jstl/JspConfig.java create mode 100644 jetty-ee10/jetty-ee10-glassfish-jstl/src/test/java/org/eclipse/jetty/ee10/jstl/JspIncludeTest.java create mode 100644 jetty-ee10/jetty-ee10-glassfish-jstl/src/test/java/org/eclipse/jetty/ee10/jstl/JstlTest.java create mode 100644 jetty-ee10/jetty-ee10-glassfish-jstl/src/test/resources/jetty-logging.properties create mode 100644 jetty-ee10/jetty-ee10-glassfish-jstl/src/test/taglibjar/META-INF/etag.tld create mode 100644 jetty-ee10/jetty-ee10-glassfish-jstl/src/test/taglibjar/META-INF/tags/errorhandler.tag create mode 100644 jetty-ee10/jetty-ee10-glassfish-jstl/src/test/webapp/WEB-INF/lib/testtaglib.jar create mode 100644 jetty-ee10/jetty-ee10-glassfish-jstl/src/test/webapp/WEB-INF/web.xml create mode 100644 jetty-ee10/jetty-ee10-glassfish-jstl/src/test/webapp/catch-basic.jsp create mode 100644 jetty-ee10/jetty-ee10-glassfish-jstl/src/test/webapp/catch-taglib.jsp create mode 100644 jetty-ee10/jetty-ee10-glassfish-jstl/src/test/webapp/included.jsp create mode 100644 jetty-ee10/jetty-ee10-glassfish-jstl/src/test/webapp/ref.jsp create mode 100644 jetty-ee10/jetty-ee10-glassfish-jstl/src/test/webapp/top.jsp create mode 100644 jetty-ee10/jetty-ee10-glassfish-jstl/src/test/webapp/urls.jsp create mode 100644 jetty-ee10/jetty-ee10-http-spi/pom.xml create mode 100644 jetty-ee10/jetty-ee10-http-spi/src/main/java/module-info.java create mode 100644 jetty-ee10/jetty-ee10-http-spi/src/main/java/org/eclipse/jetty/ee10/http/spi/DelegatingThreadPool.java create mode 100644 jetty-ee10/jetty-ee10-http-spi/src/main/java/org/eclipse/jetty/ee10/http/spi/HttpSpiContextHandler.java create mode 100644 jetty-ee10/jetty-ee10-http-spi/src/main/java/org/eclipse/jetty/ee10/http/spi/JettyExchange.java create mode 100644 jetty-ee10/jetty-ee10-http-spi/src/main/java/org/eclipse/jetty/ee10/http/spi/JettyHttpContext.java create mode 100644 jetty-ee10/jetty-ee10-http-spi/src/main/java/org/eclipse/jetty/ee10/http/spi/JettyHttpExchange.java create mode 100644 jetty-ee10/jetty-ee10-http-spi/src/main/java/org/eclipse/jetty/ee10/http/spi/JettyHttpExchangeDelegate.java create mode 100644 jetty-ee10/jetty-ee10-http-spi/src/main/java/org/eclipse/jetty/ee10/http/spi/JettyHttpServer.java create mode 100644 jetty-ee10/jetty-ee10-http-spi/src/main/java/org/eclipse/jetty/ee10/http/spi/JettyHttpServerProvider.java create mode 100644 jetty-ee10/jetty-ee10-http-spi/src/main/java/org/eclipse/jetty/ee10/http/spi/JettyHttpsExchange.java create mode 100644 jetty-ee10/jetty-ee10-http-spi/src/main/resources/META-INF/services/com.sun.net.httpserver.spi.HttpServerProvider create mode 100644 jetty-ee10/jetty-ee10-http-spi/src/test/java/org/eclipse/jetty/ee10/http/spi/LoggingUtil.java create mode 100644 jetty-ee10/jetty-ee10-http-spi/src/test/java/org/eclipse/jetty/ee10/http/spi/SPIServerTest.java create mode 100644 jetty-ee10/jetty-ee10-http-spi/src/test/java/org/eclipse/jetty/ee10/http/spi/TestEndpointMultiplePublishProblem.java create mode 100644 jetty-ee10/jetty-ee10-http-spi/src/test/java/org/eclipse/jetty/ee10/http/spi/TestSPIServer.java create mode 100644 jetty-ee10/jetty-ee10-http-spi/src/test/java/org/eclipse/jetty/ee10/http/spi/util/Pool.java create mode 100644 jetty-ee10/jetty-ee10-http-spi/src/test/java/org/eclipse/jetty/ee10/http/spi/util/PrintTask.java create mode 100644 jetty-ee10/jetty-ee10-http-spi/src/test/java/org/eclipse/jetty/ee10/http/spi/util/SpiConstants.java create mode 100644 jetty-ee10/jetty-ee10-http-spi/src/test/resources/jetty-logging.properties create mode 100644 jetty-ee10/jetty-ee10-jaas/pom.xml create mode 100644 jetty-ee10/jetty-ee10-jaas/src/main/config/etc/jetty-jaas.xml create mode 100644 jetty-ee10/jetty-ee10-jaas/src/main/config/modules/jaas.mod create mode 100644 jetty-ee10/jetty-ee10-jaas/src/main/java/module-info.java create mode 100644 jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/JAASLoginService.java create mode 100644 jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/JAASPrincipal.java create mode 100644 jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/JAASRole.java create mode 100644 jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/JAASUserPrincipal.java create mode 100644 jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/PropertyUserStoreManager.java create mode 100644 jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/callback/AbstractCallbackHandler.java create mode 100644 jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/callback/DefaultCallbackHandler.java create mode 100644 jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/callback/ObjectCallback.java create mode 100644 jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/callback/RequestParameterCallback.java create mode 100644 jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/callback/ServletRequestCallback.java create mode 100644 jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/callback/package-info.java create mode 100644 jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/package-info.java create mode 100644 jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/spi/AbstractDatabaseLoginModule.java create mode 100644 jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/spi/AbstractLoginModule.java create mode 100644 jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/spi/DataSourceLoginModule.java create mode 100644 jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/spi/JDBCLoginModule.java create mode 100644 jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/spi/LdapLoginModule.java create mode 100644 jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/spi/PropertyFileLoginModule.java create mode 100644 jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/spi/package-info.java create mode 100644 jetty-ee10/jetty-ee10-jaas/src/test/java/org/eclipse/jetty/ee10/jaas/JAASLdapLoginServiceTest.java create mode 100644 jetty-ee10/jetty-ee10-jaas/src/test/java/org/eclipse/jetty/ee10/jaas/JAASLoginServiceTest.java create mode 100644 jetty-ee10/jetty-ee10-jaas/src/test/java/org/eclipse/jetty/ee10/jaas/TestLoginModule.java create mode 100644 jetty-ee10/jetty-ee10-jaas/src/test/java/org/eclipse/jetty/ee10/jaas/TestServlet.java create mode 100644 jetty-ee10/jetty-ee10-jaas/src/test/java/org/eclipse/jetty/ee10/jaas/spi/PropertyFileLoginModuleTest.java create mode 100644 jetty-ee10/jetty-ee10-jaas/src/test/resources/jetty-logging.properties create mode 100644 jetty-ee10/jetty-ee10-jaas/src/test/resources/login.properties create mode 100644 jetty-ee10/jetty-ee10-jaspi/pom.xml create mode 100644 jetty-ee10/jetty-ee10-jaspi/src/main/config/etc/jaspi/jaspi-authmoduleconfig.xml create mode 100644 jetty-ee10/jetty-ee10-jaspi/src/main/config/etc/jaspi/jaspi-default.xml create mode 100644 jetty-ee10/jetty-ee10-jaspi/src/main/config/etc/jaspi/jaspi-demo.xml create mode 100644 jetty-ee10/jetty-ee10-jaspi/src/main/config/modules/jaspi-default-auth-config-factory.mod create mode 100644 jetty-ee10/jetty-ee10-jaspi/src/main/config/modules/jaspi-demo.mod create mode 100644 jetty-ee10/jetty-ee10-jaspi/src/main/config/modules/jaspi.mod create mode 100644 jetty-ee10/jetty-ee10-jaspi/src/main/java/module-info.java create mode 100644 jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/DefaultAuthConfigFactory.java create mode 100644 jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/JaspiAuthenticator.java create mode 100644 jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/JaspiAuthenticatorFactory.java create mode 100644 jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/JaspiMessageInfo.java create mode 100644 jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/ServletCallbackHandler.java create mode 100644 jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/SimpleAuthConfig.java create mode 100644 jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/callback/CredentialValidationCallback.java create mode 100644 jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/callback/package-info.java create mode 100644 jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/modules/BaseAuthModule.java create mode 100644 jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/modules/BasicAuthenticationAuthModule.java create mode 100644 jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/modules/package-info.java create mode 100644 jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/package-info.java create mode 100644 jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/provider/JaspiAuthConfigProvider.java create mode 100644 jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/provider/SimpleAuthConfig.java create mode 100644 jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/provider/SimpleServerAuthContext.java create mode 100644 jetty-ee10/jetty-ee10-jaspi/src/main/resources/META-INF/services/org.eclipse.jetty.security.Authenticator$Factory create mode 100644 jetty-ee10/jetty-ee10-jaspi/src/test/java/org/eclipse/jetty/ee10/security/jaspi/DefaultAuthConfigFactoryTest.java create mode 100644 jetty-ee10/jetty-ee10-jaspi/src/test/java/org/eclipse/jetty/ee10/security/jaspi/HttpHeaderAuthModule.java create mode 100644 jetty-ee10/jetty-ee10-jaspi/src/test/java/org/eclipse/jetty/ee10/security/jaspi/JaspiTest.java create mode 100644 jetty-ee10/jetty-ee10-jspc-maven-plugin/pom.xml create mode 100644 jetty-ee10/jetty-ee10-jspc-maven-plugin/src/it/package-root/invoker.properties create mode 100644 jetty-ee10/jetty-ee10-jspc-maven-plugin/src/it/package-root/pom.xml create mode 100644 jetty-ee10/jetty-ee10-jspc-maven-plugin/src/it/package-root/postbuild.groovy create mode 100644 jetty-ee10/jetty-ee10-jspc-maven-plugin/src/it/package-root/src/main/webapp/foo.jsp create mode 100644 jetty-ee10/jetty-ee10-jspc-maven-plugin/src/it/settings.xml create mode 100644 jetty-ee10/jetty-ee10-jspc-maven-plugin/src/it/simple-jsp-fail/invoker.properties create mode 100644 jetty-ee10/jetty-ee10-jspc-maven-plugin/src/it/simple-jsp-fail/pom.xml create mode 100644 jetty-ee10/jetty-ee10-jspc-maven-plugin/src/it/simple-jsp-fail/postbuild.groovy create mode 100644 jetty-ee10/jetty-ee10-jspc-maven-plugin/src/it/simple-jsp-fail/src/main/jsp/foo.jsp create mode 100644 jetty-ee10/jetty-ee10-jspc-maven-plugin/src/it/simple-jsp/invoker.properties create mode 100644 jetty-ee10/jetty-ee10-jspc-maven-plugin/src/it/simple-jsp/pom.xml create mode 100644 jetty-ee10/jetty-ee10-jspc-maven-plugin/src/it/simple-jsp/postbuild.groovy create mode 100644 jetty-ee10/jetty-ee10-jspc-maven-plugin/src/it/simple-jsp/src/main/webapp/foo.jsp create mode 100644 jetty-ee10/jetty-ee10-jspc-maven-plugin/src/main/java/org/eclipse/jetty/ee10/jspc/plugin/JspcMojo.java create mode 100644 jetty-ee10/jetty-ee10-jspc-maven-plugin/src/main/java/org/eclipse/jetty/ee10/jspc/plugin/package-info.java create mode 100644 jetty-ee10/jetty-ee10-jspc-maven-plugin/src/main/resources/META-INF/m2e/lifecycle-mapping-metadata.xml create mode 100644 jetty-ee10/jetty-ee10-jspc-maven-plugin/src/site/markdown/index.md create mode 100644 jetty-ee10/jetty-ee10-jspc-maven-plugin/src/site/site.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/README_INTEGRATION_TEST.md create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/pom.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/it-parent-pom/invoker.properties create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/it-parent-pom/pom.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-cdi-start-forked/invoker.properties create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-cdi-start-forked/pom.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-cdi-start-forked/postbuild.groovy create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-cdi-start-forked/src/main/java/test/Greeter.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-cdi-start-forked/src/main/jetty/jetty-context.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-cdi-start-forked/src/main/jetty/jetty.xml create mode 100755 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/api/pom.xml create mode 100755 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/api/src/main/java/test/Api.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/invoker.properties create mode 100755 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/pom.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/postbuild.groovy create mode 100755 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/web/pom.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/web/src/config/jetty.xml create mode 100755 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/web/src/main/java/test/ClassLoadingTestingServletContextListener.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyLibrary/pom.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyLibrary/src/main/java/jettyissue/MyAnnotation.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyLibrary/src/main/java/jettyissue/MyServletContainerInitializer.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyLibrary/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/pom.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/src/config/context.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/src/config/jetty.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/src/main/java/jettyissue/NormalClass.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/src/main/webapp/index.html create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/invoker.properties create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/pom.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/postbuild.groovy create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-distro-mojo-it/invoker.properties create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-base/pom.xml create mode 100644 jetty-ee10/jetty-ee10-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 create mode 100644 jetty-ee10/jetty-ee10-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 create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-base/src/main/resources/META-INF/web-fragment.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-webapp/pom.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-webapp/src/base/etc/test-jetty.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-webapp/src/base/modules/testmod.mod create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-webapp/src/main/webapp/WEB-INF/web.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-distro-mojo-it/pom.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-distro-mojo-it/postbuild.groovy create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-forked-mojo-it/invoker.properties create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-base/pom.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-base/src/main/java/org/eclipse/jetty/its/jetty_start_forked/Counter.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-base/src/main/java/org/eclipse/jetty/its/jetty_start_forked/HelloServlet.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-base/src/main/java/org/eclipse/jetty/its/jetty_start_forked/PingServlet.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-base/src/main/resources/META-INF/web-fragment.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-webapp/pom.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-webapp/src/config/jetty.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-webapp/src/main/webapp/WEB-INF/web.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-webapp/src/main/webapp/jsp/bean1.jsp create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-forked-mojo-it/pom.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-forked-mojo-it/postbuild.groovy create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-client/pom.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-client/src/main/java/org/olamy/App.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-client/src/main/module.gwt.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-server/pom.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-server/src/config/jetty.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-server/src/main/java/org/olamy/GreetingServiceImpl.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-server/src/main/jettyconf/context.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-server/src/main/webapp/WEB-INF/web.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-server/src/main/webapp/beer.css create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-server/src/main/webapp/favicon.ico create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-server/src/main/webapp/index.html create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-shared/pom.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-shared/src/main/java/org/olamy/FieldVerifier.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-shared/src/main/java/org/olamy/GreetingResponse.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-shared/src/main/java/org/olamy/GreetingService.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-shared/src/main/java/org/olamy/GreetingServiceAsync.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/invoker.properties create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/pom.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/postbuild.groovy create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-it/invoker.properties create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-base/pom.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-base/src/main/java/org/eclipse/jetty/its/jetty_start_mojo_it/Counter.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-base/src/main/java/org/eclipse/jetty/its/jetty_start_mojo_it/HelloServlet.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-base/src/main/java/org/eclipse/jetty/its/jetty_start_mojo_it/PingServlet.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-base/src/main/resources/META-INF/web-fragment.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-webapp/pom.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-webapp/src/config/context.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-webapp/src/config/jetty.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-webapp/src/main/webapp/WEB-INF/web.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-webapp/src/main/webapp/jsp/bean1.jsp create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-it/pom.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-it/postbuild.groovy create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/common/pom.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/common/src/main/java/mca/common/CommonService.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/invoker.properties create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/module/module-api/pom.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/module/module-api/src/main/java/mca/module/ModuleApi.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/module/module-impl/pom.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/module/module-impl/src/main/java/mca/module/ModuleImpl.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/module/pom.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/pom.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/postbuild.groovy create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/webapp-war/pom.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/webapp-war/src/config/jetty.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/webapp-war/src/main/java/mca/webapp/WebAppServletListener.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/webapp-war/src/main/webapp/WEB-INF/web.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-distro-mojo-it/invoker.properties create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-distro-mojo-it/jetty-simple-base/pom.xml create mode 100644 jetty-ee10/jetty-ee10-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 create mode 100644 jetty-ee10/jetty-ee10-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 create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-distro-mojo-it/jetty-simple-base/src/main/resources/META-INF/web-fragment.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-distro-mojo-it/jetty-simple-webapp/pom.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-distro-mojo-it/jetty-simple-webapp/src/base/etc/test-jetty.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-distro-mojo-it/jetty-simple-webapp/src/base/modules/testmod.mod create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-distro-mojo-it/jetty-simple-webapp/src/main/webapp/WEB-INF/web.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-distro-mojo-it/pom.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-distro-mojo-it/postbuild.groovy create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-forked-mojo-it/invoker.properties create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-forked-mojo-it/jetty-simple-base/pom.xml create mode 100644 jetty-ee10/jetty-ee10-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 create mode 100644 jetty-ee10/jetty-ee10-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 create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-forked-mojo-it/jetty-simple-base/src/main/resources/META-INF/web-fragment.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-forked-mojo-it/jetty-simple-webapp/pom.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-forked-mojo-it/jetty-simple-webapp/src/config/jetty.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-forked-mojo-it/jetty-simple-webapp/src/main/webapp/WEB-INF/web.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-forked-mojo-it/pom.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-forked-mojo-it/postbuild.groovy create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-mojo-it/invoker.properties create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-mojo-it/pom.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-mojo-it/postbuild.groovy create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-mojo-it/src/config/jetty.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/it/settings.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/AbstractForker.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/AbstractUnassembledWebAppMojo.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/AbstractWebAppMojo.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/ConsoleReader.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/JettyEffectiveWebXml.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/JettyEmbedder.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/JettyForkedChild.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/JettyForker.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/JettyHomeForker.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/JettyRunMojo.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/JettyRunWarMojo.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/JettyStartMojo.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/JettyStartWarMojo.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/JettyStopMojo.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/MavenMetaInfConfiguration.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/MavenQuickStartConfiguration.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/MavenServerConnector.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/MavenWebAppContext.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/MavenWebInfConfiguration.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/Overlay.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/OverlayConfig.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/OverlayManager.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/PluginLog.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/QuickStartGenerator.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/ScanPattern.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/ScanTargetPattern.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/SelectiveJarResource.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/ServerConnectorListener.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/ServerListener.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/ServerSupport.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/WarPluginInfo.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/WebAppPropertyConverter.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/package-info.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/utils/MavenProjectHelper.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/main/resources/META-INF/services/org.eclipse.jetty.ee10.webapp.Configuration create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/main/resources/jetty-maven.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/main/resources/maven.mod create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/main/resources/maven.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/site/markdown/index.md create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/site/site.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/test/java/org/eclipse/jetty/ee10/maven/plugin/MockShutdownMonitor.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/test/java/org/eclipse/jetty/ee10/maven/plugin/MockShutdownMonitorRunnable.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/test/java/org/eclipse/jetty/ee10/maven/plugin/TestForkedChild.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/test/java/org/eclipse/jetty/ee10/maven/plugin/TestJettyEmbedder.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/test/java/org/eclipse/jetty/ee10/maven/plugin/TestJettyStopMojo.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/test/java/org/eclipse/jetty/ee10/maven/plugin/TestQuickStartGenerator.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/test/java/org/eclipse/jetty/ee10/maven/plugin/TestSelectiveJarResource.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/test/java/org/eclipse/jetty/ee10/maven/plugin/TestWebAppPropertyConverter.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/test/java/org/eclipse/jetty/ee10/maven/plugin/it/IntegrationTestGetContent.java create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/test/resources/embedder-context.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/test/resources/embedder-jetty.xml create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/test/resources/jetty-logging.properties create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/test/resources/root/index.html create mode 100644 jetty-ee10/jetty-ee10-maven-plugin/src/test/resources/selective-jar-test.jar create mode 100644 jetty-ee10/jetty-ee10-openid/pom.xml create mode 100644 jetty-ee10/jetty-ee10-openid/src/main/config/etc/jetty-openid.xml create mode 100644 jetty-ee10/jetty-ee10-openid/src/main/config/modules/openid.mod create mode 100644 jetty-ee10/jetty-ee10-openid/src/main/config/modules/openid/openid-baseloginservice.xml create mode 100644 jetty-ee10/jetty-ee10-openid/src/main/java/module-info.java create mode 100644 jetty-ee10/jetty-ee10-openid/src/main/java/org/eclipse/jetty/ee10/security/openid/JwtDecoder.java create mode 100644 jetty-ee10/jetty-ee10-openid/src/main/java/org/eclipse/jetty/ee10/security/openid/OpenIdAuthConfiguration.java create mode 100644 jetty-ee10/jetty-ee10-openid/src/main/java/org/eclipse/jetty/ee10/security/openid/OpenIdAuthenticator.java create mode 100644 jetty-ee10/jetty-ee10-openid/src/main/java/org/eclipse/jetty/ee10/security/openid/OpenIdAuthenticatorFactory.java create mode 100644 jetty-ee10/jetty-ee10-openid/src/main/java/org/eclipse/jetty/ee10/security/openid/OpenIdConfiguration.java create mode 100644 jetty-ee10/jetty-ee10-openid/src/main/java/org/eclipse/jetty/ee10/security/openid/OpenIdCredentials.java create mode 100644 jetty-ee10/jetty-ee10-openid/src/main/java/org/eclipse/jetty/ee10/security/openid/OpenIdLoginService.java create mode 100644 jetty-ee10/jetty-ee10-openid/src/main/java/org/eclipse/jetty/ee10/security/openid/OpenIdUserIdentity.java create mode 100644 jetty-ee10/jetty-ee10-openid/src/main/java/org/eclipse/jetty/ee10/security/openid/OpenIdUserPrincipal.java create mode 100644 jetty-ee10/jetty-ee10-openid/src/main/resources/META-INF/services/org.eclipse.jetty.ee9.security.Authenticator$Factory create mode 100644 jetty-ee10/jetty-ee10-openid/src/main/resources/META-INF/services/org.eclipse.jetty.security.Authenticator$Factory create mode 100644 jetty-ee10/jetty-ee10-openid/src/test/java/org/eclipse/jetty/ee10/security/openid/JwtDecoderTest.java create mode 100644 jetty-ee10/jetty-ee10-openid/src/test/java/org/eclipse/jetty/ee10/security/openid/JwtEncoder.java create mode 100644 jetty-ee10/jetty-ee10-openid/src/test/java/org/eclipse/jetty/ee10/security/openid/OpenIdAuthenticationTest.java create mode 100644 jetty-ee10/jetty-ee10-openid/src/test/java/org/eclipse/jetty/ee10/security/openid/OpenIdCredentialsTest.java create mode 100644 jetty-ee10/jetty-ee10-openid/src/test/java/org/eclipse/jetty/ee10/security/openid/OpenIdProvider.java create mode 100644 jetty-ee10/jetty-ee10-openid/src/test/java/org/eclipse/jetty/ee10/security/openid/OpenIdReamNameTest.java create mode 100755 jetty-ee10/jetty-ee10-openid/src/test/resources/jetty-logging.properties create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-alpn/pom.xml create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot-jsp/pom.xml create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot-jsp/src/main/java/org/eclipse/jetty/ee10/osgi/boot/jasper/ContainerTldBundleDiscoverer.java create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot-jsp/src/main/java/org/eclipse/jetty/ee10/osgi/boot/jasper/JSTLBundleDiscoverer.java create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot-jsp/src/main/java/org/eclipse/jetty/ee10/osgi/boot/jsp/FragmentActivator.java create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot-warurl/pom.xml create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot-warurl/src/main/java/org/eclipse/jetty/ee10/osgi/boot/warurl/WarUrlActivator.java create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot-warurl/src/main/java/org/eclipse/jetty/ee10/osgi/boot/warurl/WarUrlStreamHandler.java create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot-warurl/src/main/java/org/eclipse/jetty/ee10/osgi/boot/warurl/internal/WarBundleManifestGenerator.java create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot-warurl/src/main/java/org/eclipse/jetty/ee10/osgi/boot/warurl/internal/WarURLConnection.java create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/jettyhome/contexts/README create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/jettyhome/etc/README create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/jettyhome/etc/jetty-deploy.xml create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/jettyhome/etc/jetty-http.xml create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/jettyhome/etc/jetty.xml create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/jettyhome/lib/ext/README create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/jettyhome/logs/README create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/jettyhome/resources/README create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/jettyhome/webapps/README create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/pom.xml create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/annotations/AnnotationConfiguration.java create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/annotations/AnnotationParser.java create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/AbstractContextProvider.java create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/AbstractOSGiApp.java create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/AbstractWebAppProvider.java create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/BundleContextProvider.java create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/BundleProvider.java create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/BundleWebAppProvider.java create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/JettyBootstrapActivator.java create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/OSGiDeployer.java create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/OSGiMetaInfConfiguration.java create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/OSGiServerConstants.java create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/OSGiUndeployer.java create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/OSGiWebInfConfiguration.java create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/OSGiWebappConstants.java create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/ServiceContextProvider.java create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/ServiceProvider.java create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/ServiceWebAppProvider.java create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/internal/serverfactory/DefaultJettyAtJettyHomeHelper.java create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/internal/serverfactory/JettyServerServiceTracker.java create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/internal/serverfactory/ServerInstanceWrapper.java create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/internal/webapp/LibExtClassLoaderHelper.java create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/internal/webapp/OSGiWebappClassLoader.java create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/BundleClassLoaderHelper.java create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/BundleClassLoaderHelperFactory.java create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/BundleFileLocatorHelper.java create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/BundleFileLocatorHelperFactory.java create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/EventSender.java create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/FakeURLClassLoader.java create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/OSGiClassLoader.java create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/ServerConnectorListener.java create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/TldBundleDiscoverer.java create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/Util.java create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/internal/DefaultBundleClassLoaderHelper.java create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/internal/DefaultFileLocatorHelper.java create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/internal/PackageAdminServiceTracker.java create mode 100644 jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/resources/META-INF/services/org.eclipse.jetty.ee10.webapp.Configuration create mode 100644 jetty-ee10/jetty-ee10-osgi/pom.xml create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-context/pom.xml create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-context/src/main/context/acme.xml create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-context/src/main/java/com/acme/osgi/Activator.java create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-context/src/main/resources/static/index.html create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-fragment/pom.xml create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-fragment/src/main/resources/frag.html create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-server/pom.xml create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-server/src/main/java/com/acme/osgi/Activator.java create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-server/src/main/resources/index.html create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-webapp-resources/pom.xml create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-webapp-resources/src/main/java/com/acme/HelloWorld.java create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-webapp-resources/src/main/resources/fake.properties create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-webapp-resources/src/main/webapp/WEB-INF/web.xml create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-webapp/pom.xml create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-webapp/src/main/java/com/acme/osgi/Activator.java create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-webapp/src/main/resources/index.html create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-webapp/src/main/resources/webappA/index.html create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-webapp/src/main/resources/webappB/index.html create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/README.txt create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/pom.xml create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-alpn.xml create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-deploy.xml create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http-boot-context-as-service.xml create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http-boot-webapp-as-service.xml create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http-boot-with-annotations.xml create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http-boot-with-bundle.xml create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http-boot-with-jakarta-websocket.xml create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http-boot-with-jsp.xml create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http-boot-with-resources.xml create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http-boot-with-websocket.xml create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http.xml create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http2-jdk9.xml create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http2.xml create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-https.xml create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-ssl.xml create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-testrealm.xml create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-with-custom-class.xml create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty.xml create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/keystore.p12 create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/realm.properties create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/webdefault.xml create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/java/org/eclipse/jetty/ee10/osgi/test/SimpleEchoSocket.java create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/java/org/eclipse/jetty/ee10/osgi/test/SimpleJakartaWebSocket.java create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/java/org/eclipse/jetty/ee10/osgi/test/SomeCustomBean.java create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/java/org/eclipse/jetty/ee10/osgi/test/TestJettyOSGiAnnotationParser.java create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/java/org/eclipse/jetty/ee10/osgi/test/TestJettyOSGiBootContextAsService.java create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/java/org/eclipse/jetty/ee10/osgi/test/TestJettyOSGiBootHTTP2Conscrypt.java create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/java/org/eclipse/jetty/ee10/osgi/test/TestJettyOSGiBootHTTP2JDK9.java create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/java/org/eclipse/jetty/ee10/osgi/test/TestJettyOSGiBootWebAppAsService.java create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/java/org/eclipse/jetty/ee10/osgi/test/TestJettyOSGiBootWithAnnotations.java create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/java/org/eclipse/jetty/ee10/osgi/test/TestJettyOSGiBootWithBundle.java create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/java/org/eclipse/jetty/ee10/osgi/test/TestJettyOSGiBootWithJakartaWebSocket.java create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/java/org/eclipse/jetty/ee10/osgi/test/TestJettyOSGiBootWithJsp.java create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/java/org/eclipse/jetty/ee10/osgi/test/TestJettyOSGiBootWithWebSocket.java create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/java/org/eclipse/jetty/ee10/osgi/test/TestJettyOSGiClasspathResources.java create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/java/org/eclipse/jetty/ee10/osgi/test/TestOSGiUtil.java create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/resources/jetty-logging.properties create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/resources/module-info.clazz create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/resources/module-info.java create mode 100644 jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/resources/simplelogger.properties create mode 100644 jetty-ee10/jetty-ee10-plus/pom.xml create mode 100644 jetty-ee10/jetty-ee10-plus/src/main/config/etc/jetty-plus.xml create mode 100644 jetty-ee10/jetty-ee10-plus/src/main/config/modules/plus.mod create mode 100644 jetty-ee10/jetty-ee10-plus/src/main/java/module-info.java create mode 100644 jetty-ee10/jetty-ee10-plus/src/main/java/org/eclipse/jetty/ee10/plus/annotation/ContainerInitializer.java create mode 100644 jetty-ee10/jetty-ee10-plus/src/main/java/org/eclipse/jetty/ee10/plus/annotation/Injection.java create mode 100644 jetty-ee10/jetty-ee10-plus/src/main/java/org/eclipse/jetty/ee10/plus/annotation/InjectionCollection.java create mode 100644 jetty-ee10/jetty-ee10-plus/src/main/java/org/eclipse/jetty/ee10/plus/annotation/LifeCycleCallback.java create mode 100644 jetty-ee10/jetty-ee10-plus/src/main/java/org/eclipse/jetty/ee10/plus/annotation/LifeCycleCallbackCollection.java create mode 100644 jetty-ee10/jetty-ee10-plus/src/main/java/org/eclipse/jetty/ee10/plus/annotation/PostConstructCallback.java create mode 100644 jetty-ee10/jetty-ee10-plus/src/main/java/org/eclipse/jetty/ee10/plus/annotation/PreDestroyCallback.java create mode 100644 jetty-ee10/jetty-ee10-plus/src/main/java/org/eclipse/jetty/ee10/plus/annotation/RunAs.java create mode 100644 jetty-ee10/jetty-ee10-plus/src/main/java/org/eclipse/jetty/ee10/plus/annotation/RunAsCollection.java create mode 100644 jetty-ee10/jetty-ee10-plus/src/main/java/org/eclipse/jetty/ee10/plus/annotation/package-info.java create mode 100644 jetty-ee10/jetty-ee10-plus/src/main/java/org/eclipse/jetty/ee10/plus/jndi/EnvEntry.java create mode 100644 jetty-ee10/jetty-ee10-plus/src/main/java/org/eclipse/jetty/ee10/plus/jndi/Link.java create mode 100644 jetty-ee10/jetty-ee10-plus/src/main/java/org/eclipse/jetty/ee10/plus/jndi/NamingDump.java create mode 100644 jetty-ee10/jetty-ee10-plus/src/main/java/org/eclipse/jetty/ee10/plus/jndi/NamingEntry.java create mode 100644 jetty-ee10/jetty-ee10-plus/src/main/java/org/eclipse/jetty/ee10/plus/jndi/NamingEntryUtil.java create mode 100644 jetty-ee10/jetty-ee10-plus/src/main/java/org/eclipse/jetty/ee10/plus/jndi/Resource.java create mode 100644 jetty-ee10/jetty-ee10-plus/src/main/java/org/eclipse/jetty/ee10/plus/jndi/Transaction.java create mode 100644 jetty-ee10/jetty-ee10-plus/src/main/java/org/eclipse/jetty/ee10/plus/jndi/package-info.java create mode 100644 jetty-ee10/jetty-ee10-plus/src/main/java/org/eclipse/jetty/ee10/plus/security/DataSourceLoginService.java create mode 100644 jetty-ee10/jetty-ee10-plus/src/main/java/org/eclipse/jetty/ee10/plus/security/package-info.java create mode 100644 jetty-ee10/jetty-ee10-plus/src/main/java/org/eclipse/jetty/ee10/plus/webapp/EnvConfiguration.java create mode 100644 jetty-ee10/jetty-ee10-plus/src/main/java/org/eclipse/jetty/ee10/plus/webapp/PlusConfiguration.java create mode 100644 jetty-ee10/jetty-ee10-plus/src/main/java/org/eclipse/jetty/ee10/plus/webapp/PlusDecorator.java create mode 100644 jetty-ee10/jetty-ee10-plus/src/main/java/org/eclipse/jetty/ee10/plus/webapp/PlusDescriptorProcessor.java create mode 100644 jetty-ee10/jetty-ee10-plus/src/main/java/org/eclipse/jetty/ee10/plus/webapp/package-info.java create mode 100644 jetty-ee10/jetty-ee10-plus/src/main/resources/META-INF/services/org.eclipse.jetty.ee10.webapp.Configuration create mode 100644 jetty-ee10/jetty-ee10-plus/src/test/java/org/eclipse/jetty/ee10/plus/annotation/LifeCycleCallbackCollectionTest.java create mode 100644 jetty-ee10/jetty-ee10-plus/src/test/java/org/eclipse/jetty/ee10/plus/jndi/NamingEntryUtilTest.java create mode 100644 jetty-ee10/jetty-ee10-plus/src/test/java/org/eclipse/jetty/ee10/plus/jndi/TestNamingEntries.java create mode 100644 jetty-ee10/jetty-ee10-plus/src/test/java/org/eclipse/jetty/ee10/plus/jndi/TestNamingEntryUtil.java create mode 100644 jetty-ee10/jetty-ee10-plus/src/test/java/org/eclipse/jetty/ee10/plus/webapp/PlusDescriptorProcessorTest.java create mode 100644 jetty-ee10/jetty-ee10-plus/src/test/resources/web-fragment-1.xml create mode 100644 jetty-ee10/jetty-ee10-plus/src/test/resources/web-fragment-2.xml create mode 100644 jetty-ee10/jetty-ee10-plus/src/test/resources/web-fragment-3.xml create mode 100644 jetty-ee10/jetty-ee10-plus/src/test/resources/web-fragment-4.xml create mode 100644 jetty-ee10/jetty-ee10-plus/src/test/resources/web.xml create mode 100644 jetty-ee10/jetty-ee10-proxy/pom.xml create mode 100644 jetty-ee10/jetty-ee10-proxy/src/main/config/etc/jetty-proxy.xml create mode 100644 jetty-ee10/jetty-ee10-proxy/src/main/config/modules/proxy.mod create mode 100644 jetty-ee10/jetty-ee10-proxy/src/main/java/module-info.java create mode 100644 jetty-ee10/jetty-ee10-proxy/src/main/java/org/eclipse/jetty/ee10/proxy/AbstractProxyServlet.java create mode 100644 jetty-ee10/jetty-ee10-proxy/src/main/java/org/eclipse/jetty/ee10/proxy/AfterContentTransformer.java create mode 100644 jetty-ee10/jetty-ee10-proxy/src/main/java/org/eclipse/jetty/ee10/proxy/AsyncMiddleManServlet.java create mode 100644 jetty-ee10/jetty-ee10-proxy/src/main/java/org/eclipse/jetty/ee10/proxy/AsyncProxyServlet.java create mode 100644 jetty-ee10/jetty-ee10-proxy/src/main/java/org/eclipse/jetty/ee10/proxy/BalancerServlet.java create mode 100644 jetty-ee10/jetty-ee10-proxy/src/main/java/org/eclipse/jetty/ee10/proxy/ConnectHandler.java create mode 100644 jetty-ee10/jetty-ee10-proxy/src/main/java/org/eclipse/jetty/ee10/proxy/ProxyConnection.java create mode 100644 jetty-ee10/jetty-ee10-proxy/src/main/java/org/eclipse/jetty/ee10/proxy/ProxyServlet.java create mode 100644 jetty-ee10/jetty-ee10-proxy/src/main/java/org/eclipse/jetty/ee10/proxy/package-info.java create mode 100644 jetty-ee10/jetty-ee10-proxy/src/test/java/org/eclipse/jetty/ee10/proxy/AbstractConnectHandlerTest.java create mode 100644 jetty-ee10/jetty-ee10-proxy/src/test/java/org/eclipse/jetty/ee10/proxy/AsyncMiddleManServletTest.java create mode 100644 jetty-ee10/jetty-ee10-proxy/src/test/java/org/eclipse/jetty/ee10/proxy/BalancerServletTest.java create mode 100644 jetty-ee10/jetty-ee10-proxy/src/test/java/org/eclipse/jetty/ee10/proxy/CachingProxyServlet.java create mode 100644 jetty-ee10/jetty-ee10-proxy/src/test/java/org/eclipse/jetty/ee10/proxy/ClientAuthProxyTest.java create mode 100644 jetty-ee10/jetty-ee10-proxy/src/test/java/org/eclipse/jetty/ee10/proxy/ConnectHandlerSSLTest.java create mode 100644 jetty-ee10/jetty-ee10-proxy/src/test/java/org/eclipse/jetty/ee10/proxy/ConnectHandlerTest.java create mode 100644 jetty-ee10/jetty-ee10-proxy/src/test/java/org/eclipse/jetty/ee10/proxy/EchoHttpServlet.java create mode 100644 jetty-ee10/jetty-ee10-proxy/src/test/java/org/eclipse/jetty/ee10/proxy/EmptyHttpServlet.java create mode 100644 jetty-ee10/jetty-ee10-proxy/src/test/java/org/eclipse/jetty/ee10/proxy/EmptyServerHandler.java create mode 100644 jetty-ee10/jetty-ee10-proxy/src/test/java/org/eclipse/jetty/ee10/proxy/ForwardProxyServerTest.java create mode 100644 jetty-ee10/jetty-ee10-proxy/src/test/java/org/eclipse/jetty/ee10/proxy/ForwardProxyTLSServerTest.java create mode 100644 jetty-ee10/jetty-ee10-proxy/src/test/java/org/eclipse/jetty/ee10/proxy/ProxyServer.java create mode 100644 jetty-ee10/jetty-ee10-proxy/src/test/java/org/eclipse/jetty/ee10/proxy/ProxyServletFailureTest.java create mode 100644 jetty-ee10/jetty-ee10-proxy/src/test/java/org/eclipse/jetty/ee10/proxy/ProxyServletLoadTest.java create mode 100644 jetty-ee10/jetty-ee10-proxy/src/test/java/org/eclipse/jetty/ee10/proxy/ProxyServletTest.java create mode 100644 jetty-ee10/jetty-ee10-proxy/src/test/java/org/eclipse/jetty/ee10/proxy/ReverseProxyTest.java create mode 100644 jetty-ee10/jetty-ee10-proxy/src/test/resources/client_auth/client_keystore.p12 create mode 100644 jetty-ee10/jetty-ee10-proxy/src/test/resources/client_auth/proxy_keystore.p12 create mode 100644 jetty-ee10/jetty-ee10-proxy/src/test/resources/client_auth/server_keystore.p12 create mode 100644 jetty-ee10/jetty-ee10-proxy/src/test/resources/client_keystore.p12 create mode 100644 jetty-ee10/jetty-ee10-proxy/src/test/resources/client_proxy_keystore.p12 create mode 100644 jetty-ee10/jetty-ee10-proxy/src/test/resources/client_server_keystore.p12 create mode 100644 jetty-ee10/jetty-ee10-proxy/src/test/resources/jetty-logging.properties create mode 100644 jetty-ee10/jetty-ee10-proxy/src/test/resources/proxy_keystore.p12 create mode 100644 jetty-ee10/jetty-ee10-proxy/src/test/resources/readme_keystores.txt create mode 100644 jetty-ee10/jetty-ee10-proxy/src/test/resources/server_keystore.p12 create mode 100644 jetty-ee10/jetty-ee10-quickstart/pom.xml create mode 100644 jetty-ee10/jetty-ee10-quickstart/src/main/config/etc/jetty-quickstart.xml create mode 100644 jetty-ee10/jetty-ee10-quickstart/src/main/config/modules/jetty-quickstart.d/quickstart-webapp.xml create mode 100644 jetty-ee10/jetty-ee10-quickstart/src/main/config/modules/quickstart.mod create mode 100644 jetty-ee10/jetty-ee10-quickstart/src/main/java/module-info.java create mode 100644 jetty-ee10/jetty-ee10-quickstart/src/main/java/org/eclipse/jetty/ee10/quickstart/AttributeNormalizer.java create mode 100644 jetty-ee10/jetty-ee10-quickstart/src/main/java/org/eclipse/jetty/ee10/quickstart/ExtraXmlDescriptorProcessor.java create mode 100644 jetty-ee10/jetty-ee10-quickstart/src/main/java/org/eclipse/jetty/ee10/quickstart/PreconfigureQuickStartWar.java create mode 100644 jetty-ee10/jetty-ee10-quickstart/src/main/java/org/eclipse/jetty/ee10/quickstart/QuickStartConfiguration.java create mode 100644 jetty-ee10/jetty-ee10-quickstart/src/main/java/org/eclipse/jetty/ee10/quickstart/QuickStartDescriptorProcessor.java create mode 100644 jetty-ee10/jetty-ee10-quickstart/src/main/java/org/eclipse/jetty/ee10/quickstart/QuickStartGeneratorConfiguration.java create mode 100644 jetty-ee10/jetty-ee10-quickstart/src/main/resources/META-INF/services/org.eclipse.jetty.ee10.webapp.Configuration create mode 100644 jetty-ee10/jetty-ee10-quickstart/src/test/java/org/eclipse/jetty/ee10/quickstart/FooContextListener.java create mode 100644 jetty-ee10/jetty-ee10-quickstart/src/test/java/org/eclipse/jetty/ee10/quickstart/FooFilter.java create mode 100644 jetty-ee10/jetty-ee10-quickstart/src/test/java/org/eclipse/jetty/ee10/quickstart/FooServlet.java create mode 100644 jetty-ee10/jetty-ee10-quickstart/src/test/java/org/eclipse/jetty/ee10/quickstart/TestQuickStart.java create mode 100644 jetty-ee10/jetty-ee10-quickstart/src/test/resources/context.xml create mode 100644 jetty-ee10/jetty-ee10-quickstart/src/test/resources/web.xml create mode 100644 jetty-ee10/jetty-ee10-runner/pom.xml create mode 100644 jetty-ee10/jetty-ee10-runner/src/it/demo-simple-webapp-runner-with-path/invoker.properties create mode 100644 jetty-ee10/jetty-ee10-runner/src/it/demo-simple-webapp-runner-with-path/pom.xml create mode 100644 jetty-ee10/jetty-ee10-runner/src/it/demo-simple-webapp-runner/invoker.properties create mode 100644 jetty-ee10/jetty-ee10-runner/src/it/demo-simple-webapp-runner/pom.xml create mode 100644 jetty-ee10/jetty-ee10-runner/src/it/settings.xml create mode 100644 jetty-ee10/jetty-ee10-runner/src/it/test-jar-manifest/invoker.properties create mode 100644 jetty-ee10/jetty-ee10-runner/src/it/test-jar-manifest/pom.xml create mode 100644 jetty-ee10/jetty-ee10-runner/src/it/test-jar-manifest/postbuild.groovy create mode 100644 jetty-ee10/jetty-ee10-runner/src/main/java/org/eclipse/jetty/ee10/runner/Runner.java create mode 100644 jetty-ee10/jetty-ee10-runner/src/main/java/org/eclipse/jetty/ee10/runner/package-info.java create mode 100644 jetty-ee10/jetty-ee10-runner/src/main/resources/META-INF/MANIFEST.MF create mode 100644 jetty-ee10/jetty-ee10-runner/src/main/resources/jetty-logging.properties create mode 100644 jetty-ee10/jetty-ee10-runner/src/test/java/org/eclipse/jetty/ee10/maven/jettyrunner/it/IntegrationTestJettyRunner.java create mode 100644 jetty-ee10/jetty-ee10-servlet/pom.xml create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/config/modules/security.mod create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/config/modules/servlet.mod create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/module-info.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/AsyncContentProducer.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/AsyncContextEvent.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/AsyncContextState.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/BaseHolder.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/BlockingContentProducer.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ContentProducer.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/DebugListener.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/DecoratingListener.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/DefaultServlet.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/Dispatcher.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ErrorHandler.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ErrorPageErrorHandler.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/FilterHolder.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/FilterMapping.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/Holder.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/HttpInput.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/HttpOutput.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/Invoker.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/JspPropertyGroupServlet.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ListenerHolder.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ManagedAttributeListener.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/MultiPartFormInputStream.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/NoJspServlet.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/QuietServletException.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletChannel.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletContainerInitializerHolder.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletContextHandler.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletContextRequest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletContextResponse.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletHandler.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletHolder.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletMapping.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletPathMapping.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletRequestHttpWrapper.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletRequestState.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletResponseHttpWrapper.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletTester.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/SessionHandler.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/Source.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/StatisticsServlet.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/jmx/FilterMappingMBean.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/jmx/HolderMBean.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/jmx/ServletMappingMBean.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/jmx/package-info.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/listener/ContainerInitializer.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/listener/ELContextCleaner.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/listener/IntrospectorCleaner.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/listener/package-info.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/package-info.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/AbstractLoginService.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/AbstractUserAuthentication.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/Authentication.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/Authenticator.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/ConfigurableSpnegoLoginService.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/ConstraintAware.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/ConstraintMapping.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/ConstraintSecurityHandler.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/DefaultAuthenticatorFactory.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/DefaultIdentityService.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/DefaultUserIdentity.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/EmptyLoginService.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/HashLoginService.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/IdentityService.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/JDBCLoginService.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/LoggedOutAuthentication.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/LoginService.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/PropertyUserStore.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/RoleInfo.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/RolePrincipal.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/RoleRunAsToken.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/RunAsToken.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/SecurityHandler.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/ServerAuthException.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/SpnegoUserIdentity.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/SpnegoUserPrincipal.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/UserAuthentication.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/UserDataConstraint.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/UserIdentity.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/UserPrincipal.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/UserStore.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/WrappedAuthConfiguration.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/authentication/AuthorizationService.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/authentication/BasicAuthenticator.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/authentication/ClientCertAuthenticator.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/authentication/ConfigurableSpnegoAuthenticator.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/authentication/DeferredAuthentication.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/authentication/DigestAuthenticator.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/authentication/FormAuthenticator.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/authentication/LoginAuthenticator.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/authentication/LoginCallback.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/authentication/LoginCallbackImpl.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/authentication/SessionAuthentication.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/authentication/SslClientCertAuthenticator.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/authentication/package-info.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/package-info.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/util/ServletOutputStreamWrapper.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/writer/EncodingHttpWriter.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/writer/HttpWriter.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/writer/Iso88591HttpWriter.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/writer/ResponseWriter.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/writer/Utf8HttpWriter.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/AsyncContextDispatchWithQueryStrings.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/AsyncContextListenersTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/AsyncContextTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/AsyncListenerTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/AsyncServletIOTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/AsyncServletLongPollTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/AsyncServletTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/CacheControlHeaderTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/ComplianceViolations2616Test.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/ComponentWrapTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/CustomRequestLogServletTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/DefaultServletRangesTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/DefaultServletTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/DispatcherForwardTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/DispatcherTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/EncodedURITest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/ErrorPageTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/FilterHolderTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/FormTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/GzipHandlerBreakEvenSizeTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/GzipHandlerCommitTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/GzipHandlerTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/IncludedServletTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/InitServletTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/InvokerTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/ListenerHolderTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/MultiPartServletTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/PostServletTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/RegexServletTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/RequestHeadersTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/RequestURITest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/ResponseHeadersTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/SSLAsyncIOServletTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/ServletContainerInitializerHolderTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/ServletContextHandlerTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/ServletContextResourcesTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/ServletHandlerTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/ServletHolderTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/ServletLifeCycleTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/ServletRequestLogTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/ServletUpgradeTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/ServletWrapperTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/SessionHandlerTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/StatisticsServletTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/security/AliasedConstraintTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/security/ClientCertAuthenticatorTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/security/ConstraintTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/security/DataConstraintsTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/security/DefaultIdentityServiceTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/security/HashLoginServiceTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/security/PropertyUserStoreTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/security/SessionAuthenticationTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/security/SpecExampleConstraintTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/security/TestLoginService.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/security/UnauthenticatedTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/security/UserStoreTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/security/authentication/SpnegoAuthenticatorTest.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/resources/Foo.clazz create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/resources/Foo.java create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/resources/cacerts.jks create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/resources/clientcert.jks create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/resources/contextResources/content.txt create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/resources/dispatchResourceTest/content.txt create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/resources/dispatchTest/dispatch.txt create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/resources/docroot/all/index.txt create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/resources/docroot/forbid/index.txt create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/resources/foo.properties create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/resources/jar-resource-odd.jar create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/resources/jetty-logging.properties create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/resources/keystore.p12 create mode 100644 jetty-ee10/jetty-ee10-servlet/src/test/resources/realm.properties create mode 100644 jetty-ee10/jetty-ee10-servlets/pom.xml create mode 100644 jetty-ee10/jetty-ee10-servlets/src/main/config/modules/servlets.mod create mode 100644 jetty-ee10/jetty-ee10-servlets/src/main/java/module-info.java create mode 100644 jetty-ee10/jetty-ee10-servlets/src/main/java/org/eclipse/jetty/ee10/servlets/CGI.java create mode 100644 jetty-ee10/jetty-ee10-servlets/src/main/java/org/eclipse/jetty/ee10/servlets/CloseableDoSFilter.java create mode 100644 jetty-ee10/jetty-ee10-servlets/src/main/java/org/eclipse/jetty/ee10/servlets/CrossOriginFilter.java create mode 100644 jetty-ee10/jetty-ee10-servlets/src/main/java/org/eclipse/jetty/ee10/servlets/DoSFilter.java create mode 100644 jetty-ee10/jetty-ee10-servlets/src/main/java/org/eclipse/jetty/ee10/servlets/EventSource.java create mode 100644 jetty-ee10/jetty-ee10-servlets/src/main/java/org/eclipse/jetty/ee10/servlets/EventSourceServlet.java create mode 100644 jetty-ee10/jetty-ee10-servlets/src/main/java/org/eclipse/jetty/ee10/servlets/HeaderFilter.java create mode 100644 jetty-ee10/jetty-ee10-servlets/src/main/java/org/eclipse/jetty/ee10/servlets/IncludeExcludeBasedFilter.java create mode 100644 jetty-ee10/jetty-ee10-servlets/src/main/java/org/eclipse/jetty/ee10/servlets/PushCacheFilter.java create mode 100644 jetty-ee10/jetty-ee10-servlets/src/main/java/org/eclipse/jetty/ee10/servlets/PushSessionCacheFilter.java create mode 100644 jetty-ee10/jetty-ee10-servlets/src/main/java/org/eclipse/jetty/ee10/servlets/QoSFilter.java create mode 100644 jetty-ee10/jetty-ee10-servlets/src/main/java/org/eclipse/jetty/ee10/servlets/package-info.java create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/java/org/eclipse/jetty/ee10/servlets/AbstractDoSFilterTest.java create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/java/org/eclipse/jetty/ee10/servlets/AbstractFileContentServlet.java create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/java/org/eclipse/jetty/ee10/servlets/AbstractGzipTest.java create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/java/org/eclipse/jetty/ee10/servlets/AsyncManipFilter.java create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/java/org/eclipse/jetty/ee10/servlets/AsyncScheduledDispatchWrite.java create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/java/org/eclipse/jetty/ee10/servlets/AsyncTimeoutCompleteWrite.java create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/java/org/eclipse/jetty/ee10/servlets/AsyncTimeoutDispatchWrite.java create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/java/org/eclipse/jetty/ee10/servlets/BlockingServletLengthStreamTypeWrite.java create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/java/org/eclipse/jetty/ee10/servlets/BlockingServletLengthTypeStreamWrite.java create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/java/org/eclipse/jetty/ee10/servlets/BlockingServletStreamLengthTypeWrite.java create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/java/org/eclipse/jetty/ee10/servlets/BlockingServletStreamLengthTypeWriteWithFlush.java create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/java/org/eclipse/jetty/ee10/servlets/BlockingServletStreamTypeLengthWrite.java create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/java/org/eclipse/jetty/ee10/servlets/BlockingServletTypeLengthStreamWrite.java create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/java/org/eclipse/jetty/ee10/servlets/BlockingServletTypeStreamLengthWrite.java create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/java/org/eclipse/jetty/ee10/servlets/CloseableDoSFilterTest.java create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/java/org/eclipse/jetty/ee10/servlets/CrossOriginFilterTest.java create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/java/org/eclipse/jetty/ee10/servlets/DoSFilterJMXTest.java create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/java/org/eclipse/jetty/ee10/servlets/DoSFilterTest.java create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/java/org/eclipse/jetty/ee10/servlets/EventSourceServletTest.java create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/java/org/eclipse/jetty/ee10/servlets/GzipContentLengthTest.java create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/java/org/eclipse/jetty/ee10/servlets/GzipDefaultServletDeferredContentTypeTest.java create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/java/org/eclipse/jetty/ee10/servlets/GzipDefaultServletTest.java create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/java/org/eclipse/jetty/ee10/servlets/GzipHandlerNoReCompressTest.java create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/java/org/eclipse/jetty/ee10/servlets/GzipHandlerTest.java create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/java/org/eclipse/jetty/ee10/servlets/HeaderFilterTest.java create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/java/org/eclipse/jetty/ee10/servlets/HttpOutputWriteFileContentServlet.java create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/java/org/eclipse/jetty/ee10/servlets/IncludeExcludeBasedFilterTest.java create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/java/org/eclipse/jetty/ee10/servlets/NoOpOutputStream.java create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/java/org/eclipse/jetty/ee10/servlets/PassThruInputStream.java create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/java/org/eclipse/jetty/ee10/servlets/QoSFilterTest.java create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/java/org/eclipse/jetty/ee10/servlets/TestMinGzipSizeServlet.java create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/java/org/eclipse/jetty/ee10/servlets/TestStaticMimeTypeServlet.java create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/java/org/eclipse/jetty/ee10/servlets/ThreadStarvationTest.java create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/resources/big_script.js create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/resources/big_script.js.sha1 create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/resources/jetty-logging.properties create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/resources/jetty_logo.bmp create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/resources/jetty_logo.bmp.sha1 create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/resources/jetty_logo.gif create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/resources/jetty_logo.gif.sha1 create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/resources/jetty_logo.jp2 create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/resources/jetty_logo.jp2.sha1 create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/resources/jetty_logo.jpeg create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/resources/jetty_logo.jpeg.sha1 create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/resources/jetty_logo.jpg create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/resources/jetty_logo.jpg.sha1 create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/resources/jetty_logo.png create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/resources/jetty_logo.png.sha1 create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/resources/jetty_logo.tga create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/resources/jetty_logo.tga.sha1 create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/resources/jetty_logo.tif create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/resources/jetty_logo.tif.sha1 create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/resources/jetty_logo.tiff create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/resources/jetty_logo.tiff.sha1 create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/resources/jetty_logo.xcf create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/resources/jetty_logo.xcf.sha1 create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/resources/small_script.js create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/resources/small_script.js.sha1 create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/resources/test.svg create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/resources/test.svgz create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/resources/test.svgz.sha1 create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/resources/test_quotes.br create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/resources/test_quotes.br.sha1 create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/resources/test_quotes.bz2 create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/resources/test_quotes.bz2.sha1 create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/resources/test_quotes.gz create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/resources/test_quotes.gz.sha1 create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/resources/test_quotes.rar create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/resources/test_quotes.rar.sha1 create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/resources/test_quotes.txt create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/resources/test_quotes.txt.sha1 create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/resources/test_quotes.zip create mode 100644 jetty-ee10/jetty-ee10-servlets/src/test/resources/test_quotes.zip.sha1 create mode 100644 jetty-ee10/jetty-ee10-tests/pom.xml create mode 100644 jetty-ee10/jetty-ee10-tests/test-bad-websocket-webapp/pom.xml create mode 100644 jetty-ee10/jetty-ee10-tests/test-bad-websocket-webapp/src/main/java/org/eclipse/jetty/ee10/tests/webapp/websocket/bad/BadOnCloseServerEndpoint.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-bad-websocket-webapp/src/main/java/org/eclipse/jetty/ee10/tests/webapp/websocket/bad/BadOnOpenServerEndpoint.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-bad-websocket-webapp/src/main/java/org/eclipse/jetty/ee10/tests/webapp/websocket/bad/StringSequence.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-bad-websocket-webapp/src/main/webapp/WEB-INF/web.xml create mode 100644 jetty-ee10/jetty-ee10-tests/test-bad-websocket-webapp/src/main/webapp/index.jsp create mode 100644 jetty-ee10/jetty-ee10-tests/test-cdi/pom.xml create mode 100644 jetty-ee10/jetty-ee10-tests/test-cdi/src/test/java/org/eclipse/jetty/ee10/cdi/tests/EmbeddedWeldTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-cdi/src/test/java/org/eclipse/jetty/ee10/cdi/tests/FriendlyGreetings.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-cdi/src/test/java/org/eclipse/jetty/ee10/cdi/tests/Greetings.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-cdi/src/test/java/org/eclipse/jetty/ee10/cdi/tests/GreetingsServlet.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-cdi/src/test/java/org/eclipse/jetty/ee10/cdi/tests/MyContextListener.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-cdi/src/test/java/org/eclipse/jetty/ee10/cdi/tests/MyFilter.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-cdi/src/test/java/org/eclipse/jetty/ee10/cdi/tests/websocket/JavaxWebSocketCdiTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-cdi/src/test/java/org/eclipse/jetty/ee10/cdi/tests/websocket/JettyWebSocketCdiTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-cdi/src/test/java/org/eclipse/jetty/ee10/cdi/tests/websocket/LogFactory.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-cdi/src/test/resources/META-INF/beans.xml create mode 100644 jetty-ee10/jetty-ee10-tests/test-cdi/src/test/resources/jetty-logging.properties create mode 100644 jetty-ee10/jetty-ee10-tests/test-cdi/src/test/weldtest/.donotdelete create mode 100644 jetty-ee10/jetty-ee10-tests/test-http-client-transport/pom.xml create mode 100644 jetty-ee10/jetty-ee10-tests/test-http-client-transport/src/test/java/org/eclipse/jetty/ee10/http/client/AbstractTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-http-client-transport/src/test/java/org/eclipse/jetty/ee10/http/client/AsyncIOServletTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-http-client-transport/src/test/java/org/eclipse/jetty/ee10/http/client/AsyncRequestContentTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-http-client-transport/src/test/java/org/eclipse/jetty/ee10/http/client/BlockedIOTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-http-client-transport/src/test/java/org/eclipse/jetty/ee10/http/client/ConnectionStatisticsTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-http-client-transport/src/test/java/org/eclipse/jetty/ee10/http/client/EmptyServerHandler.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-http-client-transport/src/test/java/org/eclipse/jetty/ee10/http/client/HttpChannelAssociationTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-http-client-transport/src/test/java/org/eclipse/jetty/ee10/http/client/HttpClientConnectTimeoutTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-http-client-transport/src/test/java/org/eclipse/jetty/ee10/http/client/HttpClientContinueTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-http-client-transport/src/test/java/org/eclipse/jetty/ee10/http/client/HttpClientDemandTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-http-client-transport/src/test/java/org/eclipse/jetty/ee10/http/client/HttpClientIdleTimeoutTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-http-client-transport/src/test/java/org/eclipse/jetty/ee10/http/client/HttpClientLoadTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-http-client-transport/src/test/java/org/eclipse/jetty/ee10/http/client/HttpClientStreamTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-http-client-transport/src/test/java/org/eclipse/jetty/ee10/http/client/HttpClientTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-http-client-transport/src/test/java/org/eclipse/jetty/ee10/http/client/HttpClientTimeoutTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-http-client-transport/src/test/java/org/eclipse/jetty/ee10/http/client/HttpClientTransportDynamicTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-http-client-transport/src/test/java/org/eclipse/jetty/ee10/http/client/HttpTrailersTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-http-client-transport/src/test/java/org/eclipse/jetty/ee10/http/client/ProxyWithDynamicTransportTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-http-client-transport/src/test/java/org/eclipse/jetty/ee10/http/client/RequestReaderTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-http-client-transport/src/test/java/org/eclipse/jetty/ee10/http/client/RoundRobinConnectionPoolTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-http-client-transport/src/test/java/org/eclipse/jetty/ee10/http/client/ServerTimeoutsTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-http-client-transport/src/test/java/org/eclipse/jetty/ee10/http/client/Transport.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-http-client-transport/src/test/java/org/eclipse/jetty/ee10/http/client/TransportProvider.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-http-client-transport/src/test/java/org/eclipse/jetty/ee10/http/client/TransportScenario.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-http-client-transport/src/test/resources/jetty-logging.properties create mode 100644 jetty-ee10/jetty-ee10-tests/test-http-client-transport/src/test/resources/keystore.p12 create mode 100644 jetty-ee10/jetty-ee10-tests/test-http2-webapp/pom.xml create mode 100644 jetty-ee10/jetty-ee10-tests/test-http2-webapp/src/main/java/org/eclipse/jetty/test/webapp/HTTP1Servlet.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-http2-webapp/src/main/java/org/eclipse/jetty/test/webapp/HTTP2Servlet.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-http2-webapp/src/main/webapp/WEB-INF/web.xml create mode 100644 jetty-ee10/jetty-ee10-tests/test-http2-webapp/src/test/java/org/eclipse/jetty/test/webapp/HTTP2FromWebAppIT.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-http2-webapp/src/test/resources/jetty-logging.properties create mode 100644 jetty-ee10/jetty-ee10-tests/test-http2-webapp/src/test/resources/keystore.p12 create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/pom.xml create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/java/org/eclipse/jetty/ee10/test/AliasCheckerSymlinkTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/java/org/eclipse/jetty/ee10/test/AllowedResourceAliasCheckerTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/java/org/eclipse/jetty/ee10/test/AnnotatedAsyncListenerTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/java/org/eclipse/jetty/ee10/test/CustomRequestLogTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/java/org/eclipse/jetty/ee10/test/DefaultHandlerTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/java/org/eclipse/jetty/ee10/test/DeploymentErrorInitializer.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/java/org/eclipse/jetty/ee10/test/DeploymentErrorTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/java/org/eclipse/jetty/ee10/test/DigestPostTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/java/org/eclipse/jetty/ee10/test/FailedSelectorTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/java/org/eclipse/jetty/ee10/test/GzipWithSendErrorTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/java/org/eclipse/jetty/ee10/test/HttpInputIntegrationTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/java/org/eclipse/jetty/ee10/test/HttpInputInterceptorTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/java/org/eclipse/jetty/ee10/test/KeyStoreScannerTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/java/org/eclipse/jetty/ee10/test/RecoverFailedSelectorTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/java/org/eclipse/jetty/ee10/test/jsp/FakeJspServlet.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/java/org/eclipse/jetty/ee10/test/jsp/JspAndDefaultWithAliasesTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/java/org/eclipse/jetty/ee10/test/jsp/JspAndDefaultWithoutAliasesTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/java/org/eclipse/jetty/ee10/test/rfcs/RFC2616BaseTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/java/org/eclipse/jetty/ee10/test/rfcs/RFC2616NIOHttpTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/java/org/eclipse/jetty/ee10/test/rfcs/RFC2616NIOHttpsTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/java/org/eclipse/jetty/ee10/test/support/StringUtil.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/java/org/eclipse/jetty/ee10/test/support/XmlBasedJettyServer.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/java/org/eclipse/jetty/ee10/test/support/rawhttp/HttpRequestTesterTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/java/org/eclipse/jetty/ee10/test/support/rawhttp/HttpResponseTesterTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/java/org/eclipse/jetty/ee10/test/support/rawhttp/HttpSocket.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/java/org/eclipse/jetty/ee10/test/support/rawhttp/HttpSocketImpl.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/java/org/eclipse/jetty/ee10/test/support/rawhttp/HttpTesting.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/java/org/eclipse/jetty/ee10/test/support/rawhttp/HttpsSocketImpl.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/java/org/eclipse/jetty/ee10/test/websocket/JakartaSimpleEchoSocket.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/java/org/eclipse/jetty/ee10/test/websocket/JakartaWebSocketTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/java/org/eclipse/jetty/ee10/test/websocket/JettySimpleEchoSocket.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/java/org/eclipse/jetty/ee10/test/websocket/JettyWebSocketTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/resources/DefaultHandler.xml create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/resources/NIOHttp.xml create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/resources/NIOHttps.xml create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/resources/RFC2616Base.xml create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/resources/RFC2616_Filters.xml create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/resources/RFC2616_Redirects.xml create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/resources/add-jetty-test-webapp.xml create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/resources/badKeystore create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/resources/basic-server.xml create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/resources/deploy.xml create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/resources/docroots/default/R1.txt create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/resources/docroots/default/R2.txt create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/resources/docroots/default/R3.txt create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/resources/docroots/default/alpha.txt create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/resources/docroots/default/index.html create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/resources/docroots/default/quotes.txt create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/resources/docroots/deployerror/badapp-unavailable-false.xml create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/resources/docroots/deployerror/badapp.xml create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/resources/docroots/deployerror/badapp/WEB-INF/classes/META-INF/services/jakarta.servlet.ServletContainerInitializer create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/resources/docroots/deployerror/badapp/WEB-INF/web.xml create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/resources/docroots/jsp/dump.jsp create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/resources/docroots/virtualhost/R1.txt create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/resources/docroots/virtualhost/R2.txt create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/resources/docroots/virtualhost/R3.txt create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/resources/docroots/virtualhost/index.html create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/resources/file create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/resources/jetty-logging.properties create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/resources/keystore.p12 create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/resources/logback.xml create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/resources/login-service.xml create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/resources/message.txt create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/resources/newKeystore create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/resources/oldKeystore create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/resources/realm.properties create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/resources/sibling/file create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/resources/ssl.xml create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/resources/webapp-contexts/RFC2616/rfc2616-webapp.xml create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/resources/webdefault.xml create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/resources/webroot/WEB-INF/web.xml create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/resources/webroot/documents/file create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/resources/webroot/file create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/resources/webroot/index.html create mode 100644 jetty-ee10/jetty-ee10-tests/test-integration/src/test/resources/zeros.gz.gz create mode 100644 jetty-ee10/jetty-ee10-tests/test-jmx/jmx-webapp-it/pom.xml create mode 100644 jetty-ee10/jetty-ee10-tests/test-jmx/jmx-webapp-it/src/test/java/org/eclipse/jetty/ee10/test/jmx/JmxIT.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-jmx/jmx-webapp/pom.xml create mode 100644 jetty-ee10/jetty-ee10-tests/test-jmx/jmx-webapp/src/main/java/org/eclipse/jetty/ee10/test/jmx/CommonComponent.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-jmx/jmx-webapp/src/main/java/org/eclipse/jetty/ee10/test/jmx/Echoer.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-jmx/jmx-webapp/src/main/java/org/eclipse/jetty/ee10/test/jmx/MyContainerInitializer.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-jmx/jmx-webapp/src/main/java/org/eclipse/jetty/ee10/test/jmx/PingServlet.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-jmx/jmx-webapp/src/main/java/org/eclipse/jetty/ee10/test/jmx/Pinger.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-jmx/jmx-webapp/src/main/java/org/eclipse/jetty/ee10/test/jmx/jmx/EchoerMBean.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-jmx/jmx-webapp/src/main/java/org/eclipse/jetty/ee10/test/jmx/jmx/PingerMBean.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-jmx/jmx-webapp/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer create mode 100644 jetty-ee10/jetty-ee10-tests/test-jmx/jmx-webapp/src/main/webapp/WEB-INF/web.xml create mode 100644 jetty-ee10/jetty-ee10-tests/test-jmx/jmx-webapp/src/main/webapp/index.html create mode 100644 jetty-ee10/jetty-ee10-tests/test-jmx/pom.xml create mode 100644 jetty-ee10/jetty-ee10-tests/test-jndi/pom.xml create mode 100644 jetty-ee10/jetty-ee10-tests/test-jndi/src/test/java/org/eclipse/jetty/ee10/jndi/factories/TestMailSessionReference.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-loginservice/pom.xml create mode 100644 jetty-ee10/jetty-ee10-tests/test-loginservice/src/test/java/org/eclipse/jetty/ee10/loginservice/DataSourceLoginServiceTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-loginservice/src/test/java/org/eclipse/jetty/ee10/loginservice/DatabaseLoginServiceTestServer.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-loginservice/src/test/java/org/eclipse/jetty/ee10/loginservice/JdbcLoginServiceTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-loginservice/src/test/resources/createdb.sql create mode 100644 jetty-ee10/jetty-ee10-tests/test-loginservice/src/test/resources/droptables.sql create mode 100644 jetty-ee10/jetty-ee10-tests/test-loginservice/src/test/resources/jdbcrealm.properties create mode 100644 jetty-ee10/jetty-ee10-tests/test-loginservice/src/test/resources/jetty-logging.properties create mode 100644 jetty-ee10/jetty-ee10-tests/test-quickstart/pom.xml create mode 100644 jetty-ee10/jetty-ee10-tests/test-quickstart/src/test/java/org/eclipse/jetty/ee10/quickstart/AttributeNormalizerTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-quickstart/src/test/java/org/eclipse/jetty/ee10/quickstart/AttributeNormalizerToCanonicalUriTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-quickstart/src/test/java/org/eclipse/jetty/ee10/quickstart/EnvUtils.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-quickstart/src/test/java/org/eclipse/jetty/ee10/quickstart/PreconfigureJNDIWar.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-quickstart/src/test/java/org/eclipse/jetty/ee10/quickstart/PreconfigureSpecWar.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-quickstart/src/test/java/org/eclipse/jetty/ee10/quickstart/PreconfigureStandardTestWar.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-quickstart/src/test/java/org/eclipse/jetty/ee10/quickstart/QuickStartJNDIWar.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-quickstart/src/test/java/org/eclipse/jetty/ee10/quickstart/QuickStartSpecWar.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-quickstart/src/test/java/org/eclipse/jetty/ee10/quickstart/QuickStartStandardTestWar.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-quickstart/src/test/java/org/eclipse/jetty/ee10/quickstart/QuickStartTest.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-quickstart/src/test/java/org/eclipse/jetty/ee10/quickstart/Quickstart.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-quickstart/src/test/resources/jetty-logging.properties create mode 100644 jetty-ee10/jetty-ee10-tests/test-quickstart/src/test/resources/realm.properties create mode 100644 jetty-ee10/jetty-ee10-tests/test-quickstart/src/test/resources/test-jndi.xml create mode 100644 jetty-ee10/jetty-ee10-tests/test-quickstart/src/test/resources/test-spec.xml create mode 100644 jetty-ee10/jetty-ee10-tests/test-quickstart/src/test/resources/test.xml create mode 100644 jetty-ee10/jetty-ee10-tests/test-websocket-client-provided-webapp/pom.xml create mode 100644 jetty-ee10/jetty-ee10-tests/test-websocket-client-provided-webapp/src/main/java/org/eclipse/jetty/ee10/tests/webapp/websocket/EchoEndpoint.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-websocket-client-provided-webapp/src/main/java/org/eclipse/jetty/ee10/tests/webapp/websocket/WebSocketClientServlet.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-websocket-client-provided-webapp/src/main/resources/jetty-websocket-httpclient.xml create mode 100644 jetty-ee10/jetty-ee10-tests/test-websocket-client-provided-webapp/src/main/webapp/WEB-INF/web.xml create mode 100644 jetty-ee10/jetty-ee10-tests/test-websocket-client-webapp/pom.xml create mode 100644 jetty-ee10/jetty-ee10-tests/test-websocket-client-webapp/src/main/java/org/eclipse/jetty/ee10/tests/webapp/websocket/EchoEndpoint.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-websocket-client-webapp/src/main/java/org/eclipse/jetty/ee10/tests/webapp/websocket/WebSocketClientServlet.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-websocket-client-webapp/src/main/webapp/WEB-INF/web.xml create mode 100644 jetty-ee10/jetty-ee10-tests/test-websocket-webapp/pom.xml create mode 100644 jetty-ee10/jetty-ee10-tests/test-websocket-webapp/src/main/java/org/eclipse/jetty/ee10/tests/webapp/websocket/EchoEndpoint.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-websocket-webapp/src/main/java/org/eclipse/jetty/ee10/tests/webapp/websocket/StringSequence.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-websocket-webapp/src/main/java/org/eclipse/jetty/ee10/tests/webapp/websocket/StringSequenceDecoder.java create mode 100644 jetty-ee10/jetty-ee10-tests/test-websocket-webapp/src/main/webapp/WEB-INF/web.xml create mode 100644 jetty-ee10/jetty-ee10-tests/test-websocket-webapp/src/main/webapp/index.jsp create mode 100644 jetty-ee10/jetty-ee10-webapp/pom.xml create mode 100644 jetty-ee10/jetty-ee10-webapp/src/main/config/etc/jetty-webapp.xml create mode 100644 jetty-ee10/jetty-ee10-webapp/src/main/config/etc/webdefault.xml create mode 100644 jetty-ee10/jetty-ee10-webapp/src/main/config/modules/webapp.mod create mode 100644 jetty-ee10/jetty-ee10-webapp/src/main/java/module-info.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/AbsoluteOrdering.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/AbstractConfiguration.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/CachingWebAppClassLoader.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/ClassMatcher.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/Configuration.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/Configurations.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/DecoratingListener.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/DefaultsDescriptor.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/Descriptor.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/DescriptorProcessor.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/DiscoveredAnnotation.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/FragmentConfiguration.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/FragmentDescriptor.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/IterativeDescriptorProcessor.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/JaasConfiguration.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/JaspiConfiguration.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/JettyWebXmlConfiguration.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/JmxConfiguration.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/JndiConfiguration.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/JspConfiguration.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/MetaData.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/MetaInfConfiguration.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/Ordering.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/Origin.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/OverrideDescriptor.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/RelativeOrdering.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/ServletsConfiguration.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/StandardDescriptorProcessor.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebAppClassLoader.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebAppConfiguration.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebAppContext.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebDescriptor.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebInfConfiguration.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebXmlConfiguration.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/package-info.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/main/resources/META-INF/services/org.eclipse.jetty.ee10.webapp.Configuration create mode 100644 jetty-ee10/jetty-ee10-webapp/src/test/java/org/acme/webapp/ClassInJarA.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/test/java/org/acme/webapp/TestAnnotation.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/ClassMatcherTest.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/ConfigurationsTest.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/ForcedServletTest.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/HugeResourceTest.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/MetaInfConfigurationTest.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/OrderingTest.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/StandardDescriptorProcessorTest.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/TempDirTest.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/TestMetaData.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/URLStreamHandlerUtil.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/WebAppClassLoaderTest.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/WebAppClassLoaderUrlStreamTest.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/WebAppContextTest.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/WebAppDefaultServletTest.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/WebInfConfigurationTest.java create mode 100644 jetty-ee10/jetty-ee10-webapp/src/test/resources/ext/org-acme-ext-one.jar create mode 100644 jetty-ee10/jetty-ee10-webapp/src/test/resources/ext/org-acme-ext-two.jar create mode 100644 jetty-ee10/jetty-ee10-webapp/src/test/resources/ext/sub/org-acme-ext-three.jar create mode 100644 jetty-ee10/jetty-ee10-webapp/src/test/resources/fragments/sigma.jar create mode 100644 jetty-ee10/jetty-ee10-webapp/src/test/resources/fragments/zeta.jar create mode 100644 jetty-ee10/jetty-ee10-webapp/src/test/resources/jetty-logging.properties create mode 100644 jetty-ee10/jetty-ee10-webapp/src/test/resources/mods/com-acme-janb.jar create mode 100644 jetty-ee10/jetty-ee10-webapp/src/test/resources/mods/foo-bar-janb.jar create mode 100644 jetty-ee10/jetty-ee10-webapp/src/test/resources/org/acme/resource.txt create mode 100644 jetty-ee10/jetty-ee10-webapp/src/test/resources/override-web-with-default-context-path.xml create mode 100644 jetty-ee10/jetty-ee10-webapp/src/test/resources/wars/dump.war create mode 100644 jetty-ee10/jetty-ee10-webapp/src/test/resources/web-default-with-default-context-path.xml create mode 100644 jetty-ee10/jetty-ee10-webapp/src/test/resources/web-session-config.xml create mode 100644 jetty-ee10/jetty-ee10-webapp/src/test/resources/web-with-default-context-path.xml create mode 100644 jetty-ee10/jetty-ee10-webapp/src/test/resources/web-with-empty-default-context-path.xml create mode 100644 jetty-ee10/jetty-ee10-webapp/src/test/resources/web25.xml create mode 100644 jetty-ee10/jetty-ee10-webapp/src/test/resources/web31.xml create mode 100644 jetty-ee10/jetty-ee10-webapp/src/test/resources/web31false.xml create mode 100644 jetty-ee10/jetty-ee10-webapp/src/test/webapp-alt-jsp/WEB-INF/web.xml create mode 100644 jetty-ee10/jetty-ee10-webapp/src/test/webapp-alt-jsp/hello.jsp create mode 100644 jetty-ee10/jetty-ee10-webapp/src/test/webapp/WEB-INF/classes/org/acme/other/ClassInClassesC.class create mode 100644 jetty-ee10/jetty-ee10-webapp/src/test/webapp/WEB-INF/classes/org/acme/resource.txt create mode 100644 jetty-ee10/jetty-ee10-webapp/src/test/webapp/WEB-INF/lib/acme.jar create mode 100644 jetty-ee10/jetty-ee10-webapp/src/test/webapp/WEB-INF/lib/alpha.jar create mode 100644 jetty-ee10/jetty-ee10-webapp/src/test/webapp/WEB-INF/lib/omega.jar create mode 100644 jetty-ee10/jetty-ee10-webapp/src/test/webapp/WEB-INF/test.xml create mode 100644 jetty-ee10/jetty-ee10-webapp/src/test/webapp/test.xml create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-client/pom.xml create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-client/src/main/java/module-info.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-client/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/client/JakartaWebSocketClientContainerProvider.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-client/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/client/JakartaWebSocketShutdownContainer.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-client/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/client/internal/AnnotatedClientEndpointConfig.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-client/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/client/internal/BasicClientEndpointConfig.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-client/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/client/internal/EmptyConfigurator.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-client/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/client/internal/JakartaClientUpgradeRequest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-client/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/client/internal/JakartaWebSocketClientContainer.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-client/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/client/internal/JakartaWebSocketClientFrameHandlerFactory.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-client/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/client/internal/JsrUpgradeListener.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-client/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-client/src/main/resources/META-INF/services/jakarta.websocket.ContainerProvider create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-client/src/test/java/examples/EchoEndpoint.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-client/src/test/java/examples/OriginServerConfigurator.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-client/src/test/java/examples/SecureClientContainerExample.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-client/src/test/java/examples/SecureWebSocketContainerExample.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-client/src/test/resources/jetty-logging.properties create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-client/src/test/resources/jetty-websocket-httpclient.xml create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/pom.xml create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/module-info.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/ClientEndpointConfigWrapper.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/ConfiguredEndpoint.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/EndpointConfigWrapper.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/InitException.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaWebSocketAsyncRemote.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaWebSocketBasicRemote.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaWebSocketContainer.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaWebSocketExtension.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaWebSocketExtensionConfig.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaWebSocketFrameHandler.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaWebSocketFrameHandlerFactory.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaWebSocketFrameHandlerMetadata.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaWebSocketMessageMetadata.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaWebSocketPongMessage.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaWebSocketRemoteEndpoint.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaWebSocketSession.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaWebSocketSessionListener.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/PathParamProvider.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/PutListenerMap.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/RegisteredMessageHandler.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/SendHandlerCallback.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/ServerEndpointConfigWrapper.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/SessionTracker.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/UpgradeRequest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/UpgradeRequestAdapter.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/decoders/AbstractDecoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/decoders/AvailableDecoders.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/decoders/BooleanDecoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/decoders/ByteArrayDecoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/decoders/ByteBufferDecoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/decoders/ByteDecoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/decoders/CharacterDecoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/decoders/DoubleDecoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/decoders/FloatDecoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/decoders/InputStreamDecoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/decoders/IntegerDecoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/decoders/LongDecoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/decoders/ReaderDecoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/decoders/RegisteredDecoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/decoders/ShortDecoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/decoders/StringDecoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/encoders/AbstractEncoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/encoders/AvailableEncoders.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/encoders/BooleanEncoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/encoders/ByteArrayEncoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/encoders/ByteBufferEncoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/encoders/ByteEncoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/encoders/CharacterEncoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/encoders/DoubleEncoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/encoders/EncodeFailedFuture.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/encoders/FloatEncoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/encoders/IntegerEncoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/encoders/LongEncoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/encoders/RegisteredEncoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/encoders/ShortEncoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/encoders/StringEncoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/AbstractDecodedMessageSink.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/DecodedBinaryMessageSink.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/DecodedBinaryStreamMessageSink.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/DecodedTextMessageSink.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/DecodedTextStreamMessageSink.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/AbstractJakartaWebSocketFrameHandlerTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/AbstractSessionTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/Defaults.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/DummyContainer.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/DummyFrameHandlerFactory.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaWebSocketFrameHandlerBadSignaturesTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaWebSocketFrameHandlerOnCloseTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaWebSocketFrameHandlerOnErrorTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaWebSocketFrameHandlerOnMessageBinaryStreamTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaWebSocketFrameHandlerOnMessageBinaryTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaWebSocketFrameHandlerOnMessageTextStreamTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaWebSocketFrameHandlerOnMessageTextTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/JakartaWebSocketFrameHandlerOnOpenTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/coders/tests/BadDualDecoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/coders/tests/BadDualEncoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/coders/tests/ExtDecoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/coders/tests/Fruit.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/coders/tests/FruitBinaryEncoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/coders/tests/FruitDecoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/coders/tests/FruitTextEncoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/endpoints/AbstractStringEndpoint.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/endpoints/DummyEndpoint.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/endpoints/EchoStringEndpoint.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/handlers/BaseMessageHandler.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/handlers/ByteArrayPartialHandler.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/handlers/ByteArrayWholeHandler.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/handlers/ByteBufferPartialHandler.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/handlers/ByteBufferWholeHandler.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/handlers/ComboMessageHandler.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/handlers/ExtendedMessageHandler.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/handlers/InputStreamWholeHandler.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/handlers/LongMessageHandler.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/handlers/ReaderWholeHandler.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/handlers/StringPartialHandler.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/handlers/StringWholeHandler.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/AbstractMessageSinkTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/DecodedBinaryMessageSinkTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/DecodedBinaryStreamMessageSinkTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/DecodedTextMessageSinkTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/DecodedTextStreamMessageSinkTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/InputStreamMessageSinkTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/MessageWriterTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/messages/ReaderMessageSinkTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/sockets/TrackingSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/util/InvokerUtilsStaticParamsTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/util/InvokerUtilsTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/util/NameParamIdentifier.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/common/util/ReflectUtilsTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/resources/jetty-logging.properties create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/resources/quotes-ben.txt create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-common/src/test/resources/quotes-twain.txt create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-server/pom.xml create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-server/src/main/config/modules/websocket-jakarta.mod create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-server/src/main/java/module-info.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-server/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/server/config/ContainerDefaultConfigurator.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-server/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/server/config/JakartaWebSocketConfiguration.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-server/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/server/config/JakartaWebSocketServletContainerInitializer.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-server/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/server/internal/AnnotatedServerEndpointConfig.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-server/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/server/internal/BasicServerEndpointConfig.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-server/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/server/internal/JakartaServerUpgradeRequest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-server/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/server/internal/JakartaWebSocketCreator.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-server/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/server/internal/JakartaWebSocketServerContainer.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-server/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/server/internal/JakartaWebSocketServerFrameHandlerFactory.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-server/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/server/internal/JsrHandshakeRequest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-server/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/server/internal/JsrHandshakeResponse.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-server/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/server/internal/PathParamIdentifier.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-server/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/server/internal/PathParamServerEndpointConfig.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-server/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-server/src/main/resources/META-INF/services/jakarta.websocket.server.ServerEndpointConfig$Configurator create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-server/src/main/resources/META-INF/services/org.eclipse.jetty.ee10.webapp.Configuration create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-server/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/server/browser/JsrBrowserConfigurator.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-server/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/server/browser/JsrBrowserDebugTool.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-server/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/server/browser/JsrBrowserSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-server/src/test/resources/jetty-logging.properties create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-server/src/test/resources/jsr-browser-debug-tool/index.html create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-server/src/test/resources/jsr-browser-debug-tool/main.css create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-server/src/test/resources/jsr-browser-debug-tool/websocket.js create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/fuzzingclient.json create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/fuzzingserver.json create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/pom.xml create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/BadFrame.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/BiConsumerServiceServlet.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/CompletableFutureMethodHandle.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/CoreServer.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/DataUtils.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/DummyEndpoint.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/EchoSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/EventSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/FunctionMethod.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/Fuzzer.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/LocalFuzzer.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/LocalServer.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/MessageType.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/NetworkFuzzer.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/ParserCapture.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/RawFrameBuilder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/SessionMatchers.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/Sha1Sum.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/Timeouts.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/UnitGenerator.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/UpgradeUtils.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/WSEndpointTracker.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/WSEventTracker.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/WSServer.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/WSURI.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/framehandlers/FrameEcho.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/framehandlers/FrameHandlerTracker.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/framehandlers/StaticText.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/framehandlers/WholeMessageEcho.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/matchers/IsMessageHandlerType.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/main/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/matchers/IsMessageHandlerTypeRegistered.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/com/acme/websocket/BasicEchoEndpoint.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/com/acme/websocket/BasicEchoEndpointConfigContextListener.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/com/acme/websocket/IdleTimeoutContextListener.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/com/acme/websocket/IdleTimeoutOnOpenEndpoint.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/com/acme/websocket/IdleTimeoutOnOpenSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/com/acme/websocket/LargeEchoContextListener.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/com/acme/websocket/LargeEchoDefaultSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/com/acme/websocket/OnOpenIdleTimeoutEndpoint.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/com/acme/websocket/PongContextListener.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/com/acme/websocket/PongMessageEndpoint.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/com/acme/websocket/PongSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/CloseInOnOpenTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/GracefulCloseTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/JakartaClientClassLoaderTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/JakartaClientShutdownWithServerEmbeddedTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/JakartaClientShutdownWithServerWebAppTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/JakartaOnCloseTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/JakartaWebSocketRestartTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/JettySpecificConfigTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/PathParamTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/ProgrammaticWebSocketUpgradeTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/RestartContextTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/ServerConfigTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/SingleMessageHandlerTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/SyntheticOnMessageTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/UpgradeRequestResponseTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/autobahn/JakartaAutobahnClient.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/autobahn/JakartaAutobahnServer.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/autobahn/JakartaAutobahnSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/client/AbstractClientSessionTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/client/AnnotatedClientEndpointTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/client/AnnotatedEchoClient.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/client/AnnotatedEchoTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/client/ConfiguratorTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/client/CookiesTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/client/DecoderReaderManySmallTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/client/DelayedStartClientTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/client/EndpointEchoTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/client/JsrClientEchoTrackingSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/client/JsrClientTrackingSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/client/MessageReceivingTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/client/OnCloseTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/client/SessionAddMessageHandlerTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/client/WriteTimeoutTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/client/misbehaving/AnnotatedRuntimeOnOpen.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/client/misbehaving/EndpointRuntimeOnOpen.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/client/misbehaving/MisbehavingClassTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/client/samples/CloseEndpointConfigSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/client/samples/CloseReasonSessionSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/client/samples/CloseReasonSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/client/samples/CloseSessionReasonSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/client/samples/CloseSessionSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/client/samples/CloseSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/client/samples/IntSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/coders/AvailableDecodersTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/coders/AvailableEncodersTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/coders/BadDualDecoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/coders/BadDualEncoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/coders/CoderEventTracking.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/coders/DateDecoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/coders/DateEncoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/coders/DateTimeDecoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/coders/DateTimeEncoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/coders/DecoderListTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/coders/DecoderTextStreamTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/coders/DecoderTextTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/coders/EncoderLifeCycleTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/coders/EncoderTextTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/coders/ExtDecoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/coders/FloatDecoderTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/coders/Fruit.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/coders/FruitBinaryEncoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/coders/FruitDecoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/coders/FruitTextEncoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/coders/IntegerDecoderTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/coders/LongDecoderTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/coders/Quotes.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/coders/QuotesDecoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/coders/QuotesEncoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/coders/QuotesUtil.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/coders/ShortDecoderTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/coders/TimeDecoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/coders/TimeEncoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/coders/ValidDualDecoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/coders/ValidDualEncoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/handlers/AbstractAnnotatedHandler.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/handlers/AbstractHandler.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/handlers/BaseMessageHandler.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/handlers/BinaryHandlers.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/handlers/ComboMessageHandler.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/handlers/ExtendedMessageHandler.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/handlers/LongMessageHandler.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/handlers/MessageHandlerTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/handlers/TextHandlers.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/pathparam/BooleanClassSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/pathparam/BooleanTypeSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/pathparam/ByteClassSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/pathparam/ByteTypeSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/pathparam/CharacterClassSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/pathparam/CharacterTypeSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/pathparam/DoubleClassSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/pathparam/DoubleTypeSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/pathparam/FloatClassSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/pathparam/FloatTypeSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/pathparam/IntegerClassSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/pathparam/IntegerTypeSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/pathparam/LongClassSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/pathparam/LongTypeSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/pathparam/ShortClassSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/pathparam/ShortTypeSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/pathparam/StringClassSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/quotes/Quotes.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/quotes/QuotesDecoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/quotes/QuotesDecoderTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/quotes/QuotesDecoderTextStreamTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/quotes/QuotesEncoder.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/quotes/QuotesEncoderTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/quotes/QuotesSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/quotes/QuotesUtil.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/AbstractJakartaWebSocketServerFrameHandlerTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/AddEndpointTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/AltFilterTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/AnnotatedServerEndpointTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/BasicEchoEndpointConfigContextListener.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/BasicEchoEndpointContextListener.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/BasicEchoSocketConfigContextListener.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/BasicEchoSocketContextListener.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/BinaryStreamTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/ConfiguratorTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/ContainerProviderServerTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/DeploymentExceptionTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/DeploymentTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/EndpointViaConfigTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/IdleTimeoutContextListener.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/IdleTimeoutTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/InputStreamEchoTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/JakartaWebSocketFrameHandlerOnMessageTextStreamTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/JettyServerEndpointConfiguratorTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/JsrBatchModeTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/JsrEchoTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/LargeAnnotatedTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/LargeContainerTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/LargeEchoContextListener.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/MemoryUsageTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/OnMessageReturnTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/PartialEchoTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/PingPongTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/PongContextListener.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/PongSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/PrimitivesBinaryEchoTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/PrimitivesTextEchoTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/ReaderEchoTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/ServerDecoderTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/SessionTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/SessionTrackingTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/StreamTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/TextStreamTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/UriTemplateParameterTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/WebAppClassLoaderTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/WebSocketServerContainerExecutorTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/configs/EchoSocketConfigurator.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/examples/GetHttpSessionConfigurator.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/examples/GetHttpSessionSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/examples/MyAuthedConfigurator.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/examples/MyAuthedSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/examples/StreamingEchoSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/examples/WebSocketServerExamplesTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/BasicBinaryMessageByteBufferSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/BasicCloseReasonSessionSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/BasicCloseReasonSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/BasicCloseSessionReasonSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/BasicCloseSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/BasicEchoSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/BasicErrorSessionSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/BasicErrorSessionThrowableSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/BasicErrorSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/BasicErrorThrowableSessionSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/BasicErrorThrowableSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/BasicOpenCloseSessionSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/BasicOpenCloseSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/BasicOpenSessionSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/BasicOpenSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/BasicPongMessageSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/BasicTextMessageStringSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/ByteBufferSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/ConfiguredEchoSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/DateTextSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/IdleTimeoutOnOpenEndpoint.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/IdleTimeoutOnOpenSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/InvalidCloseIntSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/InvalidErrorErrorSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/InvalidErrorIntSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/InvalidOpenCloseReasonSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/InvalidOpenIntSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/InvalidOpenSessionIntSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/StatelessTextMessageStringSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/TrackingSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/binary/ByteBufferSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/echo/BasicEchoEndpoint.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/echo/BasicEchoSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/echo/EchoAsyncTextSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/echo/EchoBasicTextSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/echo/EchoReturnEndpoint.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/echo/EchoReturnTextSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/echo/EchoStatelessAsyncTextSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/echo/EchoStatelessBasicTextSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/echo/LargeEchoConfiguredSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/echo/LargeEchoDefaultSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/idletimeout/OnOpenIdleTimeoutEndpoint.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/idletimeout/OnOpenIdleTimeoutSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/partial/PartialTextSessionSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/partial/PartialTextSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/partial/PartialTrackingSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/pong/PongMessageEndpoint.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/primitives/BooleanObjectTextParamSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/primitives/BooleanObjectTextSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/primitives/BooleanTextParamSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/primitives/BooleanTextSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/primitives/ByteObjectTextSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/primitives/ByteTextSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/primitives/CharTextSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/primitives/CharacterObjectTextSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/primitives/DoubleObjectTextSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/primitives/DoubleTextSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/primitives/FloatObjectTextSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/primitives/FloatTextSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/primitives/IntParamTextSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/primitives/IntTextSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/primitives/IntegerObjectParamTextSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/primitives/IntegerObjectTextSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/primitives/LongObjectTextSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/primitives/LongTextSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/primitives/ShortObjectTextSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/primitives/ShortTextSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/streaming/InputStreamSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/streaming/ReaderParamSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/streaming/ReaderSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/java/org/eclipse/jetty/ee10/websocket/jakarta/tests/server/sockets/streaming/StringReturnReaderParamSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/resources/alt-filter-web.xml create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/resources/basic-echo-endpoint-config-web.xml create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/resources/data/larger.png create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/resources/data/larger.png.sha create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/resources/data/largest.jpg create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/resources/data/largest.jpg.sha create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/resources/data/medium.png create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/resources/data/medium.png.sha create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/resources/data/small.png create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/resources/data/small.png.sha create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/resources/empty-web.xml create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/resources/idle-timeout-config-web.xml create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/resources/jetty-logging.properties create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/resources/jetty-websocket-httpclient.xml create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/resources/keystore.p12 create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/resources/large-echo-config-web.xml create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/resources/logback-test.xml create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/resources/pong-config-web.xml create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/resources/quotes-ben.txt create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/resources/quotes-twain.txt create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/resources/simple/jetty-websocket-httpclient.xml create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/resources/wsuf-alt-config-via-listener.xml create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/resources/wsuf-config-via-listener.xml create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/resources/wsuf-config-via-servlet-init.xml create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jakarta-tests/src/test/webapp/index.html create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-api/pom.xml create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-api/src/main/java/module-info.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-api/src/main/java/org/eclipse/jetty/ee10/websocket/api/BatchMode.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-api/src/main/java/org/eclipse/jetty/ee10/websocket/api/CloseStatus.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-api/src/main/java/org/eclipse/jetty/ee10/websocket/api/ExtensionConfig.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-api/src/main/java/org/eclipse/jetty/ee10/websocket/api/Frame.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-api/src/main/java/org/eclipse/jetty/ee10/websocket/api/RemoteEndpoint.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-api/src/main/java/org/eclipse/jetty/ee10/websocket/api/Session.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-api/src/main/java/org/eclipse/jetty/ee10/websocket/api/StatusCode.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-api/src/main/java/org/eclipse/jetty/ee10/websocket/api/SuspendToken.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-api/src/main/java/org/eclipse/jetty/ee10/websocket/api/UpgradeRequest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-api/src/main/java/org/eclipse/jetty/ee10/websocket/api/UpgradeResponse.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-api/src/main/java/org/eclipse/jetty/ee10/websocket/api/WebSocketAdapter.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-api/src/main/java/org/eclipse/jetty/ee10/websocket/api/WebSocketBehavior.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-api/src/main/java/org/eclipse/jetty/ee10/websocket/api/WebSocketConnectionListener.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-api/src/main/java/org/eclipse/jetty/ee10/websocket/api/WebSocketContainer.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-api/src/main/java/org/eclipse/jetty/ee10/websocket/api/WebSocketFrameListener.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-api/src/main/java/org/eclipse/jetty/ee10/websocket/api/WebSocketListener.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-api/src/main/java/org/eclipse/jetty/ee10/websocket/api/WebSocketPartialListener.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-api/src/main/java/org/eclipse/jetty/ee10/websocket/api/WebSocketPingPongListener.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-api/src/main/java/org/eclipse/jetty/ee10/websocket/api/WebSocketPolicy.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-api/src/main/java/org/eclipse/jetty/ee10/websocket/api/WebSocketSessionListener.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-api/src/main/java/org/eclipse/jetty/ee10/websocket/api/WriteCallback.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-api/src/main/java/org/eclipse/jetty/ee10/websocket/api/annotations/OnWebSocketClose.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-api/src/main/java/org/eclipse/jetty/ee10/websocket/api/annotations/OnWebSocketConnect.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-api/src/main/java/org/eclipse/jetty/ee10/websocket/api/annotations/OnWebSocketError.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-api/src/main/java/org/eclipse/jetty/ee10/websocket/api/annotations/OnWebSocketFrame.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-api/src/main/java/org/eclipse/jetty/ee10/websocket/api/annotations/OnWebSocketMessage.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-api/src/main/java/org/eclipse/jetty/ee10/websocket/api/annotations/WebSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-api/src/main/java/org/eclipse/jetty/ee10/websocket/api/annotations/package-info.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-api/src/main/java/org/eclipse/jetty/ee10/websocket/api/exceptions/BadPayloadException.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-api/src/main/java/org/eclipse/jetty/ee10/websocket/api/exceptions/CloseException.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-api/src/main/java/org/eclipse/jetty/ee10/websocket/api/exceptions/InvalidWebSocketException.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-api/src/main/java/org/eclipse/jetty/ee10/websocket/api/exceptions/MessageTooLargeException.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-api/src/main/java/org/eclipse/jetty/ee10/websocket/api/exceptions/PolicyViolationException.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-api/src/main/java/org/eclipse/jetty/ee10/websocket/api/exceptions/ProtocolException.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-api/src/main/java/org/eclipse/jetty/ee10/websocket/api/exceptions/UpgradeException.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-api/src/main/java/org/eclipse/jetty/ee10/websocket/api/exceptions/WebSocketException.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-api/src/main/java/org/eclipse/jetty/ee10/websocket/api/exceptions/WebSocketTimeoutException.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-api/src/main/java/org/eclipse/jetty/ee10/websocket/api/exceptions/package-info.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-api/src/main/java/org/eclipse/jetty/ee10/websocket/api/package-info.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-api/src/main/java/org/eclipse/jetty/ee10/websocket/api/util/WSURI.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-api/src/main/java/org/eclipse/jetty/ee10/websocket/api/util/WebSocketConstants.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-api/src/main/java/org/eclipse/jetty/ee10/websocket/api/util/package-info.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-client/pom.xml create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-client/src/main/config/modules/websocket-jetty-client.mod create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-client/src/main/java/module-info.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-client/src/main/java/org/eclipse/jetty/ee10/websocket/client/ClientUpgradeRequest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-client/src/main/java/org/eclipse/jetty/ee10/websocket/client/JettyUpgradeListener.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-client/src/main/java/org/eclipse/jetty/ee10/websocket/client/WebSocketClient.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-client/src/main/java/org/eclipse/jetty/ee10/websocket/client/config/JettyWebSocketClientConfiguration.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-client/src/main/java/org/eclipse/jetty/ee10/websocket/client/impl/DelegatedJettyClientUpgradeRequest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-client/src/main/java/org/eclipse/jetty/ee10/websocket/client/impl/DelegatedJettyClientUpgradeResponse.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-client/src/main/java/org/eclipse/jetty/ee10/websocket/client/impl/JettyClientUpgradeRequest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-client/src/main/java/org/eclipse/jetty/ee10/websocket/client/package-info.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-client/src/main/resources/META-INF/services/org.eclipse.jetty.ee10.webapp.Configuration create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-client/src/test/java/examples/ClientDemo.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-client/src/test/java/examples/SimpleEchoClient.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-client/src/test/java/examples/SimpleEchoSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-client/src/test/java/org/eclipse/jetty/ee10/websocket/client/HttpClientInitTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-client/src/test/java/org/eclipse/jetty/ee10/websocket/client/WebSocketClientBadUriTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-client/src/test/java/org/eclipse/jetty/ee10/websocket/client/WebSocketClientInitTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-client/src/test/resources/jetty-logging.properties create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-common/pom.xml create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-common/src/main/java/module-info.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-common/src/main/java/org/eclipse/jetty/ee10/websocket/common/ExtensionConfigParser.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-common/src/main/java/org/eclipse/jetty/ee10/websocket/common/JettyExtensionConfig.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-common/src/main/java/org/eclipse/jetty/ee10/websocket/common/JettyWebSocketFrame.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-common/src/main/java/org/eclipse/jetty/ee10/websocket/common/JettyWebSocketFrameHandler.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-common/src/main/java/org/eclipse/jetty/ee10/websocket/common/JettyWebSocketFrameHandlerFactory.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-common/src/main/java/org/eclipse/jetty/ee10/websocket/common/JettyWebSocketFrameHandlerMetadata.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-common/src/main/java/org/eclipse/jetty/ee10/websocket/common/JettyWebSocketRemoteEndpoint.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-common/src/main/java/org/eclipse/jetty/ee10/websocket/common/SessionTracker.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-common/src/main/java/org/eclipse/jetty/ee10/websocket/common/WebSocketSession.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-common/src/main/java/org/eclipse/jetty/ee10/websocket/common/package-info.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-common/src/main/resources/META-INF/services/org.eclipse.jetty.ee10.websocket.api.ExtensionConfig$Parser create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee10/websocket/common/DummyContainer.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee10/websocket/common/EndPoints.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee10/websocket/common/EventQueue.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee10/websocket/common/JettyWebSocketFrameHandlerTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee10/websocket/common/LocalEndpointMetadataTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee10/websocket/common/MessageInputStreamTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee10/websocket/common/MessageOutputStreamTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee10/websocket/common/OutgoingMessageCapture.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee10/websocket/common/TestableLeakTrackingBufferPool.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee10/websocket/common/endpoints/adapters/AdapterEchoSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee10/websocket/common/endpoints/adapters/AnnotatedEchoSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee10/websocket/common/endpoints/adapters/ListenerEchoSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee10/websocket/common/invoke/InvokerUtilsTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-common/src/test/java/org/eclipse/jetty/ee10/websocket/common/invoke/NameParamIdentifier.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-common/src/test/resources/jetty-logging.properties create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-server/pom.xml create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-server/src/main/config/modules/websocket-jetty.mod create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-server/src/main/java/module-info.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-server/src/main/java/org/eclipse/jetty/ee10/websocket/server/JettyServerUpgradeRequest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-server/src/main/java/org/eclipse/jetty/ee10/websocket/server/JettyServerUpgradeResponse.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-server/src/main/java/org/eclipse/jetty/ee10/websocket/server/JettyWebSocketCreator.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-server/src/main/java/org/eclipse/jetty/ee10/websocket/server/JettyWebSocketServerContainer.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-server/src/main/java/org/eclipse/jetty/ee10/websocket/server/JettyWebSocketServlet.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-server/src/main/java/org/eclipse/jetty/ee10/websocket/server/JettyWebSocketServletFactory.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-server/src/main/java/org/eclipse/jetty/ee10/websocket/server/config/JettyWebSocketConfiguration.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-server/src/main/java/org/eclipse/jetty/ee10/websocket/server/config/JettyWebSocketServletContainerInitializer.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-server/src/main/java/org/eclipse/jetty/ee10/websocket/server/internal/DelegatedServerUpgradeRequest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-server/src/main/java/org/eclipse/jetty/ee10/websocket/server/internal/DelegatedServerUpgradeResponse.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-server/src/main/java/org/eclipse/jetty/ee10/websocket/server/internal/JettyServerFrameHandlerFactory.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-server/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-server/src/main/resources/META-INF/services/org.eclipse.jetty.ee10.webapp.Configuration create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-server/src/test/java/org/eclipse/jetty/ee10/websocket/server/browser/BrowserDebugTool.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-server/src/test/java/org/eclipse/jetty/ee10/websocket/server/browser/BrowserSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-server/src/test/resources/browser-debug-tool/index.html create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-server/src/test/resources/browser-debug-tool/main.css create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-server/src/test/resources/browser-debug-tool/websocket.js create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/fuzzingclient.json create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/fuzzingserver.json create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/pom.xml create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/AnnoMaxMessageEndpoint.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/AnnotatedPartialListenerTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/CloseInOnOpenTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/CloseTrackingEndpoint.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/ConcurrentConnectTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/ConnectMessageEndpoint.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/ConnectionHeaderTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/EchoCreator.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/EchoSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/ErrorCloseTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/EventSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/GetAuthHeaderEndpoint.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/GracefulCloseTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/JettyClientClassLoaderTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/JettyOnCloseTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/JettyWebSocketExtensionConfigTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/JettyWebSocketFilterTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/JettyWebSocketNegotiationTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/JettyWebSocketRestartTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/JettyWebSocketServletAttributeTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/JettyWebSocketServletTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/JettyWebSocketWebApp.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/LargeDeflateTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/MaxOutgoingFramesTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/ParamsEndpoint.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/PermessageDeflateBufferTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/ProgrammaticWebSocketUpgradeTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/SimpleStatusServlet.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/SingleOnMessageTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/SuspendResumeTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/UpgradeRequestResponseTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/WebAppTester.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/WebSocketOverHTTP2Test.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/WebSocketServletExamplesTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/WebSocketStatsTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/WebSocketStopTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/autobahn/JettyAutobahnClient.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/autobahn/JettyAutobahnServer.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/autobahn/JettyAutobahnSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/client/BadNetworkTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/client/ClientCloseTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/client/ClientConfigTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/client/ClientConnectTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/client/ClientOpenSessionTracker.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/client/ClientSessionsTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/client/ClientTimeoutTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/client/ClientWriteThread.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/client/ConnectFutureTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/client/InvalidUpgradeServlet.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/client/SlowClientTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/client/WebSocketClientTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/examples/MyAdvancedEchoCreator.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/examples/MyAdvancedEchoServlet.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/examples/MyAuthedCreator.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/examples/MyAuthedServlet.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/examples/MyBinaryEchoSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/examples/MyEchoServlet.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/examples/MyEchoSocket.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/extensions/ExtensionConfigTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/listeners/AbstractAnnotatedListener.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/listeners/AbstractListener.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/listeners/BinaryListeners.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/listeners/TextListeners.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/listeners/WebSocketListenerTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/proxy/WebSocketProxy.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/proxy/WebSocketProxyTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/server/AbstractCloseEndpoint.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/server/CloseInOnCloseEndpoint.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/server/CloseInOnCloseEndpointNewThread.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/server/ContainerEndpoint.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/server/FastCloseEndpoint.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/server/FastFailEndpoint.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/server/FrameAnnotationTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/server/FrameListenerTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/server/PartialListenerTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/server/ServerCloseCreator.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/server/ServerCloseTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/server/ServerConfigTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/server/SlowServerEndpoint.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/server/SlowServerTest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/util/FutureWriteCallback.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/java/org/eclipse/jetty/ee10/websocket/tests/util/WSURITest.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/resources/jetty-logging.properties create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/resources/keystore.p12 create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/resources/wsuf-ordering1.xml create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-jetty-tests/src/test/resources/wsuf-ordering2.xml create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-servlet/pom.xml create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-servlet/src/main/java/module-info.java create mode 100644 jetty-ee10/jetty-ee10-websocket/jetty-ee10-websocket-servlet/src/main/java/org/eclipse/jetty/ee10/websocket/servlet/WebSocketUpgradeFilter.java create mode 100644 jetty-ee10/jetty-ee10-websocket/pom.xml create mode 100644 jetty-ee10/jetty-examples/pom.xml create mode 100644 jetty-ee10/jetty-examples/src/test/java/org/acme/MyServlet.java create mode 100644 jetty-ee10/jetty-examples/src/test/java/org/eclipse/jetty/examples/Jetty12Example.java create mode 100644 jetty-ee10/pom.xml diff --git a/jetty-ee10/jetty-ee10-annotations/pom.xml b/jetty-ee10/jetty-ee10-annotations/pom.xml new file mode 100644 index 00000000000..09c7f13c26e --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/pom.xml @@ -0,0 +1,95 @@ + + + org.eclipse.jetty.ee10 + jetty-ee10 + 12.0.0-SNAPSHOT + + + 4.0.0 + jetty-ee10-annotations + EE10 :: Jetty :: Servlet Annotations + Annotation support for deploying servlets in jetty. + + ${project.groupId}.annotations + org.eclipse.annotations.* + + + + + + maven-surefire-plugin + + + @{argLine} ${jetty.surefire.argLine} --add-opens org.eclipse.jetty.ee10.annotations/org.eclipse.jetty.ee10.annotations.resources=org.eclipse.jetty.ee10.plus + + + + + 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.ee10.webapp.Configuration + + + + + + + + + + org.eclipse.jetty.ee10 + jetty-ee10-plus + + + org.eclipse.jetty.ee10 + jetty-ee10-webapp + + + jakarta.annotation + jakarta.annotation-api + + + org.ow2.asm + asm + + + org.ow2.asm + asm-commons + + + org.slf4j + slf4j-api + + + org.eclipse.jetty.toolchain + jetty-jakarta-servlet-api + + + jakarta.transaction + jakarta.transaction-api + test + + + org.eclipse.jetty.toolchain + jetty-test-helper + test + + + org.eclipse.jetty + jetty-jndi + test + + + org.eclipse.jetty + jetty-slf4j-impl + test + + + diff --git a/jetty-ee10/jetty-ee10-annotations/src/main/config/modules/annotations.mod b/jetty-ee10/jetty-ee10-annotations/src/main/config/modules/annotations.mod new file mode 100644 index 00000000000..ec6a976386d --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/main/config/modules/annotations.mod @@ -0,0 +1,15 @@ +# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html + +[description] +Enables Annotation scanning for deployed web applications. + +[depend] +plus + +[lib] +lib/jetty-annotations-${jetty.version}.jar +lib/annotations/*.jar + +[jpms] +add-modules:org.objectweb.asm + diff --git a/jetty-ee10/jetty-ee10-annotations/src/main/java/module-info.java b/jetty-ee10/jetty-ee10-annotations/src/main/java/module-info.java new file mode 100644 index 00000000000..379ef78cdb7 --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/main/java/module-info.java @@ -0,0 +1,32 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 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.ee10.annotations.AnnotationConfiguration; +import org.eclipse.jetty.ee10.webapp.Configuration; + +module org.eclipse.jetty.ee10.annotations +{ + requires jakarta.annotation; + requires java.naming; + requires org.slf4j; + + requires transitive org.eclipse.jetty.ee10.plus; + requires transitive org.objectweb.asm; + + exports org.eclipse.jetty.ee10.annotations; + + uses jakarta.servlet.ServletContainerInitializer; + + provides Configuration with + AnnotationConfiguration; +} diff --git a/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/AbstractDiscoverableAnnotationHandler.java b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/AbstractDiscoverableAnnotationHandler.java new file mode 100644 index 00000000000..530ec76e2bd --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/AbstractDiscoverableAnnotationHandler.java @@ -0,0 +1,37 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.annotations; + +import org.eclipse.jetty.ee10.webapp.DiscoveredAnnotation; +import org.eclipse.jetty.ee10.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-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/AnnotationConfiguration.java b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/AnnotationConfiguration.java new file mode 100644 index 00000000000..f6ef3c82b95 --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/AnnotationConfiguration.java @@ -0,0 +1,1191 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.annotations; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +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.Collectors; +import java.util.stream.Stream; + +import jakarta.servlet.ServletContainerInitializer; +import jakarta.servlet.annotation.HandlesTypes; +import org.eclipse.jetty.ee10.plus.webapp.PlusConfiguration; +import org.eclipse.jetty.ee10.servlet.ServletContainerInitializerHolder; +import org.eclipse.jetty.ee10.servlet.Source; +import org.eclipse.jetty.ee10.servlet.Source.Origin; +import org.eclipse.jetty.ee10.webapp.AbstractConfiguration; +import org.eclipse.jetty.ee10.webapp.FragmentConfiguration; +import org.eclipse.jetty.ee10.webapp.FragmentDescriptor; +import org.eclipse.jetty.ee10.webapp.JettyWebXmlConfiguration; +import org.eclipse.jetty.ee10.webapp.MetaInfConfiguration; +import org.eclipse.jetty.ee10.webapp.WebAppClassLoader; +import org.eclipse.jetty.ee10.webapp.WebAppContext; +import org.eclipse.jetty.ee10.webapp.WebDescriptor; +import org.eclipse.jetty.ee10.webapp.WebXmlConfiguration; +import org.eclipse.jetty.util.JavaVersion; +import org.eclipse.jetty.util.Loader; +import org.eclipse.jetty.util.MultiException; +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.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"; + + public static final int DEFAULT_MAX_SCAN_WAIT = 60; /* time in sec */ + public static final boolean DEFAULT_MULTI_THREADED = true; + + protected final List _discoverableAnnotationHandlers = new ArrayList<>(); + protected ClassInheritanceHandler _classInheritanceHandler; + protected final List _containerInitializerAnnotationHandlers = new ArrayList<>(); + protected final List _sciHolders = new ArrayList<>(); + + protected List _parserTasks; + + protected CounterStatistic _containerPathStats; + protected CounterStatistic _webInfLibStats; + protected CounterStatistic _webInfClassesStats; + protected Pattern _sciExcludePattern; + + public AnnotationConfiguration() + { + addDependencies(WebXmlConfiguration.class, MetaInfConfiguration.class, FragmentConfiguration.class, PlusConfiguration.class); + addDependents(JettyWebXmlConfiguration.class); + hide("org.objectweb.asm."); + } + + /** + * TimeStatistic + * + * Simple class to capture elapsed time of an operation. + */ + public class TimeStatistic + { + public long _start = 0; + public long _end = 0; + + public void start() + { + _start = System.nanoTime(); + } + + public void end() + { + _end = System.nanoTime(); + } + + public long getStart() + { + return _start; + } + + public long getEnd() + { + return _end; + } + + public long getElapsed() + { + return (_end > _start ? (_end - _start) : 0); + } + } + + /** + * ParserTask + * + * Task to executing scanning of a resource for annotations. + */ + public class ParserTask implements Callable + { + protected Exception _exception; + 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 class ServletContainerInitializerOrdering + { + private 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.intValue(); + } + + /** + * @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.intValue(); + } + + /** + * 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 class ServletContainerInitializerComparator implements Comparator + { + private 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 Set> _handlesTypes = new HashSet<>(); + private 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, (Set)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, (Set)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, (Set)classMap.get(s)); + } + } + } + + @Override + public void preConfigure(final WebAppContext context) throws Exception + { + String tmp = (String)context.getAttribute(SERVLET_CONTAINER_INITIALIZER_EXCLUSION_PATTERN); + _sciExcludePattern = (tmp == null ? null : Pattern.compile(tmp)); + } + + public void addDiscoverableAnnotationHandler(AbstractDiscoverableAnnotationHandler handler) + { + _discoverableAnnotationHandlers.add(handler); + } + + @Override + public void configure(WebAppContext context) throws Exception + { + //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()) + { + _discoverableAnnotationHandlers.add(new WebServletAnnotationHandler(context)); + _discoverableAnnotationHandlers.add(new WebFilterAnnotationHandler(context)); + _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(context)); + + if (!_discoverableAnnotationHandlers.isEmpty() || _classInheritanceHandler != null || !_containerInitializerAnnotationHandlers.isEmpty()) + scanForAnnotations(context); + + Map> map = (Map>)context.getAttribute(AnnotationConfiguration.CLASS_INHERITANCE_MAP); + for (DiscoveredServletContainerInitializerHolder holder:_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); + + _discoverableAnnotationHandlers.clear(); + _classInheritanceHandler = null; + _containerInitializerAnnotationHandlers.clear(); + _sciHolders.clear(); + + if (_parserTasks != null) + { + _parserTasks.clear(); + _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) + 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); + _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(context, parser); + //scan non-excluded, non medatadata-complete jars in web-inf lib + parseWebInfLib(context, parser); + + long start = System.nanoTime(); + + //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(_parserTasks.size()); + final MultiException me = new MultiException(); + + for (final ParserTask p : _parserTasks) + { + task_limit.acquire(); + context.getServer().getThreadPool().execute(new Runnable() + { + @Override + public void run() + { + try + { + p.call(); + } + catch (Exception e) + { + me.add(e); + } + finally + { + task_limit.release(); + latch.countDown(); + } + } + }); + } + + boolean timeout = !latch.await(getMaxScanWait(context), TimeUnit.SECONDS); + long elapsedMs = TimeUnit.MILLISECONDS.convert(System.nanoTime() - start, TimeUnit.NANOSECONDS); + + + if (LOG.isDebugEnabled()) + { + LOG.debug("Annotation scanning elapsed time={}ms", elapsedMs); + for (ParserTask p : _parserTasks) + { + LOG.debug("Scanned {} in {}ms", p.getResource(), TimeUnit.MILLISECONDS.convert(p.getStatistic().getElapsed(), TimeUnit.NANOSECONDS)); + } + + LOG.debug("Scanned {} container path jars, {} WEB-INF/lib jars, {} WEB-INF/classes dirs in {}ms for context {}", + (_containerPathStats == null ? -1 : _containerPathStats.getTotal()), + (_webInfLibStats == null ? -1 : _webInfLibStats.getTotal()), + (_webInfClassesStats == null ? -1 : _webInfClassesStats.getTotal()), + elapsedMs, + context); + } + + if (timeout) + me.add(new Exception("Timeout scanning annotations")); + me.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).booleanValue(); + } + //try server attribute to see if we should use multithreading + o = context.getServer().getAttribute(MULTI_THREADED); + if (o instanceof Boolean) + { + return ((Boolean)o).booleanValue(); + } + //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 != null && 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 != null && 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).intValue(); + } + + @Override + public void cloneConfigure(WebAppContext template, WebAppContext context) throws Exception + { + context.getObjectFactory().addDecorator(new AnnotationDecorator(context)); + } + + public void createServletContainerInitializerAnnotationHandlers(WebAppContext context, List scis) + throws Exception + { + if (scis == null || scis.isEmpty()) + return; // nothing to do + + 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().getName()), sci); + _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); + _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()); + _containerInitializerAnnotationHandlers.add(new ContainerInitializerAnnotationHandler(holder, c)); + } + + holder.addStartupClasses(c); + } + } + } + } + + public Resource getJarFor(ServletContainerInitializer service) + throws MalformedURLException, IOException + { + URI uri = TypeUtil.getLocationOfClass(service.getClass()); + if (uri == null) + return null; + return Resource.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 + * @throws Exception if unable to determine exclusion + */ + public boolean isFromExcludedJar(WebAppContext context, ServletContainerInitializer sci, Resource sciResource) + throws Exception + { + 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 sci the ServletContainerIntializer + * @return true if the ServletContainerIntializer is excluded + */ + public boolean matchesExclusionPattern(ServletContainerInitializer sci) + { + //no exclusion pattern, no SCI is excluded by it + if (_sciExcludePattern == null) + return false; + + //test if name of class matches the regex + if (LOG.isDebugEnabled()) + LOG.debug("Checking {} against containerInitializerExclusionPattern", sci.getClass().getName()); + return _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 context the web app context + * @return the list of non-excluded servlet container initializers + * @throws Exception if unable to get list + */ + public List getNonExcludedInitializers(WebAppContext context) + throws Exception + { + ArrayList nonExcludedInitializers = new ArrayList(); + + //We use the ServiceLoader mechanism to find the ServletContainerInitializer classes to inspect + long start = 0; + if (LOG.isDebugEnabled()) + start = System.nanoTime(); + 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(), context, e); + else + LOG.info("Error: {} for {}", e.getMessage(), context); + return Stream.of(); + } + }).collect(Collectors.toList()); + + if (LOG.isDebugEnabled()) + LOG.debug("Service loaders found in {}ms", (TimeUnit.MILLISECONDS.convert((System.nanoTime() - start), TimeUnit.NANOSECONDS))); + + 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(sci)) + { + if (LOG.isDebugEnabled()) + LOG.debug("{} excluded by pattern", sci); + continue; + } + + Resource sciResource = getJarFor(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()); + Collections.sort(nonExcludedInitializers, 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 + * @throws Exception if unable to scan + */ + public void parseContainerPath(final WebAppContext context, final AnnotationParser parser) throws Exception + { + //always parse for discoverable annotations as well as class hierarchy and servletcontainerinitializer related annotations + final Set handlers = new HashSet(); + handlers.addAll(_discoverableAnnotationHandlers); + handlers.addAll(_containerInitializerAnnotationHandlers); + if (_classInheritanceHandler != null) + handlers.add(_classInheritanceHandler); + + if (LOG.isDebugEnabled()) + _containerPathStats = new CounterStatistic(); + + //scan the container classpath jars that were selected by + //filtering in MetaInfConfiguration + for (Resource r : context.getMetaData().getContainerResources()) + { + if (_parserTasks != null) + { + ParserTask task = new ParserTask(parser, handlers, r); + _parserTasks.add(task); + if (LOG.isDebugEnabled()) + { + _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 context the context for the scan + * @param parser the annotation parser to use + * @throws Exception if unable to scan and/or parse + */ + public void parseWebInfLib(final WebAppContext context, 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. + List jars = context.getMetaData().getWebInfResources(context.getMetaData().isOrdered()); + + if (LOG.isDebugEnabled()) + { + if (_webInfLibStats == null) + _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 (f == null || !WebDescriptor.isMetaDataComplete(f) || _classInheritanceHandler != null || !_containerInitializerAnnotationHandlers.isEmpty()) + { + //register the classinheritance handler if there is one + if (_classInheritanceHandler != null) + handlers.add(_classInheritanceHandler); + + //register the handlers for the @HandlesTypes values that are themselves annotations if there are any + handlers.addAll(_containerInitializerAnnotationHandlers); + + //only register the discoverable annotation handlers if this fragment is not metadata complete, or has no fragment descriptor + if (f == null || !WebDescriptor.isMetaDataComplete(f)) + handlers.addAll(_discoverableAnnotationHandlers); + + if (_parserTasks != null) + { + ParserTask task = new ParserTask(parser, handlers, r); + _parserTasks.add(task); + if (LOG.isDebugEnabled()) + { + _webInfLibStats.increment(); + task.setStatistic(new TimeStatistic()); + } + } + } + } + } + + /** + * Scan classes in WEB-INF/classes. + * + * @param context the context for the scan + * @param parser the annotation parser to use + * @throws Exception if unable to scan and/or parse + */ + public void parseWebInfClasses(final WebAppContext context, final AnnotationParser parser) + throws Exception + { + Set handlers = new HashSet(); + handlers.addAll(_discoverableAnnotationHandlers); + if (_classInheritanceHandler != null) + handlers.add(_classInheritanceHandler); + handlers.addAll(_containerInitializerAnnotationHandlers); + + if (LOG.isDebugEnabled()) + _webInfClassesStats = new CounterStatistic(); + + for (Resource dir : context.getMetaData().getWebInfClassesResources()) + { + if (_parserTasks != null) + { + ParserTask task = new ParserTask(parser, handlers, dir); + _parserTasks.add(task); + if (LOG.isDebugEnabled()) + { + _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-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/AnnotationDecorator.java b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/AnnotationDecorator.java new file mode 100644 index 00000000000..3a58fef077c --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/AnnotationDecorator.java @@ -0,0 +1,84 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.annotations; + +import java.util.Objects; + +import org.eclipse.jetty.ee10.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 Object decorate(Object o) + { + introspect(o, DecoratedObjectFactory.getAssociatedInfo()); + return o; + } + + @Override + public void destroy(Object o) + { + + } +} diff --git a/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/AnnotationIntrospector.java b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/AnnotationIntrospector.java new file mode 100644 index 00000000000..a02ad55647f --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/AnnotationIntrospector.java @@ -0,0 +1,223 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.annotations; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +import org.eclipse.jetty.ee10.servlet.BaseHolder; +import org.eclipse.jetty.ee10.servlet.Source.Origin; +import org.eclipse.jetty.ee10.webapp.WebAppContext; +import org.eclipse.jetty.ee10.webapp.WebDescriptor; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.util.thread.AutoLock; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * AnnotationIntrospector + * 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; + + /** + * IntrospectableAnnotationHandler + * + * Interface for all handlers that wish to introspect a class to find a particular annotation + */ + public interface IntrospectableAnnotationHandler + { + public void handle(Class clazz); + } + + /** + * AbstractIntrospectableAnnotationHandler + * + * 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; + } + + @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 + + @SuppressWarnings("rawtypes") + BaseHolder holder = null; + + 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; + + String descriptorLocation = holder.getSource().getResource(); + if (descriptorLocation == null) + return true; //no descriptor, can't be metadata-complete + try + { + return !WebDescriptor.isMetaDataComplete(_context.getMetaData().getFragmentDescriptor(Resource.newResource(descriptorLocation))); + } + catch (IOException e) + { + LOG.warn("Unable to get Resource for descriptor {}", descriptorLocation, e); + return false; //something wrong with the descriptor + } + } + } + } + + /** + * + */ + public void introspect(Object o, Object metaInfo) + { + if (!isIntrospectable(o, metaInfo)) + return; + + Class clazz = o.getClass(); + + try (AutoLock l = _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-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/AnnotationParser.java b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/AnnotationParser.java new file mode 100644 index 00000000000..39eff8a7c78 --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/AnnotationParser.java @@ -0,0 +1,978 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.annotations; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.net.URL; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import org.eclipse.jetty.util.JavaVersion; +import org.eclipse.jetty.util.Loader; +import org.eclipse.jetty.util.MultiException; +import org.eclipse.jetty.util.MultiReleaseJarFile; +import org.eclipse.jetty.util.StringUtil; +import org.eclipse.jetty.util.TypeUtil; +import org.eclipse.jetty.util.resource.Resource; +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 _javaPlatform; + 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. + */ + 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.isPresent()) + 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 list 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 static interface Handler + { + public void handle(ClassInfo classInfo); + + public void handle(MethodInfo methodInfo); + + public void handle(FieldInfo fieldInfo); + + public void handle(ClassInfo info, String annotationName); + + public void handle(MethodInfo info, String annotationName); + + public 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(JavaVersion.VERSION.getPlatform()); + } + + /** + * @param javaPlatform The target java version or 0 for the current runtime. + */ + public AnnotationParser(int javaPlatform) + { + _asmVersion = ASM_VERSION; + if (javaPlatform == 0) + javaPlatform = JavaVersion.VERSION.getPlatform(); + _javaPlatform = javaPlatform; + } + + public AnnotationParser(int javaPlatform, int asmVersion) + { + if (javaPlatform == 0) + javaPlatform = JavaVersion.VERSION.getPlatform(); + _javaPlatform = javaPlatform; + if (asmVersion == 0) + asmVersion = ASM_VERSION; + _asmVersion = asmVersion; + } + + /** + * Add a class as having been parsed. + * + * @param classname the name of the class + * @param location the fully qualified location of the class + */ + public void addParsedClass(String classname, Resource location) + { + Resource existing = _parsedClassNames.putIfAbsent(classname, location); + if (existing != null) + LOG.warn("{} scanned from multiple locations: {}, {}", classname, existing, location); + } + + /** + * Parse a given class + * + * @param handlers the set of handlers to find class + * @param className the class name to parse + * @throws Exception if unable to parse + */ + public void parse(Set handlers, String className) throws Exception + { + if (className == null) + return; + + String classRef = TypeUtil.toClassReference(className); + URL resource = Loader.getResource(classRef); + if (resource != null) + { + Resource r = Resource.newResource(resource); + addParsedClass(className, r); + try (InputStream is = r.getInputStream()) + { + scanClass(handlers, null, is); + } + } + } + + /** + * Parse the given class, optionally walking its inheritance hierarchy + * + * @param handlers the handlers to look for class in + * @param clazz the class to look for + * @param visitSuperClasses if true, also visit super classes for parse + * @throws Exception if unable to parse class + */ + public void parse(Set handlers, Class clazz, boolean visitSuperClasses) throws Exception + { + Class cz = clazz; + while (cz != Object.class) + { + String nameAsResource = TypeUtil.toClassReference(cz); + URL resource = Loader.getResource(nameAsResource); + if (resource != null) + { + Resource r = Resource.newResource(resource); + addParsedClass(clazz.getName(), r); + try (InputStream is = r.getInputStream()) + { + scanClass(handlers, null, is); + } + } + + if (visitSuperClasses) + cz = cz.getSuperclass(); + else + break; + } + } + + /** + * Parse the given classes + * + * @param handlers the set of handlers to look for class in + * @param classNames the class name + * @throws Exception if unable to parse + */ + public void parse(Set handlers, String[] classNames) throws Exception + { + if (classNames == null) + return; + + parse(handlers, Arrays.asList(classNames)); + } + + /** + * Parse the given classes + * + * @param handlers the set of handlers to look for class in + * @param classNames the class names + * @throws Exception if unable to parse + */ + public void parse(Set handlers, List classNames) throws Exception + { + MultiException me = new MultiException(); + + for (String className : classNames) + { + try + { + String classRef = TypeUtil.toClassReference(className); + URL resource = Loader.getResource(classRef); + if (resource != null) + { + Resource r = Resource.newResource(resource); + addParsedClass(className, r); + try (InputStream is = r.getInputStream()) + { + scanClass(handlers, null, is); + } + } + } + catch (Exception e) + { + me.add(new RuntimeException("Error scanning class " + className, e)); + } + } + me.ifExceptionThrow(); + } + + /** + * Parse classes in the supplied uris. + * + * @param handlers the handlers to look for classes in + * @param uris the uris for the jars + * @throws Exception if unable to parse + */ + public void parse(final Set handlers, final URI[] uris) throws Exception + { + if (uris == null) + return; + + MultiException me = new MultiException(); + + for (URI uri : uris) + { + try + { + parse(handlers, uri); + } + catch (Exception e) + { + me.add(new RuntimeException("Problem parsing classes from " + uri, e)); + } + } + me.ifExceptionThrow(); + } + + /** + * Parse a particular uri + * + * @param handlers the handlers to look for classes in + * @param uri the uri for the jar + * @throws Exception if unable to parse + */ + public void parse(final Set handlers, URI uri) throws Exception + { + if (uri == null) + return; + + parse(handlers, Resource.newResource(uri)); + } + + /** + * 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() && r.isDirectory()) + { + parseDir(handlers, r); + return; + } + + String fullname = r.toString(); + if (fullname.endsWith(".jar")) + { + parseJar(handlers, r); + return; + } + + if (fullname.endsWith(".class")) + { + try (InputStream is = r.getInputStream()) + { + scanClass(handlers, null, is); + return; + } + } + + if (LOG.isDebugEnabled()) + LOG.warn("Resource not scannable for classes: {}", r); + } + + /** + * Parse all classes in a directory + * + * @param handlers the set of handlers to look for classes in + * @param root the resource directory to look for classes + * @throws Exception if unable to parse + */ + protected void parseDir(Set handlers, Resource root) throws Exception + { + if (!root.isDirectory() || !root.exists() || root.getName().startsWith(".")) + return; + + if (LOG.isDebugEnabled()) + LOG.debug("Scanning dir {}", root); + + File rootFile = root.getFile(); + + MultiException me = new MultiException(); + Collection resources = root.getAllResources(); + if (resources != null) + { + for (Resource r : resources) + { + if (r.isDirectory()) + continue; + + File file = r.getFile(); + if (isValidClassFileName((file == null ? null : file.getName()))) + { + Path classpath = rootFile.toPath().relativize(file.toPath()); + String str = classpath.toString(); + str = str.substring(0, str.lastIndexOf(".class")); + str = StringUtil.replace(str, File.separatorChar, '.'); + + try + { + if (LOG.isDebugEnabled()) + LOG.debug("Scanning class {}", r); + addParsedClass(str, r); + try (InputStream is = r.getInputStream()) + { + scanClass(handlers, Resource.newResource(file.getParentFile()), is); + } + } + catch (Exception ex) + { + if (LOG.isDebugEnabled()) + LOG.debug("Error scanning file {}", file, ex); + me.add(new RuntimeException("Error scanning file " + file, ex)); + } + } + else + { + if (LOG.isDebugEnabled()) + LOG.debug("Skipping scan on invalid file {}", file); + } + } + } + + me.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 (jarResource.toString().endsWith(".jar")) + { + if (LOG.isDebugEnabled()) + LOG.debug("Scanning jar {}", jarResource); + + MultiException me = new MultiException(); + try (MultiReleaseJarFile jarFile = new MultiReleaseJarFile(jarResource.getFile(), _javaPlatform, false)) + { + jarFile.stream().forEach(e -> + { + try + { + parseJarEntry(handlers, jarResource, e); + } + catch (Exception ex) + { + me.add(new RuntimeException("Error scanning entry " + e.getName() + " from jar " + jarResource, ex)); + } + }); + } + me.ifExceptionThrow(); + } + } + + /** + * Parse a single entry in a jar file + * + * @param handlers the handlers to look for classes in + * @param entry the entry in the potentially MultiRelease jar resource to parse + * @param jar the jar file + * @throws Exception if unable to parse + */ + protected void parseJarEntry(Set handlers, Resource jar, MultiReleaseJarFile.VersionedJarEntry entry) + throws Exception + { + if (jar == null || entry == null) + return; + + //skip directories + if (entry.isDirectory()) + return; + + String name = entry.getName(); + + //check file is a valid class file name + if (isValidClassFileName(name) && isValidClassFilePath(name)) + { + String shortName = StringUtil.replace(name, '/', '.').substring(0, name.length() - 6); + addParsedClass(shortName, Resource.newResource("jar:" + jar.getURI() + "!/" + entry.getNameInJar())); + if (LOG.isDebugEnabled()) + LOG.debug("Scanning class from jar {}!/{}", jar, entry); + try (InputStream is = entry.getInputStream()) + { + scanClass(handlers, jar, is); + } + } + } + + /** + * 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 is the input stream to parse + * @throws IOException if unable to parse + */ + protected void scanClass(Set handlers, Resource containingResource, InputStream is) throws IOException + { + ClassReader reader = new ClassReader(is); + reader.accept(new MyClassVisitor(handlers, containingResource, _asmVersion), ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); + } + + /** + * Remove any parsed class names. + */ + public void resetParsedClasses() + { + _parsedClassNames.clear(); + } + + /** + * Check that the given path represents a valid class file name. + * The check is fairly cursory, checking that: + *

    + *
  • the name ends with .class
  • + *
  • it isn't a dot file or in a hidden directory
  • + *
  • the name of the class at least begins with a valid identifier for a class name
  • + *
+ * + * @param name the class file name + * @return whether the class file name is valid + */ + public boolean isValidClassFileName(String name) + { + //no name cannot be valid + if (name == null || name.length() == 0) + return false; + + //skip anything that is not a class file + String lc = name.toLowerCase(Locale.ENGLISH); + if (!lc.endsWith(".class")) + { + if (LOG.isDebugEnabled()) + LOG.debug("Not a class: {}", name); + return false; + } + + if (lc.equals("module-info.class")) + { + if (LOG.isDebugEnabled()) + LOG.debug("Skipping module-info.class"); + return false; + } + + //skip any classfiles that are not a valid java identifier + int c0 = 0; + int ldir = name.lastIndexOf('/', name.length() - 6); + c0 = (ldir > -1 ? ldir + 1 : c0); + if (!Character.isJavaIdentifierStart(name.charAt(c0))) + { + if (LOG.isDebugEnabled()) + LOG.debug("Not a java identifier: {}", name); + return false; + } + + return true; + } + + /** + * Check that the given path does not contain hidden directories + * + * @param path the class file path + * @return whether the class file path is valid + */ + public boolean isValidClassFilePath(String path) + { + //no path is not valid + if (path == null || path.length() == 0) + return false; + + // skip any classfiles that are in a hidden directory + if (path.startsWith(".") || path.contains("/.")) + { + if (LOG.isDebugEnabled()) + LOG.debug("Contains hidden dirs: {}", path); + return false; + } + + return true; + } +} diff --git a/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/ClassInheritanceHandler.java b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/ClassInheritanceHandler.java new file mode 100644 index 00000000000..1dbb8564121 --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/ClassInheritanceHandler.java @@ -0,0 +1,81 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.annotations; + +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * ClassInheritanceHandler + * + * 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-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/ContainerInitializerAnnotationHandler.java b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/ContainerInitializerAnnotationHandler.java new file mode 100644 index 00000000000..f657aa341dd --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/ContainerInitializerAnnotationHandler.java @@ -0,0 +1,104 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.annotations; + +import java.util.Objects; + +import org.eclipse.jetty.ee10.plus.annotation.ContainerInitializer; +import org.eclipse.jetty.ee10.servlet.ServletContainerInitializerHolder; + +/** + * ContainerInitializerAnnotationHandler + *

+ * 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 ContainerInitializer _initializer; + final ServletContainerInitializerHolder _holder; + final Class _annotation; + + @Deprecated + public ContainerInitializerAnnotationHandler(ContainerInitializer initializer, Class annotation) + { + _holder = null; + _annotation = Objects.requireNonNull(annotation); + _initializer = initializer; + } + + public ContainerInitializerAnnotationHandler(ServletContainerInitializerHolder holder, Class annotation) + { + _holder = Objects.requireNonNull(holder); + _annotation = Objects.requireNonNull(annotation); + _initializer = null; + } + + /** + * 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; + + if (_initializer != null) + _initializer.addAnnotatedTypeName(info.getClassName()); + else + _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; + + if (_initializer != null) + _initializer.addAnnotatedTypeName(info.getClassInfo().getClassName()); + else + _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; + if (_initializer != null) + _initializer.addAnnotatedTypeName(info.getClassInfo().getClassName()); + else + _holder.addStartupClasses(info.getClassInfo().getClassName()); + } + + @Deprecated + public ContainerInitializer getContainerInitializer() + { + return _initializer; + } +} diff --git a/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/DeclareRolesAnnotationHandler.java b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/DeclareRolesAnnotationHandler.java new file mode 100644 index 00000000000..daacb1c82f0 --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/DeclareRolesAnnotationHandler.java @@ -0,0 +1,64 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.annotations; + +import jakarta.annotation.security.DeclareRoles; +import jakarta.servlet.Servlet; +import org.eclipse.jetty.ee10.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler; +import org.eclipse.jetty.ee10.servlet.security.ConstraintAware; +import org.eclipse.jetty.ee10.servlet.security.ConstraintSecurityHandler; +import org.eclipse.jetty.ee10.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 = (DeclareRoles)clazz.getAnnotation(DeclareRoles.class); + if (declareRoles == null) + return; + + String[] roles = declareRoles.value(); + + if (roles != null && roles.length > 0) + { + for (String r : roles) + { + ((ConstraintSecurityHandler)_context.getSecurityHandler()).addRole(r); + _context.getMetaData().setOrigin("security-role." + r, declareRoles, clazz); + } + } + } +} diff --git a/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/MultiPartConfigAnnotationHandler.java b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/MultiPartConfigAnnotationHandler.java new file mode 100644 index 00000000000..fab03549372 --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/MultiPartConfigAnnotationHandler.java @@ -0,0 +1,64 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.annotations; + +import jakarta.servlet.MultipartConfigElement; +import jakarta.servlet.Servlet; +import jakarta.servlet.annotation.MultipartConfig; +import org.eclipse.jetty.ee10.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; +import org.eclipse.jetty.ee10.webapp.Descriptor; +import org.eclipse.jetty.ee10.webapp.MetaData; +import org.eclipse.jetty.ee10.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 = (MultipartConfig)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-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/PostConstructAnnotationHandler.java b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/PostConstructAnnotationHandler.java new file mode 100644 index 00000000000..89b48dcd4c1 --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/PostConstructAnnotationHandler.java @@ -0,0 +1,101 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.annotations; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +import jakarta.annotation.PostConstruct; +import org.eclipse.jetty.ee10.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler; +import org.eclipse.jetty.ee10.plus.annotation.LifeCycleCallbackCollection; +import org.eclipse.jetty.ee10.plus.annotation.PostConstructCallback; +import org.eclipse.jetty.ee10.webapp.MetaData; +import org.eclipse.jetty.ee10.webapp.Origin; +import org.eclipse.jetty.ee10.webapp.WebAppContext; + +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 (int i = 0; i < methods.length; i++) + { + Method m = (Method)methods[i]; + if (m.isAnnotationPresent(PostConstruct.class)) + { + if (m.getParameterCount() != 0) + throw new IllegalStateException(m + " has parameters"); + if (m.getReturnType() != Void.TYPE) + throw new IllegalStateException(m + " is not void"); + if (m.getExceptionTypes().length != 0) + throw new IllegalStateException(m + " throws checked exceptions"); + if (Modifier.isStatic(m.getModifiers())) + throw new IllegalStateException(m + " 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. + MetaData metaData = _context.getMetaData(); + Origin origin = metaData.getOrigin("post-construct"); + if (origin != null && + (origin == Origin.WebXml || + origin == Origin.WebDefaults || + origin == Origin.WebOverride)) + return; + + PostConstructCallback callback = new PostConstructCallback(clazz, m.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) + { + if (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)) + return true; + + return false; + } +} diff --git a/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/PreDestroyAnnotationHandler.java b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/PreDestroyAnnotationHandler.java new file mode 100644 index 00000000000..c9865d72e4c --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/PreDestroyAnnotationHandler.java @@ -0,0 +1,103 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.annotations; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +import jakarta.annotation.PreDestroy; +import org.eclipse.jetty.ee10.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler; +import org.eclipse.jetty.ee10.plus.annotation.LifeCycleCallbackCollection; +import org.eclipse.jetty.ee10.plus.annotation.PreDestroyCallback; +import org.eclipse.jetty.ee10.webapp.MetaData; +import org.eclipse.jetty.ee10.webapp.Origin; +import org.eclipse.jetty.ee10.webapp.WebAppContext; + +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 (int i = 0; i < methods.length; i++) + { + Method m = (Method)methods[i]; + if (m.isAnnotationPresent(PreDestroy.class)) + { + if (m.getParameterCount() != 0) + throw new IllegalStateException(m + " has parameters"); + if (m.getReturnType() != Void.TYPE) + throw new IllegalStateException(m + " is not void"); + if (m.getExceptionTypes().length != 0) + throw new IllegalStateException(m + " throws checked exceptions"); + if (Modifier.isStatic(m.getModifiers())) + throw new IllegalStateException(m + " 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. + MetaData metaData = _context.getMetaData(); + Origin origin = metaData.getOrigin("pre-destroy"); + if (origin != null && + (origin == Origin.WebXml || + origin == Origin.WebDefaults || + origin == Origin.WebOverride)) + return; + + PreDestroyCallback callback = new PreDestroyCallback(clazz, m.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) + { + if (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)) + return true; + + return false; + } +} diff --git a/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/ResourceAnnotationHandler.java b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/ResourceAnnotationHandler.java new file mode 100644 index 00000000000..297845968c8 --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/ResourceAnnotationHandler.java @@ -0,0 +1,394 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.annotations; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Arrays; +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.ee10.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler; +import org.eclipse.jetty.ee10.plus.annotation.Injection; +import org.eclipse.jetty.ee10.plus.annotation.InjectionCollection; +import org.eclipse.jetty.ee10.plus.jndi.NamingEntryUtil; +import org.eclipse.jetty.ee10.webapp.MetaData; +import org.eclipse.jetty.ee10.webapp.WebAppContext; +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 = + Arrays.asList(new Class[] + { + 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 (int i = 0; i < methods.length; i++) + { + handleMethod(clazz, methods[i]); + } + Field[] fields = clazz.getDeclaredFields(); + //For each field, get all of it's annotations + for (int i = 0; i < fields.length; i++) + { + handleField(clazz, fields[i]); + } + } + } + + public void handleClass(Class clazz) + { + Resource resource = (Resource)clazz.getAnnotation(Resource.class); + if (resource != null) + { + String name = resource.name(); + String mappedName = resource.mappedName(); + + if (name == null || name.trim().equals("")) + 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 = (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().equals("") ? resource.name() : name); + String mappedName = (resource.mappedName() != null && !resource.mappedName().trim().equals("") ? 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 + { + boolean bound = NamingEntryUtil.bindToENC(_context, name, mappedName); + if (!bound) + bound = NamingEntryUtil.bindToENC(_context.getServer(), name, mappedName); + 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) + { + bound = false; + } + } + //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 = (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().equals("") ? resource.name() : name); + String mappedName = (resource.mappedName() != null && !resource.mappedName().trim().equals("") ? 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 environment first + boolean bound = NamingEntryUtil.bindToENC(_context, name, mappedName); + + //try the server's environment + if (!bound) + bound = NamingEntryUtil.bindToENC(_context.getServer(), name, mappedName); + + //try the jvm's environment + 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) + { + bound = false; + } + } + + 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) + { + if (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)) + return true; + + return false; + } + + /** + * 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-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/ResourcesAnnotationHandler.java b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/ResourcesAnnotationHandler.java new file mode 100644 index 00000000000..86abac8e699 --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/ResourcesAnnotationHandler.java @@ -0,0 +1,72 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.annotations; + +import javax.naming.NamingException; + +import jakarta.annotation.Resource; +import jakarta.annotation.Resources; +import org.eclipse.jetty.ee10.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler; +import org.eclipse.jetty.ee10.plus.jndi.NamingEntryUtil; +import org.eclipse.jetty.ee10.webapp.WebAppContext; +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 = (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 (int j = 0; j < resArray.length; j++) + { + String name = resArray[j].name(); + String mappedName = resArray[j].mappedName(); + + if (name == null || name.trim().equals("")) + 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-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/RunAsAnnotationHandler.java b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/RunAsAnnotationHandler.java new file mode 100644 index 00000000000..ed1eca8d5e1 --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/RunAsAnnotationHandler.java @@ -0,0 +1,75 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.annotations; + +import jakarta.servlet.Servlet; +import org.eclipse.jetty.ee10.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; +import org.eclipse.jetty.ee10.webapp.Descriptor; +import org.eclipse.jetty.ee10.webapp.MetaData; +import org.eclipse.jetty.ee10.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 = (jakarta.annotation.security.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-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/ServletContainerInitializersStarter.java b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/ServletContainerInitializersStarter.java new file mode 100644 index 00000000000..ee0975ea2c9 --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/ServletContainerInitializersStarter.java @@ -0,0 +1,65 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.annotations; + +import java.util.List; + +import org.eclipse.jetty.ee10.plus.annotation.ContainerInitializer; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.webapp.WebAppContext; +import org.eclipse.jetty.util.component.AbstractLifeCycle; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * ServletContainerInitializersStarter + * + * Call the onStartup() method on all ServletContainerInitializers, after having + * found all applicable classes (if any) to pass in as args. + * @deprecated + */ +@Deprecated +public class ServletContainerInitializersStarter extends AbstractLifeCycle implements ServletContextHandler.ServletContainerInitializerCaller +{ + private static final Logger LOG = LoggerFactory.getLogger(ServletContainerInitializersStarter.class); + WebAppContext _context; + + public ServletContainerInitializersStarter(WebAppContext context) + { + _context = context; + } + + @Override + public void doStart() + { + List initializers = (List)_context.getAttribute(AnnotationConfiguration.CONTAINER_INITIALIZERS); + if (initializers == null) + return; + + for (ContainerInitializer i : initializers) + { + try + { + if (LOG.isDebugEnabled()) + LOG.debug("Calling ServletContainerInitializer {}", i.getTarget().getClass().getName()); + i.callStartup(_context); + } + catch (Exception e) + { + LOG.warn("Failed to call startup on {}", i, e); + throw new RuntimeException(e); + } + } + } +} diff --git a/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/ServletSecurityAnnotationHandler.java b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/ServletSecurityAnnotationHandler.java new file mode 100644 index 00000000000..26d2fdab2d0 --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/ServletSecurityAnnotationHandler.java @@ -0,0 +1,184 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.annotations; + +import java.util.ArrayList; +import java.util.List; + +import jakarta.servlet.ServletSecurityElement; +import jakarta.servlet.annotation.ServletSecurity; +import jakarta.servlet.annotation.ServletSecurity.EmptyRoleSemantic; +import jakarta.servlet.annotation.ServletSecurity.TransportGuarantee; +import org.eclipse.jetty.ee10.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletMapping; +import org.eclipse.jetty.ee10.servlet.security.ConstraintAware; +import org.eclipse.jetty.ee10.servlet.security.ConstraintMapping; +import org.eclipse.jetty.ee10.servlet.security.ConstraintSecurityHandler; +import org.eclipse.jetty.ee10.webapp.WebAppContext; +import org.eclipse.jetty.util.security.Constraint; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * ServletSecurityAnnotationHandler + * + * 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)) + { + LOG.warn("SecurityHandler not ConstraintAware, skipping security annotation processing"); + return; + } + + ServletSecurity 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 + ConstraintAware securityHandler = (ConstraintAware)_context.getSecurityHandler(); + + for (ConstraintMapping m : constraintMappings) + { + securityHandler.addConstraintMapping(m); + } + + //Servlet Spec 3.1 requires paths with uncovered http methods to be reported + securityHandler.checkPathsWithUncoveredHttpMethods(); + } + + /** + * Make a jetty Constraint object, which represents the <auth-constraint> and + * <user-data-constraint> elements, based on the security annotation. + * + * @param servlet the servlet + * @param rolesAllowed the roles allowed + * @param permitOrDeny the role / permission semantic + * @param transport the transport guarantee + * @return the constraint + */ + protected Constraint makeConstraint(Class servlet, String[] rolesAllowed, EmptyRoleSemantic permitOrDeny, TransportGuarantee transport) + { + return ConstraintSecurityHandler.createConstraint(servlet.getName(), rolesAllowed, permitOrDeny, transport); + } + + /** + * 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 (int j = 0; j < pathSpecs.length; j++) + { + //TODO decide if we need to check the origin + if (pathSpecs[j].equals(constraintMappings.get(i).getPathSpec())) + { + exists = true; + break; + } + } + } + } + return exists; + } +} diff --git a/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/WebFilterAnnotation.java b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/WebFilterAnnotation.java new file mode 100644 index 00000000000..88b740eddf4 --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/WebFilterAnnotation.java @@ -0,0 +1,208 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.annotations; + +import java.util.ArrayList; +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.ee10.servlet.FilterHolder; +import org.eclipse.jetty.ee10.servlet.FilterMapping; +import org.eclipse.jetty.ee10.servlet.Source; +import org.eclipse.jetty.ee10.webapp.DiscoveredAnnotation; +import org.eclipse.jetty.ee10.webapp.MetaData; +import org.eclipse.jetty.ee10.webapp.Origin; +import org.eclipse.jetty.ee10.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().equals("") ? 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.getName())); + 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[paths.size()])); + } + + if (filterAnnotation.servletNames().length > 0) + { + ArrayList names = new ArrayList(); + for (String s : filterAnnotation.servletNames()) + { + names.add(s); + } + mapping.setServletNames(names.toArray(new String[names.size()])); + } + + EnumSet dispatcherSet = EnumSet.noneOf(DispatcherType.class); + for (DispatcherType d : filterAnnotation.dispatcherTypes()) + { + dispatcherSet.add(d); + } + 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[paths.size()])); + } + if (filterAnnotation.servletNames().length > 0) + { + ArrayList names = new ArrayList(); + for (String s : filterAnnotation.servletNames()) + { + names.add(s); + } + mapping.setServletNames(names.toArray(new String[names.size()])); + } + + EnumSet dispatcherSet = EnumSet.noneOf(DispatcherType.class); + for (DispatcherType d : filterAnnotation.dispatcherTypes()) + { + dispatcherSet.add(d); + } + mapping.setDispatcherTypes(dispatcherSet); + _context.getServletHandler().addFilterMapping(mapping); + metaData.setOrigin(name + ".filter.mappings", filterAnnotation, clazz); + } + } + } +} diff --git a/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/WebFilterAnnotationHandler.java b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/WebFilterAnnotationHandler.java new file mode 100644 index 00000000000..220886a65c7 --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/WebFilterAnnotationHandler.java @@ -0,0 +1,57 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.annotations; + +import org.eclipse.jetty.ee10.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 (annotationName == null || !"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 (annotationName == null || !"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 (annotationName == null || !"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-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/WebListenerAnnotation.java b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/WebListenerAnnotation.java new file mode 100644 index 00000000000..31eda64e413 --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/WebListenerAnnotation.java @@ -0,0 +1,91 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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.ee10.servlet.ListenerHolder; +import org.eclipse.jetty.ee10.servlet.Source; +import org.eclipse.jetty.ee10.webapp.DiscoveredAnnotation; +import org.eclipse.jetty.ee10.webapp.MetaData; +import org.eclipse.jetty.ee10.webapp.Origin; +import org.eclipse.jetty.ee10.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.getName())); + 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-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/WebListenerAnnotationHandler.java b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/WebListenerAnnotationHandler.java new file mode 100644 index 00000000000..ea37b7e60e8 --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/WebListenerAnnotationHandler.java @@ -0,0 +1,54 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.annotations; + +import org.eclipse.jetty.ee10.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 (annotationName == null || !"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 (annotationName == null || !"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 (annotationName == null || !"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-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/WebServletAnnotation.java b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/WebServletAnnotation.java new file mode 100644 index 00000000000..b51b8694b1f --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/WebServletAnnotation.java @@ -0,0 +1,269 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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.ee10.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletMapping; +import org.eclipse.jetty.ee10.servlet.Source; +import org.eclipse.jetty.ee10.webapp.DiscoveredAnnotation; +import org.eclipse.jetty.ee10.webapp.MetaData; +import org.eclipse.jetty.ee10.webapp.Origin; +import org.eclipse.jetty.ee10.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 = (WebServlet)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().equals("") ? 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.getName()); + + 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.xml + List existingMappings = getServletMappingsForServlet(servletName); + + //if any mappings for this servlet already set by a descriptor that is not webdefault.xml forget + //about processing these url mappings + if (existingMappings.isEmpty() || !containsNonDefaultMappings(existingMappings)) + { + mapping = new ServletMapping(new Source(Source.Origin.ANNOTATION, clazz.getName())); + 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.xml + //that were for a different servlet eg a mapping in webdefault.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[allMappings.size()])); + } + } + + /** + * + */ + 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-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/WebServletAnnotationHandler.java b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/WebServletAnnotationHandler.java new file mode 100644 index 00000000000..b6595806581 --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/WebServletAnnotationHandler.java @@ -0,0 +1,64 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.annotations; + +import org.eclipse.jetty.ee10.webapp.WebAppContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * WebServletAnnotationHandler + * + * 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 (annotationName == null || !"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 (annotationName == null || !"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 (annotationName == null || !"jakarta.servlet.annotation.WebServlet".equals(annotationName)) + return; + + LOG.warn("@WebServlet annotation not supported for methods"); + } +} diff --git a/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/package-info.java b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/package-info.java new file mode 100644 index 00000000000..e756b77568a --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/package-info.java @@ -0,0 +1,17 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 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.ee10.annotations; diff --git a/jetty-ee10/jetty-ee10-annotations/src/main/resources/META-INF/services/org.eclipse.jetty.ee10.webapp.Configuration b/jetty-ee10/jetty-ee10-annotations/src/main/resources/META-INF/services/org.eclipse.jetty.ee10.webapp.Configuration new file mode 100644 index 00000000000..b553e9f28ae --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/main/resources/META-INF/services/org.eclipse.jetty.ee10.webapp.Configuration @@ -0,0 +1 @@ +org.eclipse.jetty.ee10.annotations.AnnotationConfiguration diff --git a/jetty-ee10/jetty-ee10-annotations/src/test/jar/test-sci-for-container-path.jar b/jetty-ee10/jetty-ee10-annotations/src/test/jar/test-sci-for-container-path.jar new file mode 100644 index 0000000000000000000000000000000000000000..c66593efae1dc2843d2097a901d4ae4d5601a47f GIT binary patch literal 3256 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 HGzJC$cCVw= literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-annotations/src/test/jar/test-sci-with-ordering.jar b/jetty-ee10/jetty-ee10-annotations/src/test/jar/test-sci-with-ordering.jar new file mode 100644 index 0000000000000000000000000000000000000000..37c5a2b48863a7c86553417f50801de7d6f56d12 GIT binary patch literal 3409 zcmWIWW@Zs#;Nak3Xj@$pz<>l;7+4sR^K7Xb)7ia zpDYQRBBp&nrDErt?jT|Bpx~0ZT5CmQxP>Mg2Z`1QpAPyg((~2ls}E4#%edI72hu65yFW*4}e?_P0<(~HvsJC%>OohK9MkUAj7?Nokbr{*S>rRM3A;!_Ds?Z5!j zOH9s9CCMCPJm!?77MJK2Cui!GXO?8><`<=;7G>t8>t-bukz|Dxc~%qvy;G5!(;DL2 ze_KIh&+qVveaxQQgTe(9^QVU03f2DVyf>)g^@OdAjN2~P$BQK$FEd-~|80-tGsE{M zzdXERmdbxld-3?e{Bzrn`cdKFOIp zo7T;o@Hp$!*VD#7JIp^$StK)G<>uTyvr-NlJb0+bX_sRb{pt13m(ey0pL%41Fo}a_w9qJ_(vFz%(l5c@ViBV@< zxu^7atSc73!Y!`I#8~4Hr(v?9z~Pu@fniw5lfFrky06a}C)_r_UzezPuxVPM4wudB zNjneJ>pfU>RJp#_Os2Y^mO1h2OPBduHw7u=T`gTbx!}zwzIb!niF*vhBJRwO^PDi{ z$@|l~Cw@s8tK2nj-fw;N%%7FHyR7m~pVC?ET_EUrr?F7>Q{J5OJ13sCn!m@Y@JpEK zg8;Gnei>6QEuXEay2#*{>(#m5-gU9(GiROSO!#hi{LeQ#-GXrEip6v9ue*D~{*6W? zyQNayoQsumu7cV>?;q&e56YRZU00OPWd!Cs7JSLwk^DqZP?TSgT2zvmTFiT5BiA7V z0fq~I_dQxr>?I_y<$`0zM83SH&ok$kItlNL(f+qr_I|eBoJbGLM~~aXZ&^4O-#0CA zD0g~poxZ#I_-Bhd7oy8sB2WFAnIwPeQc#41hl#D`i@H;xzPu-EChzl^cw*A0oO>!8 zHeLxSdLPAI5Aq*#;&UJQK{m-aro%5tOa}Lc2vgau}{v%(` zC?WFTzFX64c;43T+IiyDZVzX{=$;6l4X0-+nd>KWYB0 z>BdnqFJ9Q3ysG;!$zJw!;=<;o0SELw*Bml;9 zs4O!%wV0%$#TKgxS&7++MJ0)PAR}{9OZ0*vv~zx5Nn&PRYLRDNW=UpZPG(hVk@vat zSG|4C`CdJ(@8zj`>WugGVwO)$!QH2dg1qN^{_G`W{5h!G=ChHvk#Mom>Y(l&K<}_~ z^z*MsF#=i*!nnQ136up@VkDQzpm2d}gcZ9ag^M{>6C8n+1D>$ZOU_9wE}j~6)9bK< zh;4HA;?ng|3vX%}3uR z>^^5(eC~dLVrC{$-35J z_1v^yYi8DiqW4YOnV<3uSFTSvu_3y&dD+>!%Qi5VYEJp``kP&9Ux4|%3sVnVpY!30 z;a!!T>hD{$jCk1gE#7%4uscybKKFxko_kc)Hsz9um$F|KTE9uSF@J+gv^a10GpR*; ziL@&fwks&HV=_Rv28*EMxaI>Dsu%f03bMclIR0B6aw0NSuYw+FeNaYR@GWL*NrIIiHp;}MWOh`JNqI^-f6RCyu5CSdx3TZdki zp&J9z21@~;ati^Tu_74*FWu4gBNwHh(i{Oa*s$p*p=?LD3S>0Qsi3kM0geK#f)}{> ww4wU&AF?)hIgPF#Ip2cHWdv|wN3s^KpQutgz?&6lIRgVP5HbSe%L?ov00o~p`~Uy| literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-annotations/src/test/jar/test-sci.jar b/jetty-ee10/jetty-ee10-annotations/src/test/jar/test-sci.jar new file mode 100644 index 0000000000000000000000000000000000000000..8f35be4b125bc777422d4d1d38f684caa8af5f78 GIT binary patch literal 7749 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 SI3OV4!9UjUCEh}MdG$Y_2OCrX literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-annotations/src/test/java/org/acme/ClassOne.java b/jetty-ee10/jetty-ee10-annotations/src/test/java/org/acme/ClassOne.java new file mode 100644 index 00000000000..fbec15efda9 --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/test/java/org/acme/ClassOne.java @@ -0,0 +1,25 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/ClassA.java b/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/ClassA.java new file mode 100644 index 00000000000..28abaf79ecc --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/ClassA.java @@ -0,0 +1,91 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/ClassB.java b/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/ClassB.java new file mode 100644 index 00000000000..b42776d5408 --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/ClassB.java @@ -0,0 +1,50 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/FilterC.java b/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/FilterC.java new file mode 100644 index 00000000000..9615c691176 --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/FilterC.java @@ -0,0 +1,78 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/InterfaceD.java b/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/InterfaceD.java new file mode 100644 index 00000000000..f4716dafd8d --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/InterfaceD.java @@ -0,0 +1,22 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.annotations; + +/** + * InterfaceD + */ +public interface InterfaceD +{ + +} diff --git a/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/ListenerC.java b/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/ListenerC.java new file mode 100644 index 00000000000..824448ccb94 --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/ListenerC.java @@ -0,0 +1,35 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/Multi.java b/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/Multi.java new file mode 100644 index 00000000000..12f00c422bb --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/Multi.java @@ -0,0 +1,26 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/Sample.java b/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/Sample.java new file mode 100644 index 00000000000..b19fc696001 --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/Sample.java @@ -0,0 +1,26 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/ServletC.java b/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/ServletC.java new file mode 100644 index 00000000000..05a3641160e --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/ServletC.java @@ -0,0 +1,68 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/ServletD.java b/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/ServletD.java new file mode 100644 index 00000000000..7af04da5b74 --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/ServletD.java @@ -0,0 +1,26 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/ServletE.java b/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/ServletE.java new file mode 100644 index 00000000000..9d898ab88ae --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/ServletE.java @@ -0,0 +1,25 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.annotations; + +import jakarta.annotation.PreDestroy; +import jakarta.servlet.http.HttpServlet; + +public class ServletE extends HttpServlet +{ + @PreDestroy + public void preDestroy() + { + } +} diff --git a/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/TestAnnotationConfiguration.java b/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/TestAnnotationConfiguration.java new file mode 100644 index 00000000000..a9042ba25dd --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/TestAnnotationConfiguration.java @@ -0,0 +1,400 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.annotations; + +import java.io.File; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Arrays; +import java.util.List; + +import jakarta.servlet.ServletContainerInitializer; +import org.eclipse.jetty.ee10.webapp.RelativeOrdering; +import org.eclipse.jetty.ee10.webapp.WebAppContext; +import org.eclipse.jetty.ee10.webapp.WebDescriptor; +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.resource.Resource; +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.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class TestAnnotationConfiguration +{ + public class TestableAnnotationConfiguration extends AnnotationConfiguration + { + public void assertAnnotationDiscovery(boolean b) + { + if (!b) + assertTrue(_discoverableAnnotationHandlers.isEmpty()); + else + assertFalse(_discoverableAnnotationHandlers.isEmpty()); + } + } + + public File web25; + + public File web31false; + + public File web31true; + + public File jarDir; + + public File testSciJar; + + public File testContainerSciJar; + + public File testWebInfClassesJar; + + public File unpacked; + + public URLClassLoader containerLoader; + + public URLClassLoader webAppLoader; + + public List classes; + + public Resource targetClasses; + + public Resource webInfClasses; + + @BeforeEach + public void setup() throws Exception + { + web25 = MavenTestingUtils.getTestResourceFile("web25.xml"); + web31false = MavenTestingUtils.getTestResourceFile("web31false.xml"); + web31true = MavenTestingUtils.getTestResourceFile("web31true.xml"); + + // prepare an sci that will be on the webapp's classpath + jarDir = new File(MavenTestingUtils.getTestResourcesDir().getParentFile(), "jar"); + testSciJar = new File(jarDir, "test-sci.jar"); + assertTrue(testSciJar.exists()); + + testContainerSciJar = new File(jarDir, "test-sci-for-container-path.jar"); + testWebInfClassesJar = new File(jarDir, "test-sci-for-webinf.jar"); + + // unpack some classes to pretend that are in WEB-INF/classes + unpacked = new File(MavenTestingUtils.getTargetTestingDir(), "test-sci-for-webinf"); + unpacked.mkdirs(); + FS.cleanDirectory(unpacked); + JAR.unpack(testWebInfClassesJar, unpacked); + webInfClasses = Resource.newResource(unpacked); + + containerLoader = new URLClassLoader(new URL[]{ + testContainerSciJar.toURI().toURL() + }, Thread.currentThread().getContextClassLoader()); + + targetClasses = Resource.newResource(MavenTestingUtils.getTargetDir().toURI()).addPath("/test-classes"); + + classes = Arrays.asList(new Resource[]{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(); + 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(Resource.newResource(web25))); + context25.getContext().getServletContext().setEffectiveMajorVersion(2); + context25.getContext().getServletContext().setEffectiveMinorVersion(5); + config25.configure(context25); + config25.assertAnnotationDiscovery(false); + + //check that a 2.5 webapp discover annotations + TestableAnnotationConfiguration config25b = new TestableAnnotationConfiguration(); + WebAppContext context25b = new WebAppContext(); + context25b.setClassLoader(Thread.currentThread().getContextClassLoader()); + context25b.setAttribute(AnnotationConfiguration.MULTI_THREADED, Boolean.FALSE); + context25b.setAttribute(AnnotationConfiguration.MAX_SCAN_WAIT, 0); + context25b.getMetaData().setWebDescriptor(new WebDescriptor(Resource.newResource(web25))); + context25b.getContext().getServletContext().setEffectiveMajorVersion(2); + context25b.getContext().getServletContext().setEffectiveMinorVersion(5); + config25b.configure(context25b); + config25b.assertAnnotationDiscovery(true); + + //check that a 3.x webapp with metadata true won't discover annotations + TestableAnnotationConfiguration config31 = new TestableAnnotationConfiguration(); + WebAppContext context31 = new WebAppContext(); + context31.setClassLoader(Thread.currentThread().getContextClassLoader()); + context31.setAttribute(AnnotationConfiguration.MULTI_THREADED, Boolean.FALSE); + context31.setAttribute(AnnotationConfiguration.MAX_SCAN_WAIT, 0); + context31.getMetaData().setWebDescriptor(new WebDescriptor(Resource.newResource(web31true))); + context31.getContext().getServletContext().setEffectiveMajorVersion(3); + context31.getContext().getServletContext().setEffectiveMinorVersion(1); + config31.configure(context31); + config31.assertAnnotationDiscovery(false); + + //check that a 3.x webapp with metadata false will discover annotations + TestableAnnotationConfiguration config31b = new TestableAnnotationConfiguration(); + WebAppContext context31b = new WebAppContext(); + context31b.setClassLoader(Thread.currentThread().getContextClassLoader()); + context31b.setAttribute(AnnotationConfiguration.MULTI_THREADED, Boolean.FALSE); + context31b.setAttribute(AnnotationConfiguration.MAX_SCAN_WAIT, 0); + context31b.getMetaData().setWebDescriptor(new WebDescriptor(Resource.newResource(web31false))); + context31b.getContext().getServletContext().setEffectiveMajorVersion(3); + context31b.getContext().getServletContext().setEffectiveMinorVersion(1); + config31b.configure(context31b); + config31b.assertAnnotationDiscovery(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(); + List scis; + + //test 3.1 webapp loads both server and app scis + context.setClassLoader(webAppLoader); + context.getMetaData().addWebInfResource(Resource.newResource(testSciJar.toURI().toURL())); + context.getMetaData().setWebDescriptor(new WebDescriptor(Resource.newResource(web31true))); + context.getMetaData().setWebInfClassesResources(classes); + context.getContext().getServletContext().setEffectiveMajorVersion(3); + context.getContext().getServletContext().setEffectiveMinorVersion(1); + scis = config.getNonExcludedInitializers(context); + 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 + { + super.createServletContainerInitializerAnnotationHandlers(context, scis); + //check class hierarchy scanner handler is registered + assertNotNull(_classInheritanceHandler); + //check + assertEquals(1, _containerInitializerAnnotationHandlers.size()); + ContainerInitializerAnnotationHandler handler = _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(); + List scis; + + context.setClassLoader(webAppLoader); + context.getMetaData().addWebInfResource(Resource.newResource(testSciJar.toURI().toURL())); + context.getMetaData().setWebDescriptor(new WebDescriptor(Resource.newResource(web31true))); + context.getMetaData().setWebInfClassesResources(classes); + context.getContext().getServletContext().setEffectiveMajorVersion(3); + context.getContext().getServletContext().setEffectiveMinorVersion(1); + scis = config.getNonExcludedInitializers(context); + 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(); + 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(Resource.newResource(web31false))); + context.getMetaData().setWebInfClassesResources(classes); + context.getMetaData().addWebInfResource(Resource.newResource(testSciJar.toURI().toURL())); + context.getContext().getServletContext().setEffectiveMajorVersion(3); + context.getContext().getServletContext().setEffectiveMinorVersion(1); + scis = config.getNonExcludedInitializers(context); + 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(); + + File orderedFragmentJar = new File(jarDir, "test-sci-with-ordering.jar"); + assertTrue(orderedFragmentJar.exists()); + 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(); + List scis; + context.setClassLoader(orderedLoader); + context.getMetaData().setWebDescriptor(new WebDescriptor(Resource.newResource(web31true))); + RelativeOrdering ordering = new RelativeOrdering(context.getMetaData()); + context.getMetaData().setOrdering(ordering); + context.getMetaData().addWebInfResource(Resource.newResource(orderedFragmentJar.toURI().toURL())); + context.getMetaData().addWebInfResource(Resource.newResource(testSciJar.toURI().toURL())); + context.getMetaData().setWebInfClassesResources(classes); + context.getMetaData().orderFragments(); + context.getContext().getServletContext().setEffectiveMajorVersion(3); + context.getContext().getServletContext().setEffectiveMinorVersion(1); + scis = config.getNonExcludedInitializers(context); + 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(); + List scis; + context.setConfigurationDiscovered(false); + context.setClassLoader(webAppLoader); + context.getMetaData().setWebDescriptor(new WebDescriptor(Resource.newResource(web25))); + context.getMetaData().setWebInfClassesResources(classes); + context.getMetaData().addWebInfResource(Resource.newResource(testSciJar.toURI().toURL())); + context.getContext().getServletContext().setEffectiveMajorVersion(2); + context.getContext().getServletContext().setEffectiveMinorVersion(5); + scis = config.getNonExcludedInitializers(context); + assertNotNull(scis); + for (ServletContainerInitializer s : scis) + { + //should not have any of the web-inf lib scis in here + assertFalse(s.getClass().getName().equals("com.acme.ordering.AcmeServletContainerInitializer")); + assertFalse(s.getClass().getName().equals("com.acme.initializer.FooInitializer")); + //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(); + List scis; + context.setConfigurationDiscovered(true); + context.setClassLoader(webAppLoader); + context.getMetaData().setWebDescriptor(new WebDescriptor(Resource.newResource(web25))); + context.getMetaData().setWebInfClassesResources(classes); + context.getMetaData().addWebInfResource(Resource.newResource(testSciJar.toURI().toURL())); + context.getContext().getServletContext().setEffectiveMajorVersion(2); + context.getContext().getServletContext().setEffectiveMinorVersion(5); + scis = config.getNonExcludedInitializers(context); + 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-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/TestAnnotationDecorator.java b/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/TestAnnotationDecorator.java new file mode 100644 index 00000000000..4429bbb3a29 --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/TestAnnotationDecorator.java @@ -0,0 +1,122 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.annotations; + +import org.eclipse.jetty.ee10.plus.annotation.LifeCycleCallbackCollection; +import org.eclipse.jetty.ee10.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.Source; +import org.eclipse.jetty.ee10.webapp.MetaData; +import org.eclipse.jetty.ee10.webapp.WebAppContext; +import org.eclipse.jetty.ee10.webapp.WebDescriptor; +import org.eclipse.jetty.util.DecoratedObjectFactory; +import org.eclipse.jetty.util.resource.EmptyResource; +import org.eclipse.jetty.xml.XmlParser; +import org.junit.jupiter.api.Test; + +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; + +public class TestAnnotationDecorator +{ + public class TestWebDescriptor extends WebDescriptor + { + public TestWebDescriptor(MetaData.Complete metadata) + { + super(EmptyResource.INSTANCE); + _metaDataComplete = metadata; + } + + @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() throws Exception + { + 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 metdata-complete==true + context.getMetaData().setWebDescriptor(new TestWebDescriptor(MetaData.Complete.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(MetaData.Complete.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-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/TestAnnotationInheritance.java b/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/TestAnnotationInheritance.java new file mode 100644 index 00000000000..49930a5531e --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/TestAnnotationInheritance.java @@ -0,0 +1,177 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.annotations; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import javax.naming.Context; +import javax.naming.InitialContext; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.hasKey; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * + */ +public class TestAnnotationInheritance +{ + List classNames = new ArrayList(); + + class SampleHandler extends AnnotationParser.AbstractHandler + { + public final List annotatedClassNames = new ArrayList(); + public final List annotatedMethods = new ArrayList(); + public final List annotatedFields = new ArrayList(); + + @Override + public void handle(AnnotationParser.ClassInfo info, String annotation) + { + if (annotation == null || !"org.eclipse.jetty.ee10.annotations.Sample".equals(annotation)) + return; + + annotatedClassNames.add(info.getClassName()); + } + + @Override + public void handle(AnnotationParser.FieldInfo info, String annotation) + { + if (annotation == null || !"org.eclipse.jetty.ee10.annotations.Sample".equals(annotation)) + return; + annotatedFields.add(info.getClassInfo().getClassName() + "." + info.getFieldName()); + } + + @Override + public void handle(AnnotationParser.MethodInfo info, String annotation) + { + if (annotation == null || !"org.eclipse.jetty.ee10.annotations.Sample".equals(annotation)) + return; + annotatedMethods.add(info.getClassInfo().getClassName() + "." + info.getMethodName()); + } + + @Override + public String toString() + { + return annotatedClassNames.toString() + annotatedMethods + annotatedFields; + } + } + + @AfterEach + public void destroy() throws Exception + { + classNames.clear(); + InitialContext ic = new InitialContext(); + Context comp = (Context)ic.lookup("java:comp"); + comp.destroySubcontext("env"); + } + + @Test + public void testParseClassNames() throws Exception + { + classNames.add(ClassA.class.getName()); + classNames.add(ClassB.class.getName()); + + SampleHandler handler = new SampleHandler(); + AnnotationParser parser = new AnnotationParser(); + parser.parse(Collections.singleton(handler), classNames); + + //check we got 2 class annotations + assertEquals(2, handler.annotatedClassNames.size()); + + //check we got all annotated methods on each class + assertEquals(7, handler.annotatedMethods.size()); + assertTrue(handler.annotatedMethods.contains("org.eclipse.jetty.ee10.annotations.ClassA.a")); + assertTrue(handler.annotatedMethods.contains("org.eclipse.jetty.ee10.annotations.ClassA.b")); + assertTrue(handler.annotatedMethods.contains("org.eclipse.jetty.ee10.annotations.ClassA.c")); + assertTrue(handler.annotatedMethods.contains("org.eclipse.jetty.ee10.annotations.ClassA.d")); + assertTrue(handler.annotatedMethods.contains("org.eclipse.jetty.ee10.annotations.ClassA.l")); + assertTrue(handler.annotatedMethods.contains("org.eclipse.jetty.ee10.annotations.ClassB.a")); + assertTrue(handler.annotatedMethods.contains("org.eclipse.jetty.ee10.annotations.ClassB.c")); + + //check we got all annotated fields on each class + assertEquals(1, handler.annotatedFields.size()); + assertEquals("org.eclipse.jetty.ee10.annotations.ClassA.m", handler.annotatedFields.get(0)); + } + + @Test + public void testParseClass() throws Exception + { + SampleHandler handler = new SampleHandler(); + AnnotationParser parser = new AnnotationParser(); + parser.parse(Collections.singleton(handler), ClassB.class, true); + + //check we got 2 class annotations + assertEquals(2, handler.annotatedClassNames.size()); + + //check we got all annotated methods on each class + assertEquals(7, handler.annotatedMethods.size()); + assertTrue(handler.annotatedMethods.contains("org.eclipse.jetty.ee10.annotations.ClassA.a")); + assertTrue(handler.annotatedMethods.contains("org.eclipse.jetty.ee10.annotations.ClassA.b")); + assertTrue(handler.annotatedMethods.contains("org.eclipse.jetty.ee10.annotations.ClassA.c")); + assertTrue(handler.annotatedMethods.contains("org.eclipse.jetty.ee10.annotations.ClassA.d")); + assertTrue(handler.annotatedMethods.contains("org.eclipse.jetty.ee10.annotations.ClassA.l")); + assertTrue(handler.annotatedMethods.contains("org.eclipse.jetty.ee10.annotations.ClassB.a")); + assertTrue(handler.annotatedMethods.contains("org.eclipse.jetty.ee10.annotations.ClassB.c")); + + //check we got all annotated fields on each class + assertEquals(1, handler.annotatedFields.size()); + assertEquals("org.eclipse.jetty.ee10.annotations.ClassA.m", handler.annotatedFields.get(0)); + } + + @Test + public void testTypeInheritanceHandling() throws Exception + { + Map> map = new ConcurrentHashMap<>(); + + AnnotationParser parser = new AnnotationParser(); + ClassInheritanceHandler handler = new ClassInheritanceHandler(map); + + class Foo implements InterfaceD + { + } + + classNames.clear(); + classNames.add(ClassA.class.getName()); + classNames.add(ClassB.class.getName()); + classNames.add(InterfaceD.class.getName()); + classNames.add(Foo.class.getName()); + + parser.parse(Collections.singleton(handler), classNames); + + assertNotNull(map); + assertFalse(map.isEmpty()); + assertEquals(2, map.size()); + + assertThat(map, hasKey("org.eclipse.jetty.ee10.annotations.ClassA")); + assertThat(map, hasKey("org.eclipse.jetty.ee10.annotations.InterfaceD")); + Set classes = map.get("org.eclipse.jetty.ee10.annotations.ClassA"); + assertThat(classes, contains("org.eclipse.jetty.ee10.annotations.ClassB")); + + classes = map.get("org.eclipse.jetty.ee10.annotations.InterfaceD"); + assertThat(classes, containsInAnyOrder("org.eclipse.jetty.ee10.annotations.ClassB", + Foo.class.getName())); + } +} diff --git a/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/TestAnnotationIntrospector.java b/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/TestAnnotationIntrospector.java new file mode 100644 index 00000000000..ade1096f82c --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/TestAnnotationIntrospector.java @@ -0,0 +1,93 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.annotations; + +import java.io.File; + +import org.eclipse.jetty.ee10.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.Source; +import org.eclipse.jetty.ee10.webapp.FragmentDescriptor; +import org.eclipse.jetty.ee10.webapp.WebAppContext; +import org.eclipse.jetty.ee10.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 JAVAX API sourced servlet can be introspected + holder = new ServletHolder(Source.JAVAX_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.getName())); + holder.setHeldClass(ServletE.class); + assertTrue(introspector.isIntrospectable(new ServletE(), holder)); + + //a DESCRIPTOR sourced servlet can be introspected if web.xml metdata-complete==false + File file = MavenTestingUtils.getTestResourceFile("web31false.xml"); + Resource resource = Resource.newResource(file); + wac.getMetaData().setWebDescriptor(new WebDescriptor(resource)); + holder = new ServletHolder(new Source(Source.Origin.DESCRIPTOR, resource.toString())); + 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 + file = MavenTestingUtils.getTestResourceFile("web-fragment4false.xml"); + resource = Resource.newResource(file); + wac.getMetaData().addFragmentDescriptor(Resource.newResource(file.getParentFile()), new FragmentDescriptor(resource)); + holder = new ServletHolder(new Source(Source.Origin.DESCRIPTOR, resource.toString())); + 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) + file = MavenTestingUtils.getTestResourceFile("web-fragment4true.xml"); + resource = Resource.newResource(file); + wac.getMetaData().addFragmentDescriptor(Resource.newResource(file.getParentFile()), new FragmentDescriptor(resource)); + holder = new ServletHolder(new Source(Source.Origin.DESCRIPTOR, resource.toString())); + assertFalse(introspector.isIntrospectable(new ServletE(), holder)); + + //a DESCRIPTOR sourced servlet cannot be introspected if web.xml medata-complete==true + file = MavenTestingUtils.getTestResourceFile("web31true.xml"); + resource = Resource.newResource(file); + wac.getMetaData().setWebDescriptor(new WebDescriptor(resource)); + holder = new ServletHolder(new Source(Source.Origin.DESCRIPTOR, resource.toString())); + assertFalse(introspector.isIntrospectable(new ServletE(), holder)); + } + } +} diff --git a/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/TestAnnotationParser.java b/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/TestAnnotationParser.java new file mode 100644 index 00000000000..66f4593158d --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/TestAnnotationParser.java @@ -0,0 +1,298 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.annotations; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +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.IO; +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.PathResource; +import org.eclipse.jetty.util.resource.Resource; +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.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.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@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 (annotation == null || !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); + } + } + + public WorkDir testdir; + + @Test + public void testSampleAnnotation() throws Exception + { + String[] classNames = new String[]{"org.eclipse.jetty.annotations.ClassA"}; + 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 (annotation == null || !"org.eclipse.jetty.annotations.Sample".equals(annotation)) + return; + + assertEquals("org.eclipse.jetty.annotations.ClassA", info.getClassName()); + } + + @Override + public void handle(AnnotationParser.FieldInfo info, String annotation) + { + if (annotation == null || !"org.eclipse.jetty.annotations.Sample".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 (annotation == null || !"org.eclipse.jetty.annotations.Sample".equals(annotation)) + return; + assertEquals("org.eclipse.jetty.annotations.ClassA", info.getClassInfo().getClassName()); + assertThat(info.getMethodName(), is(in(methods))); + assertEquals("org.eclipse.jetty.annotations.Sample", annotation); + } + } + + //long start = System.currentTimeMillis(); + parser.parse(Collections.singleton(new SampleAnnotationHandler()), classNames); + //long end = System.currentTimeMillis(); + + //System.err.println("Time to parse class: " + ((end - start))); + } + + @Test + public void testMultiAnnotation() throws Exception + { + String[] classNames = new String[]{"org.eclipse.jetty.annotations.ClassB"}; + AnnotationParser parser = new AnnotationParser(); + + class MultiAnnotationHandler extends AnnotationParser.AbstractHandler + { + @Override + public void handle(AnnotationParser.ClassInfo info, String annotation) + { + if (annotation == null || !"org.eclipse.jetty.annotations.Multi".equals(annotation)) + return; + assertTrue("org.eclipse.jetty.annotations.ClassB".equals(info.getClassName())); + } + + @Override + public void handle(AnnotationParser.FieldInfo info, String annotation) + { + assertTrue(annotation == null || !"org.eclipse.jetty.annotations.Multi".equals(annotation), + "There should not be any"); + } + + @Override + public void handle(AnnotationParser.MethodInfo info, String annotation) + { + if (annotation == null || !"org.eclipse.jetty.annotations.Multi".equals(annotation)) + return; + assertTrue("org.eclipse.jetty.annotations.ClassB".equals(info.getClassInfo().getClassName())); + assertTrue("a".equals(info.getMethodName())); + } + } + + parser.parse(Collections.singleton(new MultiAnnotationHandler()), classNames); + } + + @Test + public void testHiddenFilesInJar() throws Exception + { + File badClassesJar = MavenTestingUtils.getTestResourceFile("bad-classes.jar"); + AnnotationParser parser = new AnnotationParser(); + Set emptySet = Collections.emptySet(); + parser.parse(emptySet, badClassesJar.toURI()); + // 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 + { + File badClassesJar = MavenTestingUtils.getTestResourceFile("jdk9/slf4j-api-1.8.0-alpha2.jar"); + AnnotationParser parser = new AnnotationParser(); + Set emptySet = Collections.emptySet(); + parser.parse(emptySet, badClassesJar.toURI()); + // Should throw no exceptions, and happily skip the module-info.class files + } + + @Test + public void testJep238MultiReleaseInJar() throws Exception + { + File badClassesJar = MavenTestingUtils.getTestResourceFile("jdk9/log4j-api-2.9.0.jar"); + AnnotationParser parser = new AnnotationParser(); + Set emptySet = Collections.emptySet(); + parser.parse(emptySet, badClassesJar.toURI()); + // Should throw no exceptions, and skip the META-INF/versions/9/* files + } + + @Test + public void testJep238MultiReleaseInJarJDK10() throws Exception + { + File jdk10Jar = MavenTestingUtils.getTestResourceFile("jdk10/multirelease-10.jar"); + AnnotationParser parser = new AnnotationParser(); + DuplicateClassScanHandler handler = new DuplicateClassScanHandler(); + Set handlers = Collections.singleton(handler); + parser.parse(handlers, new PathResource(jdk10Jar)); + // Should throw no exceptions + } + + @Test + public void testBasedirExclusion() throws Exception + { + // 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 director 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 + File basedir = testdir.getPathFile(".base/workspace/classes").toFile(); + 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 + parser.parse(Collections.singleton(tracker), basedir.toURI()); + + // Validate + assertThat("Found Class", tracker.foundClasses, contains(ClassA.class.getName())); + } + + @Test + public void testScanDuplicateClassesInJars() throws Exception + { + Resource testJar = Resource.newResource(MavenTestingUtils.getTestResourceFile("tinytest.jar")); + Resource testJar2 = Resource.newResource(MavenTestingUtils.getTestResourceFile("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()); + assertTrue(!(locations.get(0).equals(locations.get(1)))); + } + + @Test + public void testScanDuplicateClasses() throws Exception + { + Resource testJar = Resource.newResource(MavenTestingUtils.getTestResourceFile("tinytest.jar")); + 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, Resource.newResource(testClasses)); + List locations = handler.getParsedList("org.acme.ClassOne"); + assertNotNull(locations); + assertEquals(2, locations.size()); + assertTrue(!(locations.get(0).equals(locations.get(1)))); + } + + private void copyClass(Class clazz, File basedir) throws IOException + { + String classRef = TypeUtil.toClassReference(clazz); + URL url = this.getClass().getResource('/' + classRef); + assertThat("URL for: " + classRef, url, notNullValue()); + + Path outputFile = basedir.toPath().resolve(classRef); + FS.ensureDirExists(outputFile.getParent()); + + try (InputStream in = url.openStream(); + OutputStream out = Files.newOutputStream(outputFile)) + { + IO.copy(in, out); + } + } +} diff --git a/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/TestDiscoveredServletContainerInitializerHolder.java b/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/TestDiscoveredServletContainerInitializerHolder.java new file mode 100644 index 00000000000..19c105161cd --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/TestDiscoveredServletContainerInitializerHolder.java @@ -0,0 +1,98 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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.ee10.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().getName()), + 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-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/TestRunAsAnnotation.java b/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/TestRunAsAnnotation.java new file mode 100644 index 00000000000..c6f69d5d121 --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/TestRunAsAnnotation.java @@ -0,0 +1,58 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.annotations; + +import java.io.File; + +import org.eclipse.jetty.ee10.servlet.ServletHolder; +import org.eclipse.jetty.ee10.webapp.WebAppContext; +import org.eclipse.jetty.ee10.webapp.WebDescriptor; +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.assertEquals; + +public class TestRunAsAnnotation +{ + @Test + public void testRunAsAnnotation() throws Exception + { + 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/*"); + Resource fakeXml = Resource.newResource(new File(MavenTestingUtils.getTargetTestingDir("run-as"), "fake.xml")); + wac.getMetaData().setOrigin(holder2.getName() + ".servlet.run-as", new WebDescriptor(fakeXml)); + + AnnotationIntrospector parser = new AnnotationIntrospector(wac); + RunAsAnnotationHandler handler = new RunAsAnnotationHandler(wac); + parser.registerHandler(handler); + parser.introspect(new ServletC(), null); + + assertEquals("admin", holder.getRunAsRole()); + assertEquals(null, holder2.getRunAsRole()); + } +} diff --git a/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/TestSecurityAnnotationConversions.java b/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/TestSecurityAnnotationConversions.java new file mode 100644 index 00000000000..56561edc90b --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/TestSecurityAnnotationConversions.java @@ -0,0 +1,340 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.annotations; + +import java.util.Arrays; +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.ee10.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletMapping; +import org.eclipse.jetty.ee10.servlet.security.ConstraintAware; +import org.eclipse.jetty.ee10.servlet.security.ConstraintMapping; +import org.eclipse.jetty.ee10.servlet.security.ConstraintSecurityHandler; +import org.eclipse.jetty.ee10.webapp.WebAppContext; +import org.eclipse.jetty.util.security.Constraint; +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.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +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(); + expectedConstraint.setAuthenticate(true); + expectedConstraint.setDataConstraint(Constraint.DC_NONE); + + 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(); + expectedConstraint.setAuthenticate(true); + expectedConstraint.setRoles(new String[]{"tom", "dick", "harry"}); + expectedConstraint.setDataConstraint(Constraint.DC_CONFIDENTIAL); + + 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(); + expectedConstraint1.setAuthenticate(true); + expectedConstraint1.setRoles(new String[]{"tom", "dick", "harry"}); + expectedConstraint1.setDataConstraint(Constraint.DC_CONFIDENTIAL); + + //a Constraint for the PermitAll on the doGet method with a userdata + //constraint of DC_CONFIDENTIAL inherited from the class + Constraint expectedConstraint2 = new Constraint(); + expectedConstraint2.setDataConstraint(Constraint.DC_NONE); + + 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(); + expectedConstraint1.setAuthenticate(true); + expectedConstraint1.setRoles(new String[]{"tom", "dick", "harry"}); + expectedConstraint1.setDataConstraint(Constraint.DC_CONFIDENTIAL); + + //a Constraint for the Permit on the GET method with a userdata + //constraint of DC_CONFIDENTIAL + Constraint expectedConstraint2 = new Constraint(); + expectedConstraint2.setDataConstraint(Constraint.DC_CONFIDENTIAL); + + 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 (int k = 0; k < actualMappings.size(); k++) + { + ConstraintMapping am = actualMappings.get(k); + 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().getAuthenticate(), am.getConstraint().getAuthenticate()); + assertEquals(em.getConstraint().getDataConstraint(), am.getConstraint().getDataConstraint()); + if (em.getMethodOmissions() == null) + { + assertNull(am.getMethodOmissions()); + } + else + { + assertTrue(Arrays.equals(am.getMethodOmissions(), em.getMethodOmissions())); + } + + if (em.getConstraint().getRoles() == null) + { + assertNull(am.getConstraint().getRoles()); + } + else + { + assertTrue(Arrays.equals(em.getConstraint().getRoles(), am.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-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/TestServletAnnotations.java b/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/TestServletAnnotations.java new file mode 100644 index 00000000000..b6c90c6b993 --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/TestServletAnnotations.java @@ -0,0 +1,290 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.annotations; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; + +import org.eclipse.jetty.ee10.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletMapping; +import org.eclipse.jetty.ee10.servlet.security.ConstraintSecurityHandler; +import org.eclipse.jetty.ee10.webapp.DiscoveredAnnotation; +import org.eclipse.jetty.ee10.webapp.WebAppContext; +import org.junit.jupiter.api.Test; + +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.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 + */ +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() throws Exception + { + List classes = new ArrayList(); + classes.add("org.eclipse.jetty.ee10.annotations.ServletC"); + AnnotationParser parser = new AnnotationParser(); + + WebAppContext wac = new WebAppContext(); + List results = new ArrayList(); + + TestWebServletAnnotationHandler handler = new TestWebServletAnnotationHandler(wac, results); + + parser.parse(Collections.singleton(handler), classes); + + 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() throws Exception + { + //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.ee10.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.ee10.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() throws Exception + { + //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.ee10.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.ee10.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() throws Exception + { + //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.ee10.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.ee10.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() throws Exception + { + //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.ee10.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.ee10.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() throws Exception + { + //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.ee10.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() + throws Exception + { + WebAppContext wac = new WebAppContext(); + ConstraintSecurityHandler sh = new ConstraintSecurityHandler(); + wac.setSecurityHandler(sh); + sh.setRoles(new HashSet(Arrays.asList(new String[]{"humpty", "dumpty"}))); + DeclareRolesAnnotationHandler handler = new DeclareRolesAnnotationHandler(wac); + handler.doHandle(ServletC.class); + assertThat(sh.getRoles(), containsInAnyOrder("humpty", "alice", "dumpty")); + } +} diff --git a/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/resources/ResourceA.java b/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/resources/ResourceA.java new file mode 100644 index 00000000000..fde76f4f234 --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/resources/ResourceA.java @@ -0,0 +1,115 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/resources/ResourceB.java b/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/resources/ResourceB.java new file mode 100644 index 00000000000..17b436fc8f0 --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/resources/ResourceB.java @@ -0,0 +1,39 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/resources/TestResourceAnnotations.java b/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/resources/TestResourceAnnotations.java new file mode 100644 index 00000000000..1abd76b8bd8 --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/test/java/org/eclipse/jetty/ee10/annotations/resources/TestResourceAnnotations.java @@ -0,0 +1,168 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.annotations.resources; + +import java.lang.reflect.Field; +import java.util.Set; +import javax.naming.Context; +import javax.naming.InitialContext; + +import org.eclipse.jetty.ee10.annotations.AnnotationIntrospector; +import org.eclipse.jetty.ee10.annotations.ResourceAnnotationHandler; +import org.eclipse.jetty.ee10.annotations.ResourcesAnnotationHandler; +import org.eclipse.jetty.ee10.plus.annotation.Injection; +import org.eclipse.jetty.ee10.plus.annotation.InjectionCollection; +import org.eclipse.jetty.ee10.plus.jndi.EnvEntry; +import org.eclipse.jetty.ee10.webapp.WebAppContext; +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.ee10.annotations.resources.ResourceA/g")); + assertEquals(objA, env.lookup("org.eclipse.jetty.ee10.annotations.resources.ResourceA/h")); + assertEquals(objB, env.lookup("org.eclipse.jetty.ee10.annotations.resources.ResourceB/f")); + assertEquals(objB, env.lookup("org.eclipse.jetty.ee10.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-ee10/jetty-ee10-annotations/src/test/resources/bad-classes.jar b/jetty-ee10/jetty-ee10-annotations/src/test/resources/bad-classes.jar new file mode 100644 index 0000000000000000000000000000000000000000..5538c18b552da21d6a9ff7167512820508362e65 GIT binary patch literal 1977 zcmWIWW@Zs#;Nak3cofUw%zy+q8CV#6T|*poJ^kGD|D9rBU}gyLX6FE@V1g7#1F>0DLcejy0;&F^X|==4d<`_ zWlZQQ<6xiIKdoQB$vix2DtD8y%v;WPmt<5I?hT4sx4u%Vd#;E6nH@U=W?hh#3rjB) z{^`3lyoK@C1jJo>NX@1|%S~HGEH<^a;~AectEcs%Jp~0T-_8pFE)x#<1v! zP3xIGn~uymBhvb0&XGq+ty|8_30W7V2aZIcf-OglfkuKbB8rZpg*^|{QoZ7g#FYGU zq*&U4rcxYDC3Y7E7M$`t^8n~fbVtf8NQp}G(+bxW2fMEMUCnNNpos=h*Fk~=-F0GU zc47ny%r=~1@(4Ld)YCu283K(2VMONgNAoBs)rRC3KvQAgUM^rNY)Lm&GyE6U)Y=-m z<-(z$je=`jxSM;gs>(fVic@j9f7)xq2gVwWLVXUgJj31AHJjhO{#(H`W5LsfJ-eFQ zn$-@@(_H(>a^V~PGM4O#aw-eA0u!PB@_@8Pj{Y|h>I+R4K7JqGxuff2;EReqXN{LW zUddT?`^zNPqLn;v*j`qqY_QS^`C<0H<(vKvPGQNXFBTu!KSQgL=Tz3`YmfczXdkwI zag6z3;#Tg-7h1btiN6)&`)9Z~u5jl!Mo{8&+i-FIKA@L=qj-8Eny0yevdA&&;d9Q< z$3Jw%h1FZ0J<@`tKv-a3xE`)SL;}3Dg)7?uXeTMGaM){BY-M1!~-zv5jh(<5I|WIIsKxB{Vbpv@QjMA4V1W% dQy(aCBS1eexuLk3l?^1x4TQ6SUgKZ|@c{h<7GeMZ literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-annotations/src/test/resources/jdk10/multirelease-10.jar b/jetty-ee10/jetty-ee10-annotations/src/test/resources/jdk10/multirelease-10.jar new file mode 100644 index 0000000000000000000000000000000000000000..a824bc10f1c005d5e75170d3ec59a88b71bf5eba GIT binary patch literal 4059 zcmb7H2UL?;6AdLKp{oh7h%^C1H>?XNs30wYBnG4i(jgQ@YLu!F1VkbTQd|W=iWCb{ zln#q5MUXBaARKxXsY_jCH-Z0{i0JxH&dd4Ed3iJU&7E)N2B*Wo2nB$_U_iHbAR0hp zPyizUqoJ=VjlycmVpOpxO^tK+0F0x3YPCFd7BK6NZY0$Gdpk>*QkUA!@ab{c@8TZ8t38S8U-jb*g8p70g>Q!uY-9 zY%`TsA11HP6jyMw_c`x>>)L9lo_a8Rq(U*8*~t_2O-G5J4n0`Nhc$QU*ZT*{aF$_fffO%{B ztO~Ah{N>hLyBsc8p0(%(zml%2T!%JunW$Ip3@+HH{`eE6?z5v&9$Kwf( z&h{QVrDGOx@@y3j8Zw}!LxRzTyCKA`Fn~&mtj?obiIV|tf7Gazr@b6ssTzDyRbNdM zsJtVWs1qudICRpYWxe~l|7C#bh+^QKR3;OlUZE2~gPPpE>=5(9RrI{eTW+zJbtQv0 z$As}qY3-&G=CyIIy`RD-Ez{5eq z$`|8}u-CfIrFR(6>!YD|J_0#@Gev%Rff_8!O}NyQFrRG_O`6l>&B(zok=OUDPn^8F zSD)GG2j37|ilx`(4Ee|Eo|$PB2_aw0sFY(V$Zq|1^5ti8@%w;VZ^Fgin=xZ4m^ z1)5L3?E!8J65lGm=CQuj<40F?MnN@{Oe~@oaUy8a&~Z#N=+S8}p&*;3hM*O7$Lppt z?gcHz=Gvn0?tR#e8}-T;@Q(MVgdd)GN^ny{rrSB%`(-HiloU2%c{^KEF&+E%VFik$ zFmp{0G+b&5^fZm8mT}nfzcn`$)F(GByc#ByJ&IHWU3{MsH&3;l&+}LiskpulG0{|2U(h06Ah%$~wK81wpVe82B=cL(Nc@5J{+r@Zrrq}#G*_PN7GvcW1o^*%nVO-v+ za9)d-1=&0}b}WWoIyr4Hprfyd(M>S(*j=L96T{GVV)wZ*}-rwFI!lhE;P6 zY6;b1JGIjUrM&IzC&~+5y0+FoZh`{1=gk!1O6RXcsUC;KGAqm~p7(S$dNePmpL|U; za{1s*`r-oqNELA~tUL+hW6N$UJN$4a@>%7B0WfydRQ_;QNK^K2$-D)Oec6fkPkjo8 zxQ{&-%IgdDJdk-)*ctpR0v<+KVl%GKd@b|~2fsXH!;4o1xxcw@M9d&LhV|%H!;0pH z=;V9q13k0qgCrRQe2#P!X01k$q?MtWvq#o?bK9bO6VQAYVG6;O8|o2R&kZbkQ(Ggv zU>CBaJ{o*({!Q(8rYQR~{qj@u%yAzycVuvP;>?B1*h9)grK~UcThi&xwZ!Ez8~h5n7u;f=s;n!0!*+hTlt+E6|8{-t|)Xak>!$L{SGn@Amh|m2; z=E1^~Sc`VThunJiIaz}~bXQ)^CYioixbT^cQXC&Y8vECfyA&J4|KAGs zC=NgZ$Bx&LY1TC<(W3p8b&hA_d1U zno=BN@1?x~wNMN5ambOhvbagAc+lxbN4e1|79UKRP>JOA{pKe1<8oKW6RaGa@V16{ z_b-EsN6tytpryJea^*Rj85N_Afc@188+c=1GR#)uCsOC zSa*xLx&xJ9HW1Sj`HS+?jFlGx%C{mcu?LbM;dq{6+=o#KMBQh^cTle^aYhbD9$DeXl%6uZ74kFNSB+BS9L{ zJtx{xK5nj{ep|ur6vOKs4v;a~-mMhPcg0)8^vr0Gga9Qi*1VG1<>X8M^TV`A{(M>} zR7)b!%~)`^l?Z#u_X&G@2 z4uL=zG)zxNo3sdJWqU^KGs!lQ*&a+1sh6TCqF#R+Bpp2zK-K!Ra+{*{zqT)fYM1iI z>3r>lR$^16Io0lwAE^?0_c>a*Op(k~n<77ciN2>C-|dT5{80op)eOjQ;x}@8_i31)< zv@bfP%TY~+T=3sk>KC}*^t|2arF}aoje=?#EI;je2QrY%2YKj_KOe|dH%&ec0Q?I@ C0;ul* literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-annotations/src/test/resources/jdk9/log4j-api-2.9.0.jar b/jetty-ee10/jetty-ee10-annotations/src/test/resources/jdk9/log4j-api-2.9.0.jar new file mode 100644 index 0000000000000000000000000000000000000000..ab98d40e9a2e9e23cfbcf19d50e5f5baf8eb9246 GIT binary patch literal 238875 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

  • `+Pj`7B?yjqF9j8e|Zo&^l|qa1WjtgZ;f|rK$Yz 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 literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-annotations/src/test/resources/jetty-logging.properties b/jetty-ee10/jetty-ee10-annotations/src/test/resources/jetty-logging.properties new file mode 100644 index 00000000000..9c9f7d77b57 --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/test/resources/jetty-logging.properties @@ -0,0 +1,3 @@ +# Jetty Logging using jetty-slf4j-impl +#org.eclipse.jetty.LEVEL=DEBUG +#org.eclipse.jetty.annotations.LEVEL=DEBUG diff --git a/jetty-ee10/jetty-ee10-annotations/src/test/resources/tinytest.jar b/jetty-ee10/jetty-ee10-annotations/src/test/resources/tinytest.jar new file mode 100644 index 0000000000000000000000000000000000000000..4041f0e8a1b69f1514877067f4b31224ae35292f GIT binary patch literal 1568 zcmWIWW@Zs#;Nak3SXH9u&42_r8CV#6T|*poJ^kGD|D9rBU}gyLX6FE@V1gi>b*jKs#K47~~2r1_q$Z z8qmDJ0+h`!O4moIScs;A6RILHIX4xo3=$UT%EXbCIp-u67yIX>>Lr61seOC-7y|`b z&qtI+{$J{}(D|X6*F}$zB<@A~cju*~-ktrX|9g<9h};3^&YiO_%y=|=W^tO@=PLVo z49{NtD7f(D!lw&rJGvu||D5!oN1ffNG_|km!rdcLx!v9?F74$J4wG3t^WQm<%q7=0 zPQA6-B`tEn)i*lpH??YIwe0iscXAU~-KV!Oy>_BUt&n3ySYV%^)}NMMt`mAjvG3aE zbQb4UvESXHP~o#(L^kcOxs^ujpZ>}-Zk0FxgF|tS&pV49K(BoPVnkdL_IyZrKGgGm z)?9}I1m>993-`{ybm)+uu1~V2S<}>v#0~YG{Q{kp6Z+?!_S*1)LC)(A%a+3KnRjR2 zOpSkkT=;;DEW1>c*e$V`rkx#^cSHzn`S`@)Lij5U&bz*IPUTker9A6aNOaX+*Y)5{ z=*-q4+3-obycBPb=mf7$SACx-94(Nc&Q z)~L@)EKAh(@Hyw_;~#oL-|M=!&pF?%&mL)M_?|lH6XtQwQ%5hXfNkQx%9Sx!W4Ig` zrfQ!)?-O|9y!X`$EZt5|vUXi>b*jKs#K47~~2r1_q$Z z8qmDJ0+h`!O4moIScs;A6RILHIX4xo3=$UT%EXbCIp-u67yIX>>Lr61seOC-7y|`b z&qtI+{$J{}(D|X6*F}$zB<@A~cju*~-ktrX|9g<9h};3^&YiO_%y=|=W^tO@=PLVo z49{NtD7f(D!lw&rJGvu||D5!oN1ffNG_|km!rdcLx!v9?F74$J4wG3t^WQm<%q7=0 zPQA6-B`tEn)i*lpH??YIwe0iscXAU~-KV!Oy>_BUt&n3ySYV%^)}NMMt`mAjvG3aE zbQb4UvESXHP~o#(L^kcOxs^ujpZ>}-Zk0FxgF|tS&pV49K(BoPVnkdL_IyZrKGgGm z)?9}I1m>993-`{ybm)+uu1~V2S<}>v#0~YG{Q{kp6Z+?!_S*1)LC)(A%a+3KnRjR2 zOpSkkT=;;DEW1>c*e$V`rkx#^cSHzn`S`@)Lij5U&bz*IPUTker9A6aNOaX+*Y)5{ z=*-q4+3-obycBPb=mf7$SACx-94(Nc&Q z)~L@)EKAh(@Hyw_;~#oL-|M=!&pF?%&mL)M_?|lH6XtQwQ%5hXfNkQx%9Sx!W4Ig` zrfQ!)?-O|9y!X`$EZt5|vUX + + + ardvaark + + + diff --git a/jetty-ee10/jetty-ee10-annotations/src/test/resources/web-fragment4true.xml b/jetty-ee10/jetty-ee10-annotations/src/test/resources/web-fragment4true.xml new file mode 100644 index 00000000000..cf84dc66bde --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/test/resources/web-fragment4true.xml @@ -0,0 +1,9 @@ + + + + + badger + + + + diff --git a/jetty-ee10/jetty-ee10-annotations/src/test/resources/web25.xml b/jetty-ee10/jetty-ee10-annotations/src/test/resources/web25.xml new file mode 100644 index 00000000000..da2e65b6007 --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/test/resources/web25.xml @@ -0,0 +1,10 @@ + + + + Test 2.5 WebApp + + diff --git a/jetty-ee10/jetty-ee10-annotations/src/test/resources/web31false.xml b/jetty-ee10/jetty-ee10-annotations/src/test/resources/web31false.xml new file mode 100644 index 00000000000..a2307a7560b --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/test/resources/web31false.xml @@ -0,0 +1,11 @@ + + + + Test 31 WebApp + + diff --git a/jetty-ee10/jetty-ee10-annotations/src/test/resources/web31true.xml b/jetty-ee10/jetty-ee10-annotations/src/test/resources/web31true.xml new file mode 100644 index 00000000000..e44571a8a20 --- /dev/null +++ b/jetty-ee10/jetty-ee10-annotations/src/test/resources/web31true.xml @@ -0,0 +1,11 @@ + + + + Test 31 WebApp + + diff --git a/jetty-ee10/jetty-ee10-ant/pom.xml b/jetty-ee10/jetty-ee10-ant/pom.xml new file mode 100644 index 00000000000..76248ae8dc6 --- /dev/null +++ b/jetty-ee10/jetty-ee10-ant/pom.xml @@ -0,0 +1,96 @@ + + + org.eclipse.jetty.ee10 + jetty-ee10 + 12.0.0-SNAPSHOT + + 4.0.0 + jetty-ee10-ant + jar + EE10 :: Jetty :: Ant Plugin + + + ${project.groupId}.ant + + + + + org.apache.felix + maven-bundle-plugin + true + + + + osgi.extender; filter:="(osgi.extender=osgi.serviceloader.registrar)" + + + osgi.serviceloader; osgi.serviceloader=org.eclipse.jetty.ee10.webapp.Configuration + + + + + + maven-dependency-plugin + + + copy-lib-deps + process-test-resources + + copy-dependencies + + + org.eclipse.jetty + org.eclipse.jetty.orbit,org.eclipse.jetty.websocket + jetty-start + jar + ${project.build.directory}/test-lib + + + + + + org.jacoco + jacoco-maven-plugin + + true + + + + + + + org.eclipse.jetty.toolchain + jetty-test-helper + test + + + org.apache.ant + ant + + + org.apache.ant + ant-launcher + + + org.eclipse.jetty.ee10 + jetty-ee10-plus + + + org.eclipse.jetty.ee10 + jetty-ee10-webapp + + + org.eclipse.jetty.ee10 + jetty-ee10-annotations + + + org.slf4j + slf4j-api + + + org.eclipse.jetty + jetty-slf4j-impl + test + + + diff --git a/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/AntMetaInfConfiguration.java b/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/AntMetaInfConfiguration.java new file mode 100644 index 00000000000..a88696fb437 --- /dev/null +++ b/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/AntMetaInfConfiguration.java @@ -0,0 +1,81 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.ant; + +import java.io.File; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.List; + +import org.apache.tools.ant.AntClassLoader; +import org.eclipse.jetty.ee10.webapp.Configuration; +import org.eclipse.jetty.ee10.webapp.MetaInfConfiguration; +import org.eclipse.jetty.ee10.webapp.WebAppContext; + +public class AntMetaInfConfiguration extends MetaInfConfiguration +{ + + @Override + public Class replaces() + { + return MetaInfConfiguration.class; + } + + @Override + public void findAndFilterContainerPaths(WebAppContext context) throws Exception + { + // TODO Auto-generated method stub + super.findAndFilterContainerPaths(context); + } + + @Override + protected List getAllContainerJars(final WebAppContext context) throws URISyntaxException + { + List uris = new ArrayList<>(); + if (context.getClassLoader() != null) + { + ClassLoader loader = context.getClassLoader().getParent(); + while (loader != null) + { + if (loader instanceof URLClassLoader) + { + URL[] urls = ((URLClassLoader)loader).getURLs(); + if (urls != null) + for (URL url : urls) + { + uris.add(new URI(url.toString().replaceAll(" ", "%20"))); + } + } + else if (loader instanceof AntClassLoader) + { + AntClassLoader antLoader = (AntClassLoader)loader; + String[] paths = antLoader.getClasspath().split(new String(new char[]{File.pathSeparatorChar})); + if (paths != null) + { + for (String p : paths) + { + File f = new File(p); + uris.add(f.toURI()); + } + } + } + loader = loader.getParent(); + } + } + return uris; + } +} diff --git a/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/AntWebAppContext.java b/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/AntWebAppContext.java new file mode 100644 index 00000000000..90878b15f87 --- /dev/null +++ b/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/AntWebAppContext.java @@ -0,0 +1,691 @@ +// +// ======================================================================== +// Copyright (c) 1995-2012 Sabre Holdings and others. +// ------------------------------------------------------------------------ +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.ant; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.URLClassLoader; +import java.security.CodeSource; +import java.security.PermissionCollection; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.jar.Manifest; + +import jakarta.servlet.Servlet; +import org.apache.tools.ant.AntClassLoader; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.types.FileSet; +import org.eclipse.jetty.ee10.ant.types.Attribute; +import org.eclipse.jetty.ee10.ant.types.Attributes; +import org.eclipse.jetty.ee10.ant.types.FileMatchingConfiguration; +import org.eclipse.jetty.ee10.ant.utils.TaskLog; +import org.eclipse.jetty.ee10.plus.webapp.EnvConfiguration; +import org.eclipse.jetty.ee10.servlet.FilterHolder; +import org.eclipse.jetty.ee10.servlet.FilterMapping; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletMapping; +import org.eclipse.jetty.ee10.servlet.Source; +import org.eclipse.jetty.ee10.webapp.MetaInfConfiguration; +import org.eclipse.jetty.ee10.webapp.WebAppClassLoader; +import org.eclipse.jetty.ee10.webapp.WebAppContext; +import org.eclipse.jetty.util.resource.PathResource; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.xml.XmlConfiguration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Extension of WebAppContext to allow configuration via Ant environment. + */ +public class AntWebAppContext extends WebAppContext +{ + private static final Logger LOG = LoggerFactory.getLogger(WebAppContext.class); + + public static final String DEFAULT_CONTAINER_INCLUDE_JAR_PATTERN = + ".*/.*jsp-api-[^/]*\\.jar$|.*/.*jsp-[^/]*\\.jar$|.*/.*taglibs[^/]*\\.jar$|.*/.*jstl[^/]*\\.jar$|.*/.*jsf-impl-[^/]*\\.jar$|.*/.*javax.faces-[^/]*\\.jar$|.*/.*myfaces-impl-[^/]*\\.jar$"; + + /** + * Location of jetty-env.xml file. + */ + private File jettyEnvXml; + + /** + * List of web application libraries. + */ + private List libraries = new ArrayList<>(); + + /** + * List of web application class directories. + */ + private List classes = new ArrayList<>(); + + /** + * context xml file to apply to the webapp + */ + private File contextXml; + + /** + * List of extra scan targets for this web application. + */ + private FileSet scanTargets; + + /** + * context attributes to set + **/ + private Attributes attributes; + + private Project project; + + private List scanFiles; + + private FileMatchingConfiguration librariesConfiguration; + + public static void dump(ClassLoader loader) + { + while (loader != null) + { + System.err.println(loader); + if (loader instanceof URLClassLoader) + { + URL[] urls = ((URLClassLoader)loader).getURLs(); + if (urls != null) + { + for (URL u : urls) + { + System.err.println("\t" + u + "\n"); + } + } + } + loader = loader.getParent(); + } + } + + /** + * AntURLClassLoader + * + * Adapt the AntClassLoader which is not a URLClassLoader - this is needed for + * jsp to be able to search the classpath. + */ + public static class AntURLClassLoader extends URLClassLoader + { + private AntClassLoader antLoader; + + public AntURLClassLoader(AntClassLoader antLoader) + { + super(new URL[]{}, antLoader); + this.antLoader = antLoader; + } + + @Override + public InputStream getResourceAsStream(String name) + { + return super.getResourceAsStream(name); + } + + @Override + public void close() throws IOException + { + super.close(); + } + + @Override + protected void addURL(URL url) + { + super.addURL(url); + } + + @Override + public URL[] getURLs() + { + Set urls = new HashSet(); + + //convert urls from antLoader + String[] paths = antLoader.getClasspath().split(new String(new char[]{File.pathSeparatorChar})); + if (paths != null) + { + for (String p : paths) + { + File f = new File(p); + try + { + urls.add(f.toURI().toURL()); + } + catch (Exception e) + { + LOG.trace("IGNORED", e); + } + } + } + + //add in any that may have been added to us as a URL directly + URL[] ourURLS = super.getURLs(); + if (ourURLS != null) + { + for (URL u : ourURLS) + { + urls.add(u); + } + } + + return urls.toArray(new URL[urls.size()]); + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException + { + return super.findClass(name); + } + + @Override + public URL findResource(String name) + { + return super.findResource(name); + } + + @Override + public Enumeration findResources(String name) throws IOException + { + return super.findResources(name); + } + + @Override + protected PermissionCollection getPermissions(CodeSource codesource) + { + return super.getPermissions(codesource); + } + + @Override + public Class loadClass(String name) throws ClassNotFoundException + { + return super.loadClass(name); + } + + @Override + protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException + { + return super.loadClass(name, resolve); + } + + @Override + protected Object getClassLoadingLock(String className) + { + return super.getClassLoadingLock(className); + } + + @Override + public URL getResource(String name) + { + return super.getResource(name); + } + + @Override + public Enumeration getResources(String name) throws IOException + { + return super.getResources(name); + } + + @Override + protected Package definePackage(String name, Manifest man, URL url) throws IllegalArgumentException + { + return super.definePackage(name, man, url); + } + + @Override + protected Package definePackage(String name, String specTitle, String specVersion, String specVendor, String implTitle, String implVersion, + String implVendor, URL sealBase) throws IllegalArgumentException + { + return super.definePackage(name, specTitle, specVersion, specVendor, implTitle, implVersion, implVendor, sealBase); + } + + @Override + protected Package[] getPackages() + { + return super.getPackages(); + } + + @Override + protected String findLibrary(String libname) + { + return super.findLibrary(libname); + } + + @Override + public void setDefaultAssertionStatus(boolean enabled) + { + super.setDefaultAssertionStatus(enabled); + } + + @Override + public void setPackageAssertionStatus(String packageName, boolean enabled) + { + super.setPackageAssertionStatus(packageName, enabled); + } + + @Override + public void setClassAssertionStatus(String className, boolean enabled) + { + super.setClassAssertionStatus(className, enabled); + } + + @Override + public void clearAssertionStatus() + { + super.clearAssertionStatus(); + } + } + + /** + * AntServletHolder + */ + public static class AntServletHolder extends ServletHolder + { + + public AntServletHolder() + { + super(); + } + + public AntServletHolder(Class servlet) + { + super(servlet); + } + + public AntServletHolder(Servlet servlet) + { + super(servlet); + } + + public AntServletHolder(String name, Class servlet) + { + super(name, servlet); + } + + public AntServletHolder(String name, Servlet servlet) + { + super(name, servlet); + } + + protected String getSystemClassPath(ClassLoader loader) throws Exception + { + StringBuilder classpath = new StringBuilder(); + while (loader != null) + { + if (loader instanceof URLClassLoader) + { + URL[] urls = ((URLClassLoader)loader).getURLs(); + if (urls != null) + { + for (int i = 0; i < urls.length; i++) + { + Resource resource = Resource.newResource(urls[i]); + File file = resource.getFile(); + if (file != null && file.exists()) + { + if (classpath.length() > 0) + classpath.append(File.pathSeparatorChar); + classpath.append(file.getAbsolutePath()); + } + } + } + } + else if (loader instanceof AntClassLoader) + { + classpath.append(((AntClassLoader)loader).getClasspath()); + } + + loader = loader.getParent(); + } + + return classpath.toString(); + } + } + + /** + * AntServletHandler + */ + public static class AntServletHandler extends ServletHandler + { + + @Override + public ServletHolder newServletHolder(Source source) + { + return new AntServletHolder(); + } + } + + /** + * Default constructor. Takes project as an argument + * + * @param project the project. + * @throws Exception if unable to create webapp context + */ + public AntWebAppContext(Project project) throws Exception + { + super(); + this.project = project; + setAttribute(MetaInfConfiguration.CONTAINER_JAR_PATTERN, DEFAULT_CONTAINER_INCLUDE_JAR_PATTERN); + setParentLoaderPriority(true); + addConfiguration(new AntWebInfConfiguration(), new AntWebXmlConfiguration(), new AntMetaInfConfiguration()); + } + + /** + * Adds a new Ant's attributes tag object if it have not been created yet. + * + * @param atts the attributes + */ + public void addAttributes(Attributes atts) + { + if (this.attributes != null) + { + throw new BuildException("Only one tag is allowed!"); + } + + this.attributes = atts; + } + + public void addLib(FileSet lib) + { + libraries.add(lib); + } + + public void addClasses(FileSet classes) + { + this.classes.add(classes); + } + + @Override + protected ServletHandler newServletHandler() + { + return new AntServletHandler(); + } + + public void setJettyEnvXml(File jettyEnvXml) + { + this.jettyEnvXml = jettyEnvXml; + TaskLog.log("jetty-env.xml file: = " + (jettyEnvXml == null ? null : jettyEnvXml.getAbsolutePath())); + } + + public File getJettyEnvXml() + { + return this.jettyEnvXml; + } + + public List getLibraries() + { + return librariesConfiguration.getBaseDirectories(); + } + + public void addScanTargets(FileSet scanTargets) + { + if (this.scanTargets != null) + { + throw new BuildException("Only one tag is allowed!"); + } + + this.scanTargets = scanTargets; + } + + public List getScanTargetFiles() + { + if (this.scanTargets == null) + return null; + + FileMatchingConfiguration configuration = new FileMatchingConfiguration(); + configuration.addDirectoryScanner(scanTargets.getDirectoryScanner(project)); + return configuration.getBaseDirectories(); + } + + public List getScanFiles() + { + if (scanFiles == null) + scanFiles = initScanFiles(); + return scanFiles; + } + + public boolean isScanned(File file) + { + List files = getScanFiles(); + if (files == null || files.isEmpty()) + return false; + return files.contains(file); + } + + public List initScanFiles() + { + List scanList = new ArrayList(); + + if (getDescriptor() != null) + { + try (Resource r = Resource.newResource(getDescriptor());) + { + scanList.add(r.getFile()); + } + catch (IOException e) + { + throw new BuildException(e); + } + } + + if (getJettyEnvXml() != null) + { + try (Resource r = Resource.newResource(getJettyEnvXml());) + { + scanList.add(r.getFile()); + } + catch (IOException e) + { + throw new BuildException("Problem configuring scanner for jetty-env.xml", e); + } + } + + if (getDefaultsDescriptor() != null) + { + try (Resource r = Resource.newResource(getDefaultsDescriptor());) + { + if (!WebAppContext.WEB_DEFAULTS_XML.equals(getDefaultsDescriptor())) + { + scanList.add(r.getFile()); + } + } + catch (IOException e) + { + throw new BuildException("Problem configuring scanner for webdefaults.xml", e); + } + } + + if (getOverrideDescriptor() != null) + { + try + { + Resource r = Resource.newResource(getOverrideDescriptor()); + scanList.add(r.getFile()); + } + catch (IOException e) + { + throw new BuildException("Problem configuring scanner for webdefaults.xml", e); + } + } + + //add any extra classpath and libs + List cpFiles = getClassPathFiles(); + if (cpFiles != null) + scanList.addAll(cpFiles); + + //any extra scan targets + List scanFiles = (List)getScanTargetFiles(); + if (scanFiles != null) + scanList.addAll(scanFiles); + + return scanList; + } + + @Override + public void setWar(String path) + { + super.setWar(path); + + try + { + Resource war = Resource.newResource(path); + if (war.exists() && war.isDirectory() && getDescriptor() == null) + { + Resource webXml = war.addPath("WEB-INF/web.xml"); + setDescriptor(webXml.toString()); + } + } + catch (IOException e) + { + throw new BuildException(e); + } + } + + @Override + public void doStart() + { + try + { + TaskLog.logWithTimestamp("Starting web application " + this.getDescriptor()); + + if (jettyEnvXml != null && jettyEnvXml.exists()) + getConfiguration(EnvConfiguration.class).setJettyEnvResource(new PathResource(jettyEnvXml)); + + ClassLoader parentLoader = this.getClass().getClassLoader(); + if (parentLoader instanceof AntClassLoader) + parentLoader = new AntURLClassLoader((AntClassLoader)parentLoader); + + setClassLoader(new WebAppClassLoader(parentLoader, this)); + if (attributes != null && attributes.getAttributes() != null) + { + for (Attribute a : attributes.getAttributes()) + { + setAttribute(a.getName(), a.getValue()); + } + } + + //apply a context xml file if one was supplied + if (contextXml != null) + { + XmlConfiguration xmlConfiguration = new XmlConfiguration(new PathResource(contextXml)); + TaskLog.log("Applying context xml file " + contextXml); + xmlConfiguration.configure(this); + } + + super.doStart(); + } + catch (Exception e) + { + TaskLog.log(e.toString()); + } + } + + @Override + public void doStop() + { + try + { + scanFiles = null; + TaskLog.logWithTimestamp("Stopping web application " + this); + Thread.currentThread().sleep(500L); + super.doStop(); + // remove all filters and servlets. They will be recreated + // either via application of a context xml file or web.xml or annotation or servlet api. + // Event listeners are reset in ContextHandler.doStop() + getServletHandler().setFilters(new FilterHolder[0]); + getServletHandler().setFilterMappings(new FilterMapping[0]); + getServletHandler().setServlets(new ServletHolder[0]); + getServletHandler().setServletMappings(new ServletMapping[0]); + } + catch (InterruptedException e) + { + TaskLog.log(e.toString()); + } + catch (Exception e) + { + TaskLog.log(e.toString()); + } + } + + /** + * @return a list of classpath files (libraries and class directories). + */ + public List getClassPathFiles() + { + List classPathFiles = new ArrayList(); + Iterator classesIterator = classes.iterator(); + while (classesIterator.hasNext()) + { + FileSet clazz = classesIterator.next(); + classPathFiles.add(clazz.getDirectoryScanner(project).getBasedir()); + } + + Iterator iterator = libraries.iterator(); + while (iterator.hasNext()) + { + FileSet library = iterator.next(); + String[] includedFiles = library.getDirectoryScanner(project).getIncludedFiles(); + File baseDir = library.getDirectoryScanner(project).getBasedir(); + + for (int i = 0; i < includedFiles.length; i++) + { + classPathFiles.add(new File(baseDir, includedFiles[i])); + } + } + + return classPathFiles; + } + + /** + * @return a FileMatchingConfiguration object describing the + * configuration of all libraries added to this particular web app + * (both classes and libraries). + */ + public FileMatchingConfiguration getLibrariesConfiguration() + { + FileMatchingConfiguration config = new FileMatchingConfiguration(); + + Iterator classesIterator = classes.iterator(); + while (classesIterator.hasNext()) + { + FileSet clazz = classesIterator.next(); + config.addDirectoryScanner(clazz.getDirectoryScanner(project)); + } + + Iterator librariesIterator = libraries.iterator(); + while (librariesIterator.hasNext()) + { + FileSet library = librariesIterator.next(); + config.addDirectoryScanner(library.getDirectoryScanner(project)); + } + + return config; + } + + public File getContextXml() + { + return contextXml; + } + + public void setContextXml(File contextXml) + { + this.contextXml = contextXml; + } +} diff --git a/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/AntWebInfConfiguration.java b/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/AntWebInfConfiguration.java new file mode 100644 index 00000000000..f629ade0a6b --- /dev/null +++ b/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/AntWebInfConfiguration.java @@ -0,0 +1,59 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.ant; + +import java.io.File; +import java.util.List; + +import org.eclipse.jetty.ee10.webapp.Configuration; +import org.eclipse.jetty.ee10.webapp.WebAppClassLoader; +import org.eclipse.jetty.ee10.webapp.WebAppContext; +import org.eclipse.jetty.ee10.webapp.WebInfConfiguration; +import org.eclipse.jetty.ee10.webapp.WebXmlConfiguration; + +public class AntWebInfConfiguration extends WebInfConfiguration +{ + + @Override + public Class replaces() + { + return WebInfConfiguration.class; + } + + /** + * Adds classpath files into web application classloader, and + * sets web.xml and base directory for the configured web application. + * + * @see WebXmlConfiguration#configure(WebAppContext) + */ + @Override + public void configure(WebAppContext context) throws Exception + { + if (context instanceof AntWebAppContext) + { + List classPathFiles = ((AntWebAppContext)context).getClassPathFiles(); + if (classPathFiles != null) + { + for (File cpFile : classPathFiles) + { + if (cpFile.exists()) + { + ((WebAppClassLoader)context.getClassLoader()).addClassPath(cpFile.getCanonicalPath()); + } + } + } + } + super.configure(context); + } +} diff --git a/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/AntWebXmlConfiguration.java b/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/AntWebXmlConfiguration.java new file mode 100644 index 00000000000..7599f50e808 --- /dev/null +++ b/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/AntWebXmlConfiguration.java @@ -0,0 +1,64 @@ +// +// ======================================================================== +// Copyright (c) 1995-2012 Sabre Holdings and others. +// ------------------------------------------------------------------------ +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.ant; + +import java.io.File; +import java.util.List; + +import org.eclipse.jetty.ee10.webapp.Configuration; +import org.eclipse.jetty.ee10.webapp.WebXmlConfiguration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This configuration object provides additional way to inject application + * properties into the configured web application. The list of classpath files, + * the application base directory and web.xml file could be specified in this + * way. + */ +public class AntWebXmlConfiguration extends WebXmlConfiguration +{ + private static final Logger LOG = LoggerFactory.getLogger(WebXmlConfiguration.class); + + /** + * List of classpath files. + */ + private List classPathFiles; + + /** + * Web application root directory. + */ + private File webAppBaseDir; + + public AntWebXmlConfiguration() + { + super(); + } + + @Override + public Class replaces() + { + return WebXmlConfiguration.class; + } + + public void setClassPathFiles(List classPathFiles) + { + this.classPathFiles = classPathFiles; + } + + public void setWebAppBaseDir(File webAppBaseDir) + { + this.webAppBaseDir = webAppBaseDir; + } +} diff --git a/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/JettyRunTask.java b/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/JettyRunTask.java new file mode 100644 index 00000000000..6c015478143 --- /dev/null +++ b/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/JettyRunTask.java @@ -0,0 +1,310 @@ +// +// ======================================================================== +// Copyright (c) 1995-2012 Sabre Holdings and others. +// ------------------------------------------------------------------------ +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.ant; + +import java.io.File; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.taskdefs.Property; +import org.eclipse.jetty.ee10.ant.types.Connector; +import org.eclipse.jetty.ee10.ant.types.Connectors; +import org.eclipse.jetty.ee10.ant.types.ContextHandlers; +import org.eclipse.jetty.ee10.ant.types.LoginServices; +import org.eclipse.jetty.ee10.ant.types.SystemProperties; +import org.eclipse.jetty.ee10.ant.utils.TaskLog; +import org.eclipse.jetty.ee10.servlet.security.LoginService; +import org.eclipse.jetty.ee10.webapp.WebAppContext; +import org.eclipse.jetty.server.RequestLog; + +/** + * Ant task for running a Jetty server. + */ +public class JettyRunTask extends Task +{ + private int scanIntervalSeconds; + + /** + * Temporary files directory. + */ + private File tempDirectory; + + /** + * List of web applications to be deployed. + */ + private List webapps = new ArrayList<>(); + + /** + * Location of jetty.xml file. + */ + private File jettyXml; + + /** + * List of server connectors. + */ + private Connectors connectors = null; + + /** + * Server request logger object. + */ + private RequestLog requestLog; + + /** + * List of login services. + */ + private LoginServices loginServices; + + /** + * List of system properties to be set. + */ + private SystemProperties systemProperties; + + /** + * List of other contexts to deploy + */ + private ContextHandlers contextHandlers; + + /** + * Port Jetty will use for the default connector + */ + private int jettyPort = 8080; + + private int stopPort; + + private String stopKey; + + private boolean daemon; + + public JettyRunTask() + { + TaskLog.setTask(this); + } + + /** + * Creates a new WebApp Ant object. + * + * @param webapp the webapp context + */ + public void addWebApp(AntWebAppContext webapp) + { + webapps.add(webapp); + } + + /** + * Adds a new Ant's connector tag object if it have not been created yet. + * + * @param connectors the connectors + */ + public void addConnectors(Connectors connectors) + { + if (this.connectors != null) + throw new BuildException("Only one tag is allowed!"); + this.connectors = connectors; + } + + public void addLoginServices(LoginServices services) + { + if (this.loginServices != null) + throw new BuildException("Only one tag is allowed!"); + this.loginServices = services; + } + + public void addSystemProperties(SystemProperties systemProperties) + { + if (this.systemProperties != null) + throw new BuildException("Only one tag is allowed!"); + this.systemProperties = systemProperties; + } + + public void addContextHandlers(ContextHandlers handlers) + { + if (this.contextHandlers != null) + throw new BuildException("Only one tag is allowed!"); + this.contextHandlers = handlers; + } + + public File getTempDirectory() + { + return tempDirectory; + } + + public void setTempDirectory(File tempDirectory) + { + this.tempDirectory = tempDirectory; + } + + public File getJettyXml() + { + return jettyXml; + } + + public void setJettyXml(File jettyXml) + { + this.jettyXml = jettyXml; + } + + public void setRequestLog(String className) + { + try + { + this.requestLog = (RequestLog)Class.forName(className).getDeclaredConstructor().newInstance(); + } + catch (ClassNotFoundException e) + { + throw new BuildException("Unknown request logger class: " + className); + } + catch (Exception e) + { + throw new BuildException("Request logger instantiation exception: " + e); + } + } + + public String getRequestLog() + { + if (requestLog != null) + { + return requestLog.getClass().getName(); + } + + return ""; + } + + /** + * Sets the port Jetty uses for the default connector. + * + * @param jettyPort The port Jetty will use for the default connector + */ + public void setJettyPort(final int jettyPort) + { + this.jettyPort = jettyPort; + } + + /** + * Executes this Ant task. The build flow is being stopped until Jetty + * server stops. + * + * @throws BuildException if unable to build + */ + @Override + public void execute() throws BuildException + { + + TaskLog.log("Configuring Jetty for project: " + getProject().getName()); + + setSystemProperties(); + + List connectorsList = null; + + if (connectors != null) + connectorsList = connectors.getConnectors(); + else + connectorsList = new Connectors(jettyPort, 30000).getDefaultConnectors(); + + List loginServicesList = (loginServices != null ? loginServices.getLoginServices() : new ArrayList()); + ServerProxyImpl server = new ServerProxyImpl(); + server.setConnectors(connectorsList); + server.setLoginServices(loginServicesList); + server.setRequestLog(requestLog); + server.setJettyXml(jettyXml); + server.setDaemon(daemon); + server.setStopPort(stopPort); + server.setStopKey(stopKey); + server.setContextHandlers(contextHandlers); + server.setTempDirectory(tempDirectory); + server.setScanIntervalSecs(scanIntervalSeconds); + + try + { + for (WebAppContext webapp : webapps) + { + server.addWebApplication((AntWebAppContext)webapp); + } + } + catch (Exception e) + { + throw new BuildException(e); + } + + server.start(); + } + + public int getStopPort() + { + return stopPort; + } + + public void setStopPort(int stopPort) + { + this.stopPort = stopPort; + TaskLog.log("stopPort=" + stopPort); + } + + public String getStopKey() + { + return stopKey; + } + + public void setStopKey(String stopKey) + { + this.stopKey = stopKey; + TaskLog.log("stopKey=" + stopKey); + } + + /** + * @return the daemon + */ + public boolean isDaemon() + { + return daemon; + } + + /** + * @param daemon the daemon to set + */ + public void setDaemon(boolean daemon) + { + this.daemon = daemon; + TaskLog.log("Daemon=" + daemon); + } + + public int getScanIntervalSeconds() + { + return scanIntervalSeconds; + } + + public void setScanIntervalSeconds(int secs) + { + scanIntervalSeconds = secs; + TaskLog.log("scanIntervalSecs=" + secs); + } + + /** + * Sets the system properties. + */ + private void setSystemProperties() + { + if (systemProperties != null) + { + Iterator propertiesIterator = systemProperties.getSystemProperties().iterator(); + while (propertiesIterator.hasNext()) + { + Property property = ((Property)propertiesIterator.next()); + SystemProperties.setIfNotSetAlready(property); + } + } + } +} diff --git a/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/JettyStopTask.java b/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/JettyStopTask.java new file mode 100644 index 00000000000..18af53fe70c --- /dev/null +++ b/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/JettyStopTask.java @@ -0,0 +1,114 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.ant; + +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 org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Task; +import org.eclipse.jetty.ee10.ant.utils.TaskLog; + +/** + * JettyStopTask + */ +public class JettyStopTask extends Task +{ + + private int stopPort; + + private String stopKey; + + private int stopWait; + + /** + * + */ + public JettyStopTask() + { + TaskLog.setTask(this); + } + + @Override + public void execute() throws BuildException + { + try + { + Socket s = new Socket(InetAddress.getByName("127.0.0.1"), stopPort); + if (stopWait > 0) + s.setSoTimeout(stopWait * 1000); + try + { + OutputStream out = s.getOutputStream(); + out.write((stopKey + "\r\nstop\r\n").getBytes()); + out.flush(); + + if (stopWait > 0) + { + TaskLog.log("Waiting" + (stopWait > 0 ? (" " + stopWait + "sec") : "") + " for jetty to stop"); + LineNumberReader lin = new LineNumberReader(new InputStreamReader(s.getInputStream())); + String response = lin.readLine(); + if ("Stopped".equals(response)) + System.err.println("Stopped"); + } + } + finally + { + s.close(); + } + } + catch (ConnectException e) + { + TaskLog.log("Jetty not running!"); + } + catch (Exception e) + { + TaskLog.log(e.getMessage()); + } + } + + public int getStopPort() + { + return stopPort; + } + + public void setStopPort(int stopPort) + { + this.stopPort = stopPort; + } + + public String getStopKey() + { + return stopKey; + } + + public void setStopKey(String stopKey) + { + this.stopKey = stopKey; + } + + public int getStopWait() + { + return stopWait; + } + + public void setStopWait(int stopWait) + { + this.stopWait = stopWait; + } +} diff --git a/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/ServerProxyImpl.java b/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/ServerProxyImpl.java new file mode 100644 index 00000000000..ab085811757 --- /dev/null +++ b/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/ServerProxyImpl.java @@ -0,0 +1,491 @@ +// +// ======================================================================== +// Copyright (c) 1995-2012 Sabre Holdings and others. +// ------------------------------------------------------------------------ +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.ant; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import org.eclipse.jetty.ee10.ant.types.Connector; +import org.eclipse.jetty.ee10.ant.types.ContextHandlers; +import org.eclipse.jetty.ee10.ant.utils.ServerProxy; +import org.eclipse.jetty.ee10.ant.utils.TaskLog; +import org.eclipse.jetty.ee10.servlet.security.LoginService; +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.RequestLog; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.ShutdownMonitor; +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.util.Scanner; +import org.eclipse.jetty.util.resource.PathResource; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.xml.XmlConfiguration; +import org.xml.sax.SAXException; + +/** + * A proxy class for interaction with Jetty server object. Used to have some + * level of abstraction over standard Jetty classes. + */ +public class ServerProxyImpl implements ServerProxy +{ + + /** + * Proxied Jetty server object. + */ + private Server server; + + /** + * Temporary files directory. + */ + private File tempDirectory; + + /** + * Collection of context handlers (web application contexts). + */ + private ContextHandlerCollection contexts; + + /** + * Location of jetty.xml file. + */ + private File jettyXml; + + /** + * List of connectors. + */ + private List connectors; + + /** + * Request logger. + */ + private RequestLog requestLog; + + /** + * User realms. + */ + private List loginServices; + + /** + * List of added web applications. + */ + private List webApplications = new ArrayList(); + + /** + * other contexts to deploy + */ + private ContextHandlers contextHandlers; + + /** + * scan interval for changed files + */ + private int scanIntervalSecs; + + /** + * port to listen for stop command + */ + private int stopPort; + + /** + * security key for stop command + */ + private String stopKey; + + /** + * wait for all jetty threads to exit or continue + */ + private boolean daemon; + + private boolean configured = false; + + /** + * WebAppScannerListener + * + * Handle notifications that files we are interested in have changed + * during execution. + */ + public static class WebAppScannerListener implements Scanner.BulkListener + { + AntWebAppContext awc; + + public WebAppScannerListener(AntWebAppContext awc) + { + this.awc = awc; + } + + @Override + public void filesChanged(Set changedFileNames) + { + boolean isScanned = false; + try + { + Iterator itor = changedFileNames.iterator(); + while (!isScanned && itor.hasNext()) + { + isScanned = awc.isScanned(Resource.newResource(itor.next()).getFile()); + } + if (isScanned) + { + awc.stop(); + awc.start(); + } + } + catch (Exception e) + { + TaskLog.log(e.getMessage()); + } + } + } + + /** + * Default constructor. Creates a new Jetty server with a standard connector + * listening on a given port. + */ + public ServerProxyImpl() + { + server = new Server(); + server.setStopAtShutdown(true); + } + + @Override + public void addWebApplication(AntWebAppContext webApp) + { + webApplications.add(webApp); + } + + public int getStopPort() + { + return stopPort; + } + + public void setStopPort(int stopPort) + { + this.stopPort = stopPort; + } + + public String getStopKey() + { + return stopKey; + } + + public void setStopKey(String stopKey) + { + this.stopKey = stopKey; + } + + public File getJettyXml() + { + return jettyXml; + } + + public void setJettyXml(File jettyXml) + { + this.jettyXml = jettyXml; + } + + public List getConnectors() + { + return connectors; + } + + public void setConnectors(List connectors) + { + this.connectors = connectors; + } + + public RequestLog getRequestLog() + { + return requestLog; + } + + public void setRequestLog(RequestLog requestLog) + { + this.requestLog = requestLog; + } + + public List getLoginServices() + { + return loginServices; + } + + public void setLoginServices(List loginServices) + { + this.loginServices = loginServices; + } + + public List getWebApplications() + { + return webApplications; + } + + public void setWebApplications(List webApplications) + { + this.webApplications = webApplications; + } + + public File getTempDirectory() + { + return tempDirectory; + } + + public void setTempDirectory(File tempDirectory) + { + this.tempDirectory = tempDirectory; + } + + @Override + public void start() + { + try + { + configure(); + + configureWebApps(); + + server.start(); + + System.setProperty("jetty.ant.server.port", "" + ((ServerConnector)server.getConnectors()[0]).getLocalPort()); + + String host = ((ServerConnector)server.getConnectors()[0]).getHost(); + + if (host == null) + { + System.setProperty("jetty.ant.server.host", "localhost"); + } + else + { + System.setProperty("jetty.ant.server.host", host); + } + + startScanners(); + + TaskLog.log("Jetty AntTask Started"); + + if (!daemon) + server.join(); + } + catch (InterruptedException e) + { + new RuntimeException(e); + } + catch (Exception e) + { + e.printStackTrace(); + new RuntimeException(e); + } + } + + @Override + public Object getProxiedObject() + { + return server; + } + + /** + * @return the daemon + */ + public boolean isDaemon() + { + return daemon; + } + + /** + * @param daemon the daemon to set + */ + public void setDaemon(boolean daemon) + { + this.daemon = daemon; + } + + /** + * @return the contextHandlers + */ + public ContextHandlers getContextHandlers() + { + return contextHandlers; + } + + /** + * @param contextHandlers the contextHandlers to set + */ + public void setContextHandlers(ContextHandlers contextHandlers) + { + this.contextHandlers = contextHandlers; + } + + public int getScanIntervalSecs() + { + return scanIntervalSecs; + } + + public void setScanIntervalSecs(int scanIntervalSecs) + { + this.scanIntervalSecs = scanIntervalSecs; + } + + /** + * Configures Jetty server before adding any web applications to it. + */ + private void configure() + { + if (configured) + return; + + configured = true; + + if (stopPort > 0 && stopKey != null) + { + ShutdownMonitor monitor = ShutdownMonitor.getInstance(); + monitor.setPort(stopPort); + monitor.setKey(stopKey); + monitor.setExitVm(false); + } + + if (tempDirectory != null && !tempDirectory.exists()) + tempDirectory.mkdirs(); + + // Applies external configuration via jetty.xml + applyJettyXml(); + + // Configures connectors for this server instance. + if (connectors != null) + { + for (Connector c : connectors) + { + ServerConnector jc = new ServerConnector(server); + + jc.setPort(c.getPort()); + jc.setIdleTimeout(c.getMaxIdleTime()); + + server.addConnector(jc); + } + } + + // Configures login services + if (loginServices != null) + { + for (LoginService ls : loginServices) + { + server.addBean(ls); + } + } + + // Does not cache resources, to prevent Windows from locking files + Resource.setDefaultUseCaches(false); + + // Set default server handlers + configureHandlers(); + } + + /** + * + */ + private void configureHandlers() + { + if (requestLog != null) + server.setRequestLog(requestLog); + + contexts = server.getDescendant(ContextHandlerCollection.class); + if (contexts == null) + { + contexts = new ContextHandlerCollection(); + Handler.Collection handlers = server.getDescendant(Handler.Collection.class); + if (handlers == null) + server.setHandler(new Handler.Collection(contexts, new DefaultHandler())); + else + handlers.addHandler(contexts); + } + + //if there are any extra contexts to deploy + if (contextHandlers != null && contextHandlers.getContextHandlers() != null) + { + for (ContextHandler c : contextHandlers.getContextHandlers()) + { + contexts.addHandler(c); + } + } + } + + /** + * Applies jetty.xml configuration to the Jetty server instance. + */ + private void applyJettyXml() + { + if (jettyXml != null && jettyXml.exists()) + { + TaskLog.log("Configuring jetty from xml configuration file = " + jettyXml.getAbsolutePath()); + XmlConfiguration configuration; + try + { + configuration = new XmlConfiguration(new PathResource(jettyXml)); + configuration.configure(server); + } + catch (MalformedURLException e) + { + throw new RuntimeException(e); + } + catch (SAXException e) + { + throw new RuntimeException(e); + } + catch (IOException e) + { + throw new RuntimeException(e); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + } + } + + /** + * Starts web applications' scanners. + */ + private void startScanners() throws Exception + { + for (AntWebAppContext awc : webApplications) + { + if (scanIntervalSecs <= 0) + return; + + TaskLog.log("Web application '" + awc + "': starting scanner at interval of " + scanIntervalSecs + " seconds."); + Scanner.Listener changeListener = new WebAppScannerListener(awc); + Scanner scanner = new Scanner(); + scanner.setScanInterval(scanIntervalSecs); + scanner.addListener(changeListener); + scanner.setScanDirs(awc.getScanFiles()); + scanner.setReportExistingFilesOnStartup(false); + scanner.start(); + } + } + + /** + * + */ + private void configureWebApps() + { + for (AntWebAppContext awc : webApplications) + { + awc.setAttribute(AntWebAppContext.BASETEMPDIR, tempDirectory); + if (contexts != null) + contexts.addHandler(awc); + } + } +} diff --git a/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/package-info.java b/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/package-info.java new file mode 100644 index 00000000000..1d6ee91f7ce --- /dev/null +++ b/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/package-info.java @@ -0,0 +1,18 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 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 Ant : Ant Tasks and Configuration + */ +package org.eclipse.jetty.ee10.ant; + diff --git a/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/types/Attribute.java b/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/types/Attribute.java new file mode 100644 index 00000000000..ef2b5c37353 --- /dev/null +++ b/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/types/Attribute.java @@ -0,0 +1,42 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.ant.types; + +public class Attribute +{ + + String name; + + String value; + + public void setName(String name) + { + this.name = name; + } + + public void setValue(String value) + { + this.value = value; + } + + public String getName() + { + return name; + } + + public String getValue() + { + return value; + } +} diff --git a/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/types/Attributes.java b/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/types/Attributes.java new file mode 100644 index 00000000000..91bd7504a78 --- /dev/null +++ b/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/types/Attributes.java @@ -0,0 +1,33 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.ant.types; + +import java.util.ArrayList; +import java.util.List; + +public class Attributes +{ + + List _attributes = new ArrayList(); + + public void addAttribute(Attribute attr) + { + _attributes.add(attr); + } + + public List getAttributes() + { + return _attributes; + } +} diff --git a/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/types/Connector.java b/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/types/Connector.java new file mode 100644 index 00000000000..8b6ed4c32b4 --- /dev/null +++ b/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/types/Connector.java @@ -0,0 +1,54 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.ant.types; + +/** + * Connector + */ +public class Connector +{ + private int port; + private int maxIdleTime; + + public Connector() + { + + } + + public Connector(int port, int maxIdleTime) + { + this.port = port; + this.maxIdleTime = maxIdleTime; + } + + public int getPort() + { + return port; + } + + public void setPort(int port) + { + this.port = port; + } + + public int getMaxIdleTime() + { + return maxIdleTime; + } + + public void setMaxIdleTime(int maxIdleTime) + { + this.maxIdleTime = maxIdleTime; + } +} diff --git a/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/types/Connectors.java b/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/types/Connectors.java new file mode 100644 index 00000000000..3b7bc4e9ec6 --- /dev/null +++ b/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/types/Connectors.java @@ -0,0 +1,76 @@ +// +// ======================================================================== +// Copyright (c) 1995-2012 Sabre Holdings and others. +// ------------------------------------------------------------------------ +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.ant.types; + +import java.util.ArrayList; +import java.util.List; + +/** + * Specifies a jetty configuration <connectors/> element for Ant build file. + */ +public class Connectors +{ + private List connectors = new ArrayList(); + private List defaultConnectors = new ArrayList(); + + /** + * Default constructor. + */ + public Connectors() + { + this(8080, 30000); + } + + /** + * Constructor. + * + * @param port The port that the default connector will listen on + * @param maxIdleTime The maximum idle time for the default connector + */ + public Connectors(int port, int maxIdleTime) + { + defaultConnectors.add(new Connector(port, maxIdleTime)); + } + + /** + * Adds a connector to the list of connectors to deploy. + * + * @param connector A connector to add to the list + */ + public void add(Connector connector) + { + connectors.add(connector); + } + + /** + * Returns the list of known connectors to deploy. + * + * @return The list of known connectors + */ + public List getConnectors() + { + return connectors; + } + + /** + * Gets the default list of connectors to deploy when no connectors + * were explicitly added to the list. + * + * @return The list of default connectors + */ + public List getDefaultConnectors() + { + return defaultConnectors; + } +} diff --git a/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/types/ContextHandlers.java b/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/types/ContextHandlers.java new file mode 100644 index 00000000000..7fec12413fc --- /dev/null +++ b/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/types/ContextHandlers.java @@ -0,0 +1,37 @@ +// +// ======================================================================== +// Copyright (c) 1995-2012 Sabre Holdings and others. +// ------------------------------------------------------------------------ +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.ant.types; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jetty.server.handler.ContextHandler; + +/** + * Specifies <contextHandlers/> element in web app configuration. + */ +public class ContextHandlers +{ + private List contextHandlers = new ArrayList(); + + public void add(ContextHandler handler) + { + contextHandlers.add(handler); + } + + public List getContextHandlers() + { + return contextHandlers; + } +} diff --git a/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/types/FileMatchingConfiguration.java b/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/types/FileMatchingConfiguration.java new file mode 100644 index 00000000000..7617d16b945 --- /dev/null +++ b/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/types/FileMatchingConfiguration.java @@ -0,0 +1,91 @@ +// +// ======================================================================== +// Copyright (c) 1995-2012 Sabre Holdings and others. +// ------------------------------------------------------------------------ +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.ant.types; + +import java.io.File; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.apache.tools.ant.DirectoryScanner; + +/** + * Describes set of files matched by <fileset/> elements in ant configuration + * file. It is used to group application classes, libraries, and scannedTargets + * elements. + */ +public class FileMatchingConfiguration +{ + + private List directoryScanners; + + public FileMatchingConfiguration() + { + this.directoryScanners = new ArrayList<>(); + } + + /** + * @param directoryScanner new directory scanner retrieved from the + * <fileset/> element. + */ + public void addDirectoryScanner(DirectoryScanner directoryScanner) + { + this.directoryScanners.add(directoryScanner); + } + + /** + * @return a list of base directories denoted by a list of directory + * scanners. + */ + public List getBaseDirectories() + { + List baseDirs = new ArrayList<>(); + Iterator scanners = directoryScanners.iterator(); + while (scanners.hasNext()) + { + DirectoryScanner scanner = (DirectoryScanner)scanners.next(); + baseDirs.add(scanner.getBasedir()); + } + + return baseDirs; + } + + /** + * Checks if passed file is scanned by any of the directory scanners. + * + * @param pathToFile a fully qualified path to tested file. + * @return true if so, false otherwise. + */ + public boolean isIncluded(String pathToFile) + { + Iterator scanners = directoryScanners.iterator(); + while (scanners.hasNext()) + { + DirectoryScanner scanner = (DirectoryScanner)scanners.next(); + scanner.scan(); + String[] includedFiles = scanner.getIncludedFiles(); + + for (int i = 0; i < includedFiles.length; i++) + { + File includedFile = new File(scanner.getBasedir(), includedFiles[i]); + if (pathToFile.equalsIgnoreCase(includedFile.getAbsolutePath())) + { + return true; + } + } + } + + return false; + } +} diff --git a/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/types/LoginServices.java b/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/types/LoginServices.java new file mode 100644 index 00000000000..d803da438ec --- /dev/null +++ b/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/types/LoginServices.java @@ -0,0 +1,37 @@ +// +// ======================================================================== +// Copyright (c) 1995-2012 Sabre Holdings and others. +// ------------------------------------------------------------------------ +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.ant.types; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jetty.ee10.servlet.security.LoginService; + +/** + * Specifies a jetty configuration <loginServices/> element for Ant build file. + */ +public class LoginServices +{ + private List loginServices = new ArrayList(); + + public void add(LoginService service) + { + loginServices.add(service); + } + + public List getLoginServices() + { + return loginServices; + } +} diff --git a/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/types/SystemProperties.java b/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/types/SystemProperties.java new file mode 100644 index 00000000000..c746dea86f5 --- /dev/null +++ b/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/types/SystemProperties.java @@ -0,0 +1,59 @@ +// +// ======================================================================== +// Copyright (c) 1995-2012 Sabre Holdings and others. +// ------------------------------------------------------------------------ +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.ant.types; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.tools.ant.taskdefs.Property; +import org.eclipse.jetty.ee10.ant.utils.TaskLog; + +/** + * SystemProperties + *

    + * Ant <systemProperties/> tag definition. + */ +public class SystemProperties +{ + + private List systemProperties = new ArrayList(); + + public List getSystemProperties() + { + return systemProperties; + } + + public void addSystemProperty(Property property) + { + systemProperties.add(property); + } + + /** + * Set a System.property with this value if it is not already set. + * + * @param property the property to test + * @return true if property has been set + */ + public static boolean setIfNotSetAlready(Property property) + { + if (System.getProperty(property.getName()) == null) + { + System.setProperty(property.getName(), (property.getValue() == null ? "" : property.getValue())); + TaskLog.log("Setting property '" + property.getName() + "' to value '" + property.getValue() + "'"); + return true; + } + + return false; + } +} diff --git a/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/types/package-info.java b/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/types/package-info.java new file mode 100644 index 00000000000..7a2eaeea6fd --- /dev/null +++ b/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/types/package-info.java @@ -0,0 +1,18 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 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 Ant : Ant Wrappers of Jetty Internals + */ +package org.eclipse.jetty.ee10.ant.types; + diff --git a/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/utils/ServerProxy.java b/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/utils/ServerProxy.java new file mode 100644 index 00000000000..49c0bf6ad76 --- /dev/null +++ b/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/utils/ServerProxy.java @@ -0,0 +1,34 @@ +// +// ======================================================================== +// Copyright (c) 1995-2012 Sabre Holdings and others. +// ------------------------------------------------------------------------ +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.ant.utils; + +import org.eclipse.jetty.ee10.ant.AntWebAppContext; + +public interface ServerProxy +{ + + /** + * Adds a new web application to this server. + * + * @param awc a AntWebAppContext object. + */ + public void addWebApplication(AntWebAppContext awc); + + /** + * Starts this server. + */ + public void start(); + + public Object getProxiedObject(); +} diff --git a/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/utils/TaskLog.java b/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/utils/TaskLog.java new file mode 100644 index 00000000000..6cb08648333 --- /dev/null +++ b/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/utils/TaskLog.java @@ -0,0 +1,52 @@ +// +// ======================================================================== +// Copyright (c) 1995-2012 Sabre Holdings and others. +// ------------------------------------------------------------------------ +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.ant.utils; + +import java.text.SimpleDateFormat; +import java.util.Date; + +import org.apache.tools.ant.Task; + +/** + * Provides logging functionality for classes without access to the Ant project + * variable. + */ +public class TaskLog +{ + + private static Task task; + + private static final SimpleDateFormat format = new SimpleDateFormat( + "yyyy-MM-dd HH:mm:ss.SSS"); + + public static void setTask(Task task) + { + TaskLog.task = task; + } + + public static void log(String message) + { + task.log(message); + } + + public static void logWithTimestamp(String message) + { + String date; + synchronized (format) + { + date = format.format(new Date()); + } + task.log(date + ": " + message); + } +} diff --git a/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/utils/package-info.java b/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/utils/package-info.java new file mode 100644 index 00000000000..b7883cb3cf7 --- /dev/null +++ b/jetty-ee10/jetty-ee10-ant/src/main/java/org/eclipse/jetty/ee10/ant/utils/package-info.java @@ -0,0 +1,18 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 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 Ant : Utility Classes + */ +package org.eclipse.jetty.ee10.ant.utils; + diff --git a/jetty-ee10/jetty-ee10-ant/src/main/resources/META-INF/services/org.eclipse.jetty.webapp.Configuration b/jetty-ee10/jetty-ee10-ant/src/main/resources/META-INF/services/org.eclipse.jetty.webapp.Configuration new file mode 100644 index 00000000000..817ad816933 --- /dev/null +++ b/jetty-ee10/jetty-ee10-ant/src/main/resources/META-INF/services/org.eclipse.jetty.webapp.Configuration @@ -0,0 +1,2 @@ +org.eclipse.jetty.ee10.ant.AntWebInfConfiguration +org.eclipse.jetty.ee10.ant.AntWebXmlConfiguration diff --git a/jetty-ee10/jetty-ee10-ant/src/main/resources/tasks.properties b/jetty-ee10/jetty-ee10-ant/src/main/resources/tasks.properties new file mode 100644 index 00000000000..8a3d090bf0f --- /dev/null +++ b/jetty-ee10/jetty-ee10-ant/src/main/resources/tasks.properties @@ -0,0 +1,2 @@ +jetty.run=org.eclipse.jetty.ee10.ant.JettyRunTask +jetty.stop=org.eclipse.jetty.ee10.ant.JettyStopTask \ No newline at end of file diff --git a/jetty-ee10/jetty-ee10-ant/src/test/config/build.xml b/jetty-ee10/jetty-ee10-ant/src/test/config/build.xml new file mode 100644 index 00000000000..fd80592ab9a --- /dev/null +++ b/jetty-ee10/jetty-ee10-ant/src/test/config/build.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-ant/src/test/java/org/eclipse/jetty/ee10/ant/AntBuild.java b/jetty-ee10/jetty-ee10-ant/src/test/java/org/eclipse/jetty/ee10/ant/AntBuild.java new file mode 100644 index 00000000000..5b8e00fe7fb --- /dev/null +++ b/jetty-ee10/jetty-ee10-ant/src/test/java/org/eclipse/jetty/ee10/ant/AntBuild.java @@ -0,0 +1,289 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.ant; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.tools.ant.DefaultLogger; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.ProjectHelper; +import org.eclipse.jetty.toolchain.test.IO; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; + +public class AntBuild +{ + private Thread _process; + private String _ant; + + private int _port; + private String _host; + + public AntBuild(String ant) + { + _ant = ant; + } + + private class AntBuildProcess implements Runnable + { + List connList; + + @Override + public void run() + { + File buildFile = new File(_ant); + + Project antProject = new Project(); + try + { + antProject.setBaseDir(MavenTestingUtils.getBaseDir()); + antProject.setUserProperty("ant.file", buildFile.getAbsolutePath()); + DefaultLogger logger = new DefaultLogger(); + + ConsoleParser parser = new ConsoleParser(); + //connList = parser.newPattern(".*([0-9]+\\.[0-9]*\\.[0-9]*\\.[0-9]*):([0-9]*)",1); + connList = parser.newPattern("Jetty AntTask Started", 1); + + PipedOutputStream pos = new PipedOutputStream(); + PipedInputStream pis = new PipedInputStream(pos); + + PipedOutputStream pose = new PipedOutputStream(); + PipedInputStream pise = new PipedInputStream(pose); + + startPump("STDOUT", parser, pis); + startPump("STDERR", parser, pise); + + logger.setErrorPrintStream(new PrintStream(pos)); + logger.setOutputPrintStream(new PrintStream(pose)); + logger.setMessageOutputLevel(Project.MSG_VERBOSE); + antProject.addBuildListener(logger); + + antProject.fireBuildStarted(); + antProject.init(); + + ProjectHelper helper = ProjectHelper.getProjectHelper(); + + antProject.addReference("ant.projectHelper", helper); + + helper.parse(antProject, buildFile); + + antProject.executeTarget("jetty.run"); + + parser.waitForDone(10000, TimeUnit.MILLISECONDS); + } + catch (Exception e) + { + antProject.fireBuildFinished(e); + } + } + + public void waitForStarted() throws Exception + { + while (connList == null || connList.isEmpty()) + { + Thread.sleep(10); + } + } + } + + public void start() throws Exception + { + System.out.println("Starting Ant Build ..."); + AntBuildProcess abp = new AntBuildProcess(); + _process = new Thread(abp); + + _process.start(); + + abp.waitForStarted(); + + // once this has returned we should have the connection info we need + //_host = abp.getConnectionList().get(0)[0]; + //_port = Integer.parseInt(abp.getConnectionList().get(0)[1]); + + } + + public int getJettyPort() + { + return Integer.parseInt(System.getProperty("jetty.ant.server.port")); + } + + public String getJettyHost() + { + return System.getProperty("jetty.ant.server.host"); + } + + /** + * Stop the jetty server + */ + public void stop() + { + System.out.println("Stopping Ant Build ..."); + _process.interrupt(); + } + + private static class ConsoleParser + { + private List patterns = new ArrayList(); + private CountDownLatch latch; + private int count; + + public List newPattern(String exp, int cnt) + { + ConsolePattern pat = new ConsolePattern(exp, cnt); + patterns.add(pat); + count += cnt; + + return pat.getMatches(); + } + + public void parse(String line) + { + for (ConsolePattern pat : patterns) + { + Matcher mat = pat.getMatcher(line); + if (mat.find()) + { + int num = 0; + int count = mat.groupCount(); + String[] match = new String[count]; + while (num++ < count) + { + match[num - 1] = mat.group(num); + } + pat.getMatches().add(match); + + if (pat.getCount() > 0) + { + getLatch().countDown(); + } + } + } + } + + public void waitForDone(long timeout, TimeUnit unit) throws InterruptedException + { + getLatch().await(timeout, unit); + } + + private CountDownLatch getLatch() + { + synchronized (this) + { + if (latch == null) + { + latch = new CountDownLatch(count); + } + } + + return latch; + } + } + + private static class ConsolePattern + { + private Pattern pattern; + private List matches; + private int count; + + ConsolePattern(String exp, int cnt) + { + pattern = Pattern.compile(exp); + matches = new ArrayList(); + count = cnt; + } + + public Matcher getMatcher(String line) + { + return pattern.matcher(line); + } + + public List getMatches() + { + return matches; + } + + public int getCount() + { + return count; + } + } + + private void startPump(String mode, ConsoleParser parser, InputStream inputStream) + { + ConsoleStreamer pump = new ConsoleStreamer(mode, inputStream); + pump.setParser(parser); + Thread thread = new Thread(pump, "ConsoleStreamer/" + mode); + thread.start(); + } + + /** + * Simple streamer for the console output from a Process + */ + private static class ConsoleStreamer implements Runnable + { + private String mode; + private BufferedReader reader; + private ConsoleParser parser; + + public ConsoleStreamer(String mode, InputStream is) + { + this.mode = mode; + this.reader = new BufferedReader(new InputStreamReader(is)); + } + + public void setParser(ConsoleParser connector) + { + this.parser = connector; + } + + @Override + public void run() + { + String line; + //System.out.printf("ConsoleStreamer/%s initiated%n",mode); + try + { + while ((line = reader.readLine()) != (null)) + { + if (parser != null) + { + parser.parse(line); + } + System.out.println("[" + mode + "] " + line); + } + } + catch (IOException ignore) + { + /* ignore */ + } + finally + { + IO.close(reader); + } + //System.out.printf("ConsoleStreamer/%s finished%n",mode); + } + } +} diff --git a/jetty-ee10/jetty-ee10-ant/src/test/java/org/eclipse/jetty/ee10/ant/JettyAntTaskTest.java b/jetty-ee10/jetty-ee10-ant/src/test/java/org/eclipse/jetty/ee10/ant/JettyAntTaskTest.java new file mode 100644 index 00000000000..ecd9a1bbfee --- /dev/null +++ b/jetty-ee10/jetty-ee10-ant/src/test/java/org/eclipse/jetty/ee10/ant/JettyAntTaskTest.java @@ -0,0 +1,66 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.ant; + +import java.net.HttpURLConnection; +import java.net.URI; + +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +public class JettyAntTaskTest +{ + + @Test + public void testConnectorTask() throws Exception + { + AntBuild build = new AntBuild(MavenTestingUtils.getTestResourceFile("connector-test.xml").getAbsolutePath()); + + build.start(); + + URI uri = new URI("http://" + build.getJettyHost() + ":" + build.getJettyPort()); + + HttpURLConnection connection = (HttpURLConnection)uri.toURL().openConnection(); + + connection.connect(); + + assertThat("response code is 404", connection.getResponseCode(), is(404)); + + build.stop(); + } + + @Disabled //TODO + @Test + public void testWebApp() throws Exception + { + AntBuild build = new AntBuild(MavenTestingUtils.getTestResourceFile("webapp-test.xml").getAbsolutePath()); + + build.start(); + + URI uri = new URI("http://" + build.getJettyHost() + ":" + build.getJettyPort() + "/"); + + HttpURLConnection connection = (HttpURLConnection)uri.toURL().openConnection(); + + connection.connect(); + + assertThat("response code is 200", connection.getResponseCode(), is(200)); + + System.err.println("Stop build!"); + build.stop(); + } +} diff --git a/jetty-ee10/jetty-ee10-ant/src/test/resources/connector-test.xml b/jetty-ee10/jetty-ee10-ant/src/test/resources/connector-test.xml new file mode 100644 index 00000000000..78a0b8b2949 --- /dev/null +++ b/jetty-ee10/jetty-ee10-ant/src/test/resources/connector-test.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-ant/src/test/resources/foo/WEB-INF/acme-taglib.tld b/jetty-ee10/jetty-ee10-ant/src/test/resources/foo/WEB-INF/acme-taglib.tld new file mode 100644 index 00000000000..09b70aece57 --- /dev/null +++ b/jetty-ee10/jetty-ee10-ant/src/test/resources/foo/WEB-INF/acme-taglib.tld @@ -0,0 +1,28 @@ + + + + + + 1.0 + 1.2 + acme + http://www.acme.com/taglib + taglib example + + com.acme.TagListener + + + + date + com.acme.DateTag + TAGDEPENDENT + Display Date + + tz + false + + + + diff --git a/jetty-ee10/jetty-ee10-ant/src/test/resources/foo/WEB-INF/acme-taglib2.tld b/jetty-ee10/jetty-ee10-ant/src/test/resources/foo/WEB-INF/acme-taglib2.tld new file mode 100644 index 00000000000..3edb7bb14f3 --- /dev/null +++ b/jetty-ee10/jetty-ee10-ant/src/test/resources/foo/WEB-INF/acme-taglib2.tld @@ -0,0 +1,35 @@ + + + + Acme JSP2 tags + 1.0 + acme2 + http://www.acme.com/taglib2 + + Simple Date formatting + date2 + com.acme.Date2Tag + scriptless + + Day of the Month + day + + + Month of the Year + month + + + Year + year + + + format + true + true + + + + diff --git a/jetty-ee10/jetty-ee10-ant/src/test/resources/foo/WEB-INF/tags/panel.tag b/jetty-ee10/jetty-ee10-ant/src/test/resources/foo/WEB-INF/tags/panel.tag new file mode 100644 index 00000000000..fa0540a61db --- /dev/null +++ b/jetty-ee10/jetty-ee10-ant/src/test/resources/foo/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-ee10/jetty-ee10-ant/src/test/resources/foo/WEB-INF/web.xml b/jetty-ee10/jetty-ee10-ant/src/test/resources/foo/WEB-INF/web.xml new file mode 100644 index 00000000000..b3b1176cac8 --- /dev/null +++ b/jetty-ee10/jetty-ee10-ant/src/test/resources/foo/WEB-INF/web.xml @@ -0,0 +1,28 @@ + + + + Test WebApp + + + org.eclipse.jetty.server.context.ManagedAttributes + QoSFilter,TransparentProxy.ThreadPool,TransparentProxy.HttpClient + + + + + foo.jsp + /jsp/foo/foo.jsp + + + foo.jsp + /jsp/foo/ + + + + + + diff --git a/jetty-ee10/jetty-ee10-ant/src/test/resources/foo/index.html b/jetty-ee10/jetty-ee10-ant/src/test/resources/foo/index.html new file mode 100644 index 00000000000..bab0d7c3a91 --- /dev/null +++ b/jetty-ee10/jetty-ee10-ant/src/test/resources/foo/index.html @@ -0,0 +1,5 @@ + + +

    INDEX!

    + + diff --git a/jetty-ee10/jetty-ee10-ant/src/test/resources/foo/jsp/bean1.jsp b/jetty-ee10/jetty-ee10-ant/src/test/resources/foo/jsp/bean1.jsp new file mode 100644 index 00000000000..0c15da2ca4e --- /dev/null +++ b/jetty-ee10/jetty-ee10-ant/src/test/resources/foo/jsp/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-ee10/jetty-ee10-ant/src/test/resources/foo/jsp/bean2.jsp b/jetty-ee10/jetty-ee10-ant/src/test/resources/foo/jsp/bean2.jsp new file mode 100644 index 00000000000..624dc2e59d4 --- /dev/null +++ b/jetty-ee10/jetty-ee10-ant/src/test/resources/foo/jsp/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-ee10/jetty-ee10-ant/src/test/resources/foo/jsp/dump.jsp b/jetty-ee10/jetty-ee10-ant/src/test/resources/foo/jsp/dump.jsp new file mode 100644 index 00000000000..fb73b0b0002 --- /dev/null +++ b/jetty-ee10/jetty-ee10-ant/src/test/resources/foo/jsp/dump.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-ee10/jetty-ee10-ant/src/test/resources/foo/jsp/expr.jsp b/jetty-ee10/jetty-ee10-ant/src/test/resources/foo/jsp/expr.jsp new file mode 100644 index 00000000000..e0b25e20203 --- /dev/null +++ b/jetty-ee10/jetty-ee10-ant/src/test/resources/foo/jsp/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-ee10/jetty-ee10-ant/src/test/resources/foo/jsp/foo/foo.jsp b/jetty-ee10/jetty-ee10-ant/src/test/resources/foo/jsp/foo/foo.jsp new file mode 100644 index 00000000000..7ec8955932d --- /dev/null +++ b/jetty-ee10/jetty-ee10-ant/src/test/resources/foo/jsp/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-ee10/jetty-ee10-ant/src/test/resources/foo/jsp/index.html b/jetty-ee10/jetty-ee10-ant/src/test/resources/foo/jsp/index.html new file mode 100644 index 00000000000..644ebc7ff40 --- /dev/null +++ b/jetty-ee10/jetty-ee10-ant/src/test/resources/foo/jsp/index.html @@ -0,0 +1,20 @@ + + + +

    JSP Examples

    + + +Main Menu + + + + diff --git a/jetty-ee10/jetty-ee10-ant/src/test/resources/foo/jsp/jstl.jsp b/jetty-ee10/jetty-ee10-ant/src/test/resources/foo/jsp/jstl.jsp new file mode 100644 index 00000000000..9fa7b57e96c --- /dev/null +++ b/jetty-ee10/jetty-ee10-ant/src/test/resources/foo/jsp/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-ee10/jetty-ee10-ant/src/test/resources/foo/jsp/tag.jsp b/jetty-ee10/jetty-ee10-ant/src/test/resources/foo/jsp/tag.jsp new file mode 100644 index 00000000000..069d8c67b17 --- /dev/null +++ b/jetty-ee10/jetty-ee10-ant/src/test/resources/foo/jsp/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-ee10/jetty-ee10-ant/src/test/resources/foo/jsp/tag2.jsp b/jetty-ee10/jetty-ee10-ant/src/test/resources/foo/jsp/tag2.jsp new file mode 100644 index 00000000000..8071927562a --- /dev/null +++ b/jetty-ee10/jetty-ee10-ant/src/test/resources/foo/jsp/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-ee10/jetty-ee10-ant/src/test/resources/foo/jsp/tagfile.jsp b/jetty-ee10/jetty-ee10-ant/src/test/resources/foo/jsp/tagfile.jsp new file mode 100644 index 00000000000..67299f0229c --- /dev/null +++ b/jetty-ee10/jetty-ee10-ant/src/test/resources/foo/jsp/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-ee10/jetty-ee10-ant/src/test/resources/webapp-test.xml b/jetty-ee10/jetty-ee10-ant/src/test/resources/webapp-test.xml new file mode 100644 index 00000000000..fe7023df669 --- /dev/null +++ b/jetty-ee10/jetty-ee10-ant/src/test/resources/webapp-test.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-apache-jsp/pom.xml b/jetty-ee10/jetty-ee10-apache-jsp/pom.xml new file mode 100644 index 00000000000..f74c5c1606f --- /dev/null +++ b/jetty-ee10/jetty-ee10-apache-jsp/pom.xml @@ -0,0 +1,105 @@ + + + org.eclipse.jetty.ee10 + jetty-ee10 + 12.0.0-SNAPSHOT + + + 4.0.0 + jetty-ee10-apache-jsp + EE10 :: Jetty :: Apache JSP Implementation + + + ${project.groupId}.apache-jsp + + + + + + org.apache.felix + maven-bundle-plugin + true + + + Jetty-specific ServletContainerInitializer for Jasper + + org.eclipse.jetty.ee10.apache.jsp.*;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}", org.eclipse.jetty.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 + + + + + + + org.jacoco + jacoco-maven-plugin + + true + + + + + + + + org.slf4j + slf4j-api + + + org.eclipse.jetty + jetty-util + + + org.mortbay.jasper + apache-jsp + + + org.eclipse.jetty.ee10 + jetty-ee10-servlet + test + + + org.eclipse.jetty.toolchain + jetty-jakarta-servlet-api + + + org.eclipse.jetty + jetty-http-tools + test + + + org.eclipse.jetty + jetty-slf4j-impl + test + + + org.eclipse.jetty.toolchain + jetty-test-helper + test + + + + + diff --git a/jetty-ee10/jetty-ee10-apache-jsp/src/main/config/modules/apache-jsp.mod b/jetty-ee10/jetty-ee10-apache-jsp/src/main/config/modules/apache-jsp.mod new file mode 100644 index 00000000000..00e801cd81f --- /dev/null +++ b/jetty-ee10/jetty-ee10-apache-jsp/src/main/config/modules/apache-jsp.mod @@ -0,0 +1,8 @@ +# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html + +[description] +Enables use of the apache implementation of JSP. + +[lib] +lib/apache-jsp/*.jar + diff --git a/jetty-ee10/jetty-ee10-apache-jsp/src/main/java/module-info.java b/jetty-ee10/jetty-ee10-apache-jsp/src/main/java/module-info.java new file mode 100644 index 00000000000..b7c51ed21b8 --- /dev/null +++ b/jetty-ee10/jetty-ee10-apache-jsp/src/main/java/module-info.java @@ -0,0 +1,30 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 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.ee10.apache.jsp +{ + requires java.xml; + requires jetty.servlet.api; + requires org.eclipse.jetty.util; + requires org.mortbay.apache.jasper; + requires org.slf4j; + + exports org.eclipse.jetty.ee10.apache.jsp; + exports org.eclipse.jetty.ee10.jsp; + + provides org.apache.juli.logging.Log with + org.eclipse.jetty.ee10.apache.jsp.JuliLog; + + provides jakarta.servlet.ServletContainerInitializer with + org.eclipse.jetty.ee10.apache.jsp.JettyJasperInitializer; +} diff --git a/jetty-ee10/jetty-ee10-apache-jsp/src/main/java/org/eclipse/jetty/ee10/apache/jsp/JettyJasperInitializer.java b/jetty-ee10/jetty-ee10-apache-jsp/src/main/java/org/eclipse/jetty/ee10/apache/jsp/JettyJasperInitializer.java new file mode 100644 index 00000000000..c31367b2f33 --- /dev/null +++ b/jetty-ee10/jetty-ee10-apache-jsp/src/main/java/org/eclipse/jetty/ee10/apache/jsp/JettyJasperInitializer.java @@ -0,0 +1,98 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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.equals("") && 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-ee10/jetty-ee10-apache-jsp/src/main/java/org/eclipse/jetty/ee10/apache/jsp/JettyTldPreScanned.java b/jetty-ee10/jetty-ee10-apache-jsp/src/main/java/org/eclipse/jetty/ee10/apache/jsp/JettyTldPreScanned.java new file mode 100644 index 00000000000..4c00556916f --- /dev/null +++ b/jetty-ee10/jetty-ee10-apache-jsp/src/main/java/org/eclipse/jetty/ee10/apache/jsp/JettyTldPreScanned.java @@ -0,0 +1,87 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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-ee10/jetty-ee10-apache-jsp/src/main/java/org/eclipse/jetty/ee10/apache/jsp/JuliLog.java b/jetty-ee10/jetty-ee10-apache-jsp/src/main/java/org/eclipse/jetty/ee10/apache/jsp/JuliLog.java new file mode 100644 index 00000000000..a2d0ede5aab --- /dev/null +++ b/jetty-ee10/jetty-ee10-apache-jsp/src/main/java/org/eclipse/jetty/ee10/apache/jsp/JuliLog.java @@ -0,0 +1,182 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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-ee10/jetty-ee10-apache-jsp/src/main/java/org/eclipse/jetty/ee10/jsp/JettyJspServlet.java b/jetty-ee10/jetty-ee10-apache-jsp/src/main/java/org/eclipse/jetty/ee10/jsp/JettyJspServlet.java new file mode 100644 index 00000000000..4c57b740997 --- /dev/null +++ b/jetty-ee10/jetty-ee10-apache-jsp/src/main/java/org/eclipse/jetty/ee10/jsp/JettyJspServlet.java @@ -0,0 +1,119 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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-ee10/jetty-ee10-apache-jsp/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer b/jetty-ee10/jetty-ee10-apache-jsp/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer new file mode 100644 index 00000000000..c629fccbace --- /dev/null +++ b/jetty-ee10/jetty-ee10-apache-jsp/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer @@ -0,0 +1 @@ +org.eclipse.jetty.ee10.apache.jsp.JettyJasperInitializer diff --git a/jetty-ee10/jetty-ee10-apache-jsp/src/main/resources/META-INF/services/org.apache.juli.logging.Log b/jetty-ee10/jetty-ee10-apache-jsp/src/main/resources/META-INF/services/org.apache.juli.logging.Log new file mode 100644 index 00000000000..f82252d4373 --- /dev/null +++ b/jetty-ee10/jetty-ee10-apache-jsp/src/main/resources/META-INF/services/org.apache.juli.logging.Log @@ -0,0 +1 @@ +org.eclipse.jetty.ee10.apache.jsp.JuliLog diff --git a/jetty-ee10/jetty-ee10-apache-jsp/src/test/java/org/eclipse/jetty/ee10/jsp/TestJettyJspServlet.java b/jetty-ee10/jetty-ee10-apache-jsp/src/test/java/org/eclipse/jetty/ee10/jsp/TestJettyJspServlet.java new file mode 100644 index 00000000000..56a8d33cb66 --- /dev/null +++ b/jetty-ee10/jetty-ee10-apache-jsp/src/test/java/org/eclipse/jetty/ee10/jsp/TestJettyJspServlet.java @@ -0,0 +1,122 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.jsp; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.net.URLClassLoader; + +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.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.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()); + File baseDir = MavenTestingUtils.getTestResourceDir("base"); + _server = new Server(); + _connector = new LocalConnector(_server); + _server.addConnector(_connector); + ServletContextHandler context = new ServletContextHandler(_server, "/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.getPath().toString()); + context.setResourceBase(baseDir.toPath()); + 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 + { + //TODO this test is producing an IllegalStateException in ServletHolder.init + //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-ee10/jetty-ee10-apache-jsp/src/test/java/org/eclipse/jetty/ee10/jsp/TestJettyTldPreScanned.java b/jetty-ee10/jetty-ee10-apache-jsp/src/test/java/org/eclipse/jetty/ee10/jsp/TestJettyTldPreScanned.java new file mode 100644 index 00000000000..19eeba613f3 --- /dev/null +++ b/jetty-ee10/jetty-ee10-apache-jsp/src/test/java/org/eclipse/jetty/ee10/jsp/TestJettyTldPreScanned.java @@ -0,0 +1,67 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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.ee10.apache.jsp.JettyTldPreScanned; +import org.eclipse.jetty.ee10.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-ee10/jetty-ee10-apache-jsp/src/test/java/org/eclipse/jetty/ee10/jsp/TestJspFileNameToClass.java b/jetty-ee10/jetty-ee10-apache-jsp/src/test/java/org/eclipse/jetty/ee10/jsp/TestJspFileNameToClass.java new file mode 100644 index 00000000000..ca17006f8c9 --- /dev/null +++ b/jetty-ee10/jetty-ee10-apache-jsp/src/test/java/org/eclipse/jetty/ee10/jsp/TestJspFileNameToClass.java @@ -0,0 +1,52 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.jsp; + +import org.eclipse.jetty.ee10.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-ee10/jetty-ee10-apache-jsp/src/test/resources/META-INF/foo-taglib.tld b/jetty-ee10/jetty-ee10-apache-jsp/src/test/resources/META-INF/foo-taglib.tld new file mode 100644 index 00000000000..f13333980ce --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-apache-jsp/src/test/resources/base/dir/empty.txt b/jetty-ee10/jetty-ee10-apache-jsp/src/test/resources/base/dir/empty.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/jetty-ee10/jetty-ee10-apache-jsp/src/test/resources/base/foo.jsp b/jetty-ee10/jetty-ee10-apache-jsp/src/test/resources/base/foo.jsp new file mode 100644 index 00000000000..fb73b0b0002 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-apache-jsp/src/test/resources/taglib.jar b/jetty-ee10/jetty-ee10-apache-jsp/src/test/resources/taglib.jar new file mode 100644 index 0000000000000000000000000000000000000000..af1aa0c11867ce1b1eb366d8a02c4f44b04bd5d6 GIT binary patch literal 3322 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 + 4.0.0 + + + org.eclipse.jetty.ee10 + jetty-ee10 + 12.0.0-SNAPSHOT + + + jetty-ee10-bom + EE10 :: Jetty :: BOM + Jetty EE10 APIs BOM artifact + pom + + + + + org.codehaus.mojo + flatten-maven-plugin + ${flatten.maven.plugin.version} + + ${project.build.directory} + flattened-pom.xml + bom + true + + remove + remove + + + + + flatten + + flatten + + process-resources + + + flatten-clean + + clean + + clean + + + + + + + + + + org.eclipse.jetty + jetty-bom + 12.0.0-SNAPSHOT + pom + import + + + + org.eclipse.jetty.ee10 + jetty-ee10-annotations + ${project.version} + + + org.eclipse.jetty.ee10 + jetty-ee10-apache-jsp + ${project.version} + + + org.eclipse.jetty.ee10 + jetty-ee10-cdi + ${project.version} + + + org.eclipse.jetty.ee10 + jetty-ee10-glassfish-jstl + ${project.version} + + + org.eclipse.jetty.ee10 + jetty-ee10-jaas + ${project.version} + + + org.eclipse.jetty.ee10 + jetty-ee10-jaspi + ${project.version} + + + org.eclipse.jetty.ee10 + jetty-ee10-jspc-maven-plugin + ${project.version} + + + + org.eclipse.jetty.ee10 + jetty-ee10-openid + ${project.version} + + + org.eclipse.jetty.ee10 + jetty-ee10-plus + ${project.version} + + + org.eclipse.jetty.ee10 + jetty-ee10-proxy + ${project.version} + + + org.eclipse.jetty.ee10 + jetty-ee10-quickstart + ${project.version} + + + org.eclipse.jetty.ee10 + jetty-ee10-servlet + ${project.version} + + + org.eclipse.jetty.ee10 + jetty-ee10-servlets + ${project.version} + + + org.eclipse.jetty.ee10 + jetty-ee10-webapp + ${project.version} + + + org.eclipse.jetty.ee10 + jetty-ee10-websocket-jakarta-client + ${project.version} + + + org.eclipse.jetty.ee10 + jetty-ee10-websocket-jakarta-common + ${project.version} + + + org.eclipse.jetty.ee10 + jetty-ee10-websocket-jakarta-server + ${project.version} + + + org.eclipse.jetty.ee10 + jetty-ee10-websocket-jetty-api + ${project.version} + + + org.eclipse.jetty.ee10 + jetty-ee10-websocket-jetty-client + ${project.version} + + + org.eclipse.jetty.ee10 + jetty-ee10-websocket-jetty-common + ${project.version} + + + org.eclipse.jetty.ee10 + jetty-ee10-websocket-jetty-server + ${project.version} + + + org.eclipse.jetty.ee10 + jetty-ee10-websocket-servlet + ${project.version} + + + + org.eclipse.jetty.toolchain + jetty-jakarta-servlet-api + ${jetty.servlet.api.version} + + + org.eclipse.jetty.orbit + javax.servlet.jsp.jstl + ${jakarta.servlet.jsp.jstl.impl.version} + + + org.eclipse.jetty.orbit + javax.servlet + + + org.eclipse.jetty.orbit + javax.servlet.jsp + + + + + org.eclipse.jetty.toolchain + jetty-jakarta-websocket-api + ${jakarta.websocket.api.version} + + + + diff --git a/jetty-ee10/jetty-ee10-cdi/pom.xml b/jetty-ee10/jetty-ee10-cdi/pom.xml new file mode 100644 index 00000000000..875885d0ffc --- /dev/null +++ b/jetty-ee10/jetty-ee10-cdi/pom.xml @@ -0,0 +1,87 @@ + + + org.eclipse.jetty.ee10 + jetty-ee10 + 12.0.0-SNAPSHOT + + 4.0.0 + jetty-ee10-cdi + EE10 :: Jetty :: CDI + jar + + ${project.groupId}.cdi + + + + org.eclipse.jetty + jetty-util + compile + + + org.eclipse.jetty.ee10 + jetty-ee10-webapp + compile + + + org.eclipse.jetty.ee10 + jetty-ee10-annotations + 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.ee10.webapp.Configuration, + osgi.serviceloader; osgi.serviceloader=jakarta.servlet.ServletContainerInitializer + + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven.surefire.plugin.version} + + + + + + + + + + jdk16 + + [16,) + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + --add-opens java.base/java.lang=ALL-UNNAMED + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-cdi/src/main/config/etc/cdi/jetty-cdi.xml b/jetty-ee10/jetty-ee10-cdi/src/main/config/etc/cdi/jetty-cdi.xml new file mode 100644 index 00000000000..05911314079 --- /dev/null +++ b/jetty-ee10/jetty-ee10-cdi/src/main/config/etc/cdi/jetty-cdi.xml @@ -0,0 +1,8 @@ + + + + + org.eclipse.jetty.ee10.cdi + + + diff --git a/jetty-ee10/jetty-ee10-cdi/src/main/config/modules/cdi-decorate.mod b/jetty-ee10/jetty-ee10-cdi/src/main/config/modules/cdi-decorate.mod new file mode 100644 index 00000000000..f222fa0fbaf --- /dev/null +++ b/jetty-ee10/jetty-ee10-cdi/src/main/config/modules/cdi-decorate.mod @@ -0,0 +1,17 @@ +# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html + +[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. + +[tag] +cdi + +[provides] +cdi-mode + +[depend] +cdi + +[ini] +jetty.cdi.mode=CdiDecoratingListener diff --git a/jetty-ee10/jetty-ee10-cdi/src/main/config/modules/cdi-spi.mod b/jetty-ee10/jetty-ee10-cdi/src/main/config/modules/cdi-spi.mod new file mode 100644 index 00000000000..c608e93e653 --- /dev/null +++ b/jetty-ee10/jetty-ee10-cdi/src/main/config/modules/cdi-spi.mod @@ -0,0 +1,17 @@ +# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html + +[description] +Configures Jetty to use the "CdiSpiDecorator" as the default CDI mode. +This mode uses the CDI SPI to integrate an arbitrary CDI implementation. + +[tag] +cdi + +[provides] +cdi-mode + +[depend] +cdi + +[ini] +jetty.cdi.mode=CdiSpiDecorator diff --git a/jetty-ee10/jetty-ee10-cdi/src/main/config/modules/cdi.mod b/jetty-ee10/jetty-ee10-cdi/src/main/config/modules/cdi.mod new file mode 100644 index 00000000000..3b0aa310bc9 --- /dev/null +++ b/jetty-ee10/jetty-ee10-cdi/src/main/config/modules/cdi.mod @@ -0,0 +1,29 @@ +# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html + +[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.ee10.cdi" +init parameter or defaults to the mode set by the "org.eclipse.jetty.ee10.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.ee10.cdi.decorator". + +[tag] +cdi + +[provides] +cdi + +[depend] +deploy + +[xml] +etc/cdi/jetty-cdi.xml + +[lib] +lib/jetty-cdi-${jetty.version}.jar diff --git a/jetty-ee10/jetty-ee10-cdi/src/main/java/module-info.java b/jetty-ee10/jetty-ee10-cdi/src/main/java/module-info.java new file mode 100644 index 00000000000..59937fb2ddd --- /dev/null +++ b/jetty-ee10/jetty-ee10-cdi/src/main/java/module-info.java @@ -0,0 +1,22 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 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.ee10.cdi +{ + requires org.eclipse.jetty.ee10.annotations; + + requires transitive org.eclipse.jetty.ee10.servlet; + requires transitive org.eclipse.jetty.ee10.webapp; + + exports org.eclipse.jetty.ee10.cdi; +} diff --git a/jetty-ee10/jetty-ee10-cdi/src/main/java/org/eclipse/jetty/ee10/cdi/CdiConfiguration.java b/jetty-ee10/jetty-ee10-cdi/src/main/java/org/eclipse/jetty/ee10/cdi/CdiConfiguration.java new file mode 100644 index 00000000000..d6b86915f75 --- /dev/null +++ b/jetty-ee10/jetty-ee10-cdi/src/main/java/org/eclipse/jetty/ee10/cdi/CdiConfiguration.java @@ -0,0 +1,34 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.cdi; + +import org.eclipse.jetty.ee10.annotations.AnnotationConfiguration; +import org.eclipse.jetty.ee10.plus.webapp.PlusConfiguration; +import org.eclipse.jetty.ee10.webapp.AbstractConfiguration; + +/** + *

    CDI Configuration

    + *

    This configuration configures the WebAppContext server/system classes to + * be able to see the {@link CdiServletContainerInitializer}. + *

    + */ +public class CdiConfiguration extends AbstractConfiguration +{ + public CdiConfiguration() + { + protectAndExpose("org.eclipse.jetty.ee10.cdi.CdiServletContainerInitializer"); + addDependents(AnnotationConfiguration.class, PlusConfiguration.class); + } +} + diff --git a/jetty-ee10/jetty-ee10-cdi/src/main/java/org/eclipse/jetty/ee10/cdi/CdiDecoratingListener.java b/jetty-ee10/jetty-ee10-cdi/src/main/java/org/eclipse/jetty/ee10/cdi/CdiDecoratingListener.java new file mode 100644 index 00000000000..1940e970dd4 --- /dev/null +++ b/jetty-ee10/jetty-ee10-cdi/src/main/java/org/eclipse/jetty/ee10/cdi/CdiDecoratingListener.java @@ -0,0 +1,32 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.cdi; + +import org.eclipse.jetty.ee10.servlet.DecoratingListener; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; + +/** + * A DecoratingListener that listens for "org.eclipse.jetty.ee10.cdi.decorator" + */ +public class CdiDecoratingListener extends DecoratingListener +{ + public static final String MODE = "CdiDecoratingListener"; + public static final String ATTRIBUTE = "org.eclipse.jetty.ee10.cdi.decorator"; + + public CdiDecoratingListener(ServletContextHandler contextHandler) + { + super(contextHandler, ATTRIBUTE); + contextHandler.setAttribute(CdiServletContainerInitializer.CDI_INTEGRATION_ATTRIBUTE, MODE); + } +} diff --git a/jetty-ee10/jetty-ee10-cdi/src/main/java/org/eclipse/jetty/ee10/cdi/CdiServletContainerInitializer.java b/jetty-ee10/jetty-ee10-cdi/src/main/java/org/eclipse/jetty/ee10/cdi/CdiServletContainerInitializer.java new file mode 100644 index 00000000000..c8007430ed2 --- /dev/null +++ b/jetty-ee10/jetty-ee10-cdi/src/main/java/org/eclipse/jetty/ee10/cdi/CdiServletContainerInitializer.java @@ -0,0 +1,92 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.cdi; + +import java.util.Objects; +import java.util.Set; + +import jakarta.servlet.ServletContainerInitializer; +import jakarta.servlet.ServletContext; +import org.eclipse.jetty.ee10.annotations.AnnotationConfiguration; +import org.eclipse.jetty.ee10.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.ee10.cdi" init parameter or default to the mode set by the + * "org.eclipse.jetty.ee10.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.ee10.cdi.decorator".
    + *
    + * + * @see AnnotationConfiguration.ServletContainerInitializerOrdering + */ +public class CdiServletContainerInitializer implements ServletContainerInitializer +{ + public static final String CDI_INTEGRATION_ATTRIBUTE = "org.eclipse.jetty.ee10.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-ee10/jetty-ee10-cdi/src/main/java/org/eclipse/jetty/ee10/cdi/CdiSpiDecorator.java b/jetty-ee10/jetty-ee10-cdi/src/main/java/org/eclipse/jetty/ee10/cdi/CdiSpiDecorator.java new file mode 100644 index 00000000000..d0648da6e55 --- /dev/null +++ b/jetty-ee10/jetty-ee10-cdi/src/main/java/org/eclipse/jetty/ee10/cdi/CdiSpiDecorator.java @@ -0,0 +1,218 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.cdi; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.eclipse.jetty.ee10.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 {@link MethodHandle}s 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); + public static final String MODE = "CdiSpiDecorator"; + + private final ServletContextHandler _context; + private final Map _decorated = new HashMap<>(); + + private final MethodHandle _current; + private final MethodHandle _getBeanManager; + private final MethodHandle _createAnnotatedType; + private final MethodHandle _createInjectionTarget; + private final MethodHandle _createCreationalContext; + private final MethodHandle _inject; + private final MethodHandle _dispose; + private final MethodHandle _release; + private final Set _undecorated = new HashSet<>(Collections.singletonList("org.jboss.weld.environment.servlet.Listener")); + + 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 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 creationalContextClass = classLoader.loadClass("jakarta.enterprise.context.spi.CreationalContext"); + Class contextualClass = classLoader.loadClass("jakarta.enterprise.context.spi.Contextual"); + + MethodHandles.Lookup lookup = MethodHandles.lookup(); + _current = lookup.findStatic(cdiClass, "current", MethodType.methodType(cdiClass)); + _getBeanManager = lookup.findVirtual(cdiClass, "getBeanManager", MethodType.methodType(beanManagerClass)); + _createAnnotatedType = lookup.findVirtual(beanManagerClass, "createAnnotatedType", MethodType.methodType(annotatedTypeClass, Class.class)); + _createInjectionTarget = lookup.findVirtual(beanManagerClass, "createInjectionTarget", MethodType.methodType(injectionTargetClass, annotatedTypeClass)); + _createCreationalContext = lookup.findVirtual(beanManagerClass, "createCreationalContext", MethodType.methodType(creationalContextClass, contextualClass)); + _inject = lookup.findVirtual(injectionTargetClass, "inject", MethodType.methodType(Void.TYPE, Object.class, creationalContextClass)); + _dispose = lookup.findVirtual(injectionTargetClass, "dispose", MethodType.methodType(Void.TYPE, Object.class)); + _release = lookup.findVirtual(creationalContextClass, "release", MethodType.methodType(Void.TYPE)); + } + 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()); + // AnnotatedType annotatedType = manager.createAnnotatedType((Class)o.getClass()); + Object annotatedType = _createAnnotatedType.invoke(manager, o.getClass()); + // CreationalContext creationalContext = manager.createCreationalContext(null); + _creationalContext = _createCreationalContext.invoke(manager, null); + // InjectionTarget injectionTarget = manager.createInjectionTarget(); + _injectionTarget = _createInjectionTarget.invoke(manager, annotatedType); + // injectionTarget.inject(o, creationalContext); + _inject.invoke(_injectionTarget, o, _creationalContext); + } + + public void destroy(Object o) throws Throwable + { + _dispose.invoke(_injectionTarget, o); + _release.invoke(_creationalContext); + } + } +} diff --git a/jetty-ee10/jetty-ee10-cdi/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer b/jetty-ee10/jetty-ee10-cdi/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer new file mode 100644 index 00000000000..b53b8aa711a --- /dev/null +++ b/jetty-ee10/jetty-ee10-cdi/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer @@ -0,0 +1 @@ +org.eclipse.jetty.ee10.cdi.CdiServletContainerInitializer diff --git a/jetty-ee10/jetty-ee10-cdi/src/main/resources/META-INF/services/org.eclipse.jetty.ee10.webapp.Configuration b/jetty-ee10/jetty-ee10-cdi/src/main/resources/META-INF/services/org.eclipse.jetty.ee10.webapp.Configuration new file mode 100644 index 00000000000..4162381dd87 --- /dev/null +++ b/jetty-ee10/jetty-ee10-cdi/src/main/resources/META-INF/services/org.eclipse.jetty.ee10.webapp.Configuration @@ -0,0 +1,2 @@ +org.eclipse.jetty.ee10.cdi.CdiConfiguration + diff --git a/jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-jar/pom.xml b/jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-jar/pom.xml new file mode 100644 index 00000000000..1a8cc8615f8 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-jar/pom.xml @@ -0,0 +1,32 @@ + + + org.eclipse.jetty.ee10.demos + demo-async-rest + 12.0.0-SNAPSHOT + + + 4.0.0 + demo-async-rest-jar + jar + EE10 :: Jetty Demo :: Async Rest :: Jar + + + ${project.parent.groupId}.async.rest + + + + + org.eclipse.jetty + jetty-client + + + org.eclipse.jetty + jetty-util-ajax + + + org.eclipse.jetty.toolchain + jetty-jakarta-servlet-api + provided + + + diff --git a/jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-jar/src/main/java/org/eclipse/jetty/ee10/demos/AbstractRestServlet.java b/jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-jar/src/main/java/org/eclipse/jetty/ee10/demos/AbstractRestServlet.java new file mode 100644 index 00000000000..aa64f492f9c --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-jar/src/main/java/org/eclipse/jetty/ee10/demos/AbstractRestServlet.java @@ -0,0 +1,137 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-jar/src/main/java/org/eclipse/jetty/ee10/demos/AsyncRestServlet.java b/jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-jar/src/main/java/org/eclipse/jetty/ee10/demos/AsyncRestServlet.java new file mode 100644 index 00000000000..eabd6e198ad --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-jar/src/main/java/org/eclipse/jetty/ee10/demos/AsyncRestServlet.java @@ -0,0 +1,207 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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.api.Response; +import org.eclipse.jetty.client.api.Result; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.util.BufferUtil; +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 = System.nanoTime(); + + // 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, System.nanoTime() - 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 = System.nanoTime(); + long total = now - start0; + long generate = now - start; + 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 extends Response.Listener.Adapter + { + 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.toString()); + 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-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-jar/src/main/java/org/eclipse/jetty/ee10/demos/SerialRestServlet.java b/jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-jar/src/main/java/org/eclipse/jetty/ee10/demos/SerialRestServlet.java new file mode 100644 index 00000000000..4c87f8b88ae --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-jar/src/main/java/org/eclipse/jetty/ee10/demos/SerialRestServlet.java @@ -0,0 +1,96 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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.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 = System.nanoTime(); + + 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 now = System.nanoTime(); + long total = now - 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-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-jar/src/main/resources/META-INF/resources/asyncrest.html b/jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-jar/src/main/resources/META-INF/resources/asyncrest.html new file mode 100644 index 00000000000..f92f7f661d4 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-async-rest/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-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-jar/src/main/resources/META-INF/resources/asyncrest/green.png b/jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-jar/src/main/resources/META-INF/resources/asyncrest/green.png new file mode 100644 index 0000000000000000000000000000000000000000..d0fb8420c5dbe9403c699c66f0d173cf09f5dbac GIT binary patch literal 166 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1SGw4HSYi^#^NA%Cx&(BWL^R}oCO|{#S9GG z!XV7ZFl&wkP>{XE)7O>#CW{cGp=NP}b`?-avcxr_#5q4VH#M(>!MP|ku_QG`p**uB zL&4qCHz2%`PaLR7*we)^gyVX0%8&C6q6UTrJgS@wXPg-MB`*Gc1C(a)boFyt=akR{ E01^u){XE)7O>#CW{cGA&0%8wF*#3vcxr_#5q4VH#M(>!MP|ku_QG`p**uB zL&4qCHz2%`PaLR7(9^{+gyVYhpYsi}28KMEoD63j8TqfZ?P>r@GI+ZBxvX + + + + SerialRestServlet + SerialRestServlet + org.eclipse.jetty.demos.SerialRestServlet + + + SerialRestServlet + /testSerial + + + + AsyncRestServlet + AsyncRestServlet + org.eclipse.jetty.demos.AsyncRestServlet + true + + + AsyncRestServlet + /testAsync + + diff --git a/jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-server/pom.xml b/jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-server/pom.xml new file mode 100644 index 00000000000..004b1825f6c --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-server/pom.xml @@ -0,0 +1,23 @@ + + + org.eclipse.jetty.ee10.demos + demo-async-rest + 12.0.0-SNAPSHOT + + + 4.0.0 + demo-async-rest-server + jar + EE10 :: Jetty Demo :: Async Rest :: Server + + + ${project.parent.groupId}.async.rest.server + + + + + org.eclipse.jetty.ee10 + jetty-ee10-webapp + + + diff --git a/jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-server/src/main/java/org/eclipse/jetty/ee10/demos/AsyncRestServer.java b/jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-server/src/main/java/org/eclipse/jetty/ee10/demos/AsyncRestServer.java new file mode 100644 index 00000000000..6e68c1d5291 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-server/src/main/java/org/eclipse/jetty/ee10/demos/AsyncRestServer.java @@ -0,0 +1,47 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.eclipse.jetty.ee10.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-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-webapp/pom.xml b/jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-webapp/pom.xml new file mode 100644 index 00000000000..7d0453fcc71 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-webapp/pom.xml @@ -0,0 +1,33 @@ + + + org.eclipse.jetty.ee10.demos + demo-async-rest + 12.0.0-SNAPSHOT + + + 4.0.0 + demo-async-rest-webapp + war + EE10 :: Jetty Demo :: Async Rest :: WebApp + + + + org.slf4j + slf4j-api + + + org.eclipse.jetty + jetty-slf4j-impl + compile + + + org.eclipse.jetty.ee10.demos + demo-async-rest-jar + + + org.eclipse.jetty.toolchain + jetty-jakarta-servlet-api + provided + + + diff --git a/jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-webapp/src/main/config/modules/demo-async-rest.mod b/jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-webapp/src/main/config/modules/demo-async-rest.mod new file mode 100644 index 00000000000..7f98f8d366f --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-webapp/src/main/config/modules/demo-async-rest.mod @@ -0,0 +1,15 @@ +# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html + +[description] +Demo Async Rest webapp + +[tags] +demo +webapp + +[depends] +deploy + +[files] +maven://org.eclipse.jetty.demos/demo-async-rest-webapp/${jetty.version}/war|webapps/demo-async-rest.war + diff --git a/jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-webapp/src/main/webapp/META-INF/MANIFEST.MF b/jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-webapp/src/main/webapp/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..5e9495128c0 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-async-rest/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-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-webapp/src/main/webapp/WEB-INF/jetty-web.xml b/jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-webapp/src/main/webapp/WEB-INF/jetty-web.xml new file mode 100644 index 00000000000..0a572e46421 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-async-rest/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-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-webapp/src/main/webapp/WEB-INF/web.xml b/jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-webapp/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000000..c674332aeb0 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-webapp/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,9 @@ + + + + Async REST Webservice Example + + diff --git a/jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-webapp/src/main/webapp/demo.css b/jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-webapp/src/main/webapp/demo.css new file mode 100644 index 00000000000..f2b91d3365d --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-async-rest/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-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-webapp/src/main/webapp/index.html b/jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-webapp/src/main/webapp/index.html new file mode 100644 index 00000000000..262cf633e9d --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-webapp/src/main/webapp/index.html @@ -0,0 +1,62 @@ + + + + + + + + +
    +
    + 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-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-webapp/src/main/webapp/small_powered_by.gif b/jetty-ee10/jetty-ee10-demos/demo-async-rest/demo-async-rest-webapp/src/main/webapp/small_powered_by.gif new file mode 100644 index 0000000000000000000000000000000000000000..c5dd44319f0aa17ea93b15fbcc8a18e14ca397d5 GIT binary patch literal 4787 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 + + org.eclipse.jetty.ee10.demos + jetty-ee10-demos + 12.0.0-SNAPSHOT + + + 4.0.0 + demo-async-rest + pom + EE10 :: Jetty Demo :: Async Rest + + + demo-async-rest-jar + demo-async-rest-webapp + demo-async-rest-server + + + diff --git a/jetty-ee10/jetty-ee10-demos/demo-jaas-webapp/pom.xml b/jetty-ee10/jetty-ee10-demos/demo-jaas-webapp/pom.xml new file mode 100644 index 00000000000..7bcb475b1ee --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jaas-webapp/pom.xml @@ -0,0 +1,54 @@ + + + 4.0.0 + + org.eclipse.jetty.ee10.demos + jetty-ee10-demos + 12.0.0-SNAPSHOT + + demo-jaas-webapp + EE10 :: Jetty Demo :: JAAS :: WebApp + war + + ${project.groupId}.jaas + + + + + org.apache.maven.plugins + maven-deploy-plugin + + + false + + + + + + + org.eclipse.jetty + jetty-maven-plugin + ${project.version} + + 10 + + + ${basedir}/src/main/config/modules/demo.d + + ${basedir}/src/main/config/modules/demo.d/demo-login.conf + + + /test-jaas + + + Test JAAS Realm + xyz + + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-demos/demo-jaas-webapp/src/main/config/modules/demo-jaas.mod b/jetty-ee10/jetty-ee10-demos/demo-jaas-webapp/src/main/config/modules/demo-jaas.mod new file mode 100644 index 00000000000..8c97d60007c --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jaas-webapp/src/main/config/modules/demo-jaas.mod @@ -0,0 +1,26 @@ +# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html + +[description] +Demo Spec webapp + +[tags] +demo +webapp + +[depends] +deploy +jaas +jdbc +jsp +annotations +ext + +[files] +basehome:modules/demo.d/demo-jaas.xml|webapps/demo-jaas.xml +basehome:modules/demo.d/demo-login.conf|etc/demo-login.conf +basehome:modules/demo.d/demo-login.properties|etc/demo-login.properties +maven://org.eclipse.jetty.demos/demo-jaas-webapp/${jetty.version}/war|webapps/demo-jaas.war + +[ini] +# Enable security via jaas, and configure it +jetty.jaas.login.conf?=etc/demo-login.conf diff --git a/jetty-ee10/jetty-ee10-demos/demo-jaas-webapp/src/main/config/modules/demo.d/demo-jaas.xml b/jetty-ee10/jetty-ee10-demos/demo-jaas-webapp/src/main/config/modules/demo.d/demo-jaas.xml new file mode 100644 index 00000000000..5a38dbb2c5b --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jaas-webapp/src/main/config/modules/demo.d/demo-jaas.xml @@ -0,0 +1,26 @@ + + + + + + + + + /test-jaas + /demo-jaas.war + + true + + + + + + Demo JAAS Realm + xyz + + + + + + + diff --git a/jetty-ee10/jetty-ee10-demos/demo-jaas-webapp/src/main/config/modules/demo.d/demo-login.conf b/jetty-ee10/jetty-ee10-demos/demo-jaas-webapp/src/main/config/modules/demo.d/demo-login.conf new file mode 100644 index 00000000000..8b7891c1abe --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jaas-webapp/src/main/config/modules/demo.d/demo-login.conf @@ -0,0 +1,5 @@ +xyz { +org.eclipse.jetty.jaas.spi.PropertyFileLoginModule required +debug="true" +file="${jetty.base}/etc/demo-login.properties"; +}; diff --git a/jetty-ee10/jetty-ee10-demos/demo-jaas-webapp/src/main/config/modules/demo.d/demo-login.properties b/jetty-ee10/jetty-ee10-demos/demo-jaas-webapp/src/main/config/modules/demo.d/demo-login.properties new file mode 100644 index 00000000000..61e32037316 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jaas-webapp/src/main/config/modules/demo.d/demo-login.properties @@ -0,0 +1 @@ +me=me,me,roleA diff --git a/jetty-ee10/jetty-ee10-demos/demo-jaas-webapp/src/main/webapp/WEB-INF/jetty-web.xml b/jetty-ee10/jetty-ee10-demos/demo-jaas-webapp/src/main/webapp/WEB-INF/jetty-web.xml new file mode 100644 index 00000000000..f375ddfa644 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-jaas-webapp/src/main/webapp/WEB-INF/web.xml b/jetty-ee10/jetty-ee10-demos/demo-jaas-webapp/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000000..e3c7acf9cb0 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jaas-webapp/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,41 @@ + + + JAAS Test + + + index.html + + + + + + JAAS Role + /auth.html + + + roleA + + + + + + FORM + Test JAAS Realm + + + /login.html + + + /authfail.html + + + + + + roleA + + diff --git a/jetty-ee10/jetty-ee10-demos/demo-jaas-webapp/src/main/webapp/auth.html b/jetty-ee10/jetty-ee10-demos/demo-jaas-webapp/src/main/webapp/auth.html new file mode 100644 index 00000000000..2041c6dce03 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-jaas-webapp/src/main/webapp/authfail.html b/jetty-ee10/jetty-ee10-demos/demo-jaas-webapp/src/main/webapp/authfail.html new file mode 100644 index 00000000000..62d7e5efec8 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-jaas-webapp/src/main/webapp/demo.css b/jetty-ee10/jetty-ee10-demos/demo-jaas-webapp/src/main/webapp/demo.css new file mode 100644 index 00000000000..f2b91d3365d --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-jaas-webapp/src/main/webapp/index.html b/jetty-ee10/jetty-ee10-demos/demo-jaas-webapp/src/main/webapp/index.html new file mode 100644 index 00000000000..8d3c88c9c45 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jaas-webapp/src/main/webapp/index.html @@ -0,0 +1,50 @@ + + + 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. +

    + +

    Preparation

    +

    To use JAAS in a base jetty instance enable the jaas module: +

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

    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-ee10/jetty-ee10-demos/demo-jaas-webapp/src/main/webapp/login.html b/jetty-ee10/jetty-ee10-demos/demo-jaas-webapp/src/main/webapp/login.html new file mode 100644 index 00000000000..7486bcc4ca3 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-jaas-webapp/src/main/webapp/logout.jsp b/jetty-ee10/jetty-ee10-demos/demo-jaas-webapp/src/main/webapp/logout.jsp new file mode 100644 index 00000000000..7f780c55cc7 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-jaas-webapp/src/main/webapp/small_powered_by.gif b/jetty-ee10/jetty-ee10-demos/demo-jaas-webapp/src/main/webapp/small_powered_by.gif new file mode 100644 index 0000000000000000000000000000000000000000..c5dd44319f0aa17ea93b15fbcc8a18e14ca397d5 GIT binary patch literal 4787 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 + + + + + + + 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-ee10/jetty-ee10-demos/demo-jetty-webapp/pom.xml b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/pom.xml new file mode 100644 index 00000000000..2cded100a2c --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/pom.xml @@ -0,0 +1,200 @@ + + + + org.eclipse.jetty.ee10.demos + jetty-ee10-demos + 12.0.0-SNAPSHOT + + 4.0.0 + demo-jetty-webapp + EE10 :: Jetty Demo :: Jetty :: WebApp + war + + ${project.groupId}.webapp + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + test + test + + + + + **/WebAppTest.java + **/Test*.java + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + web-bundle-assembly + package + + single + + + + src/main/assembly/web-bundle.xml + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + + + 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* + + / + + .,WEB-INF/classes + + + + + + maven-war-plugin + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + + + + org.eclipse.jetty + jetty-maven-plugin + ${project.version} + + + org.eclipse.jetty + jetty-client + ${project.version} + + + org.eclipse.jetty + jetty-servlets + ${project.version} + + + + 8087 + foo + 1 + + 222 + + + /test + ${project.build.directory}/work + + + + Test Realm + src/test/resources/test-realm.properties + + + + + + + + + + org.eclipse.jetty.ee10 + jetty-ee10-servlets + provided + + + org.eclipse.jetty.toolchain + jetty-jakarta-servlet-api + provided + + + jakarta.servlet + jakarta.servlet-api + + + + + org.eclipse.jetty.ee10 + jetty-ee10-webapp + test + + + org.eclipse.jetty + jetty-jmx + test + + + org.eclipse.jetty.toolchain + jetty-test-helper + test + + + org.eclipse.jetty + jetty-server + provided + + + jakarta.servlet.jsp + jakarta.servlet.jsp-api + provided + + + jakarta.servlet.jsp.jstl + jakarta.servlet.jsp.jstl-api + provided + + + jakarta.annotation + jakarta.annotation-api + provided + + + org.eclipse.jetty.toolchain + jetty-jakarta-websocket-api + provided + + + org.eclipse.jetty.ee10 + jetty-ee10-websocket-jetty-api + provided + + + org.eclipse.jetty.ee10 + jetty-ee10-websocket-jetty-server + provided + + + org.eclipse.jetty.ee10 + jetty-ee10-websocket-jakarta-server + test + + + diff --git a/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/assembly/embedded-jetty-web-for-webbundle.xml b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/assembly/embedded-jetty-web-for-webbundle.xml new file mode 100644 index 00000000000..d961c6f07f9 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/assembly/embedded-jetty-web-for-webbundle.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + / + /webapps/test.war + + + + + true + false + /etc/webdefault.xml + /etc/override-web.xml + + + + + + + + + Test Realm + + /realm.properties + + + + + + true + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/assembly/web-bundle.xml b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/assembly/web-bundle.xml new file mode 100644 index 00000000000..92951efa91e --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/config/modules/demo-jetty.mod b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/config/modules/demo-jetty.mod new file mode 100644 index 00000000000..37d6c8b1fe3 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/config/modules/demo-jetty.mod @@ -0,0 +1,26 @@ +# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html + +[description] +Demo Jetty Webapp + +[tags] +demo +webapp + +[depends] +deploy +jdbc +jsp +jstl +annotations +ext +servlets +websocket-jakarta +websocket-jetty +demo-realm + +[files] +webapps/demo-jetty.d/ +basehome:modules/demo.d/demo-jetty.xml|webapps/demo-jetty.xml +basehome:modules/demo.d/demo-jetty-override-web.xml|webapps/demo-jetty.d/demo-jetty-override-web.xml +maven://org.eclipse.jetty.demos/demo-jetty-webapp/${jetty.version}/war|webapps/demo-jetty.war diff --git a/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/config/modules/demo-moved-context.mod b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/config/modules/demo-moved-context.mod new file mode 100644 index 00000000000..d697f8a28df --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/config/modules/demo-moved-context.mod @@ -0,0 +1,14 @@ +# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html + +[description] +Demonstrate a Moved Context Handler. + +[tags] +demo + +[depends] +deploy + +[files] +basehome:modules/demo.d/demo-moved-context.xml|webapps/demo-moved-context.xml + diff --git a/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/config/modules/demo-rewrite.mod b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/config/modules/demo-rewrite.mod new file mode 100644 index 00000000000..8fcf581d4db --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/config/modules/demo-rewrite.mod @@ -0,0 +1,17 @@ +# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html + +[description] +Demonstrate the rewrite module. + +[tags] +demo + +[depends] +rewrite + +[xml] +etc/demo-rewrite-rules.xml + +[files] +basehome:modules/demo.d/demo-rewrite-rules.xml|etc/demo-rewrite-rules.xml + diff --git a/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/config/modules/demo.d/demo-jetty-override-web.xml b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/config/modules/demo.d/demo-jetty-override-web.xml new file mode 100644 index 00000000000..7376ec2833b --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/config/modules/demo.d/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-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/config/modules/demo.d/demo-jetty.xml b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/config/modules/demo.d/demo-jetty.xml new file mode 100644 index 00000000000..a0775b54d22 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/config/modules/demo.d/demo-jetty.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + /test + /demo-jetty.war + + + + + + true + false + /etc/webdefault.xml + /demo-jetty.d/demo-jetty-override-web.xml + + + + + org.eclipse.jetty.websocket.jakarta + true + + + + + + 2048 + + + + diff --git a/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/config/modules/demo.d/demo-moved-context.xml b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/config/modules/demo.d/demo-moved-context.xml new file mode 100644 index 00000000000..c9595b71ce3 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/config/modules/demo.d/demo-moved-context.xml @@ -0,0 +1,12 @@ + + + + + + /oldContextPath + /test/dump/moved + false + false + false + -1 + diff --git a/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/config/modules/demo.d/demo-rewrite-rules.xml b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/config/modules/demo.d/demo-rewrite-rules.xml new file mode 100644 index 00000000000..cedd98cd877 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/config/modules/demo.d/demo-rewrite-rules.xml @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + + + + /favicon.ico + Cache-Control + Max-Age=3600,public + true + + + + + + + + + /test/rewrite/ + /test/rewrite/info.html + + + + + + + + + /test/some/old/context + /test/rewritten/newcontext + + + + + + + + + /test/rewrite/for/* + /test/rewritten/ + + + + + + + + + (.*?)/reverse/([^/]*)/(.*) + $1/reverse/$3/$2 + + + + + + + + + /* + visited + yes + + + + + + + + + /test/redirect/* + /test/redirected + + + + + + + + + /400Error + 400 + ResponsePatternRule Demo + + + + + diff --git a/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/AddListServletRequestListener.java b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/AddListServletRequestListener.java new file mode 100644 index 00000000000..05e3a3d1a18 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/AddListServletRequestListener.java @@ -0,0 +1,45 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/ChatServlet.java b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/ChatServlet.java new file mode 100644 index 00000000000..a0bc85965d8 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/ChatServlet.java @@ -0,0 +1,218 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/CookieDump.java b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/CookieDump.java new file mode 100644 index 00000000000..87df2127d00 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/CookieDump.java @@ -0,0 +1,130 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/DispatchServlet.java b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/DispatchServlet.java new file mode 100644 index 00000000000..16f60d25e2f --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/DispatchServlet.java @@ -0,0 +1,254 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/Dump.java b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/Dump.java new file mode 100644 index 00000000000..9f93119ac0d --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/Dump.java @@ -0,0 +1,1082 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/HelloWorld.java b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/HelloWorld.java new file mode 100644 index 00000000000..c9ed178ee45 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/HelloWorld.java @@ -0,0 +1,54 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/JakartaWebSocketChat.java b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/JakartaWebSocketChat.java new file mode 100644 index 00000000000..53742952ed3 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/JakartaWebSocketChat.java @@ -0,0 +1,83 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/LoginServlet.java b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/LoginServlet.java new file mode 100644 index 00000000000..c02c23db312 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/LoginServlet.java @@ -0,0 +1,71 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/RegTest.java b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/RegTest.java new file mode 100644 index 00000000000..1c0a8a3effb --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/RegTest.java @@ -0,0 +1,171 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/RewriteServlet.java b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/RewriteServlet.java new file mode 100644 index 00000000000..5eb7436b3f8 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/RewriteServlet.java @@ -0,0 +1,70 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/SecureModeServlet.java b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/SecureModeServlet.java new file mode 100644 index 00000000000..da35e0f33db --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/SecureModeServlet.java @@ -0,0 +1,366 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/SessionDump.java b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/SessionDump.java new file mode 100644 index 00000000000..e6ab93231a7 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/SessionDump.java @@ -0,0 +1,191 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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 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<>()); + } + 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-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/TestFilter.java b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/TestFilter.java new file mode 100644 index 00000000000..c652f24140c --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/TestFilter.java @@ -0,0 +1,105 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/TestListener.java b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/TestListener.java new file mode 100644 index 00000000000..fc41eda98e5 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/TestListener.java @@ -0,0 +1,232 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/TestServlet.java b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/TestServlet.java new file mode 100644 index 00000000000..3dcc7baf69d --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/TestServlet.java @@ -0,0 +1,34 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/WebSocketChatServlet.java b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/WebSocketChatServlet.java new file mode 100644 index 00000000000..f5810222565 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/java/org/example/WebSocketChatServlet.java @@ -0,0 +1,116 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.websocket.api.RemoteEndpoint; +import org.eclipse.jetty.ee10.websocket.api.Session; +import org.eclipse.jetty.ee10.websocket.api.annotations.OnWebSocketClose; +import org.eclipse.jetty.ee10.websocket.api.annotations.OnWebSocketConnect; +import org.eclipse.jetty.ee10.websocket.api.annotations.OnWebSocketMessage; +import org.eclipse.jetty.ee10.websocket.api.annotations.WebSocket; +import org.eclipse.jetty.ee10.websocket.server.JettyServerUpgradeRequest; +import org.eclipse.jetty.ee10.websocket.server.JettyServerUpgradeResponse; +import org.eclipse.jetty.ee10.websocket.server.JettyWebSocketCreator; +import org.eclipse.jetty.ee10.websocket.server.JettyWebSocketServlet; +import org.eclipse.jetty.ee10.websocket.server.JettyWebSocketServletFactory; + +@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; + volatile RemoteEndpoint remote; + + @OnWebSocketConnect + public void onOpen(Session sess) + { + this.session = sess; + this.remote = sess.getRemote(); + 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.remote.sendString(data, null); + } + } + + @OnWebSocketClose + public void onClose(int code, String message) + { + members.remove(this); + } + } +} diff --git a/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/WEB-INF/jetty-web.xml b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/WEB-INF/jetty-web.xml new file mode 100644 index 00000000000..deaf55036d2 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/WEB-INF/jetty-web.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + org.eclipse.jetty.util. + org.eclipse.jetty.ee10.servlets. + + + + + + + The test-jetty webapp is deployed. DO NOT USE IN PRODUCTION! + + diff --git a/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/WEB-INF/web.xml b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000000..993d7792731 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,304 @@ + + + + Test WebApp + + + org.eclipse.jetty.server.context.ManagedAttributes + PushFilter,QoSFilter,TransparentProxy.ThreadPool,TransparentProxy.HttpClient + + + + + org.example.TestListener + + + + PushFilter + org.eclipse.jetty.ee10.servlets.PushCacheFilter + true + + + PushFilter + /* + + + + QoSFilter + org.eclipse.jetty.ee10.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/* + + + + CGI + org.eclipse.jetty.ee10.servlets.CGI + 1 + true + + + + CGI + /cgi-bin/* + + + + Chat + org.example.ChatServlet + 1 + true + + + + Chat + /chat/* + + + + WSChat + org.example.WebSocketChatServlet + 1 + + + + WSChat + /ws/* + + + + + 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-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/auth.html b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/auth.html new file mode 100644 index 00000000000..42f6daec214 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/auth/file.txt b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/auth/file.txt new file mode 100644 index 00000000000..cb74356f3b0 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/auth/relax.txt b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/auth/relax.txt new file mode 100644 index 00000000000..cb74356f3b0 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/auth2/index.html b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/auth2/index.html new file mode 100644 index 00000000000..f46164c410a --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/cgi-bin/hello.sh b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/cgi-bin/hello.sh new file mode 100644 index 00000000000..1ded600bc48 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/chat/index.html b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/chat/index.html new file mode 100644 index 00000000000..53e4795b1c2 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/chat/index.html @@ -0,0 +1,165 @@ + + + Async Chat + + + + +
    +
    +
    + Username:  +
    + +
    + + + diff --git a/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/d.txt b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/d.txt new file mode 100644 index 00000000000..cb74356f3b0 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/da.txt b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/da.txt new file mode 100644 index 00000000000..39101db7ccf --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/da.txt.gz b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/da.txt.gz new file mode 100644 index 0000000000000000000000000000000000000000..9ee9be82f0e9aa29ee3fbc58eb477e797fb08c28 GIT binary patch literal 2565 zcmb2|=3uxyE7*;JdFc)7LOS?dR;zCmeF(;?~qzvH^wRt*Ntg!<0ioT&RpmhrGD7HPx-s?8I+3!a8?;+vHQ#`Qs)KG!J~U6{+ma3Ht+twPJQMvvLd}PqeC&^W zaUO|8u#f%GFC&uh_St{r%ZMbRefA&yG9n3gpZX(TMkEpLQ-Ac!h$Q-F{1d!vpY+S! z=YPjbdBeZT%YbQMyv)@`~2^G cX>a&fecAsbUq+Z9zt4Yhn}dvn9`6_!05KAx-2eap literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/dat.txt b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/dat.txt new file mode 100644 index 00000000000..c6f093c6931 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/data.txt b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/data.txt new file mode 100644 index 00000000000..f28670f2968 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/data.txt.gz b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/data.txt.gz new file mode 100644 index 0000000000000000000000000000000000000000..42b9da0307cc858b2c2556a3c62f4fcbc7d0a9e3 GIT binary patch literal 25691 zcmeHPK}Zx~7!{1HRWNJR1Z9auY%y7vpc08CJ!m>r+X*Z|VMe<&$gtsdll=(=6z8G&xl-pYo?V#2(LcK_5>($@ zCm-~u4vXEMAwhRXPX)b0td}2TJX*vy-iaFCpzTv5)8;iLU8aR33vW1MK2p->%K|-y1MzSEGL7kHsL z-l0V#3vQT@ta=p?iq%N^m6=#vkT1^96)(>wDwl3Y0;y)_v_6=AF}3h(NSyomJ^Au6 zw^?;=Pc}B%3wz{CM&zu&gm;=d#Xf89gr`t*W`&(fdT=61{#Ks+YL%xMk@l^pyoj`K zJ>@M8+S@cp=nmSP1_^WU#39YN*EE+QG=L2kbo%x zW^+Mef5i+y7%|>>iI(@b`jFk6{7OuiWtUmVT`UatoduK>~(rb|QG z!gOg!TbM2lX$#Y(A#Gv0G@K2b%>hHMFAiu6Q!l(&jYo*T>)O9L3E&z-+!%SvC$G3f zu7S@7QgIMIA4rKIsva1H)dOR&df+Ik9!Nw`R6Q`t@_{SF5K<3}83841lg!_L1A;C? z%>PheHr3tB4P=?5D*_qZFsDEUH}t}af^pDW5kKs$NGYZ_ z-DlY>B$)I5g@puHZlqaA&^5J0OC#n3X($piA4o%yEF|b2P6OrxsR%k^ zyk{Z7oHx-dB)D3h!$N{?c@7H+;wghHB#74{!$N|rKn5`>Sgbm%Kqlu+G%Ju;|m6f2Z=%e0AD&JITS(6reK3Nz+0K7 +

    Not Found ERROR

    +custom 404 page + \ No newline at end of file diff --git a/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/favicon.ico b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..ea9e174b48b0130d4294813a6b5d487f35b436cc GIT binary patch literal 1150 zcmbtT*-leY6um|h48jdk3$&%Q(B4i^j0|O{SS`gNpioe>$}E$@lZJ1QNB+PF^2Rq1 z-~0si(Ki_^w7s|RV6t5MRE#FRd7IU<_sLm%SZ5ziJI7yDmBxRswtPX;YBWs?5!~WV zX`jnKzth8Su;vVy6F!(zey)BLp7>CB>W4KOr0qw3w28if>`f;Q+U+p8Z%!Bx?#XHx z){FsbAqZ=}v|o5k{<>)7Pyike?g%;$(&VSpzKYFT`wR zDrX^M;CRr3qk($tMJ~gfG?=p)=1f4>OZ>eMmFKZ z{#6_645KLgIrf(Ep}0){MOd9rM%JLm7=wuWGDy2FVWh5HVfe~LjL`pd&kbuajQk^N z@{D>BMfSbkW<%5whu%d#k%;ZURdyb!Ya7ZI-%;ttJJuxJLz0cWW_*%td~zN%2=2D-ky` z(BtG2iSC_JOXHW6$^Qk_6M1L8#NWE%;LLH} zWIj0`Qp53(2ibm?;-@;O`5*Ztr|<+(CpGA+Q$34j#g!J$e~9zt#$j(AUOLK=Y~>vE zu?9lyH@sWXP4aHWYq5K$p7{f?rUNjigPh|g$c%mOb6~yGu6q2P&lSgd%&yLxlcl$% z^3Jn=&e49e4f|0W4m)dTSK~8hVtc3o?+0Ai>UUzBcl=?*!x|+Q-zjIsoMPQ_f7DOj qU^eMe?`tnuMcawfS5dwZz6;KEaVb>dF}Ls}x8hTO;ZooK`hEgZQ79q+ literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/index.html b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/index.html new file mode 100644 index 00000000000..54e42e32e93 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/jakarta.websocket/index.html b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/jakarta.websocket/index.html new file mode 100644 index 00000000000..050a0b1e3ef --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/logon.html b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/logon.html new file mode 100644 index 00000000000..6bededa576d --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/logonError.html b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/logonError.html new file mode 100644 index 00000000000..66a83869061 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/logonError.html @@ -0,0 +1,4 @@ + +

    Authentication ERROR

    +Username, password or role incorrect. + diff --git a/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/remote.html b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/remote.html new file mode 100644 index 00000000000..63acd9e5d41 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/rewrite/index.html b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/rewrite/index.html new file mode 100644 index 00000000000..ed308429b1a --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/rewrite/info.html b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/rewrite/info.html new file mode 100644 index 00000000000..a3817ad09a9 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/small_powered_by.gif b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/main/webapp/small_powered_by.gif new file mode 100644 index 0000000000000000000000000000000000000000..c5dd44319f0aa17ea93b15fbcc8a18e14ca397d5 GIT binary patch literal 4787 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 + WebSocket Chat + + + +
    +
    +
    + Username:  +
    + +
    + + +

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

    + + + + diff --git a/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/test/java/org/eclipse/jetty/ee10/ChatServletTest.java b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/test/java/org/eclipse/jetty/ee10/ChatServletTest.java new file mode 100644 index 00000000000..e822e9bc7b2 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/test/java/org/eclipse/jetty/ee10/ChatServletTest.java @@ -0,0 +1,87 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10; + +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.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, "/"); + 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-ee10/jetty-ee10-demos/demo-jetty-webapp/src/test/java/org/eclipse/jetty/ee10/DispatchServletTest.java b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/test/java/org/eclipse/jetty/ee10/DispatchServletTest.java new file mode 100644 index 00000000000..fe04bdbef59 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/test/java/org/eclipse/jetty/ee10/DispatchServletTest.java @@ -0,0 +1,147 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10; + +import org.eclipse.jetty.ee10.servlet.DefaultServlet; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.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(server, "/tests"); + 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-ee10/jetty-ee10-demos/demo-jetty-webapp/src/test/java/org/eclipse/jetty/ee10/TestServer.java b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/test/java/org/eclipse/jetty/ee10/TestServer.java new file mode 100644 index 00000000000..6b86a06337a --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/test/java/org/eclipse/jetty/ee10/TestServer.java @@ -0,0 +1,174 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10; + +import java.lang.management.ManagementFactory; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.eclipse.jetty.ee10.servlet.security.HashLoginService; +import org.eclipse.jetty.ee10.webapp.Configurations; +import org.eclipse.jetty.ee10.webapp.MetaInfConfiguration; +import org.eclipse.jetty.ee10.webapp.WebAppContext; +import org.eclipse.jetty.jmx.MBeanContainer; +import org.eclipse.jetty.server.CustomRequestLog; +import org.eclipse.jetty.server.ForwardedRequestCustomizer; +import org.eclipse.jetty.server.Handler; +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.DefaultHandler; +import org.eclipse.jetty.server.handler.ResourceHandler; +import org.eclipse.jetty.session.DefaultSessionCache; +import org.eclipse.jetty.session.FileSessionDataStore; +import org.eclipse.jetty.util.thread.QueuedThreadPool; +import org.junit.jupiter.api.Disabled; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Disabled("Not a test case") +public class TestServer +{ + private static final Logger LOG = LoggerFactory.getLogger(TestServer.class); + + public static void main(String[] args) throws Exception + { + // TODO don't depend on this file structure + Path jettyRoot = FileSystems.getDefault().getPath(".").toAbsolutePath().normalize(); + if (!Files.exists(jettyRoot.resolve("VERSION.txt"))) + jettyRoot = FileSystems.getDefault().getPath("../../..").toAbsolutePath().normalize(); + if (!Files.exists(jettyRoot.resolve("VERSION.txt"))) + throw new IllegalArgumentException(jettyRoot.toString()); + + // 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(); + Handler.Collection handlers = new Handler.Collection(contexts, new DefaultHandler()); + + // Add restart handler to test the ability to save sessions and restart + /* RestartHandler restart = new RestartHandler(); + restart.setHandler(handlers); + server.setHandler(restart);*/ + + // Setup context + HashLoginService login = new HashLoginService(); + login.setName("Test Realm"); + login.setConfig(jettyRoot.resolve("tests/test-webapps/test-jetty-webapp/src/main/config/demo-base/etc/realm.properties").toString()); + 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); + webapp.setResourceBase(jettyRoot.resolve("tests/test-webapps/test-jetty-webapp/src/main/webapp")); + webapp.setAttribute(MetaInfConfiguration.CONTAINER_JAR_PATTERN, + ".*/test-jetty-webapp/target/classes.*$|" + + ".*/jetty-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(); + srcroot.setResourceBase(jettyRoot.resolve("tests/test-webapps/test-jetty-webapp/src")); + 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-ee10/jetty-ee10-demos/demo-jetty-webapp/src/test/resources/jetty-logging.properties b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/test/resources/jetty-logging.properties new file mode 100644 index 00000000000..53f3d1ebb2f --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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.ee10.annotations.LEVEL=DEBUG diff --git a/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/test/resources/test-realm.properties b/jetty-ee10/jetty-ee10-demos/demo-jetty-webapp/src/test/resources/test-realm.properties new file mode 100644 index 00000000000..9d9bc368493 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-jndi-webapp/pom.xml b/jetty-ee10/jetty-ee10-demos/demo-jndi-webapp/pom.xml new file mode 100644 index 00000000000..8794d3eca3c --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jndi-webapp/pom.xml @@ -0,0 +1,103 @@ + + + 4.0.0 + + org.eclipse.jetty.ee10.demos + jetty-ee10-demos + 12.0.0-SNAPSHOT + + demo-jndi-webapp + EE10 :: Jetty Demo :: JNDI :: WebApp + war + + ${project.groupId}.jndi + + + + + maven-antrun-plugin + + + generate-xml-files + process-resources + + + + + + + + + + + + run + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + package + + copy-dependencies + + + jakarta.transaction-api,demo-mock-resources + ${project.build.directory}/lib/jndi + + + + + + + + + org.eclipse.jetty + jetty-maven-plugin + ${project.version} + + ${project.build.directory}/plugin-context.xml + + src/main/webapp + src/main/webapp/WEB-INF/web.xml + /test-jndi + + + + + org.eclipse.jetty.ee10.demos + demo-mock-resources + ${project.version} + + + + + + + + + org.eclipse.jetty.ee10.demos + demo-mock-resources + provided + + + jakarta.transaction + jakarta.transaction-api + provided + + + org.eclipse.jetty.toolchain + jetty-jakarta-servlet-api + provided + + + org.eclipse.jetty.orbit + javax.mail.glassfish + provided + + + diff --git a/jetty-ee10/jetty-ee10-demos/demo-jndi-webapp/src/main/config/modules/demo-jndi.mod b/jetty-ee10/jetty-ee10-demos/demo-jndi-webapp/src/main/config/modules/demo-jndi.mod new file mode 100644 index 00000000000..472885ce4ab --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jndi-webapp/src/main/config/modules/demo-jndi.mod @@ -0,0 +1,20 @@ +# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html + +[description] +Demo JNDI Resources Webapp + +[tags] +demo +webapp + +[depends] +deploy +ext +jdbc +plus +demo-mock-resources + +[files] +basehome:modules/demo.d/demo-jndi.xml|webapps/demo-jndi.xml +maven://org.eclipse.jetty.demos/demo-jndi-webapp/${jetty.version}/war|webapps/demo-jndi.war +maven://jakarta.mail/jakarta.mail-api/2.0.0/jar|lib/ext/jakarta.mail-api-2.0.0.jar diff --git a/jetty-ee10/jetty-ee10-demos/demo-jndi-webapp/src/main/config/modules/demo.d/demo-jndi.xml b/jetty-ee10/jetty-ee10-demos/demo-jndi-webapp/src/main/config/modules/demo.d/demo-jndi.xml new file mode 100644 index 00000000000..4268592a1dd --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jndi-webapp/src/main/config/modules/demo.d/demo-jndi.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + /test-jndi + /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-ee10/jetty-ee10-demos/demo-jndi-webapp/src/main/java/org/example/JNDITest.java b/jetty-ee10/jetty-ee10-demos/demo-jndi-webapp/src/main/java/org/example/JNDITest.java new file mode 100644 index 00000000000..850cb37817e --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jndi-webapp/src/main/java/org/example/JNDITest.java @@ -0,0 +1,136 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-demos/demo-jndi-webapp/src/main/templates/env-definitions.xml b/jetty-ee10/jetty-ee10-demos/demo-jndi-webapp/src/main/templates/env-definitions.xml new file mode 100644 index 00000000000..93314edb3fd --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jndi-webapp/src/main/templates/env-definitions.xml @@ -0,0 +1,27 @@ + + + + + woggle + 4000 + false + + + + + + wiggle + 100 + true + + + + + + jdbc/mydatasource + + + + + + diff --git a/jetty-ee10/jetty-ee10-demos/demo-jndi-webapp/src/main/templates/jetty-test-jndi-header.xml b/jetty-ee10/jetty-ee10-demos/demo-jndi-webapp/src/main/templates/jetty-test-jndi-header.xml new file mode 100644 index 00000000000..516ea06c1b9 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jndi-webapp/src/main/templates/jetty-test-jndi-header.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + /test-jndi + /test-jndi.war + true + false + true + diff --git a/jetty-ee10/jetty-ee10-demos/demo-jndi-webapp/src/main/templates/plugin-context-header.xml b/jetty-ee10/jetty-ee10-demos/demo-jndi-webapp/src/main/templates/plugin-context-header.xml new file mode 100644 index 00000000000..afc07d00f31 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jndi-webapp/src/main/templates/plugin-context-header.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-demos/demo-jndi-webapp/src/main/webapp/WEB-INF/jetty-env.xml b/jetty-ee10/jetty-ee10-demos/demo-jndi-webapp/src/main/webapp/WEB-INF/jetty-env.xml new file mode 100644 index 00000000000..581d672a39c --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-jndi-webapp/src/main/webapp/WEB-INF/jetty-web.xml b/jetty-ee10/jetty-ee10-demos/demo-jndi-webapp/src/main/webapp/WEB-INF/jetty-web.xml new file mode 100644 index 00000000000..d0f0fb5d2c0 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-jndi-webapp/src/main/webapp/WEB-INF/web.xml b/jetty-ee10/jetty-ee10-demos/demo-jndi-webapp/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000000..5dfe864dd29 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jndi-webapp/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,54 @@ + + + + Test 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-ee10/jetty-ee10-demos/demo-jndi-webapp/src/main/webapp/demo.css b/jetty-ee10/jetty-ee10-demos/demo-jndi-webapp/src/main/webapp/demo.css new file mode 100644 index 00000000000..f2b91d3365d --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-jndi-webapp/src/main/webapp/index.html b/jetty-ee10/jetty-ee10-demos/demo-jndi-webapp/src/main/webapp/index.html new file mode 100644 index 00000000000..dba3813acfb --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-jndi-webapp/src/main/webapp/small_powered_by.gif b/jetty-ee10/jetty-ee10-demos/demo-jndi-webapp/src/main/webapp/small_powered_by.gif new file mode 100644 index 0000000000000000000000000000000000000000..c5dd44319f0aa17ea93b15fbcc8a18e14ca397d5 GIT binary patch literal 4787 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 + + + org.eclipse.jetty.ee10.demos + jetty-ee10-demos + 12.0.0-SNAPSHOT + + + 4.0.0 + demo-jsp-webapp + EE10 :: Jetty Demo :: JSP :: Webapp + war + + + ${project.groupId}.jsp + + + + + + 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.* + /demo-jsp + .,WEB-INF/classes + + + + + + maven-war-plugin + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + maven-assembly-plugin + + + web-bundle-assembly + package + + single + + + + src/main/assembly/web-bundle.xml + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + + + + + + + org.eclipse.jetty.toolchain + jetty-jakarta-servlet-api + provided + + + jakarta.servlet.jsp + jakarta.servlet.jsp-api + provided + + + jakarta.servlet.jsp.jstl + jakarta.servlet.jsp.jstl-api + provided + + + + + + precompile-jsp + + + + org.eclipse.jetty + jetty-jspc-maven-plugin + ${project.version} + + + jspc + + jspc + + + + + + + org.apache.maven.plugins + maven-war-plugin + + ${basedir}/target/web.xml + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/assembly/web-bundle.xml b/jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/assembly/web-bundle.xml new file mode 100644 index 00000000000..803a7455f19 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/config/modules/demo-jsp.mod b/jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/config/modules/demo-jsp.mod new file mode 100644 index 00000000000..a81e59f423f --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/config/modules/demo-jsp.mod @@ -0,0 +1,14 @@ +[description] +Demo Simple JSP Webapp + +[tags] +demo +webapp + +[depends] +jsp +jstl +deploy + +[files] +maven://org.eclipse.jetty.demos/demo-jsp-webapp/${jetty.version}/war|webapps/demo-jsp.war diff --git a/jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/java/org/example/Counter.java b/jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/java/org/example/Counter.java new file mode 100644 index 00000000000..0f82c76d09f --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/java/org/example/Counter.java @@ -0,0 +1,38 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/java/org/example/Date2Tag.java b/jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/java/org/example/Date2Tag.java new file mode 100644 index 00000000000..214987c5cca --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/java/org/example/Date2Tag.java @@ -0,0 +1,51 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/java/org/example/DateTag.java b/jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/java/org/example/DateTag.java new file mode 100644 index 00000000000..d4c685d9f99 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/java/org/example/DateTag.java @@ -0,0 +1,102 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/java/org/example/TagListener.java b/jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/java/org/example/TagListener.java new file mode 100644 index 00000000000..6ac0a97ee36 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/java/org/example/TagListener.java @@ -0,0 +1,133 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/WEB-INF/acme-taglib.tld b/jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/WEB-INF/acme-taglib.tld new file mode 100644 index 00000000000..6ccbd1764bc --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/WEB-INF/acme-taglib2.tld b/jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/WEB-INF/acme-taglib2.tld new file mode 100644 index 00000000000..64e29a87c12 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/WEB-INF/jetty-web.xml b/jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/WEB-INF/jetty-web.xml new file mode 100644 index 00000000000..23ff79579d1 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/WEB-INF/tags/panel.tag b/jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/WEB-INF/tags/panel.tag new file mode 100644 index 00000000000..798b80e7630 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/WEB-INF/web.xml b/jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000000..f91c2df9489 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,18 @@ + + + + Demo JSP Web Application + + + foo.jsp + /foo/foo.jsp + + + foo.jsp + /foo/ + + + diff --git a/jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/bean1.jsp b/jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/bean1.jsp new file mode 100644 index 00000000000..adb8eb1f79e --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/bean2.jsp b/jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/bean2.jsp new file mode 100644 index 00000000000..0598739e177 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/demo.css b/jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/demo.css new file mode 100644 index 00000000000..6cc26f40ece --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/dump.jsp b/jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/dump.jsp new file mode 100644 index 00000000000..b0cb8f131c9 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/expr.jsp b/jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/expr.jsp new file mode 100644 index 00000000000..e0b25e20203 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/foo/foo.jsp b/jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/foo/foo.jsp new file mode 100644 index 00000000000..7ec8955932d --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/index.jsp b/jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/index.jsp new file mode 100644 index 00000000000..5c5959f49d1 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/jstl.jsp b/jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/jstl.jsp new file mode 100644 index 00000000000..9fa7b57e96c --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/small_powered_by.gif b/jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/small_powered_by.gif new file mode 100644 index 0000000000000000000000000000000000000000..c5dd44319f0aa17ea93b15fbcc8a18e14ca397d5 GIT binary patch literal 4787 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 + + +<%@ 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-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/tag2.jsp b/jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/tag2.jsp new file mode 100644 index 00000000000..8071927562a --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/tagfile.jsp b/jetty-ee10/jetty-ee10-demos/demo-jsp-webapp/src/main/webapp/tagfile.jsp new file mode 100644 index 00000000000..67299f0229c --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-mock-resources/pom.xml b/jetty-ee10/jetty-ee10-demos/demo-mock-resources/pom.xml new file mode 100644 index 00000000000..204040de844 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-mock-resources/pom.xml @@ -0,0 +1,60 @@ + + 4.0.0 + + org.eclipse.jetty.ee10.demos + jetty-ee10-demos + 12.0.0-SNAPSHOT + + EE10 :: Jetty Demo :: Mock Resources + demo-mock-resources + jar + + ${project.groupId}.mocks + + + + + org.apache.felix + maven-bundle-plugin + true + + + + manifest + + + + org.eclipse.jetty.demos.demo-mock-resources + Mock resources used for testing + + org.example;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}" + + + javax.sql, jakarta.transaction;version="2.0.0" + + <_nouses>true + + + + + + + + + + jakarta.transaction + jakarta.transaction-api + provided + + + org.eclipse.jetty.toolchain + jetty-jakarta-servlet-api + provided + + + jakarta.mail + jakarta.mail-api + provided + + + diff --git a/jetty-ee10/jetty-ee10-demos/demo-mock-resources/src/main/config/modules/demo-mock-resources.mod b/jetty-ee10/jetty-ee10-demos/demo-mock-resources/src/main/config/modules/demo-mock-resources.mod new file mode 100644 index 00000000000..dd8faf6d661 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-mock-resources/src/main/config/modules/demo-mock-resources.mod @@ -0,0 +1,14 @@ +# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html + +[description] +Download and install some Demo Mock Resources + +[tags] +demo + +[depends] +jdbc +annotations + +[files] +maven://org.eclipse.jetty.demos/demo-mock-resources/${jetty.version}/jar|lib/ext/demo-mock-resources-${jetty.version}.jar diff --git a/jetty-ee10/jetty-ee10-demos/demo-mock-resources/src/main/java/org/example/MockDataSource.java b/jetty-ee10/jetty-ee10-demos/demo-mock-resources/src/main/java/org/example/MockDataSource.java new file mode 100644 index 00000000000..2e2383ea606 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-mock-resources/src/main/java/org/example/MockDataSource.java @@ -0,0 +1,83 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-demos/demo-mock-resources/src/main/java/org/example/MockTransport.java b/jetty-ee10/jetty-ee10-demos/demo-mock-resources/src/main/java/org/example/MockTransport.java new file mode 100644 index 00000000000..b1371b548e1 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-mock-resources/src/main/java/org/example/MockTransport.java @@ -0,0 +1,41 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-demos/demo-mock-resources/src/main/java/org/example/MockUserTransaction.java b/jetty-ee10/jetty-ee10-demos/demo-mock-resources/src/main/java/org/example/MockUserTransaction.java new file mode 100644 index 00000000000..1ae700886c1 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-mock-resources/src/main/java/org/example/MockUserTransaction.java @@ -0,0 +1,62 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.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 implements UserTransaction +{ + + @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-ee10/jetty-ee10-demos/demo-mock-resources/src/main/resources/META-INF/javaxmail.providers b/jetty-ee10/jetty-ee10-demos/demo-mock-resources/src/main/resources/META-INF/javaxmail.providers new file mode 100644 index 00000000000..f787ee3cb04 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-proxy-webapp/pom.xml b/jetty-ee10/jetty-ee10-demos/demo-proxy-webapp/pom.xml new file mode 100644 index 00000000000..7bb76d1b4ba --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-proxy-webapp/pom.xml @@ -0,0 +1,91 @@ + + + + org.eclipse.jetty.ee10.demos + jetty-ee10-demos + 12.0.0-SNAPSHOT + + 4.0.0 + demo-proxy-webapp + EE10 :: Jetty Demo :: Proxy :: Webapp + war + + ${project.groupId}.proxy + + + + + maven-war-plugin + + + src/main/webapp/META-INF/MANIFEST.MF + + + + + + + + org.slf4j + slf4j-api + + + org.eclipse.jetty + jetty-slf4j-impl + compile + + + org.eclipse.jetty.ee10 + jetty-ee10-proxy + + + org.eclipse.jetty.toolchain + jetty-jakarta-servlet-api + provided + + + org.eclipse.jetty.ee10 + jetty-ee10-webapp + test + + + org.eclipse.jetty + jetty-client + + + org.eclipse.jetty + jetty-jmx + test + + + org.eclipse.jetty + jetty-server + provided + + + org.eclipse.jetty.http2 + http2-server + test + + + org.eclipse.jetty + jetty-alpn-java-server + test + + + org.eclipse.jetty.ee10 + jetty-ee10-annotations + test + + + jakarta.servlet.jsp + jakarta.servlet.jsp-api + provided + + + jakarta.servlet.jsp.jstl + jakarta.servlet.jsp.jstl-api + provided + + + diff --git a/jetty-ee10/jetty-ee10-demos/demo-proxy-webapp/src/main/config/modules/demo-proxy.mod b/jetty-ee10/jetty-ee10-demos/demo-proxy-webapp/src/main/config/modules/demo-proxy.mod new file mode 100644 index 00000000000..4f96bd24eed --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-proxy-webapp/src/main/config/modules/demo-proxy.mod @@ -0,0 +1,14 @@ +# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html + +[description] +Demo Proxy Webapp + +[tags] +demo +webapp + +[depends] +deploy + +[files] +maven://org.eclipse.jetty.demos/demo-proxy-webapp/${jetty.version}/war|webapps/demo-proxy.war diff --git a/jetty-ee10/jetty-ee10-demos/demo-proxy-webapp/src/main/webapp/META-INF/MANIFEST.MF b/jetty-ee10/jetty-ee10-demos/demo-proxy-webapp/src/main/webapp/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..d46c0166acb --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-proxy-webapp/src/main/webapp/WEB-INF/jetty-web.xml b/jetty-ee10/jetty-ee10-demos/demo-proxy-webapp/src/main/webapp/WEB-INF/jetty-web.xml new file mode 100644 index 00000000000..459ee9eaccc --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-proxy-webapp/src/main/webapp/WEB-INF/web.xml b/jetty-ee10/jetty-ee10-demos/demo-proxy-webapp/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000000..3a8edfcfae5 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-proxy-webapp/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,31 @@ + + + + Transparent Proxy WebApp + + + JavadocTransparentProxy + org.eclipse.jetty.proxy.ProxyServlet$Transparent + + proxyTo + https://www.eclipse.org/jetty/javadoc/jetty-11/index.html?overview-summary.html + + + hostHeader + www.eclipse.org + + 1 + true + + + + JavadocTransparentProxy + /current/* + + + diff --git a/jetty-ee10/jetty-ee10-demos/demo-proxy-webapp/src/test/java/org/eclipse/jetty/ee10/demos/ProxyWebAppTest.java b/jetty-ee10/jetty-ee10-demos/demo-proxy-webapp/src/test/java/org/eclipse/jetty/ee10/demos/ProxyWebAppTest.java new file mode 100644 index 00000000000..78ab9897701 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-proxy-webapp/src/test/java/org/eclipse/jetty/ee10/demos/ProxyWebAppTest.java @@ -0,0 +1,93 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.ee10.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.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; + +/** + * 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.getServerClassMatcher().add("-org.eclipse.jetty.proxy."); + webapp.setWar(MavenTestingUtils.getProjectDirPath("src/main/webapp").toString()); + webapp.setExtraClasspath(MavenTestingUtils.getTargetPath().resolve("classes").toString()); + server.setHandler(webapp); + + server.start(); + + client = new HttpClient(); + client.start(); + } + + @AfterEach + public void teardown() + { + LifeCycle.stop(client); + LifeCycle.stop(server); + } + + @Disabled + @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(), containsString("All Classes")); + } +} diff --git a/jetty-ee10/jetty-ee10-demos/demo-proxy-webapp/src/test/resources/jetty-logging.properties b/jetty-ee10/jetty-ee10-demos/demo-proxy-webapp/src/test/resources/jetty-logging.properties new file mode 100644 index 00000000000..bf725104bbd --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-simple-webapp/pom.xml b/jetty-ee10/jetty-ee10-demos/demo-simple-webapp/pom.xml new file mode 100644 index 00000000000..409b35041b1 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-simple-webapp/pom.xml @@ -0,0 +1,18 @@ + + + + org.eclipse.jetty.ee10.demos + jetty-ee10-demos + 12.0.0-SNAPSHOT + + + 4.0.0 + demo-simple-webapp + EE10 :: Jetty Demo :: Simple :: Webapp + war + + + ${project.groupId}.simple + + + diff --git a/jetty-ee10/jetty-ee10-demos/demo-simple-webapp/src/main/config/modules/demo-simple.mod b/jetty-ee10/jetty-ee10-demos/demo-simple-webapp/src/main/config/modules/demo-simple.mod new file mode 100644 index 00000000000..7b59bbc3c82 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-simple-webapp/src/main/config/modules/demo-simple.mod @@ -0,0 +1,12 @@ +[description] +Demo Simple Webapp + +[tags] +demo +webapp + +[depends] +deploy + +[files] +maven://org.eclipse.jetty.demos/demo-simple-webapp/${jetty.version}/war|webapps/demo-simple.war diff --git a/jetty-ee10/jetty-ee10-demos/demo-simple-webapp/src/main/webapp/WEB-INF/web.xml b/jetty-ee10/jetty-ee10-demos/demo-simple-webapp/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000000..79d16dcd850 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-simple-webapp/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,15 @@ + + + + Simple Web Application + + + + icon + image/vnd.microsoft.icon + + + diff --git a/jetty-ee10/jetty-ee10-demos/demo-simple-webapp/src/main/webapp/index.html b/jetty-ee10/jetty-ee10-demos/demo-simple-webapp/src/main/webapp/index.html new file mode 100644 index 00000000000..40917f44acd --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-simple-webapp/src/main/webapp/index.html @@ -0,0 +1,6 @@ + + + +

    Hello World!

    + + diff --git a/jetty-ee10/jetty-ee10-demos/demo-simple-webapp/src/main/webapp/jetty.icon b/jetty-ee10/jetty-ee10-demos/demo-simple-webapp/src/main/webapp/jetty.icon new file mode 100644 index 0000000000000000000000000000000000000000..54e2e6104332729142765d92f2a8168ca4a84891 GIT binary patch literal 6586 zcmZQzU}Rur5D;Jh(h3Y2EDQ``3=9kk3QRygBZC1W1H(R`n1KP5&&XYLHY5)R|{v06fC&|E|0i=OaKoN*IkYZo}nu8AhkAl$<7!83D8v^L%0jYdYIimo? z4nPbluRx#yOhO1IAO*r801RXh9#8;r$-t4I@G=161R#co7bMt#B9O4c3@>ol0mE&S zh9n;Pg#$QJMu`Cn0Z@Kv0ND=ABe49!zyT6~fWcP23~1ptYWZjgjE2By2#kinXb6nZ z5P-D{M(NQI7!85Z5Eu=C(GVC7fsqpeodJICyj)UTKp*pZdbk9E2M%B(2PK?SPXi^4 zJzX3_GVZ-SwGcR{z{8~Qzdp)8BDTS&szZUJ2^%`}-r)14hnGTf=DLGI($m$?Wt~$( F697q|!wUca literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/demo-simple-webapp/src/main/webapp/jetty.png b/jetty-ee10/jetty-ee10-demos/demo-simple-webapp/src/main/webapp/jetty.png new file mode 100644 index 0000000000000000000000000000000000000000..d579fffddfe176f020f7f74bdc663195fd39697f GIT binary patch literal 5465 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 P00000NkvXXu0mjf*FjSZ literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/demo-simple-webapp/src/main/webapp/jetty.webp b/jetty-ee10/jetty-ee10-demos/demo-simple-webapp/src/main/webapp/jetty.webp new file mode 100644 index 0000000000000000000000000000000000000000..2d1bfea3ef792ce55a4c1f518947a93d29468bd7 GIT binary patch literal 3534 zcmV;<4KeakNk&G-4FCXFMM6+kP&il$0000G0002T001}u06|PpNL2*@00E#yZQJq2 zdXlEAyBK7TZQHhO+qP}nwr$(C?MbE>)kxlh=KbIAPtqREK}1Xd{`s1xrPdZn;vMB% z#%`X$62E9fd_%xDh}fMo8C`GpHjs${RkA5LhFAno=< zT#6{t9_{UVCpDA-IugGaN-dIu&QOn}*|}yUP49Arh`$0Do^#O|lw>C9FlU%02{Let zB;VokJ2lh=8WO(+N^6NqJHu7U5%6~MEKdiVJNCfpe0*twq%Uj39nI zkU55XVo-EG4tfZfLL`HhNo)xKFAEp)Kd^%Mwoq#djf0i}MM+xxf#d;z?!+$uQiO9K zO^S+99NZchC<#kqlPm*_Z@8#*z;xpKLh(SoZq}J4nqTm{@2zUDUMg!PCuuLR=}Foej`vHx0I*7?O%!DS?s4|qz;NQnLAAyC zQa2Li<64O_Fh3+afvhC0y;1TNs80Mr!0S5~Y*v+-oZcVvB)^Q@GRaps&Q8+4KnIE4 z+)XkUIH#c$(4F{sP~Fb?&fuoxIQWN>-ArduAz+QIWviTT zs&|xRG?&OG8NjTU{Ph4{NLE4NJ`J^jX2h?9@~6&MxFSi*b2Uj?lzAmN4J0LL`;3cD zYCYc3h#WTV5rBJ~Er&9*-Ra(9j3m;Vn<5D{IZ95_2|xo;mKP+fgLTBW2C5OiAIj^H z(~rP$$qn%5CFkL|k*F##M`FoD(m`n=xeq4boQC4+rt1_lQ9PkE@Jh1Mz%Y-GdN!Iyu!De7B@y&G@BHFB9cVLyWzMEX;Vs(;UC4L@|CX##Wp~HU3w@&)y z1sW?W*xF^LO%z3Pk!gUr#P@)XKP0=&L3;f%0LHV5{uoV{6-DY-`;18AdcYjaS-f<} zM$!SOW7p{f0QlWja}r4OP|f}kdfCnX%IrCTvBZynjvYlsfZk#H^o@li2ka{P0!V&D zNmum$tIt#1RJI@R_UC>lRF`6Bge1fSz+7+6v6HHgbu>8uvJF{!@TB%*9ZiUxbJ)^@ zC$%5#o+f3=jNMlqzJBxO&0EjDhA3Et^h*wE-SbyN37`-0GogM7iO<0u5C22Dw}-GS z@fQG(4_rh_9gfH*yQ5?=q<g$fYB6W+X1~+&` z5Yrs)*chzW@vLx%oi)YmD-HKNI9Ofa?VZyr!%gOC(Y#r+rcD|*YFIZl-1dKo6(;iY z@iH0kZ(aaaP&gn;2mk=^CjgxRD#!pi06vjCnMtLiA|WdioH(!&32Xq=iGluS`2nrn z(my1Q&|^pE|% z(?9Ngg@0@PfxmS}uwA=^FpQ--}`3;;M#wFRx0O2t6MT zAvj|;tv9DjM;oGnK(R@H-nj74`+65!q5&Y)}ERWEM0bqsmWb9A>{>D&R z0m)c+-$-Gp%}ZPX$LHLS)R+I@mHYCVLHdby%li_-+^l42x}FVYasEA7^KYqUM9bOr zPjv2&tE#LTyxiKbcA4Lix&*uV#^!127t9&qJ}e(}mo7IMRUYXq={;+v`CQ$g)=(BN zi7NW*M@X~vck~)MABVt-56s?A-NQ`YRk3^KCJH zLspg~k-uar;D!Jo;UVNmF}wqfGvw^`&rP|HWp+zO``GL$=T8$0FMfyR9S$Kfy_N^;&lJh`3-JTs9U z5rIjgp3HWQ`tD{Xfef$LjUok{>0k7Q)aT)kdO&!_q7C6gQUL$;MaFxnh0fm;e=BR3 zm`#gg24+iHi5km(um=Te4g18ZC{%5ty%wHoMV?;aIj{X_5Xf&KS&CmU&Le)VKTZ;x zQ@Qd&DmCm1vEEdmqq-u(%=6BCfC)v(IoOZk=uP^s!boObrnanwVzJ8~?9ZAdE0D42 zK|ejyk;$Ql3Nf~gxrDk*fUe8_A;%s*Ws6gDQlj<5tXi%XhKq^Si5{IDOgb!4Cz+P# zL!&p0x~mQUu@&}3bsnj>w5I<_arnO+wDvwjme3yJdCNMuRoya`R{li98lQ=^$IOw3+OH1eXbS1A zZ$7{__z%Fdt9lSEngCJHw_9>%auuSS6o)q$^yg#<-kFN2Zx>y&_%I$h8!dgxr#&Jr?mSTYE}?(Ai0|+5HMwF_Ci3FI|E&=n}3?~d4~`WRt|SBqaq)Wi_$Q%kjxSg4U!SkPAk*tAlgs%YMP zhd>pM6Y_=S9PrY+xg$Xc(f0u!0f_^kPx}IUz{7GHo!k1L;l0CH?%==u+*Bn03iyk1 zDe!Dhg&NbjwmOCms{{yd#{5PfGwV!^kPn}9%~mdCz3h3Gy$_=}ZJv5pR&!LyDIk6g&U14;mQTD;q7)@l0k z37VYtRrI;Oc^v0T32nr1Dq{cqKy}xTA2QY`<$vHxr9yhDATL`8j9>NIHc1>r?>i+> z1LcAVau^#lK`eOwl;>h{cb za@=w)*M;yo{YOEWva`a<^oY>4zGA>k_|eHktkbi6#4J3Q4vxe2jtA;YvEUAYvl*P{ zs#U{NtYD*_yRwxzi)xY0&_>Vzzz?I40GNLK{+}*R^~J{%3&QdZNJ?N(I7(tyRJDsh zX$W#H0=nMtIC6q(QMa3u!pTnkv5N)MMTSq%+Z?u)@2f3J?Sxes*7rG3;2&tfT6n25vz0lMu1eUQ}60B~joI8?yN7)bXR+-nc7$ zA26NoBkN;SC9kbwwUVS1yl9rNL=CfC_)^s}B*b&O=AU=)7P5((8O(KF2ykBly;@Qi z8SptNGoJ^t5LQr*{`-Ma-8Z~AOL8C6tOe$I=UwT4q^E3J9QeTzpjSD1`{cNjZ}DVB zXQ*bj94FU~dZQEcS-^nTHLMqv#1B#f+42;Ru-Ho;_KFNC#^5HT00MaAFxVFePRswK I1ONa40I<~Z`v3p{ literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/demo-spec/demo-container-initializer/pom.xml b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-container-initializer/pom.xml new file mode 100644 index 00000000000..4c8fe41629b --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-container-initializer/pom.xml @@ -0,0 +1,41 @@ + + 4.0.0 + + org.eclipse.jetty.ee10.demos + jetty-ee10-demos + 12.0.0-SNAPSHOT + ../../pom.xml + + demo-container-initializer + jar + EE10 :: Jetty Demo :: Servlet Spec :: ServletContainerInitializer Jar + + ${project.groupId}.sci + + + + + org.apache.felix + maven-bundle-plugin + true + + + org.eclipse.jetty.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 + + + + + + + + org.eclipse.jetty.toolchain + jetty-jakarta-servlet-api + provided + + + diff --git a/jetty-ee10/jetty-ee10-demos/demo-spec/demo-container-initializer/src/main/java/org/example/initializer/Foo.java b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-container-initializer/src/main/java/org/example/initializer/Foo.java new file mode 100644 index 00000000000..ee6807330f4 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-container-initializer/src/main/java/org/example/initializer/Foo.java @@ -0,0 +1,27 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-demos/demo-spec/demo-container-initializer/src/main/java/org/example/initializer/FooInitializer.java b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-container-initializer/src/main/java/org/example/initializer/FooInitializer.java new file mode 100644 index 00000000000..131cab6a175 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-container-initializer/src/main/java/org/example/initializer/FooInitializer.java @@ -0,0 +1,96 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.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-ee10/jetty-ee10-demos/demo-spec/demo-container-initializer/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-container-initializer/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer new file mode 100644 index 00000000000..c072ba5d1a5 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-container-initializer/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer @@ -0,0 +1 @@ +org.example.initializer.FooInitializer diff --git a/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/pom.xml b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/pom.xml new file mode 100644 index 00000000000..af029de37f3 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/pom.xml @@ -0,0 +1,226 @@ + + + 4.0.0 + + org.eclipse.jetty.ee10.demos + jetty-ee10-demos + 12.0.0-SNAPSHOT + ../../pom.xml + + EE10 :: Jetty Demo :: Servlet Spec :: Webapp + demo-spec-webapp + war + + ${project.groupId}.spec.webapp + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + web-bundle-assembly + package + + single + + + + src/main/assembly/web-bundle.xml + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + + + + maven-war-plugin + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + target + + plugin-context.xml + + META-INF + + + + + + + org.apache.felix + maven-bundle-plugin + true + + + war + + + Test Webapp for Servlet 5.0 Features + + + jakarta.transaction*;version="2.0.0", jakarta.servlet*;version="[5,6)", 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 + / + .,WEB-INF/classes,WEB-INF/lib + /META-INF/plugin-context.xml + + + + + + maven-antrun-plugin + + + generate-xml-files + process-resources + + + + + + + + + + + + run + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + package + + copy + + + + + org.eclipse.jetty.ee10.demos + 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 + + + + + + + + + + + + org.eclipse.jetty + jetty-maven-plugin + ${project.version} + + 10 + ${project.build.directory}/plugin-context.xml + + src/main/webapp + src/main/webapp/WEB-INF/web.xml + /test-spec + .*/jetty-jakarta-servlet-api-[^/]*\.jar$ + true + ${basedir}/src/main/webapp/WEB-INF/jetty-env.xml + + + + Test Realm + src/etc/realm.properties + + + + + + org.eclipse.jetty.ee10.demos + demo-mock-resources + ${project.version} + + + + + + + + + + jakarta.transaction + jakarta.transaction-api + provided + + + org.eclipse.jetty.toolchain + jetty-jakarta-servlet-api + provided + + + jakarta.annotation + jakarta.annotation-api + provided + + + org.eclipse.jetty.ee10.demos + demo-web-fragment + + + org.eclipse.jetty.ee10.demos + demo-container-initializer + + + + + + diff --git a/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/etc/realm.properties b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/etc/realm.properties new file mode 100644 index 00000000000..9d88b852b7f --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-spec/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-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/assembly/web-bundle.xml b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/assembly/web-bundle.xml new file mode 100644 index 00000000000..2f3701a1995 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/assembly/web-bundle.xml @@ -0,0 +1,18 @@ + + webbundle + + jar + + false + + + ${basedir}/${project.build.directory}/${project.build.finalName}/ + + + **/*.* + + + + diff --git a/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/config/modules/demo-spec.mod b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/config/modules/demo-spec.mod new file mode 100644 index 00000000000..7ffb9d23f8d --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/config/modules/demo-spec.mod @@ -0,0 +1,21 @@ +# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html + +[description] +Download and deploy the Test Spec webapp demo. + +[tags] +demo +webapp + +[depends] +deploy +jdbc +jsp +annotations +ext +demo-realm +demo-mock-resources + +[files] +basehome:modules/demo.d/demo-spec.xml|webapps/demo-spec.xml +maven://org.eclipse.jetty.demos/demo-spec-webapp/${jetty.version}/war|webapps/demo-spec.war diff --git a/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/config/modules/demo.d/demo-spec.xml b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/config/modules/demo.d/demo-spec.xml new file mode 100644 index 00000000000..9854cf11e9f --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/config/modules/demo.d/demo-spec.xml @@ -0,0 +1,33 @@ + + + + + /test-spec + /demo-spec.war + + true + + + + + + + + + + + + maxAmount + 100 + true + + + + + jdbc/mydatasource + + + + + + diff --git a/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/java/org/example/test/AnnotatedListener.java b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/java/org/example/test/AnnotatedListener.java new file mode 100644 index 00000000000..c3fca372c06 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/java/org/example/test/AnnotatedListener.java @@ -0,0 +1,175 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/java/org/example/test/AnnotationTest.java b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/java/org/example/test/AnnotationTest.java new file mode 100644 index 00000000000..f647dc00a7d --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/java/org/example/test/AnnotationTest.java @@ -0,0 +1,349 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/java/org/example/test/AsyncListenerServlet.java b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/java/org/example/test/AsyncListenerServlet.java new file mode 100644 index 00000000000..cada5d10177 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/java/org/example/test/AsyncListenerServlet.java @@ -0,0 +1,114 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/java/org/example/test/Bar.java b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/java/org/example/test/Bar.java new file mode 100644 index 00000000000..8a38267c7b9 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/java/org/example/test/Bar.java @@ -0,0 +1,22 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/java/org/example/test/ClassLoaderServlet.java b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/java/org/example/test/ClassLoaderServlet.java new file mode 100644 index 00000000000..cd3f9cf371e --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/java/org/example/test/ClassLoaderServlet.java @@ -0,0 +1,125 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/java/org/example/test/MultiPartTest.java b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/java/org/example/test/MultiPartTest.java new file mode 100644 index 00000000000..d027a876483 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/java/org/example/test/MultiPartTest.java @@ -0,0 +1,165 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/java/org/example/test/RoleAnnotationTest.java b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/java/org/example/test/RoleAnnotationTest.java new file mode 100644 index 00000000000..baa19a48e0f --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/java/org/example/test/RoleAnnotationTest.java @@ -0,0 +1,84 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/java/org/example/test/SecuredServlet.java b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/java/org/example/test/SecuredServlet.java new file mode 100644 index 00000000000..6bddc92c221 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/java/org/example/test/SecuredServlet.java @@ -0,0 +1,53 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/java/org/example/test/TestListener.java b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/java/org/example/test/TestListener.java new file mode 100644 index 00000000000..15bd486294a --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/java/org/example/test/TestListener.java @@ -0,0 +1,221 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/templates/annotations-context-header.xml b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/templates/annotations-context-header.xml new file mode 100644 index 00000000000..e758dc04985 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/templates/annotations-context-header.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + /test-spec.war + true + + + + + Test Realm + /etc/realm.properties + + + + diff --git a/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/templates/env-definitions.xml b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/templates/env-definitions.xml new file mode 100644 index 00000000000..3bc12268a27 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/templates/env-definitions.xml @@ -0,0 +1,19 @@ + + + + maxAmount + 100 + true + + + + + + jdbc/mydatasource + + + + + + + diff --git a/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/templates/plugin-context-header.xml b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/templates/plugin-context-header.xml new file mode 100644 index 00000000000..05f966c0a5b --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/templates/plugin-context-header.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/webapp/WEB-INF/jetty-env.xml b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/webapp/WEB-INF/jetty-env.xml new file mode 100644 index 00000000000..46067757a90 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/webapp/WEB-INF/jetty-env.xml @@ -0,0 +1,17 @@ + + + + + + + + + + maxAmount + 55.0 + true + + + + + diff --git a/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/webapp/WEB-INF/jetty-web.xml b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/webapp/WEB-INF/jetty-web.xml new file mode 100644 index 00000000000..364dfaf1197 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-spec/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-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/webapp/WEB-INF/web.xml b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000000..ad443fee34b --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,106 @@ + + + + Test Annotations 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-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/webapp/authfail.html b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/webapp/authfail.html new file mode 100644 index 00000000000..914a42fa284 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-spec/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-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/webapp/demo.css b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/webapp/demo.css new file mode 100644 index 00000000000..f2b91d3365d --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-spec/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-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/webapp/dynamic.jsp b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/webapp/dynamic.jsp new file mode 100644 index 00000000000..cea8a15c635 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-spec/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-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/webapp/index.html b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/webapp/index.html new file mode 100644 index 00000000000..2d803c54621 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-spec/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 5.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-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/webapp/login.html b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/webapp/login.html new file mode 100644 index 00000000000..8e838e560e2 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-spec/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-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/webapp/logout.jsp b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/webapp/logout.jsp new file mode 100644 index 00000000000..efec5569f4a --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-spec/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-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/webapp/small_powered_by.gif b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-spec-webapp/src/main/webapp/small_powered_by.gif new file mode 100644 index 0000000000000000000000000000000000000000..c5dd44319f0aa17ea93b15fbcc8a18e14ca397d5 GIT binary patch literal 4787 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 + + + + + + + + + maxAmount + 55.0 + true + + + + + + + + + + + + + + + + + + + + + + + jdbc/mydatasource + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-demos/demo-spec/demo-web-fragment/pom.xml b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-web-fragment/pom.xml new file mode 100644 index 00000000000..a070c5e3309 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-web-fragment/pom.xml @@ -0,0 +1,24 @@ + + 4.0.0 + + org.eclipse.jetty.ee10.demos + jetty-ee10-demos + 12.0.0-SNAPSHOT + ../../pom.xml + + + EE10 :: Jetty Demo :: Servlet Spec :: Fragment Jar + demo-web-fragment + jar + + + ${project.groupId}.spec.fragment + + + + + org.eclipse.jetty.toolchain + jetty-jakarta-servlet-api + + + diff --git a/jetty-ee10/jetty-ee10-demos/demo-spec/demo-web-fragment/src/main/java/org/example/fragment/FragmentServlet.java b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-web-fragment/src/main/java/org/example/fragment/FragmentServlet.java new file mode 100644 index 00000000000..b1c10159c79 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-web-fragment/src/main/java/org/example/fragment/FragmentServlet.java @@ -0,0 +1,67 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-demos/demo-spec/demo-web-fragment/src/main/resources/META-INF/resources/fragmentA/index.html b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-web-fragment/src/main/resources/META-INF/resources/fragmentA/index.html new file mode 100644 index 00000000000..02303e909ae --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-spec/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-ee10/jetty-ee10-demos/demo-spec/demo-web-fragment/src/main/resources/META-INF/web-fragment.xml b/jetty-ee10/jetty-ee10-demos/demo-spec/demo-web-fragment/src/main/resources/META-INF/web-fragment.xml new file mode 100644 index 00000000000..24c67ec7510 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-spec/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-ee10/jetty-ee10-demos/demo-spec/pom.xml b/jetty-ee10/jetty-ee10-demos/demo-spec/pom.xml new file mode 100644 index 00000000000..d390fee6eb1 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-spec/pom.xml @@ -0,0 +1,18 @@ + + + 4.0.0 + + org.eclipse.jetty.ee10.demos + jetty-ee10-demos + 12.0.0-SNAPSHOT + + EE10 :: Jetty Demo :: Servlet Spec + demo-spec + pom + + + demo-spec-webapp + demo-container-initializer + demo-web-fragment + + diff --git a/jetty-ee10/jetty-ee10-demos/demo-template/pom.xml b/jetty-ee10/jetty-ee10-demos/demo-template/pom.xml new file mode 100644 index 00000000000..d6e2335caab --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/demo-template/pom.xml @@ -0,0 +1,28 @@ + + + + org.eclipse.jetty.ee10.demos + jetty-ee10-demos + 12.0.0-SNAPSHOT + + + 4.0.0 + demo-template + EE10 :: Jetty Demo :: Template + jar + + + + + org.apache.maven.plugins + maven-jar-plugin + + + index.html + small_powered_by.gif + + + + + + diff --git a/jetty-ee10/jetty-ee10-demos/demo-template/src/main/resources/demo.css b/jetty-ee10/jetty-ee10-demos/demo-template/src/main/resources/demo.css new file mode 100644 index 00000000000..f2b91d3365d --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-template/src/main/resources/index.html b/jetty-ee10/jetty-ee10-demos/demo-template/src/main/resources/index.html new file mode 100644 index 00000000000..cf08af07220 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/demo-template/src/main/resources/small_powered_by.gif b/jetty-ee10/jetty-ee10-demos/demo-template/src/main/resources/small_powered_by.gif new file mode 100644 index 0000000000000000000000000000000000000000..c5dd44319f0aa17ea93b15fbcc8a18e14ca397d5 GIT binary patch literal 4787 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 + + org.eclipse.jetty.ee10.demos + jetty-ee10-demos + 12.0.0-SNAPSHOT + + 4.0.0 + demos-jetty-embedded + EE10 :: Jetty Demo :: Embedded Jetty + Embedded Jetty Demos + + ${project.groupId}.embedded + + + + org.slf4j + slf4j-api + + + org.eclipse.jetty + jetty-slf4j-impl + runtime + + + org.eclipse.jetty + jetty-util-ajax + + + org.eclipse.jetty.ee10 + jetty-ee10-webapp + + + org.eclipse.jetty.ee10 + jetty-ee10-servlets + + + org.eclipse.jetty + jetty-deploy + + + org.eclipse.jetty + jetty-rewrite + + + org.eclipse.jetty + jetty-jmx + + + org.eclipse.jetty.ee10 + jetty-ee10-websocket-jakarta-server + + + org.eclipse.jetty.ee10 + jetty-ee10-websocket-servlet + + + org.eclipse.jetty.ee10 + jetty-ee10-websocket-jetty-server + + + org.eclipse.jetty.http2 + http2-server + + + org.eclipse.jetty + jetty-alpn-server + ${project.version} + + + org.eclipse.jetty + jetty-alpn-java-server + + + org.eclipse.jetty + jetty-alpn-conscrypt-server + + + org.eclipse.jetty.ee10 + jetty-ee10-annotations + + + org.eclipse.jetty.ee10.demos + demo-mock-resources + + + org.eclipse.jetty.ee10 + jetty-ee10-proxy + + + org.eclipse.jetty.ee10 + jetty-ee10-jaas + + + org.eclipse.jetty.ee10 + jetty-ee10-plus + + + org.eclipse.jetty.ee10 + jetty-ee10-apache-jsp + + + org.eclipse.jetty.ee10 + jetty-ee10-glassfish-jstl + + + jakarta.transaction + jakarta.transaction-api + compile + + + org.eclipse.jetty.toolchain + jetty-test-helper + test + + + org.eclipse.jetty.ee10 + jetty-ee10-websocket-jetty-client + test + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + ${settings.localRepository} + + false + + + + + diff --git a/jetty-ee10/jetty-ee10-demos/embedded/prodDb.properties b/jetty-ee10/jetty-ee10-demos/embedded/prodDb.properties new file mode 100644 index 00000000000..5130d856783 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/embedded/prodDb.script b/jetty-ee10/jetty-ee10-demos/embedded/prodDb.script new file mode 100644 index 00000000000..382d243636c --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/AsyncEchoServlet.java b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/AsyncEchoServlet.java new file mode 100644 index 00000000000..79b94bb2a04 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/AsyncEchoServlet.java @@ -0,0 +1,116 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/DumpServlet.java b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/DumpServlet.java new file mode 100644 index 00000000000..5c547494ec6 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/DumpServlet.java @@ -0,0 +1,70 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/ExampleServer.java b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/ExampleServer.java new file mode 100644 index 00000000000..7e65774c1ac --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/ExampleServer.java @@ -0,0 +1,50 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.Handler; +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(); + + 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(new Handler.Collection(context, new 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-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/ExampleServerXml.java b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/ExampleServerXml.java new file mode 100644 index 00000000000..7b84fa8cb47 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/ExampleServerXml.java @@ -0,0 +1,43 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.resource.Resource; +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 + Resource serverXml = Resource.newSystemResource("exampleserver.xml"); + XmlConfiguration xml = new XmlConfiguration(serverXml); + xml.getProperties().put("http.port", Integer.toString(port)); + Server server = (Server)xml.configure(); + 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-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/ExampleUtil.java b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/ExampleUtil.java new file mode 100644 index 00000000000..05d47b5e79e --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/ExampleUtil.java @@ -0,0 +1,81 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/FastFileServer.java b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/FastFileServer.java new file mode 100644 index 00000000000..9ddf85b9580 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/FastFileServer.java @@ -0,0 +1,216 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.channels.FileChannel.MapMode; +import java.nio.file.StandardOpenOption; + +import jakarta.servlet.AsyncContext; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.eclipse.jetty.ee10.servlet.DefaultServlet; +import org.eclipse.jetty.ee10.servlet.HttpOutput; +import org.eclipse.jetty.http.MimeTypes; +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.Request; +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.Callback; +import org.eclipse.jetty.util.URIUtil; +import org.eclipse.jetty.util.resource.Resource; + +/** + * 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-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/FileServer.java b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/FileServer.java new file mode 100644 index 00000000000..1af34e679a0 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/FileServer.java @@ -0,0 +1,69 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; + +import org.eclipse.jetty.server.Handler; +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.PathResource; +import org.eclipse.jetty.util.resource.Resource; + +/** + * 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); + + // 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(Arrays.asList(new String[]{"index.html"})); + resourceHandler.setBaseResource(baseResource.getPath()); + + // Add the ResourceHandler to the server. + server.setHandler(new Handler.Collection(resourceHandler, new DefaultHandler())); + + 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")); + PathResource pathResource = new PathResource(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-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/FileServerXml.java b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/FileServerXml.java new file mode 100644 index 00000000000..8eb0a93ff31 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/FileServerXml.java @@ -0,0 +1,51 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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.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 baseResource) throws Exception + { + // Find Jetty XML (in classpath) that configures and starts Server. + // See src/main/resources/fileserver.xml + Resource fileServerXml = Resource.newSystemResource("fileserver.xml"); + XmlConfiguration configuration = new XmlConfiguration(fileServerXml); + configuration.getProperties().put("http.port", Integer.toString(port)); + configuration.getProperties().put("fileserver.baseresource", baseResource.toAbsolutePath().toString()); + return (Server)configuration.configure(); + } + + 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-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/HelloHandler.java b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/HelloHandler.java new file mode 100644 index 00000000000..39fda8295cc --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/HelloHandler.java @@ -0,0 +1,51 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import jakarta.servlet.http.HttpServletResponse; +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.Processor +{ + 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 void process(Request request, Response response, Callback callback) throws Exception + { + response.setContentType("text/html; charset=utf-8"); + response.setStatus(HttpServletResponse.SC_OK); + + response.write(true, callback, "

    " + greeting + "

    \n" + body); + } +} diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/HelloServlet.java b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/HelloServlet.java new file mode 100644 index 00000000000..2ca7ae0d8e5 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/HelloServlet.java @@ -0,0 +1,48 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/HelloSessionServlet.java b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/HelloSessionServlet.java new file mode 100644 index 00000000000..482af7f768d --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/HelloSessionServlet.java @@ -0,0 +1,79 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/HelloWorld.java b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/HelloWorld.java new file mode 100644 index 00000000000..1dce3c5a78b --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/HelloWorld.java @@ -0,0 +1,48 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import jakarta.servlet.http.HttpServletResponse; +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.Processor +{ + @Override + public void process(Request request, Response response, Callback callback) throws Exception + { + + // Declare response encoding and types + response.setContentType("text/html; charset=utf-8"); + + // Declare response status code + response.setStatus(HttpServletResponse.SC_OK); + + // Write back response + response.write(true, callback, "

    Hello World

    \n"); + } + + 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-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/Http2Server.java b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/Http2Server.java new file mode 100644 index 00000000000..0c463e7a74a --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/Http2Server.java @@ -0,0 +1,194 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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.ee10.servlet.DefaultServlet; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlets.PushCacheFilter; +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(server, "/", ServletContextHandler.SESSIONS); + Path docroot = Paths.get("src/main/resources/docroot"); + if (!Files.exists(docroot)) + throw new FileNotFoundException(docroot.toString()); + + context.setResourceBase(docroot); + context.addFilter(PushCacheFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST)); + // context.addFilter(PushSessionCacheFilter.class,"/*",EnumSet.of(DispatcherType.REQUEST)); + 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-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/JarServer.java b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/JarServer.java new file mode 100644 index 00000000000..bc3bb131625 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/JarServer.java @@ -0,0 +1,60 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import java.io.FileNotFoundException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.eclipse.jetty.ee10.servlet.DefaultServlet; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.DefaultHandler; +import org.eclipse.jetty.util.resource.Resource; + +/** + * 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) throws Exception + { + Server server = new Server(port); + + Path jarFile = Paths.get("src/main/other/content.jar"); + if (!Files.exists(jarFile)) + throw new FileNotFoundException(jarFile.toString()); + + ServletContextHandler context = new ServletContextHandler(); + Resource.setDefaultUseCaches(true); + Resource base = Resource.newResource("jar:" + jarFile.toAbsolutePath().toUri().toASCIIString() + "!/"); + context.setResourceBase(base.getPath()); + context.addServlet(new ServletHolder(new DefaultServlet()), "/"); + + server.setHandler(new Handler.Collection(context, new 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-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/JettyDemos.java b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/JettyDemos.java new file mode 100644 index 00000000000..69d83c4cd58 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/JettyDemos.java @@ -0,0 +1,159 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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 final String VERSION; + + 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("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"; + Path pomFile = demosDir.resolve("pom.xml"); + try (Stream lineStream = Files.lines(pomFile)) + { + String versionLine = lineStream + .filter((line) -> line.contains("")) + .findFirst() + .orElseThrow(() -> + { + throw 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) throws FileNotFoundException + { + String expandedPath = path.replaceAll("@VER@", VERSION); + Path result = get().resolve(expandedPath); + if (!Files.exists(result)) + { + throw new FileNotFoundException(result.toString()); + } + return result; + } + + public static void main(String... arg) + { + System.err.println("Jetty Demos Dir is " + JETTY_DEMOS_DIR); + } +} diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/LikeJettyXml.java b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/LikeJettyXml.java new file mode 100644 index 00000000000..38ea23b41fa --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/LikeJettyXml.java @@ -0,0 +1,188 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +/** + * Starts the Jetty Distribution's demo-base directory using entirely + * embedded jetty techniques. + */ +public class LikeJettyXml +{ + // TODO +// public static Server createServer(int port, int securePort, boolean addDebugListener) throws Exception +// { +// Path configDir = Paths.get("src/main/resources/demo").toAbsolutePath(); +// Path runtimeDir = Paths.get("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); +// +// // Scheduler +// server.addBean(new ScheduledExecutorScheduler(null, false)); +// +// // 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(new HandlerList(contexts, new DefaultHandler())); +// +// // === jetty-jmx.xml === +// MBeanContainer mbContainer = new MBeanContainer( +// ManagementFactory.getPlatformMBeanServer()); +// server.addBean(mbContainer); +// +// // === jetty-http.xml === +// ServerConnector http = new ServerConnector(server, +// new HttpConnectionFactory(httpConfig)); +// http.setPort(port); +// http.setIdleTimeout(30000); +// server.addConnector(http); +// +// // === jetty-https.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"); +// +// // SSL HTTP Configuration +// HttpConfiguration httpsConfig = new HttpConfiguration(httpConfig); +// httpsConfig.addCustomizer(new SecureRequestCustomizer()); +// +// // 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.addLifeCycleBinding(new DebugListenerBinding(debug)); +// } +// deployer.setContexts(contexts); +// deployer.setContextAttribute( +// "org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern", +// ".*/jetty-jakarta-servlet-api-[^/]*\\.jar$|.*/jakarta.servlet.jsp.jstl-.*\\.jar$|.*/[^/]*taglibs.*\\.jar$"); +// +// Path webappsDir = runtimeDir.resolve("webapps"); +// mkdir(webappsDir); +// +// Path testWebapp = webappsDir.resolve("test.war"); +// if (!Files.exists(testWebapp)) +// { +// Path testWebappSrc = JettyDemos.find("demo-simple-webapp/target/demo-simple-webapp-@VER@.war"); +// Files.copy(testWebappSrc, testWebapp); +// } +// +// WebAppProvider webAppProvider = new WebAppProvider(); +// webAppProvider.setMonitoredDirName(webappsDir.toString()); +// webAppProvider.setDefaultsDescriptor(configDir.resolve("webdefault.xml").toString()); +// webAppProvider.setScanInterval(1); +// webAppProvider.setExtractWars(true); +// webAppProvider.setConfigurationManager(new PropertiesConfigurationManager()); +// +// 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); +// +// // === test-realm.xml === +// HashLoginService login = new HashLoginService(); +// login.setName("Test Realm"); +// login.setConfig(configDir.resolve("demo-realm.properties").toString()); +// login.setHotReload(false); +// 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-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/ManyConnectors.java b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/ManyConnectors.java new file mode 100644 index 00000000000..f821523f003 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/ManyConnectors.java @@ -0,0 +1,134 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/ManyContexts.java b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/ManyContexts.java new file mode 100644 index 00000000000..7e7395744b8 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/ManyContexts.java @@ -0,0 +1,58 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/ManyHandlers.java b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/ManyHandlers.java new file mode 100644 index 00000000000..9ac4e323329 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/ManyHandlers.java @@ -0,0 +1,165 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Map; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.eclipse.jetty.server.CustomRequestLog; +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.Request; +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.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 HandlerWrapper} 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 HandlerList} 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 HandlerCollection} 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 +{ + //TODO fix me + /** + * Produce output that lists all of the request parameters + */ + /* public static class ParamHandler extends AbstractHandler + { + @Override + public void handle(String target, + Request baseRequest, + HttpServletRequest request, + HttpServletResponse response) throws IOException, + ServletException + { + Map params = request.getParameterMap(); + if (!params.isEmpty()) + { + response.setContentType("text/plain"); + response.getWriter().println(new JSON().toJSON(params)); + baseRequest.setHandled(true); + } + } + } + + *//** + * Add a request attribute, but produce no output. + *//* + public static class WelcomeWrapHandler extends HandlerWrapper + { + @Override + public void handle(String target, + Request baseRequest, + HttpServletRequest request, + HttpServletResponse response) throws IOException, + ServletException + { + response.setHeader("X-Welcome", "Greetings from WelcomeWrapHandler"); + super.handle(target, baseRequest, request, response); + } + } + + public static Server createServer(int port) throws IOException + { + Server server = new Server(port); + + // create the handlers + Handler param = new ParamHandler(); + Handler.Wrapper 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); + + // create the handlers list + Handler.Collection handlers = new Handler.Collection(); + + // 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 + handlers.addHandler(gzipHandler); + handlers.addHandler(new DefaultHandler()); + server.setHandler(handlers); + + At this point you have the following handler hierarchy. + * + * Server.handler: + * HandlerList + * \- 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-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/ManyServletContexts.java b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/ManyServletContexts.java new file mode 100644 index 00000000000..98033381cd0 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/ManyServletContexts.java @@ -0,0 +1,66 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import java.lang.management.ManagementFactory; + +import org.eclipse.jetty.ee10.servlet.DefaultServlet; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.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(contexts, "/", + ServletContextHandler.SESSIONS); + // 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(contexts, + "/other", ServletContextHandler.SESSIONS); + // 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-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/MinimalServlets.java b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/MinimalServlets.java new file mode 100644 index 00000000000..394afbcc6b8 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/MinimalServlets.java @@ -0,0 +1,76 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import java.io.IOException; + +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.eclipse.jetty.ee10.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-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/OneConnector.java b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/OneConnector.java new file mode 100644 index 00000000000..2f0c108c868 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/OneConnector.java @@ -0,0 +1,52 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/OneContext.java b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/OneContext.java new file mode 100644 index 00000000000..68bcb0dc7be --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/OneContext.java @@ -0,0 +1,45 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/OneHandler.java b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/OneHandler.java new file mode 100644 index 00000000000..0e3864d120e --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/OneHandler.java @@ -0,0 +1,34 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/OneServletContext.java b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/OneServletContext.java new file mode 100644 index 00000000000..e4e18540c64 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/OneServletContext.java @@ -0,0 +1,140 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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.ee10.servlet.DefaultServlet; +import org.eclipse.jetty.ee10.servlet.ListenerHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.resource.PathResource; +import org.eclipse.jetty.util.resource.Resource; + +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.setResourceBase(baseResource.getPath()); + 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, new PathResource(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-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/OneServletContextJmxStats.java b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/OneServletContextJmxStats.java new file mode 100644 index 00000000000..cfdbb07f202 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/OneServletContextJmxStats.java @@ -0,0 +1,55 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import java.lang.management.ManagementFactory; + +import org.eclipse.jetty.ee10.servlet.DefaultServlet; +import org.eclipse.jetty.ee10.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-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/OneServletContextWithSession.java b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/OneServletContextWithSession.java new file mode 100644 index 00000000000..eba03b117ba --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/OneServletContextWithSession.java @@ -0,0 +1,72 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.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.PathResource; +import org.eclipse.jetty.util.resource.Resource; + +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.setResourceBase(baseResource.getPath()); + 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")); + PathResource baseResource = new PathResource(dir); + Server server = createServer(port, baseResource); + + server.start(); + server.dumpStdErr(); + server.join(); + } +} diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/OneWebApp.java b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/OneWebApp.java new file mode 100644 index 00000000000..89a7ff959e2 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/OneWebApp.java @@ -0,0 +1,67 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import java.io.IOException; +import java.nio.file.Path; + +import org.eclipse.jetty.ee10.webapp.Configurations; +import org.eclipse.jetty.ee10.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("/"); + Path warFile = JettyDemos.find("demo-async-rest/demo-async-rest-webapp/target/demo-async-rest-webapp-@VER@.war"); + 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-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/OneWebAppWithJsp.java b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/OneWebAppWithJsp.java new file mode 100644 index 00000000000..3c80a4ea2b6 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/OneWebAppWithJsp.java @@ -0,0 +1,106 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import java.io.FileNotFoundException; +import java.net.URL; +import java.nio.file.Path; + +import org.eclipse.jetty.ee10.annotations.AnnotationConfiguration; +import org.eclipse.jetty.ee10.servlet.security.HashLoginService; +import org.eclipse.jetty.ee10.webapp.WebAppContext; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.resource.PathResource; + +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("/"); + Path warFile = JettyDemos.find("demo-jsp-webapp/target/demo-jsp-webapp-@VER@.war"); + webapp.setWarResource(new PathResource(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"; + ClassLoader classLoader = OneWebAppWithJsp.class.getClassLoader(); + URL realmProps = classLoader.getResource(realmResourceName); + if (realmProps == null) + throw new FileNotFoundException("Unable to find " + realmResourceName); + + HashLoginService loginService = new HashLoginService(); + loginService.setName("Test Realm"); + loginService.setConfig(realmProps.toExternalForm()); + 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-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/ProxyServer.java b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/ProxyServer.java new file mode 100644 index 00000000000..b5aabf6c342 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/ProxyServer.java @@ -0,0 +1,56 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import org.eclipse.jetty.ee10.proxy.ConnectHandler; +import org.eclipse.jetty.ee10.proxy.ProxyServlet; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; + +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(proxy, "/", + ServletContextHandler.SESSIONS); + 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-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/RewriteServer.java b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/RewriteServer.java new file mode 100644 index 00000000000..390037f33d8 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/RewriteServer.java @@ -0,0 +1,57 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import java.util.Arrays; + +import org.eclipse.jetty.ee10.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-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/SecuredHelloHandler.java b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/SecuredHelloHandler.java new file mode 100644 index 00000000000..f50112d625e --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/SecuredHelloHandler.java @@ -0,0 +1,114 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import java.io.FileNotFoundException; +import java.net.URL; +import java.util.Collections; + +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.security.ConstraintMapping; +import org.eclipse.jetty.ee10.servlet.security.ConstraintSecurityHandler; +import org.eclipse.jetty.ee10.servlet.security.HashLoginService; +import org.eclipse.jetty.ee10.servlet.security.LoginService; +import org.eclipse.jetty.ee10.servlet.security.authentication.BasicAuthenticator; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.security.Constraint; + +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"; + ClassLoader classLoader = SecuredHelloHandler.class.getClassLoader(); + URL realmProps = classLoader.getResource(realmResourceName); + if (realmProps == null) + throw new FileNotFoundException("Unable to find " + realmResourceName); + + LoginService loginService = new HashLoginService("MyRealm", + realmProps.toExternalForm()); + 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(); + constraint.setName("auth"); + constraint.setAuthenticate(true); + constraint.setRoles(new String[]{"user", "admin"}); + + // 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-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/ServerWithAnnotations.java b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/ServerWithAnnotations.java new file mode 100644 index 00000000000..91b99ae8887 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/ServerWithAnnotations.java @@ -0,0 +1,93 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import java.io.FileNotFoundException; +import java.net.URL; +import java.nio.file.Path; +import javax.naming.NamingException; + +import org.eclipse.jetty.ee10.annotations.AnnotationConfiguration; +import org.eclipse.jetty.ee10.plus.jndi.EnvEntry; +import org.eclipse.jetty.ee10.plus.jndi.NamingDump; +import org.eclipse.jetty.ee10.plus.jndi.Resource; +import org.eclipse.jetty.ee10.plus.jndi.Transaction; +import org.eclipse.jetty.ee10.plus.webapp.EnvConfiguration; +import org.eclipse.jetty.ee10.plus.webapp.PlusConfiguration; +import org.eclipse.jetty.ee10.servlet.security.HashLoginService; +import org.eclipse.jetty.ee10.webapp.WebAppContext; +import org.eclipse.jetty.server.Server; + +/** + * 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("/"); + Path warFile = JettyDemos.find("demo-spec/demo-spec-webapp/target/demo-spec-webapp-@VER@.war"); + 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(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"; + ClassLoader classLoader = ServerWithAnnotations.class.getClassLoader(); + URL realmProps = classLoader.getResource(realmResourceName); + if (realmProps == null) + throw new FileNotFoundException("Unable to find " + realmResourceName); + + HashLoginService loginService = new HashLoginService(); + loginService.setName("Test Realm"); + loginService.setConfig(realmProps.toExternalForm()); + 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-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/ServerWithJMX.java b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/ServerWithJMX.java new file mode 100644 index 00000000000..8639c8916b5 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/ServerWithJMX.java @@ -0,0 +1,58 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/ServerWithJNDI.java b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/ServerWithJNDI.java new file mode 100644 index 00000000000..6569af3c798 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/ServerWithJNDI.java @@ -0,0 +1,96 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import java.io.FileNotFoundException; +import java.nio.file.Path; +import javax.naming.NamingException; + +import org.eclipse.jetty.ee10.plus.webapp.EnvConfiguration; +import org.eclipse.jetty.ee10.plus.webapp.PlusConfiguration; +import org.eclipse.jetty.ee10.webapp.WebAppContext; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.resource.PathResource; + +/** + * 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("/"); + Path testJndiWar = JettyDemos.find("demo-jndi-webapp/target/demo-jndi-webapp-@VER@.war"); + webapp.setWarResource(new PathResource(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.ee10.plus.jndi.Transaction( + new org.example.MockUserTransaction()); + + // Define an env entry with Server 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.ee10.plus.jndi.EnvEntry(server, "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.ee10.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.ee10.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-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/SimplestServer.java b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/SimplestServer.java new file mode 100644 index 00000000000..2c12f7dd547 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/SimplestServer.java @@ -0,0 +1,40 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/SplitFileServer.java b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/SplitFileServer.java new file mode 100644 index 00000000000..46658af57bf --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/SplitFileServer.java @@ -0,0 +1,97 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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.PathResource; +import org.eclipse.jetty.util.resource.Resource; + +/** + * 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.setResourceBase(baseResource0.getPath()); + 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.setResourceBase(baseResource1.getPath()); + 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 = new PathResource(Paths.get("src/test/resources/dir0")); + Resource resource1 = new PathResource(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-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/WebSocketServer.java b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/WebSocketServer.java new file mode 100644 index 00000000000..70eabbb2aa8 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/main/java/org/eclipse/jetty/ee10/demos/WebSocketServer.java @@ -0,0 +1,84 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.websocket.api.Session; +import org.eclipse.jetty.ee10.websocket.api.WriteCallback; +import org.eclipse.jetty.ee10.websocket.api.annotations.OnWebSocketMessage; +import org.eclipse.jetty.ee10.websocket.api.annotations.WebSocket; +import org.eclipse.jetty.ee10.websocket.server.JettyWebSocketServlet; +import org.eclipse.jetty.ee10.websocket.server.JettyWebSocketServletFactory; +import org.eclipse.jetty.ee10.websocket.server.config.JettyWebSocketServletContainerInitializer; +import org.eclipse.jetty.server.Server; + +/** + * 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.getRemote().sendString(message, WriteCallback.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-ee10/jetty-ee10-demos/embedded/src/main/other/content.jar b/jetty-ee10/jetty-ee10-demos/embedded/src/main/other/content.jar new file mode 100644 index 0000000000000000000000000000000000000000..846f71fa3c4d79821cf1bfd5e67012e83d63a154 GIT binary patch literal 767 zcmWIWW@Zs#;Nak3(26#9V?Y9&3@i-3t|5-Po_=on|4uP5Ff#;rvvYt{FhP|C;M6Pv zQ~}rQ>*(j{<{BKL=j-;__snS@Z(Y5MyxzK6=gyqp9At3C_`%a6JuhD!Pv48Bt5`TA zUPvC1mek1jA+D-PhCAjkg4n(C2zL=Wd|w%nM2SP zL&CNg>Vs@CgxkUbw&l^COuUb*aDew ztw?bLGJ%T$cTgfsI0R%u0~TQ^C}@#G3>35o@D#`dYk>ze@z!ZDg7tzz7}YxDphLCJ Z8>R&w;sM^QY#`^b0bw*ywhxFI7yv(: [, ...] +# +# 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-ee10/jetty-ee10-demos/embedded/src/main/resources/demo/webdefault.xml b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/demo/webdefault.xml new file mode 100644 index 00000000000..8449542450d --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/demo/webdefault.xml @@ -0,0 +1,443 @@ + + + + + + + + + + + + + + + + + + + + + + + Default web.xml file. + This file is applied to a Web application before its own WEB_INF/web.xml file + + + + + + + + org.eclipse.jetty.ee10.servlet.listener.ELContextCleaner + + + + + + + + org.eclipse.jetty.ee10.servlet.listener.IntrospectorCleaner + + + + + + + + + + + + + + + + + default + org.eclipse.jetty.ee10.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-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/push.html b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/push.html new file mode 100644 index 00000000000..f6807e7dd47 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/push.html @@ -0,0 +1,100 @@ + + + + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile00.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile00.jpg new file mode 100644 index 0000000000000000000000000000000000000000..dc303075c63c896c921b324ceeba518f86e31d47 GIT binary patch literal 15228 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 Gr~d;x6fa%? literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile01.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile01.jpg new file mode 100644 index 0000000000000000000000000000000000000000..66c2e252532c90574e7ee9cea498f4307d2f9860 GIT binary patch literal 15168 zcmeHOcU%*}v%e&S&^ri9iHd|y5?VrUq4%Z~l@J0(AV5M_L{L!?X$lG=C`z+nrHBm# zMJ%8QsMtUh#0n}{PVOGHfsVr*9>g|XhhC51_9S2myctS zH{c_4r^{Le<7fE6fX3ubmxTs;d1q)5K=aMunE;cPi-CB6V`Bjg9hk9!fX5>aOv}PP zM;@5=73@gVEX@Z&=ovZ__)n;G+J+5)7M`Kg04-AbtGCA?MMkc{KMt8pgrp zXM;p=h9=_iBm)A$fJnv?v<IOi$w1Li1Nqsphy3>M5ES6&7myJX z7nYD)s30%5P*xVJsENZaQC})6tE{i04o0MojslKsOd=X-YU>bTB?w+#UIBgqX+c40 z;$qpw#Q*v?)d-35K!H#T3b6!2iXu>=h^ZDx0oaKKQ4J1kGe0mqxzOAg9$r3v0H_gx zkO&kC$%R6rxquT8N$~LH5=DzGCYW=JyHPPqq9lkZIR|-`TAXTixF zAT6_SnG#M}MO8;vPoG3Ku(YCB+t}LKdw6FQjZrZ#h zZR@t}>AAah=jHF&TTobf=x|wiMP=2I(`U}s)YjFXYq-+fa`js4^|l+G_qw`!?mu|g z+y8vv#mm8=SHrJIKYsc=HvZ-7x9@Ph5TL1Nf72EFqh6w*UPvx36c+}r7Xldv4wNVt zdNF}p%-jt_jS^o%OyQBR$T@hbiFc{C`-o&9>n@*^qE7#^QMhWHn*C=LOZ{KfY_4Lz z>eUMgpg{i?MTtVDP;IzNnNDws{jf>XJEsXuO_FJiO}qc#&MLX8onbC(hn-8_IT|F_ z=z3uYxAu7qJ5CU)^Uh8|Hf5Q1GEp;qkS3RxafB zqsbV+#adm_? z?)rF{NKk;5NzJkRN0~g(PsZ(#O_{=-2X7l|di5?}b2XG@=wX!Js~6H)7%r6PRpl4O zeti9-pjM7=IOGj|89OZXqF5-I&$*PMb}^%$Y1b&*b1^F|xWVkBe*V@+g@Jzc{?$Zb z|L&UNVG-q=@yx~^U7XvA$WBeQ=c4NuBrHkGv{Fr~th(oMd3)@k(GL{rdY9KCY+R^UpJ57V#l)CWcETDV*AUa@r)S4IaXEXRJ#3yR<>bo;4$1p8z2|8iFpm*VEzvX> z%nDy}P9{BiGSSnzaVYoVI=)xqtsQEutipv#5jlgPMaRmoTrJ86tFMnRM96sohC zTCcD(;yK3KutCV$wz4H#x$%WC%jJ#BBRxZ%0KFo`Z`~soPi1%pc3HPv7(Mwp%=_)G zYsWukRIuV(huwZw^CoN^G7{eXBD!Nta#i@S2>QQTha*frv<}97_E~mBU(8W=&vCh^Px7qUDy-|krOZBK9g@-jnMF?9j z33$!j(2eXN}Avh_`Gn#EKLcIHZ=)Q2jU%2bX!OCV1(SJfL& zL8>*<;Q{RYFQ|kKx!c{J_mRjl#;=9XzQ!m%_OI&j3iJH9?7D)wOGyQ0^tSemb4|CL zD_&6;@5ga_!{QotdnD|LQR;7BJ@_M`Qt85_o7zt7#Ss@IGLyBvjVeA7EfP2SVa6(S zkLEU6hF~r(j7A$W&C=IjIGZVaNMDlK;dHJcbzQAMZFc#pVA4<}f2-nm7Jfq>&FQXE zkESvN^T8FSGCaa)+Z7bwck` zORoOXCc#1Sz?L4F8#dEjWN?lh9t- zueBbn*_T(ArmU%N+i5lhMJ~4r$QJ#mggIE{Z4^UkO)QQ!p%qS;JZsP!xD%KndCmG} zLE~pJ5w&vuMTA@SroxjKZEHs_#R}g^-|E__b?fC7|Dl3G)A6pZk!Ci5Zuj^k*Fd}f zBWFt`{$cmf{X5n6Dsw4SfD&1;vQzoP6JG(TS+xcNy%CvZ$^T zKH&;)MvVD9D;*L`wp_kdqqpBETfg_94=qdJO2COTMInzmkN27zw7t~Wf9!VM+QDl= z_^2lS*J?^rkX_G(u?gdG4WvY<*9|4<^$%WdYimW_OI1@jR&yEm_DlLFztK|{p4xFi zcI4I@JOd>gO6!MwSsOl!K5^cub9`ae2SapizGU-5Gk*FR&3!2GlGrR#X?@VW_vwQU zr2X4XlWKE1Ce#sQ?b^2`OKK*cPwd=qLgCxj$tmb7rb=ozQl^P3Kw)=a_O-0MFjfLI zl5Bs+Pghao;;Bt3KN(K>yzbjD6Xfa-Ve6-$Gr>VQO*LMdHVwC-Mz3l;*qyf&3g{z# zmW=O3V?q%fU+jLkkSa<^uXyD*Wr1KJ)=wib(s^-=)lDvgD$UOR{rQe@s*O{S?;SkT zaKCW11L~(r?2<^C#?Oj5Hg$`y?%t9fRbNkf?N?(pu6HYTE0md)lJm7EA5WA!og|U$ zOGCFV&gx3}QFYARD5E(E*&Zj)$c-3x+`Q@g=vVg=q28wwr!t=uo%-gsNTI!^z-zCt zii?rJknZJ*rqFULeT~*fCAs-`k4mg|Ha~WBfM&1%Ixb+O`;?@F$6cnrbLTN5T=@m> zo>4X0`j2M>JN20ZDgH^9%0C=={Oq0Ar$t9>4}BB$omB2NsyDeQV9;=Olfs3Kh$-m6 zeN%x~rw*H~y{TjpvDnP?JFo75Wud(n?!3t}ULk9hI4$=gMV!O>#jcwXn-}u}_$ZFpG?7Pdk;fx>M=3e#f_Sk%VGx`0V z?@c>vv;wTRgq*D_ChZP1Yd?`{I22WVL5b-0v)krXd_wW^1ry)Zx;K=Q&IqvVzC6!5 z*+R%E9@8q3mfiK;eNlaiDSlO=h}zxpjFE0dLbz+Rv+)+fyN%4PCf)dwcvE6_5?XVN zY0%Yhtj12#M9zKK>&r8$u!ygG&UEFx3zb=Ai|ah@sND9wa+0LrW?tt` zmL5O$IH%O{K-h9)=sTb4hle7WE~Xw77oyIWoeJu8hEZ>me?}*;GGZD{E_d#>Q&C99 z4BMUCzId;A=78?)cW%x|A}M0ut`AQ5653bK8-52PZl>=FO>2B|?_5PYjz7-|huzfl znRK8jrETfxQGtxa_pLpJ9+?|+qdIih3F2?>dbTX6diQiMEhF>rcdv4@LpgQNbl!xn zTX!wKydi${&bETQtn#x7rN-J9^@?sMKop5a>?DjriSuiPszz%fr-Hq2Ysv``yzxa48#KJT`wA>|MRayMPM1jaxT{MVa!K~J%uPE%Hj0eex{#B zR!l*!P~WT!65A<9$o|5#Q^pOi5Q{Hn>M^YCRWVsyZl& zv<(!qX+`0d)JC^FtWRbjRmyH#xaw7^`+^Z_o=sO<8JJlCIb$V^uv!vF( zq|((?wM~iGS~YM~bW-x7Y^qESebg?ylAOjboR@XLc+^W*Lr*%VI(!*z;V0bQ;#H5o z2TegGFYj5L{~FYxwkXBn%kDi#wDa6tZoK7v*HdIwVd($Ue5u6O2RvU}<-P6QzFZ>5F@%#gqXk<#E>J#g!PGY{`|hofzC`S;yn`n?qxpA|o+j z3%3<$E|V^3m2MEE3NvgmFMyNXAZKXH=EROIDV50LIzIg3*#| z&BtxL?W&bTUw@NvqwF2TlguZdnvM)6UAtT$9juP64{ zk|tWKThXM~^SOM$2c(SHcRe*qCokj^23^#ouXU}8#8mii$YU=LGJnv%^=78z zvq9C`$jAq}?!jjXCPq)Lt`is-)eUhu-H=nDD4uf}Q9GK|(p~w(NM_$Vf$aS2k3D54 zWvVimo@L?frnh%0U6U36L9x+2P?#Yq5gT}3$XbE=Q@QJG#Hp1wYG>WIkUu`?Q>f-K zJ-$4BSwTU;#x^5OYg0sTLjv>IE%LGVnYs7;2Fm_0O*mOT(yf3$B!&FwZ2cn9yZCand6V zN8iukVfRN4^0@gre){6axF%%y z`Hj!_+9IuYz27u?u8l5_xq7Yu-||KFXW{^9*w@0l)23wmm5PZQq2010WV!KTGzJHz z#*LNc7kyj~esFwRBq08^``pqLr4>96UX$Mns$>vP=M;5uo8djP+yzOtovrzbxN;S? zNm`C&s$a#8N^WBVH`hozQpCiNj{D_;SpCBjtxt{+njw-*Y5JfgZFllv?Ax_ggOP^J zVTz=!1ql@=$8%f`k^VaDKz6y_bvc7{<%&C~?NfED}2Fwz6xSq`21S{JxX? zdL}iA)@lM}c9>$D0g7bN4vHK$H};ry<_bnnL>pC}T2D(^jdBo;+w3(FUG zrwBWKDNP^VzG<1=phF(NT4k>gO7!VP7 z2m2kK)6YSE@wU=!?%_V>JhdnHPXWJQC`B3FRa4x+PZfZhz~u(;?*m_Gt&iNH{p zhIiZ`2^KIA(9$p+GEJ*4EGC=*=&yj52&YroV0RhbR#%Ls)7AhQ4`>0Fr@JMfjUb3eAY>jLIFF8|#{)aT z9(zP&0*eu{CK{_uQ^69*WD?ev9v4QBj@EFc(n6`MAgpCXcqEmX0727rhNU1e*tb~V zWIX~|PfvrW3EV&T`sDDksZ99$ldFW$^OL)EP$Z8 zEf6H#`-`V|0PKcuhM?MxdG(Qk`^%c>=tu)Ct+=>2O$ME&2|IM|_iq7n$bU}ED^C+H zZ_amED|#?BCM+5YJCzm@77@e3vLmT9I#%P)PW-^P5|UW035j$_K&W@OATDu!2<2Z4au9Rv z=E&;}!6R?E!q9ZT2Q8A4_d1+s@+AWz5#S_RRdHBdOjg5sd{PzrebwFAnA@}NSf1Ud{=LDkS{ zs1CXSH9^;)o6sGo8+ruwK`)^<&#5qJ0q788u@et9E7)E?Vd`F^?0!Rs@98w9X zg(M-(kq$^tFdb?nRa&k0EQ3O~{+bZe$A8g22?sKA61H~Mm36cNNy8Lij$tliIx#OXUwQa> z6nKa{);zvEOrBJpe4Zmbmv}mPhIl4;MR=F-8t}UC2Jt5FX7L{8ZQ$+TeaSn)C(4K8 z1N#MhYxz?6_VQKpUFCbs_lcjMe+fUC-;JNazmb10|4IJq{Qdmj1jGbX1uO;p1!4p; z1u6s@1s)1~5)>3v5;PO^6^s_l6s!_#7JMrBRY+V&Q^-yzNGMropHQt(r_g(0Ug2fJ z=EAFl6NU4I&j@!2zZKyXQ4+Bfp^9t}*(Y*NGN=iw}PAW_)OX{>#kJR@Catq8C&=+i9P`#jY!58U8(q__h>2&E6(%sVE zWw0_7nNXP=nL3##3%M4mE_7QMx3GBO)rD^tNh~s4L|c@;==7pqS){D0th?+w*+a6o zWXI&>>F%XhmX0VYC^{*wS3IiNyNq`kY1x`( zdzQ5>`=W$XTCTKJsZQw?P8w&2OT-<;JysS}HdT&NE>-SQ;Z`B3gsSXUxuc3y)m2@i zx=*!34XLK5#!%a@c2^y(PF82Cm#W{_5YRB!h}Af%(XT0`>8P2iS*Q74OHs>LD@UtU zYZ9-E55phEKO%?`>lEue&=uEp(%q_i zMfazkz8*`jT5niiQ9nSxNdG=bg5*ldAYCV;$rj{f@&)oY13iOigVP2d3^fcx4UZVU zGQt_rjSd?P7%wrV8Xq+7Gr^h!m>e+aGhJ*-H7zxLZno4c$gJFK$XvyIt@$zY_ZD~y zmPL)l7fS=nB+EuCl$DKDx>Y+xjN(Npq&&4=VjW_A)Oy55*Jhnfqb=Ij!8XVCzMY&M z&92IB#9rS%$-c!wz`?_z(BZkGsw2y>!3pVP@3hP5vGX!#rgNPO`s@0q`S+~Et&CsUwrb(3uvHgV3$LcGJ`;cr zSRQaB;0M)(T1p)Yv9HCj#_R!u0nFZ|*8m1f5bLm6D#=*J4uR@GNc7+VDFFQkh`W($k!g{AQ3g?Y zQ6nq|RyiBNUdgVHmH>;XTQSNpTVncSO=9=OevR{vI}ld$2S>K;zo^)^nVng7DE6GcfHz&W`XtS|0g)b#E<#sAQH8=J1rsbO&Hp^|^ zxOrfU&6cBSLTRkD`&$jSmTcqN#@N=rU3+`M_Q~|1^cy?yJMwq@$e?B1*h$#Acjr`Q zaAtd!URH57cQ!M-C&x6WGFLP=F?V2>)2`ax*xl*7Kj*E^Ys=TkFWJMlCuUFIUdO%l z1&RgP1wRVc6n5{k*mt5xwrG3Nm;FKeJB!VVt4ricc9eWSz&P;Wpv}SBQl--TL)?dA z4-FpnKHOSHDyu4ASiYnDX9csOuhPA;rAoi5>WJ)-tfR=IF-M1w`5(J;-0FCJwMO;9 z6H+I3oPAa`H}!7T-qO8Qb6fXzO}k!uZHInG{T=e1hPy_0FLj!B zHr=zjcdg5=>t?re_uU?^o(K2+@Ao|jdhqID*u&A@*xrdp8y}+|r#}&SlK)ipY3VbS zXD9mf`Y!fc_1}8#@%+&MZQ#ud_KP1cHxCL9<_{?hRlOpz8jL-%Pws zdnfv?=)Lm$vmeYp+!|Ri@^X|lI`uK*lgy`z&pMy4jJb|I9S%rr=4<6Q(zn*{ z%fAmz#Qnhh*!xrEXTzk!z z!V|TKL=Aw@U?(u6sqq?2_EMmM56jVE1#UiAj7ZKRZMwKW3fi? zrGYawnh{I4VuaAy(I!M)J$0h4J~&9=AgdGgi0VXr9dH01Nk?5*8xIZwpaGwxtxhEC zscYj2>e>VnILPX{L~v*m0T1ZW$Abf4_2Fw>kPi-R9c`ka9@)a&fw~ve@OUlostq1LBRD^MPR8e7%goQ7lldj$#bf}l&*=YyJqLH9 zhSE(qFF?%zIgDS~5kd5@>HNQ5jhYqUK;|U=&$PLb7CcPmb@6}E?ZKFv8$!SJ`q|+; z-RS|$z$qe#5u9K}ji#G`RwiqJf1-{jcu7fD&j3$Q122ndgEs#I340G_S+eLr@IMir zhny950>eLu8cqFA#DBxiD&XjhV0lJFgqfJb5ew_OCL%h5y(S_OYw4klb)wQ3O!#`* z{`qhaF#Zd7(!tAr|Ja{!&b%lBU;Z9NwDm}OI_f%jvL2q3g7~0MAWo-54re;m27Fz@ zG)6}qmeS{>AeMkQ0XQH9937G_o|A$&0%#CLKnmiHzBY(Ta0>7sCBrERAVCV3r43>f zoWiAOgV+V9Ko+DNSrEB&2s$7aPJy)`o#yCsXcC-~ft?`bV92_F2D`7ElnkdJ7U~el zKqs8SvSbo0OD4e>G6}|z$s7!sz@cGnWR5m687`5`(Mg6a0WT@)se^c_4q~jjp1Qs| z2`qnsYX}5&0#TiytxnKU2k{et2>R+E*b)I2gkTVXL2%XuaT0O;DB*8ekfu2hB+Q zl|Ls5Z_lylY&JYO+PhM0zy{JBV@7yBWKM3rZ}q`+^q)F>)}UYB<18%xkw^anU&E$F zFv0B32K!FIjF1@C^fV95oL=jA(4%1eGgn~@6CbQ)Al8zF)tHWKfWyIZ!rTmaUbX*; z?&+RS0b$IW&f>TZe#v)6Za#a?UDjb#_L@jW{2U&v5@zs!X3fc1f~`E32fUF7FIzYs zn#Y=hxiaGEVJoaamtk^D(ZlPIX5fEj{VwD4O9rgeW{cp+%*&dSneBC85Q3F0H4L5z zNg4!#29fMJ`{J$|0neETImZ4a@RusEh6HP^M0(JFtm~J=oH9y$WCS~gH9cquTHrPB zxrUgRF$cGHjtLK>vrGuuTC-K2WzAt`Rl2ewLRi#rc)7)?(VzS|0gp8?(b06!Saf=j zH#IDVZlbHF2PO$EJzZUGy&37hL38rvk#trxsKc}@Jbe*=m;D_uCpT-51%t|-men9= zY5!sEugp0)uu!10zzP>EYk;XU_}`dwQ0oX9HO!ID42fQ2LLmL}**x~|Sl2KrlRiH@ zXp=}JcwtP^2kUf>A2~Yz!uiG{d{s7FW zu#90xM})i6nL%KiJIG|VXZ^vRgU>bx$E~yA-v>$iz_=17fwuZ2h$oeggrf1!8bU6&1+z2{M#Ar zA84DN$v9IG7Sr7=vi~kJzxizxd#3Gngxa8DVsjzaDMQm!F6GU4bnF1R7QXR_niX zF1WpPa{9*=oV|$+51(-_2Ww!*WJkk6$Ii+mCWaAYK(Qv{b@g?v_07%AttbS7Imr^- zaaa;8NF*|uVh%#~UvjYN(@d)fS`6HZc2=C;!LarzT_#k8Us}yPYLp#L$2bKtk3yA{Xbu0y+ zqacw8F7R;&{CpDrA&_DyQE{%t1nLrVH*^XBErey~AvhrTqZW{-3W|Y0XhEUSvl6zrFt)n(rSOqq6Lq{=mK9 z8z*EJt!g_{qpXyd|FS>gI(uhX(!{!5WjR$2p_N|->N+00?2z+6v9O~2%Y*&?YYmI? z))ySt3(-YmA_^;FM&ju=rEO&M4_7RcE2r-9l3h#muhKdH^T)o=y}X0@w)W5a7pPXp zR~vafeC!`wC!?a(H6CpdRak%CM&8kf;J~y!`?FhK#-K1L_fS;Y?y}2^NUS#v?5Ia; zbtB#-e;)h%H2c%@j4x&6M;W1)p8ohcZXBXKuxWR3t|2QZlXUKOr%m1_ZpUv@>T4g7 zoA#8*y;=2+Q5fBj)n%Eq9OqKGrg(?G-OV`OXKhcMcWvxGBofe3IR#m=T0T~5@kYEI z9TQSp&Pw}G@os}l<(;aVov&rQ^}F6bNtPV?`9ro@%AYX2OxfPk-QCSEbb|mjOH(q{ zeaAZHo8q543t|=~d5-NJy=}9hXJ2&j(<$iLcXqS>z4fPFxcm6Mt-b$koO*X((-@`V z+@<@gvy|>1xhh9{NIE-OcJuJhI<4MGhpmby12aa)2VLw9dVhw#x>=SRe^AEe+X>0m zCnvaty*9~L=#0J?I(+|q!yZ{{m&>OPm0#=MXL|LTro+m!7vviJnKG&Qc{wGrjl}NKd*?r98Vk?`!tIu;k6omx6GL`vathC4 zu9t7e#T1(#wU!c!UwrfCiT$A$e@aIWT(0u(-XJ&yRkhuHwfbJ%s+3QKVds8mO7J1L>8x=|8RMVu}_kG6Qg2MK|P^{S8;r1BW5 zGV?@*rc^h1+zngdiac<7g{y?JAKE-XSzUbNX;kJ7!LvFK_;$cj{AlMZjjk*Bq#9j) z_);5PYX#5hu2AJycCEEIa8g2wZzKEyfCQxM$Nl<{SAmA)x|<%v2#~G{S<9PpZvk>f!$YQLp_+ literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile02.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile02.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f281dfd3f6ca47ec21f78d6b2e0806ae9aac0af5 GIT binary patch literal 15217 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 plZZk4JTzLA){{UUk9|-^e literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile03.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile03.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4787e69e903dc3a7713274a4f98bd2d89ec79abf GIT binary patch literal 15323 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 literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile05.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile05.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1530c7687b3a1a712f96d76067673eb4af373dbd GIT binary patch literal 15276 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 F{13*fNk#ww literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile06.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile06.jpg new file mode 100644 index 0000000000000000000000000000000000000000..cd59c5f7eefaa8a4623889a943bf618dfc023904 GIT binary patch literal 15302 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 cOzVl+B>7v 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 Mk2>NPIQ!&(08MSxC;$Ke literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile10.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile10.jpg new file mode 100644 index 0000000000000000000000000000000000000000..11c6faeb2e9bcea9b4b842903f122131e86261e7 GIT binary patch literal 16093 zcmeHOc|26#`@b`Tv1Tu_O~}~IjBUogjV1dYS}-Oi3}(g}DV0jH6{SR!EmA65E2X4` z5|ZL$D`}-7QT)yfNuR#!@4w&c^}F12pZlEm^W5h==bY!9d*+_`p7{YtV4bCnB?N=P zAZzdk%@3~JM~tCtfgl?jWr!1kAZ~~s28TERl?+Y+K(7MVRB#Hx5D*MpndfR4JL`HH z&=QMuC7=}-F>pX*fh;e$UIHf$&?4a42u@-6f*vM@vX_merxVbX2naIwqlQrzQWVkA z6{Us2SfDIytZdv&?I5PXEcDN-m5sd<3a5@&$6_EH2CJoqA?RUsQCM9)Eu0<>4@p4B zk{0Zg1`j*2NGkwZcHb`>(2Mk4pdHFULTuoI!Qrroj0Kz30iJD_9KqA}s)D?nOKmV3MwUq&OA(HzTY zCXV_143O|G(l`tTuZP9z;Rq^E)EE>*w2{#^k-k30rEflA{>Y= z?~5?NVAdfw;KLW`C}t&q<7aRBd7WP#CzzE71m~Ya<`5?b2aM!;ktxF8H62%B$)WPqLQAgVC~+u{MbCmTBll9P*@ z2LNgWAUF(xfU_ak+1Y>-UVDeutaor|c;w})(br>dCO&=sGCB2i`r8b%Trg18KaYii{jFSrpj>b^HUt}zSuPkn z8k`70Hug1G4k1%#q+f)vEN&;Kh*@S~brYAIrpvgfKkYI1N_nlpwG+&uu}b!z6)fq0 zRkEdm{VG>4#ESs!TM!`#8AG+9j>THN1?$F)n%>*bB5M+iYpmKfygFDeRemtUaqE~v z!FxNs#2RgPB=*5^*D*L9|BLknSwG-e-68r!(${5-VfR51V4 z)@%z$LR-S|-n{b{^4Y1!c_T%h_*)Sldg}R)V(-#oTT3ple7(b?u?AUoN9nA;&xc`| zbs7RwsW*chi!Hk6x-NG+a6qk-+QMGOrP^C5A<4xF&kl@ibBWcy6q44@!G}G2G+a); zi~gc2OcQ-~s#qYvSHq~LGUs_3C-j5zFmP8Ie@Ed%Lv{CF&!F4EG<{ctlwO^{j$@%K z;@r!9BIqyfe&W-}^bUnQps$msSB~VbNaS`XBC1?V9i-YcN_JmMPu_Cb*zzjxrbU~F9Q8qs#F|ob2sqqf##v8LR^~< zze;8gcg-fdy}ND8O?tMu^6jnJ?2##T^g8_wzT`;ujvqs_FN29*M-5bP1vZw*;KV>^*SddUO)MyZqqOxd|k&PJA0V$(S zw-46Z?N={zFl-3vp?`UWrFB_zhGOFgKh5#2<8vK-Enl5H`EOn0*Q!(9{5vh1uTGr* z65{dh(4DiNQcGzuEo06kJjvDYE9$~akif-yUNEhd!9=B>c_<`e(bcG zpa6F7^%ZWBU*;H3ce?NY)C%H$S(Qv6Pyg*R7iy}SYJRwW6;ol8$C9BMvx zMQ>>4r33fD3%cSnU%$2y<>RJm_s$uAJWk8pFInI8_Lh)x?!hckgz9LSoOs!kg9!Wr zbyL0JJfvJB7V1mS`HG0$p0(d)XaG+TH+;i?=?zl;#fI{B_Yk*FYwyaaIu?{7Cmw3v zYiN4lQ2Lrk`7nh(8WMf^uxsprNQJ?+&9A=4mML7leqYm`z9#IdNLr$%he7FQoLSrs zALL}Ic12c`c_8xIY6iPL)g&eU>ZLUPQ@WzmcKe3QN!x09YconVZNZP0@wCX#&@kJx zN%oHwy45{6cQ%S=e$z*mL(vt-29i5$Lsxcl2$l=2i*NakAK>4VJvuybMy2Q0P-2py zT^*Nl`g7aC%?C7nZarCdM?W|n_qti$dNa?f6@xLex1Q8jrYnqzowBMvJdidp<7rz# zRK@o8o3jlTHSxV748QHI9epI>)A7{($$LrbTbgTBZKX%iH-;ZZ4A58DnpHf0H*3^> z_w9plzW(C1XZoT)PvQl6_g2EvtGG&wYvUSA=MWpYy{>2oX58Auok z(L0v+a~r=138<9tNMIkVGv=SWW?egRJ&ONP%3h}qjR!ApZWzsdWjxi{Iev?dCEL6> z&ol_&wl|!GDFr1s0l@IH- zzPhsl6Vb%;Mnz#Bvgy7$Icqqj1{Vo-zo#G;-}7=`YYXB@l8RDg%?CaSOY z+ps}4gqC}p!v))m>PNk4+rJ3*IULkFyE^@&K6`DB=&ffaJmib&#}UE>QR(=i`hX`N zQeN5OPwY2NsLgDjRfSEqX+9J!sF@p@J-Gdx%(v;ed1xA0zVa|!yot?M=CFUpo%HMw zS}Zi4xbBgUw!FZ#>Rmg3Q0#NKT=pSn2~{6M;^(1@TLLnhYTS428f!&N+}7whoGl0W z4&c6s#`LlygJJDoZN59=ON;QYxukccgJ2;vNWxQ6xX_f%O^&aWZaHii%(07BZk&g_ zA7QBaC-|#u5kHioWW&W9zsP4=)vdXGcuz`1eLen-PmRTt&V#7EP+I!V%<1kN3{L7o zf=Hq_iM?e_dgspX<&~xeskai~ZPC({tgtD&-MeNcrdt+^R?g!Jk`?nA+s#eD_0 z5GZ50g}X{Kfl_!{v)UWUD7t58I-0bWNmB zxP370sL}AX+!J`IE+2o`-=ytalKyB!)l~(Y^N%j8*DU^%4mX2;y25pHjPSAigBE90i-^b|5QeUoA^7UQ_4#Na>;X&GUE}XVW?t zg4k5$i_9XslOdjl&fJ;N1WEzgEFdh`VsFEe=uTcsgaFFH#!d6D9I!u z$7~w*uQ@85Hmv>dy|V)xhYve`=%YQRfHduP&!;f#e#)WX?TDp(9rtQdzXxF003cq{o*1W3xeg9EXYTD@;_Y#v+nRS0@y$#;B z?M_U|<(P>_`*O3>OWu*jieGt^QFR;>>5eWhUPxL;+cowBcG>THv4!X zR1YSoZ;b72Pr8wByw0cMK*ho8=P$-A@oDn5IYHB3?(`SfB?R+$Q@sBY(#cLD2v|4Q zJsP_D=17G)MaDk4c(uUDE-ZWYwGWlem+lFKO57Ko4ouPbQnQMeboE43TANAx6H$`w zrF}L>hc@QUiNjFCo!pOO)D7yeV{Id!Dg(`obAn48bCeZ@ufE7l7d_bf{GHGKmx#H1 z$%uCt<(e1KScekp5wjbK^sL0q6IEH=_noMT)t+p$p)uqqkt4?t2E@}T4}FY3kFTGH zUL(GB-m;<<%Xg*TRqyC`JNo@3UxP2dbmLQV@)_^Dn~d={#kade>}qN7HCBEl2)FhZ zvT8w~WosGD&*~E?&z?(uKiTLw{B=j^rmV1I%`WAoD&zytW9;km`|aNtcgxnUD=2d^ zR&G_mwUiH62+oOKlS~rNBv066lo677__Nbb8cw)ttLcbkR)wx5t^SNYn!oAAOu#%; z@bZb-mFa+X6^Wg;Uk@KSqnYjOc<&w8`|do8QvD4-OyxwTdpM_Cq`q!B{h0<&GUgtO zRO|fI;Hro@5~2E_f8V3KD zYsGR~#2WD;1`@{!s5m#={VDg)GWtYEJ*`x6hL2=o%dPDSDd$!cNfg5~Yg?hgI&lWj7bL8gCx2qP|D8g`X9dV|V8;Hk#}WUa8X^RVmvg;2_?v9LdXZ@b8BeRXEGc=`7m+W+Lcz@h_EZTTq z>GQFVyH|e7deDPUje6f*qj3Ie4)&F!irAgbP2tGW4coKno&lykZF}#hiT?FUxi&n! zN84q~C9IJ_-|cO@!xP$pju$Rx=E@6a-hkCkq&Ih!eK!z4{+>4@=k5zP$vN@zRH|EX zXq)lFg9>*fg})Q6v`-#O6%>i`zp}zo#_xw>=cTafjaDj`T=oz?^$f^VaT=fXOj(Yk72VMzCC9jBiL2dI8|4U-G;i(YlJ=$vU9cKxh2t%*NM-a13Ddo#yAP-|3pTG_j^BSM~z7jim#+kO7(L%Fj;|I1sinc6&! z_r2fLy6;SQM&547#Wa7F{1G>dAM-Zz=&&l-f3tMI6-PEpB;$?L*tGz(`#Oi zg&*zu^LT})yBg$nDy--1c|&-|r<97jkeS!XVS;f>cj3cZceLclqf3&o%-JUT`-ah58) z#Wu)%t6`#O-T|T%Dk}?%j?Rz$=<4wPvhmSR-#-&Y29!Ryc8f?vWbjMpckSf2`&yJT zwtv@Jn^(5kJSt_qD-eSH*Wmr#eTV3#g1)+Nr$7TbQB>$fO|}2#EV4?ITC3=Rm z6lxe8ES82+88jCwGZa{EK_T0~f*2QA1j9m*ABi4r?`rABTpF<L*CVw2zBL2Zlnz`ZziO_(7 zfEHuYfeW-M3$s9*FzJ9$Y5>46Z4M6z4PesEfZi7sNd`0mtaC%jQ514CpgRFA9}*c# z0rWJWMMBAbbg;V2TvnH7kV!#+#sHd^=H_A!Xafl1f9se?O3H7fDX5B6zK0CAO47T#YFWxWNU%ap)2*OVS-(>va`R77V-5v-M>;1)( zKM7XDcSBHZ`?B(^WVV+e1|wWgLnAsmTAe~BsWTnAbo^Vu67ruD%busseBP4pP!{AZ zevu&z6w|4su#m7w8j2q7M4I5Y*#f<;;+j1MLZ6Nkyb*1}X^I2ayg1S7)iU@kB(m@h04 zMukPf;$gdB2VhySW3WP4IjkDi0BeG^!XCq(!3JStuurfVI0DWK7lBK`72q0hJlqs+ z3wMKWga^VS;BoNX@HF^QcoDo3UJGx6--ma>2jFkvlL!dGgII}JgHS`@5#|U-gg0Uf zf`-_RNI~QviV#(Z%Lwq6j2J+?LwsZ7U=wCTv8l1?vstsbvys>$*tWB!vK?b9W20DGm;fl^kdeT@Gsw zFOIDo+c;7=PH>##XykazG0ZUuejyM?svr%Kjz~Ww1DTA>MOGqjAUlvRk<*+!oHCp^ zPD@U2PAX>-XAb8X&g+~VoTHqxTmoEcx%9XkxdOOixzf2#b6w_Y=X%LC%Pq)_<_7Bp z+*`SKav$Zc;=axOg8MTM505MlfybGL!n1?tD9?GGyF7zD-*|<1m3hs1H}FRCrty~Y zHu65>{mjS5r@&{z=gr69OXDl&yT#YfH@!l5h58Dc6#**}R~%nayP{*o2YxR8wfv_1 zoA~4SbNDavxAVUf;1W;}Fc*e-Bfph4iNz@(tCAXd;(FjO#A@Qh%q;A-RtM;#|TGg@YtC)nCi5OWdMeLkdm)MLr zN}MPjES@P|C*HT3ZME`h=he}x^H<+q{Z2weLSKR;ks@(HqE`|wsVwOtxlQtv%4h39fz(!zfj~;G**mIEK=-L;!wgX1uLCUdZY|j)>aNuKCaxZ z0$0&dp{Sftd92E=N>HV$7O6f}<5e?Ni&Cpl8&qGZZl|84UZ?&+LtevMBU7VAV-BN@ z3BjDkJjV)Q*I|>em$9F4$~Y44IIdHZPt#H}QL|oiLQ6%9td+0Tqb;m$uf13MruGjV zT^*WEmCl&1ysocqp6*k;2;K>wioZ)>Czufu30Dc<^mOzXdKdIQ>Z|Do>z~noZGbi) z8=N*6Hk38=Gb}V5FhUvm8l5y6FkWNqXIx}FWFlu0U{Yc-YN}+q)wI&|gBiw*W>#bN z)m+a!!MxD|VPR#FV$ntvBDxcg5&JD=EdwnpEXS?1t+rV;TC-c*T4!26wUM$R*_7Li zuhU(Zu&&vb*VfhcnC+0AvK`ItvOU~>o&6#E7Y=J3s19|GkfV*`A;&%^v{QuB6=x1- zC+FkNuUxP$@h&Z{!mgWL&$v#xnYpFAJ$F}hk95E3!SAubquk@Gr-f&h=fHZ6^$F|m zd#(1Oc-4C&y*<23yeEB#KG{AmH)wB2-q5`fy)kBE>!#J4LN;C7%)i-h^F?2FUr*mN zzTf>E{fhi1{cZg7{KrY=q$8xa0VV;51IEaP&7Q-!BTV4km1|A9=3o;JM33^8{ zryQe9Y_;B6u=Q)OV{mEke28brxlqnf-_QoC5H* zODmzn=o{(v3=uGydJw4?xhHZk$|&l1)O55*^u-v#7;4OeSmoILv7>R8amCx%wvo2o zj9(MKGk!3^G@)=iY`g#Vn~8FXyAxmTu-Z|!lY3|I&WA~uq^zVbyF7PY-YvCz$L`@h zR(mRvS0vMtpYGM)TdOjWm$q*aaqHM><`r*MjcK${3UyHc59ASPQelGBauf2j@liq&y~;3 z$o+mS=vdcrv*YLTB=h#?eLWFyq9flVzp6m0;6TC5Ny^EdLaV~sB88%yQyizFPQ5zq zak`}#UtC_Yy5vB~k5X#sK$%NfbGdGL`5DPG=@syb$cnMb4V911TAZz~QmZOFxANS9 z^U(Ro^KYtstGh4QUbuM?f3fP)+Dmyg!Zj(iu-e$#@w%Y8fqJj{#|`TmZeG^ETysVJ zO6k=#SC3y4y_SBR`}*$d^EcvdOg4r$zH16>8os&t=JQ+YZ*?|1H@Drky?ytN<(*qC zrY$$_8s5Fys@K|Z4}Y)jzRvyH2igy69%?_VY13(|ZP#tDe?)k6`LV&{>m9}&O;0SI z-08IGyx--}^|;%;yXWbKrvp6!J+GgIJe%l^>YaVQ;|2SRlsIyay* zaBa|H@WGJl(DPx^@Y@mk$oH4KU-7-l8I>6=e~o*6b3pcDO5xB$169`C`y1K+Ed#eV;^o#4x(7c(a5fG4sN8lU=kq!ZEVDC5Th3p8Xih! z_@QD#L#TAU7(+BmxgMaIYz;I3(E_&UxmZ{(LcpCNdfA*aT2noY7N`N{-~@~i?`G0&G^;3WaS(5laQ|AU6%xmTVgHz7-S<*%LFy>kJEAkA1P_;=wYxb;A1gOQ0IR`GQG!SnbXKX@IMh=hWsgP z54wMVAH(lI5&sSQQvpk77|ks#EX2r^8L?1KL1BzAdQez6%G^~GW$#C#P?`4&_AiHn zfbn0rlMFuo`^Wxd<}8aM%-i3ih^7u+M@v-;L(svnQV<_>vABg4$Koucnt-p3UBGCm zGNp7`DTpN?P5=%_0Y?k3jbWuAjsO}&5s-qoqpJy`5;FyOkP?_F9w0%=e3mANQOuP2 z6ipDjm?@A2DN7baE-kDU$YrL$T97VqbXhc>nG%4VAZ1|)+JFYDudI~7OhGKv!V-W^ zX3CT$;F+=nJQG8}Gcg1L3q!!NXr?v-OB;c}d?JCRlfbkDe59zO3gV?Ih_R|Vs=BIp zF#QFt!D3ahI905sDppGs#7_Xi>Z*cZivw5?fXL zHEatb+K)#5cb$!;WC^?^&g|!Il+cAZ!~6;rr-k|%XBMBl1X&XLV}LRbQv<^oIbc!Z zkNhP`=JFhkOs6vk$8}CbE3kmH#8?zw4q1|0?ps|j9Q~&b|7p-K@3AHp|Hz~Nfv=gS zg;BxiP6z8wTPT5%w1r_Fn7J_5aV1AE^)KFqP>j4#=Kd&i8cJ;;vH=bY%L;Rg;AO@B zC%P9}J`sd54>FDAI_5{di*n1^OYX7^@uLTYQ(~6zV3x3m|9jSwj5%1!qq#B{@|e>W zmWP(HmS9el7;?yZ3(#b!EK_taTKGly-?M&~@%kkLW@ zeCEAW5z8``;Fb=Nq5fo=5mr;}z`#WGs?x#U!6hHcc ztQuBB^KaJv%3P8I69qC2%y7Z92AH~t|Bbl>wG1Qqh1ij)fs7y{EdH0zma%`wI)(UA z$;-ooCLWJxPK@!oV4lwMBTMHWxPPE#c}NCt7t9H^hBo*F`0r|#KO#WvzZ_873khSp}#Sg zpspk`m2B=8&TJrVvEjeqe`G9){5DxPW1e8c1IltC%l1e~fFO zYy8_0?H_1c7|B>e5Q?cc+>b_QH8Mjqvrk0QNaTf=pv4}?9L9hZ|IKSb0BPx^AUu*5 z!dj&WAZd_8$Y8``(7}5FmbttEhUfs2-j*<0s2{_K66zO7*4P?O4m8mC#r^M|8~*bx zXX)|3z2hxqFMBar;_0ylT2?m((exj&nfCtTgI}A$Sh0l?LN@y2r_JT^%W%Idu%>`O zLyJXe{FlxJub1}r|G0oZUt&W;7v0Ok>e*1~3}(==u`r5^qy*>@EeRNHU2RKUQ&Uq5 zA{J|kHwSMV=2$a4orR(!RHq>{XfvZ6d1t%ftvRl^u!&}h6J!O)y&ZDXyX1JYOO=Rs#;pO8K4zROBcVR;cjDsDH;9?g>}<@~6-L=?*6%yu@7Pt5}y&6NTBj z_qYy5^VUn|#dd{xWKUwh(6p68Bc-o0W~T-EyerkP4ZAJQUTTQAB%}JRV^YpDe&wZx zyiO6R>swDe?RmJZ{C2SD>FRgK^mDrf-V7rM9|K#&%34nliB+x)#@KZkgN|0N4I5-% zy0?qAP-}ye*Hc~Ng-(~}Q7w5jr_Rc0uZ@U`2)K1fq;}+hW1n;Tm2SVfF10UT%;h`k zJ)0WS<>U+^zf=a6rP5S`bLJsr;jY10+*s+ELuJLmf3^S6-l|I}>1n0Q=5Xy0iS&1X zzIQj!D#K}k>ghIWn_>nJRD2v%_ma~2aqifV(%FaCZeM}PlXsEiP$OtYO!hduN08g<^B?cAcB&ikfsZPRDHl-tPL0j;F9Z+Q5NwE8VKd`^G< zaRb5I>;0YSc?kV@Opfqfr!doW=7i6=p(}HVg}xWAJ@K2~PI+H?{AG@Nw*STl$6FYr zC#AiINe00ZTi?t>3b}OOE?ZAdd#mle*M*;VW<2>IL*xjytsn6j%?L@=RG{xV|HHgD zr_~_7wCl!Xv{qbRMAx-L2OPL)W5&T7WnReM{E(o2Q{{mA>&~uy>vgE-zPU&6byfLv20aD^phH+9mXz*>*K^Bc4=C zAIi>mY{peyPSEe8IY&Rde(hGJkBvfMv4OKDdqba`@+g9e3wm%oZ}rC8CtJk^+Qsix zX`P*&k{MDVRDIdMvX3|Gll-2E#+&tFXtXPtv!8Ux7IYHI_psK+u-c`*GmJ*kzPZOt53yB z#KIy5H#}y<2_MMPtS#W)EYVTW&zq3*y`YqEM=$csI(45bDPFjiRfUNqAFjQ*{xZUY z{)mI$+{D=wrgBBLG*RJz@YW_tOEa3d$%O1)E$uyDc}(s)){Bi1dlVHjXwGg1rv=*}k*90iV)`Ya($q*Tr}ae>*%!Es zl2dpj|02A;>U?QiE~S)1Rb>ylynLfoycoAQKkm?&XMnSXGw0y-n}?#b%KZ9ZR_{A9 z?p26Fq;ywnczeqlP2!5XV$TZDq8r&bc8?k=o-g!J&HE_#f?L(JhkhS?Hx0=u9+esy zy_sf`n}VTVEX}8+WDY==~;X&5U0A&nD8XGD9a-hvq6Twtq|9w)<4bT#iIx zzxDC>%^_lbYvj7eOy@*Gk0eV}|FC}T9c(tPl+4*9z@Ad$6ODaOX3%>E6N#9<536XYx?#U0Wkb)hP*dN4s z$T|Jcwo}L{WfQe)J4Q>v_N=Oktm6l%j=L16?>(xEb>mH}9R0R+s=Y~K;)m6}kOL*I zpEu^^+iTh%e0)C7k@^@3rF}}4oLQY~PAl1fRy4f(TDSS>?e$SP1LvDmxOb&Tc*P0m ze5)*R%hnc;D0?FzXcc*SW24NPw8QwB;4i3Mv1?r!A$8xjZDS-q@v)XR|BGQ>XC#gk zZ`ngqvcK-nxG9iStXWa9d91^1U+J()i15GQ`PR`XC=?x+ZRbriow@6K%6$?0#2 zUq#+A+C?Rz{dO2>?aDN(T7htt;<-B<3`@Jjhg#*^z}@oR%C}d8q5$jZnL2s4qSdIc zi7kmE-X2!Xc3-xj__G_g;-+3F>n7pGKlTs zWVexemT>WoaUAjo;vxgAwoagBX6^UF-_b-uc=?UoD%GUd7kjXk7(T>YN}PZ8KmU-@ A$N&HU literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile11.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile11.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fee67603106adf0268004c65b5ba0517754439d4 GIT binary patch literal 16024 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 literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile13.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile13.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4227207ddba95156ce767de73816226fb9b9a9f5 GIT binary patch literal 16012 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 literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile14.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile14.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6071c0ffbf022ccd99edac061feac42f96c0e6d7 GIT binary patch literal 16069 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 h*W-MU;Sh}c)ys+^dA`agiG%`^Z2 literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile15.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile15.jpg new file mode 100644 index 0000000000000000000000000000000000000000..64a0bf99b852fe74ce514804c00969806537e437 GIT binary patch literal 15916 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 i!ldShC6YggSPyPg`lcgehU>MjdXKMS}CDY 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 literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile18.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile18.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5a568e28011f2be28913c4c1a50215b1acfcff1a GIT binary patch literal 15868 zcmeHOc|26#`@b`TvF|FfjL6u{jBUogjV1e9mX*%s-p&hxj*`TbV;J z7!0xmf6)BEssqGmsxJgtSt&y75Cm~Td@wk~2B;)(@&kG`xTb(p5QczY;L13~V64pR zSwOE{q|XCdeh~u)G#1EmgX>jr;s7lSuJzy)f-mS{V5s|9n0h(^eI5ZprX*T0Z6QSw z?VV8?7>pUp%*w*b#n=X77|cX3T`jC^9Z)z`yebw0;V@VY9SlJStBt~H>uBI~aCm4f zl$*FtV^S)@$>4a-=-z!>Go z zK{IfS=N|?M&mxV(VDLIvtPYNV!m8`2<8-iiPy>JRI?poN6bw4;i)3Es!2!&H1Q+#y z%l{w11G}Adp&*81Qqhn(XajKIGdRR(iB1GOpnkOohP&7x#9}{V^wXujI0NLD`XU^N zuI!62z+luNC*Z>u=}1N;fMcmQEnVkV#tBB{0m1nfkSWB@#)f2LWk({B931SN+`>HE zTwL6ufll8X8h)f<7Lnr>d@jV<>@faBy&Q zaj)UwS%Z@nm&X0q$9z2`zz%ssO$gXJ2rd9a2*Bo>ASqxcD~M{0z_xgR?#aT+hGgg9 zu?F|!X_C7l?@y+>Q3V#-t@)=w~s#w^)?R!(>tg>kLdXrivQI;BLzkSp$ z|AUQAe3hmv65D#hdDLbWTak5j9-5-&Kc9!jy3Un)H4~=DqqYI$(aqAA<{{rR0en;2 zv&`%X?Qti1PF}j4$4WcF9U=V0+k)7(QOA1(dygK|Qe3y{?QXaFDrD(hg$v%EABUti zsPRvw-1fIGGV7k}s_V96gIXpvh1?BGG`CX&lZxV=9~$1_6r*`HFs+Y`2Ycaoh>UDk zSZ_tJI{MyJ5x>tiHG``2*)P)Ap`X+?zddPu9R+Rrs;)g7{qF?Ob)EH+d$jyIa)Wqd zUCTT}!+P(1=26S=2!h<8>B+OJhVyviIqeFGN;gslXjb*&-8a&ceCv!pYiI9&k?ZYQ z>s5i{^XjU~8|9bJm`bbf)1V{bLF@?l|OV;f7^sApHR|;xJ6XROXY>QL4P?GUOv-rwk zm|n)SiyOjT-D_5cAv!}{Cl5xh?i}hm-bR{h(-`gbBQ;kj*;(g!@$7@bpWv58Sn-30um*-5#>G5)*`euj;GSEoAA8OL8eZ=5Y;=1Y2yNV+$CWN#iaj^Ino zSJiow9<=V7Xma>mtc!X5Nal?loNuR^A1XD|V>@Zy_SZMg!K308MIsHX)*$VWQmSFL z0UCQrwNk0UgGe{sI$m?j(x$`m^}~F0`}g)Qv~)GLX`Ph)-Zg%sGR4Ka)4b{W#HFu+ zZevI8Uih3+LXU19b^KYu5wm|pkMHPk_`^w&EnTT$qFl5S7VW7Y*?~Wl93E^p?Do;F zk!nxg)s>!g@?3PYbF~G+AyVb8-I|OK8O_~iZcMmNb~F#Sg-Lexgz8dm)*^o7o>dXx z$L_z$>k{#GF8pbN>%q@0Anun|NcnN~-8px;s-mIlr)&C;U9b3zYE%tvl3kug93Qq4 zoOzvC9V*+QJ@+uZJ^hOGgTP}lne5G{;`_9&&A2UB~KJaz^e*Q|@0n zgEOz~I8!fci_T0>w-aS!r>ghQ8GbrJ&p0Sv+wi_oP%$SpQv{(rQYs@_I%Ovezf9Xw zt3MAZR;>xz7M4AYh}o5S&}pzAPY~6A$9MG|QnuHt?4fI*%jfm?q?GOROOO+7>i4fT zwAz)tB~m|5p^pbf)g5(?ITRr`(7yG}kC;-q>o*^$+lEO8Ul&e`S9jAZ`GPZv-R+5- zEYU2_Y%ujh-Vh6C)ukCF@4S9Bjqi-M2<@TmwYtO|)!fyGi?{gVM@qSxWoPJ^U0Gz? z$8z1OZtMy5q8ZiXn^xMicdw6eA22>RAgTWEh6ueC|}dPcvfP4RWSb7CVNl|rdeaReRGpGAsY66EF_dL}2bj6Zw!)T!|;q-b*Y) z?Ot<;8MoSw;Tq>D>#}t+1XAbl@Q4ihw3P8C>^JYhmz#)sG`skxMr$vIj($i9dsr0O zdC@&c>ixJrr%S1IZ2rDmtyNm5^bTwH6u6Vqxo>Z~c;%$ui;fFD#yTypRZg96tJ(hM zE-xmuf$N=;+&pB}eSLCPe@X=|9N>CiZq3eTuMe~|Bc3EGDV(pmg&vzu{^B`NdA-ky z1+pSE-)A4n-&I&U;z8f_Rp6yvs>TJe^iR61)!8DA&yBb!S5!|Rgz_WP@rAWMPd+BU zvBsY|Xc$+W@o-idHrcM;CX!z@H#nQR>!Q^6Z*%j|H)PqWqj1p%mTgi;y$|0_&kCf+ zK;!Wn9(iiY^53Z3lkk&jo6X^L069yj_!PKv9=hV|lhIJ+x@XU53u5Aq+OwlsGSIeu z+*gt49#&)k?BTT44|{w`A^t6gX6f|Kgdb(+jrCF*wWdX^~>6GmS?^Tc+AOn>D3xM;MS?Tx<~5zZrD6@ z`l%uJ+sd;>+aJgo1WOwk&TwcBndWYAMgL_`Ni=zM@LK8iCl4r7;Ro)u3A`?Pnco<< zas1c;$v$G@fQxyve}BJJd|!A8wg!8~UiF3mx?Eu%;;fIC@3ZCiY)Sf9ymQw{M>|EU zJ6X=wRub*yes8=q3dr0ekzK}#`WEfY@7*83#8n@E^*CZzcg2%p@uZ@yJu+L{Nb;1+ zN5hUPwQc76{I1sI;g5P7wO>rs9SNe(5ku3g3WE8KLe>B00% zP1uaQNwxen;zwqj*48E%Vz$KcD?Oe{8Sj$C204V=>F>jS*iGAS(1pp5HpCr{V^y7` z>2%heud)(x7xv-z=UBZnxq3tY&f=6Z6zm(P9YsFtdTDx*bdB>Pg*K1dm+(@K#x+ia zHB;w%GYV}^2X54dW;hi;J?BreH*_Z2<20sIrIc%QL&xHOhR4uTBI*rpbsV))kcvl+ zT3tIReOxGQNVDyOqa7TF4?c0^lPxBn{LSUQXF>3Tjxl zr^cynj_G-rcq`9vgJ=1n^3=)~y`$#%G+FCx|8HOK_T}5e1#o##JzfcFWhD~$Et_f{ z4PJkDtX!2UWt&ta#y`9V%bIoL+onCHRQh`BuR z&@qf+Rc{p5uGn(ew+{zCl6>3aL2>D?t;GJ|uQoXSd+D2HA|+t%jw*}gOEURS*#ztq7{ zu|*EoTsBlLFeh?DJW(`*GGTSNl#s;5mz926f5KH$MQcq)MbLV(*cbHiye+*mKJ!ri z>nA3Eee-#!v^K$d`slH9>RFEV_s2Lsbe}XU(e?UiEF=8w8T+?piD}=nU+C~eL(b6% zmCn!CoaHgcLX}(lhMyGX5r?3J-t@T}n~`Yo{M$*XNc2R}!`_{H10owjLXd%C2Xa)` zugPg%Q;!$cTYG|ligm#~m~#ChrAxHe(MS;u_Y{wBZrml8e37?6{PBLPwa7<~);YPe z>O~x=u*|bj8W(Rx88;Wny=>b3{%-6z^|U&#sFhS|Xqmxldb-;-q@9HcwJFn_lTH7? z9W&wnfd>70@kg+iX)nJtoO=^@=f0(Q|FGl@_+akm0nDAc2}ku5wzX+-79N>v?sOKc zyFEM)t=+~KyYppJVc&s$nLDm!cfX4LtIRj9g!9&Jz2OvBg`DtF+oQ)0H){CpUjM9V z$LrmOS&`Z*0w3ub_QEw1->}m0@*dGc=g&30S4o9eI9T1G(Ap zP+`f2Z~&zY^y@F|fWx~t?aUC+k8u~%Aiw{uGfvczjwR@g=#<7e&rAEb%AdZSn! z67o#b$@ePOK=0+99o$0`ntt||>oRg=g)(lzswdK$x=MfOiJthteK`AGuZ#GcXjux) zr6{P~uq{>YuDH+-qJ`$^+!O)fNbkRR&80{`Qax9F*w8^U_(Vrb`E3FkkiWj^(+&aw$u^&_M9`131IE{bYaiXS_+?JUORFh+4; z@E!J1VysxpBT~(aVI2(lezpC#rvX07U*E!H0!dM;Zx>!PPQz}R)ug|))xwX4Ki06B z61aES46AZRt@Y+mr<;e?Ang83?nhdc5?2drcUEZN)A0g!M-Q7X)1K73yt-fCyU$dg zRQu5LU8Vc(#Kwp_*K#mT)8ap4hw!5wCT<-T`3G;8%-#>^5+5f>Oy#j6(O_uYU21&8 z-M-+HP2Wjwp>JK+WD?{yu|Io97~@e$!ClTc*~w;vaY=XL!CQ7TXUn3C6~YXXGE5Ua zOYWDl>FYQ;gjnH&XGb)gZskO3pPg-fc@EnM;YACR-1<44sG5ewCd#3e2*gQ{3_ zCGH|CWS+$kQRL(yq68{46N`?@i}~bi_o2@4_~##Ah{F8}ADz2}*M=VElg#T%;Io-7 zOddVBXT8-M>ntv%(jH!fK;I2`U-!!+Va5X6wBZhZdSOHn!QQG$@2!~>r3RH2kwej~ z)`C`Y=B5wFe_W3v=8=GaKfnw?_uWjjO!*<@LVzZNDd+S<=QpC9DjWMyi))xpW$+{%UsmZ%|! z+tP+g3xn|T zOm#CCzMv;6mJTS??|J|BPJkRj4+o1y4gjg{LkS}TdM}^@qryWN{Bb}Fdj~LR#)=yx zOa}%6dJTj2TcDMhm<8I1LHh*Jd;o@FbBIrn4})$3^nu6-3ZM~Sof}Guq*9^)-3e&f zz=$9!puYiHIEX?D1FOr7Wp&wb3fUjf7(jE=U7Sn-tp`Eu+m^vJY2Cdvk!vS z^!(zi#9@CWcNct89-d=dd6xDcxl(g+m<9$|{G zM|dE75p={ZL^2{9QHZEO)FHrIGNKVaa7F zWvOOqVtK+cz%tG<$I8Ph&Z@+!!)nF4iPeuaiZzKfo3)s=iuDd_7wc=*DK<8?RcvTB zZ8l3bced?pJJ?d#PO)8Nt7m)6HpDgwejyM=Dk1fe_DB*k9GQg7L7qq6LUtfuBfqh8 zu}iVz*v;8J*lFyE?Ah$+*l)6Tu#d3Ma`1Dk=g{G>=kVc(;YjB=%TdSikmEJSET;e` zniH%SaBk;J;5^P*!Fh+Xm-7o37uPy20+%Bfm1{TGajr{T_qYbQzHZwK#3J`TS1 ze8zlR_+t68`L6IiYS)n^ZuZ7u!(ZW{3+l3DaUl48=9v2Z5(GYPH zi558_azmtl6=Id#Dyvn2tI}6pUeX0^m>_sasO-P$DQ}R3Pd&stGj? zevYw}j*u>pZkL{2hhFEpE^*z(buVQ&W$-e-GDl@@%Z$rP$=b^9lr5L-S4*MCWh$G`p;5yZL)Xml7)oax!G?X+b8hIMeG=(&6HTP@Y z*8HiZtwqGd2)!wFkQu`@h81H~j!QUgW5=;p3gzJRwI$Ap6I+t}m>8j`k=$_Mk ztB2O3=$+LY(qE@f(l5~OH$WL|GdOL~Zzyd@GAuM4G?Fp$F)B71F;+0%ZhYSOqY1`@ zZc=43ZK`7$XIgKDFtadAHftve5?zV8#6I(N=6>eo=HnKc7CS8JEm;em*<0Bkv481+b_jL& z%aP5|!SRIS8z-#OPN!yPA?K~m=bWcpOkC1kUbxD;M!4Q~<8$+JD|4IPXtptPWB(?# zO>vtZxQn?{-D^FN9&R4R9+RF#&n(Z^UYcG>Ufr9~o1-_kY!TZMxaG!HzOAIKSGKWk z+qmuAwjU&WQXy&5+sgZ-_c+;>P+Qjw{#&+vUCMcDzjd-uTzMEq0eCa3%yKv?XE^GZVk=*|?`}uf*Qn zdx!Q}>?=>=O`<0~-LJbp{{YJY>Vfuy>IZWU&L#UK-#>&olzr$&3OVI|DmL|a>U^4S zT6?-ydfs8S!?eTQ8HO39nF5)ynL|fxk5nH;9Zf#^HEU~DOSVRK{xQyD5y$$E+Z?aW zkXH%i-k_<)i1l&Of?fcA>UHrJ~^C zs*8s%L6;&fy{p_-*?rmi^6e}5D-~DQUp-kRRFzx}tB$E2uko+xuXV3|d~L(E+jY8i zRe!1eRdQYW`iUDNH_~r%-rRe0{#NX*$@-A`u?D|}q1#(;zi8al*xBUR)PBeM&b_^(I_Nz3Vu(ESemHFS$Lqasc-~}>NR5=e#l5{gYCihto#(sP??c|tjwO8% z_;B*0{Ku=Gj6SuFZyJ9+L7$laobpBVOUYM_ueT>1Ci|v>re>#;zlnY;{f__MJhO3T zWH#yt^2hO?3P0=Stmk^?gTMy|147Rh=Xta+RgzDzH${~k9HbUa3Q@zVV$>i#!)P$` z3ZR6eyeWQEnm+o|U$@XGs*gT;qoys!HpGUv zB#07DLPZA!(!zA2_0de_I)G-d)zAP$_wm(nGBaO~o3IZ0c1TsYPDm$Kgq#>ioo42YvpdW>5g zJ5o3`l43^nqlASU;54B+-rh-aH?yl<8-wMCdMXMBGD9!GbI8FOC)IH zOtI#8QxiN!!(3ZW?N7^=?X{;ReBa(M?t~@qf|mL7!XhLcg{8rS81Y z=mAW}HrR*i8)HTarx<`*Ca8daoQ4bdNJ&#m2ZL1tAB(AjI{zDz;XMY+luiMH|B3Jl z9&WaMFJw{u_2l0aIr%-6c3U(7>1xu}}{F!QsJS{=p$AQ)hLQEs0E}G42=a zUkL{RhO!2RpoL+kAUL7M8QXmUbrYwkD8dwdG%SeH>AYI^SGif{{B>+1?%ES;f0S#7PnJIyhf>@}5 zB>zhJa<#3~dCaHUfe1L;_PMfnf>wNKs1}#7kumW0kd( zwUzN;`U_lx#VTWQ%2;(}tcEg(p8$l_RtCWq2e2Rng9r?QvnGhsAk2b@3<9mLrm?xH znVE^Hu_;jgw9QR$7#xvctf7uE#+vG>{aIs-*2Uzm3^l(Pzq0>js96wL4mHf% zm7&=sI7B6o;v2qb!OE=VC#;OI+BnsP=*!f!GG`gSGQ9o{UzxKE|67E$qLD#AVQT+d z&a#Aauy1%2iB9=W$D9RB`k(H5HgMZL*NfpU=9i9TCo(L|5(>AiDhNP=#b#B2>L>& z!K#7Jyq7CtMaDAR+%6)>n?g6hs;ey(d5N`*SyJgh5B8&zf*8{+W{LjJUlwroj|dN^ zfXbp!eB4NZ5flSWEiEuesA*|xs%tGu{|Q=_Hx8lD!$BDqWEsO3?swVW0n2hr2ANPv zVGFVLUI(<}%bgm`n<^q0s!o{SC1AUp`yG{vGQO zNTN|zh6i;#9?zH<xeVRt55ltv3HSe>;FHw`ae6lvU0X(#uO(otkL|!7RD;Z3(g=0MBr#vq#hBR0d{UwrUuGZ-s;seu%OKYrR=DZc{uy8=rp z2sHEzA)H~6zl{K^FTK>Hzq3u+l3OWYbx1YWzm zeTciV>00m)eF?*TJ`alWD(udP1oWLU?3mPQJyjch@l=~C&xQR|xx}Xil>KW@d?tkJ zSOuszvuxg)f4kaT()#jd4Z4|kwjkm}xkK-!mv-WxWRCijU3^|qLzK`U9TA^4p*}e` zHaZ>=5m5LL|NOLJXp40}pOCKE&Yd~ft(mXZ&nVLh^<|!qPhV8h%k1(x^Vf&(Ukomq ziaxkq-KcnX=G2R#Zx;=xzuIhijt?ErS)b^xlYz>pOeNM`3oPbuIY>HsBqD0d&s6$5 zjhSoju|v6OKU1|v^>55Wx4u5OR}|07W)sshkvTG>%&Rgvs&_l@WS&%=ihkRVUDw;T zlnY0s`&wR|xI-T`40?{dFMhb53D zUs>k9%6qTRs}pv%GrIiK$H?frn_j1_CzK6aeGL7uv6*Igrh`D&I7GQcUlDev-o`?5=UCiK;<_R?4Ha9?-j%{{1_K6h)&om$PpUL(#{tMH7< z+y_ZVdQM>qv@H8N-qi97*oa{wcN)+5KaDL~<*K8Ti`cEwDlY%GQHfX$mE6|16RDQW6_?qX9v=p!JD1S&J z@8>ip`k-ZpsCcw_3_yF1yl<69Szm)OVJ~nw5{IRc|Aj^_}TTbK~(Ue zWc{L+h;;@>4B~3%q1z@GN;kXt%Y;Ss%J}D*Z!PLeEfs!74V`HOgwF0YUnb9tTW^8C|J&-MOsDfS-Qyvu)UXz>_81W8Ao_Vt{hyd^UAySwwdpU$oDn9n{PPt5I$R&et9Y7E<4)P@^*FEh);;Q iaoTsVLtF{mJA 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 literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile22.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile22.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f788a4849a1b3c3ad4d2d713e0633bb39268e1d7 GIT binary patch literal 15740 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 L29EENSa|q9;b1d} literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile23.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile23.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1fbc64d0ba3133e757127fec048228f148e892b0 GIT binary patch literal 15950 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 DhSZL< literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile25.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile25.jpg new file mode 100644 index 0000000000000000000000000000000000000000..18a4d0697cb0338776633f91901418d616da7c19 GIT binary patch literal 16426 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 Iu<-PM0Ct;f_5c6? literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile30.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile30.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ea68292a07d20dfd25180e52c89e546074d1073d GIT binary patch literal 15638 zcmeHOc|4TC|9^HD>%NIn){%AFUDvMrTK9bwIX&l}d7jazvDn(&_9XN=lSa zQ4%7ZRCEx(*>$A)p1=Qouh(yR=J`CId4E1L^O>2?%rno-{FC`Xh}Y88!W4qRV30ZZ zgXUif?fJy=vFQ8X}dkVPtVF(BY?(}OljG1vi3FtM8 z^cg_QE@I$-#sXPRaK8*L9H0fky%Agj@C7||3}qh^Lr*uL&mbVk*ozuOT?kP`J13MH z24jLUu`si6HnfK58D^lD?q(J?_9&b(UKxvla2Twb7KWgO)kI-6wbXE0I6Sll%1d0x z=Q@zsu|--2(31Oq}hCEYa)`#JotS0{d~7EaYJ)pm`SQBtY|){3 z>8FE$YmvrbFnBF2RtrZ!VO6zMaaveBsDZzE-6!d73I-kDhGg95!4G5y;$PGQZvX!S z7TD#a4F%E-a|jKYf;IpLHiJX-mgq>p1L{|cpqq;YLM-+(dOuz2i$8$;QeT7v(UpA> z1{m}@WCwisA{{}m1n^txO-uLrm41R=c|dT!A2NnmSy+%P%&bTxl8ud(ol}sDlY@g( zlwW{HaP4aGb!%6PiJ_#G(I`npDKRlwO*uu-Bh}Qz(F9#QPDfc)4M$f3V`F3E)(7M#K#JGLoEoHBn0P!A^2ePEs!|KCo}MBbjP;%1KpE}nFYzp#?Ao% z)w~cKhCslX5X{U>zzDE7diQ1GW9FB@8nOsDcp)W21aS!&#jH|B7n+0|A5KWCdeaiw z*o9Y#h^}5QgO-((S5w!}#1piPO^Bvu<`$Mt&MvMSHoCc!e8}5;{V3Z*!@?sXqoQMW z@7bHQZ~uYh%)?pPIY*A>=9Qc{S$e9hyyEo5OP8x_YU}D7ZZ@~vy4`xG?QYkj?w;Pq zPoDO@92y>ZH9Gcs{LSR2&tImdzkd7vgI+EesOqKPLc#th7au4WoQVm+grt`X29E?6 zf{%$=0?Wd0=z#PJ5s<_sunHPw6klj!lTvk@5b~xyWEYlJd%1p+UNlC@{Vr5DpndZp_#l0#CeW@_t*^*(T(9Z9%?z?SPQTi$W7FuNinSGo0_?Vr+ZMgI z){3uIcR^y?^PR@6XRuY-m*=5rO3~AK=v~k0@=dLTY0|ijKWTik#QAw>+X;W3>FwDj zc7%?&{Jw(o7YmuG`JCZ`kG#!@_cv&Hk74i7qT5b22*2Lt+E|S&zb$vx+vCHqxTOm3 zbjnRXyHb5ho@;61`&W!nUdl zJY>$$ziHNOAD~;gv12V;D8+OqED~ec6*s zzc20G%7O-xix)YZ5=iY=SeJ5 z)*4Lk~eLMtt%o9b8jnpZE9ZLk}lgg!b7urYuB%#t>&pwApN~(;@X82XYX#)maCKJzXZ6x zJAC`>r<5{URO`6I&nmX)ePcR2StDVcQ$kyM4uy(xQ1i_?4t-z+_E50Dw_U&2N3&ME zBY9^}T6V$ds8**MGlYGF(rsIjjQ1I>y(g|sx=eMoj@%Dj*V7lGO}<`-n9Vz>#K()> zcb(fg{L5U};{=xjpW1-mFRv2!)^5s{4lL@*AY|vrQG9E&8W7o#eft)H+ zugq*R_C;P>9mcFpHAs%VdO4Nngr*R+)26;5aYqejP5P-V+wf!M9IetnXqcVZB%6ma zy~?hv35}u|-?fnyP-JD^U{aS=pl~k>Uj@HqZ0jt3kY`Kw*zn|Og(uBJ@rkgzp=R_!LP3Ekik3@yurjXo$r$jvbe%kC!E+CQy8X4%A3)9(V#K>z&kMVB0PAdFHN}*GyiI;CYa|&%R5g{ngD)W4WXH)7{+@&7oMb#k2EF!yTLE z5IQHUt9~-PW7c%}nCA70Nr2Cv( zp!nMfU3TYktC*s_H`=Q;j_IUp_7%I4(l~E=p1V}w+uwDz&rqxFmC~^@_iMM0-sZ-H zG;zF9keP=pdaq8+=uRua1^r#_%80~1d9}Z-74axhLGDcT4fMOO$)7zYFI*k4V1g_N zt#?_6i*}aOjk(iye&Ks=dr0l<>a>sA%r!Ye%})(D$d{D!5duXKY50;lpGO~(N3HP3 z4(P|#WOU9b!lpV@?+X=G&kfBS+Idd=`?tAy=o_*^I14V?#N;WS<(+;zEjxe~4Nb&b zKJZYN=Dl`dcfwDKO%9vme&h_H>SI9cJalQBPexO<%kJIdZHUQRDo?Vqr6A8i+!vv! zK4zpptn;hItR23r1pk_C-R?BtEcjoN@RVdWG-Yd(-Kbo%?WUJG){*j!^N{-k3|0FW zPn8wor(A?&uxR5K=?t@4iCbBFlSAt2@NYb-O{O*4Blbb5X$cwMdUG(iwHMnNE))7E=-|Cs#dSj62oAb275dBKJ64r|0as&ieA^2pig zaE__pC~NXRWumFn+Fz8J^RQBItF7Ug%3+eF=9@^*iJl8Wf=&;qnzmhMbkL`+y7o>g zkYYbw;_A|*4kv7iyMF59>1QwAyL?`A+Wf?KKKD7<9-TV9dz@Mgmv@U_-36P6jz8At ze0|}h!S;JHdO;Ef`ajszhmG?rUC>wbE)b0#9H=kf{^%ZgI&A-)`+ToTpBFX9ZJ0Q+ zf878v@ujnAtKZF+E8MYRC!cO@bAG%U*9%65dmmRR-^=M1_a4L3ZfrG8Q#jR{7 zD~q)qmA<2!)QU-*V-Y=uiMnPTt#94#!NimwesMo++i=N)Z1$+?en&)(rop;n&L8x< zs#QEq_xfJ0EyQPe8+4pY)E*0|x+;Tn_}OFjIx4zw!>XAd3Ozed;V*I0EWQqQ1G)R|H_zk69Smz72_n;H zo@JC+9}n1|3;kf1|M-+Q)lT1uXopk#dPrQcRy*Wf{LipxT1t4M-i@v-3pw$4<_M(u9;fiBkE3)9<=+M0<`XT70RrB0GhLW*yp_{isPj$1w45Q>N5 zls8BBbtc{@)VK7gJXm??LjSXIQ+%qlRgT}cFSiGZtmFJS+$rua_%*T<3B2YlwGW1_ zzBy8 zjt*_kofCzjhP&AxMk(vmV#hm1KArJ3($DcfWtSr_D{%E$Zko`czW#R}2VNoO3dKU+ zVdSfyMPhAFnU5IVh!4$--#S^9*?Z5P8h>E}6K!Z5`AP6d9zutBGWouT{^yB}^U!O= z_wHshTB&qT${ppd0q3K$$GPe~dDb;PHYT5Tzq3Uje^Yd)W618-dQW}%Q9ii2H@{gc z0xek+*7CG2p7OL`Z1#Ag-SF34Wm__X@>(1#$`r^4`=e~?3I}Z7==Vz2SQeGr>&v&v z;94t&EBWSxu8AdzW{@W>(#r`+96Z@+$8{%N)Rizdd37*1GoVwv(S}@I-y~@o=T? zPxVf+m?I&I?E@o^N(za?P{Omcxoew|XtAQ3Nr#Z=$sr83W3cD=nFGeJ47$}MUkeki0u?-ec0)e~uJW<+VpG-YSe-G9qO zaPVc5Zll-(*z?rqpPNpP#@)JWE;cx_?ize3@6$`nt%gYl)fARBsc~lRnIgBkizRQ4 z%tLEDd17Lpx0DR*-h16f8ka^~NDd|O% zy>rDDccGC2ui`9)+s@f7#cldqC#tCL5goy2MWvWsSoHO#`uv48dLzzAHj!E#3g%qy z^ij@yRdzLXDoAIY%RVdXPb+iqFt)rcF5g|Zx97-AW6BSj+^5UdnmD{SZX*`0zrU=1 z{No!80vR`8HIr#AJ>|1HqWSMR({t`Ta~7Kutw^Cdmj-s| z-#;XCTTEbd6p2QfXW8_~By}>?6 zj9K0Gz^k@@L<>W@TVwa_vA>Vvm)9_<0I$eZH%rbLreZhFsMDU?XyC`g9;#VS^WC{< zf>k=9(tdro+tpoT2zK{J-Un*60!JHjZ+1w)T@wIU&VgL4CBY$ja<9TiVoZ?o4M=XBQ`-;J6*_(M1!GmSGnOeH@o7G z)&m8c0^fS-r4nQ|vOal3c*iA|g1eYe(9L3iaZYpO!kc%s=18MY$%X1AWf&)Vl-(_7 z(baOW54OMu&5Wrz-pGy6JUP?){4};1!i$z9j~bJ*;!mR9Z8sSW)~1dVh0Klch{(09 zXV=1#-vk^_Kc#VJtyZ#Z*@LcqlY9w7qh34WlJwsU>&6{Y*{573eOKqx#UvC9&CF7^ z%5ul0*Ol??`j3r{9NfHHMkMvxo5B=}LkFY_xGy_w?w-a=Z+E+rf1X1_uR6w5fwR;C zS!gy)6e>7KT#L%g#G)e$qdz*?zHiV!`f2tvQE*W1gHx~Invis!b%i|%Jl0=JlE)A1 zUT-mKmCd10-p7sL8@L7^=zV@T)R50p6K?OT6G{}~e^!0Ldut|Hp-HJt=wMX46~Bdy zsd4AT?A17e&W?w~(AN0ei~S=mlVT3H%DvT`*SQzSwr;=5E-k!BwGQj#ohV>kbFeu#6Ug|YEgdq+D{3u_`+qJ|(&b88AU z2o4rY1F2y&M>8W7SZ+ZfJHUb%8(0LxLXa0JG}y+;)S13CVnH-Ug#ysx`p@ERx|gx= z1wB$Sw@0CVkNdYze57Dn7+5T_2S`;PawrMVdjK5}85T_EPXJob+n-L;SKJ^$8pt4^ zMd-Be0YV0D?ktS%i!Ciwvx187c~v!gMfbs&h9({}~!y@C!SM}d5THTIz3Xd1=W zFAOD1l0#t$1U$-|92r0k3sbW9BKdpKd{D+gfx%wXXb4&;GhGVer`r|mgwmmGC=V)vPC^w>6?74*g|0$P z&~4}*^Z@FC`k_JS74#OGfTp1tut2P=kEz%Ib*VNI|$*hAP;*h|=Wz<90BKq3&PjJW#B4sJlqg& z1$TyThWo-p;4$z$@KpFwcnSOryawI`zX$Ju55nKVrw|Z=10jr%Kqw*b2xEjD!X2>< zK|}0BBqMSVC5S3S0|LAyBL)%g5Z{?tm;{(mOiE1JOy*23OeCfdrkzYFOnFS@Of^g` zOplmeGEFeeF>^7CF)J`@FEYYyva*6XZYtYfS*Y`kpi*|gZ~*nHTc+0xifvNf=Evb|!PVdrB_^$F*l)2vWB<&-!6C^(;BereaO~nZ%5k3K4#!K5@0|Rc@|?z;n>fQcQ#s2x z8#$kHe&*uhlHoGoa_0);O698HYUUc?`o=B5t;}t~?ZX|)F=h*ZZwMvc7fw zR~fX-2AO>_wKA{KB4`VA47w8iOqNSlUp7RxM7CRwMGi0LFLzAtfjnGZUEWVVU%pcT zuArenQ8=dXP?1@Yph#6LQGBe#sbr`Wp;W2#QdwBpS~*dv5D9Q>?fQ&j)cp{b*plznySXD)~QaaDX5Xv3e}#d3#i+u?^D02{!>F! zgQiiXF|H}C>8V+u`4}&Vx5ua8?+};?Mud36Rl;{I4XrS(i&`JGm9+h}Piw!{LFvhi#tOuD7My*4jaK7Iug2p4+4CL+r0OusGN| zzxm+j!4y zwHw8)&K>FQ>VC?7%7f^U?eS`p`lh5!y_?aSqc*o~S-mA-%eAdMTfMek@?`eh;Cb3} z*2~VT#B0ji!n?qGf@DlOLVD|C;FIMuPSz!7lE=2`Zp+;E+E>^2uHYZ{?wMBbwN=<4}%qglY$3Bv_i5&CTLc) zQ=zcX&7pN+f?zV$9xfZcH~eLUUPOMxw@BB>OHq7L)Ts7o`RD`DV=<;Nr8}5*kapaR zm55D;T=2Zmd(rCR%}e-8RhQRaE~pl$POgE~MAuBz`qd8Bxz#W}#!Hw^le4B=EZoS#xys^2v#i6C+mes90w@q(1w;HzI zxTAaLYMWMD{ayUs+It%JYTDJ?tM9AdukO(3sOi+~tb0Is(C|>_;q@;4uBJyOk8XEc zbl>Z-?RnVi();A`rpJR%e4f018t`Cde8U1;DhXk z%O4Fswoh!Fcr{6zod1;aS@d(+7qu@pr|hQ&rUR#Ez9xSY{Z{@R|Go9ch96@yk+aCz zqd(<-Hq2SgJ(~{%8xCFyJXxIQQA3rze1g2m%A}w`l_;-Z6|6Ew1=7)v0y8gvau~{+ z>`S5QqCZ}_fksh$bkQ5sZ7?>$#$-Q=c?^y06l3d5it#5Ae9-!O>^f0eQGvmMElL=ZsWU4x;(0fH^op1%p$;;gkSE zDKwfI<`tzx4V3~K*ij4}CgA0RMhRw2(iV~nqip-7?6-o^V;Cg^;wY33{ZY%-D~u9B zHlg^EL&NlN>KckTbxm;L!9`HSY2Xxbnrh$zJiMBsx+(@-SU>|lUR4oC&`?yxU=>xd zcyJLE)p6ib#Q`4Bqlp0*z-rQ;)j>SCRMk{*+8P8SLnADaXpF@f69I)K5;SqfSW~>Q z5gwyvs;Q&$cg~jcYe%DyffeZG)x$0#SMrvDZpPlmH2a`X#{3mbX7zB3=)WSCMK(H; zLxTds8M-yWt`!VM1#Gp!U={^eMlZ{_{cD+((aSQwL|mv8VD&})f3TO~HeUW@J;ny8 zMIeLmD>}%B9Iz1o*Vd>d0S07Q;{QyY>!{GX$%-caFPc5*bIV=mw^qN@ofjHCfN9wT z`B1h+n|Oth^*}8Xl)yhu%^7S`QrFPJU=_e-F;!6Ke<11BqqB@@WFYvT2(Lgc3EP0~ z@8cEb^`D6UhFwy?&>2K?4hjm;Go*Vgl)Yb2SWu{6P%z5aNfl+|MWRsY&kOlq2?q}2 zzc42mZ2tSl_N2$G@FMib-@S;c23|u=Q4K@Tz%WAKA2hMJg%HQ!EQG3nuZ~^7s43E= zG#MfACBRPr4hR894X=)2gussg8h8;90>7iF3cM0M1b7e<=ph~;K}b(a75FH6NKZu- z_%3<~WI@P~1)fU{s|I4}A;>KV7dV;>8cz=iAfF&)Utp}z(w;~*t4jFEj7CH{(E zmZUGw(a52p^uf{6o@fRZkd_&X!Yd)maw~1C35KKp)Zj}Q`ei-F#Nr=W^gpmQJ!?T! zFuI3=b*F6<-*DQ(Fb}f1FxPP+htTyeJ_S(p+)&2eC}SE*X~DAr4gmhoVgu!#RNYFWk@EalOh=nHxDX$!+b zD_F}gdrA~JV513WGE_!ZG%#xTMfjglzstD&k^wWdr6d?KE25TVmRcR?gkWat6+j;d z@k&^%5{}@!v~gDfi(w3ejLiNe@RuquhXix27_!fQtm~J=vNB>+a8PJCZK2a(Rlqjy z<%(Dlu?#o04G;7t)AX>aDoaIPVl88qRNB*md}&^R^ywC(M1S&^1)TiC!@|g*vdCl~ zSFeC@vYxtz1{fq%G}P5qH5R4+1})1Q29s%FpbQJL^x+HlyX^0PWx1sc8Bx4K7i5*N zDyo0v_E+Sx9GEDOX<&v6rZpg|i}>G|%TUuGl2?E=nd%$nr-#M=ve^pu?^yc)FDiMZ zdr-yW@$`u?UK7mI8Fpmo{0sLF)U0&L;O&Aw!B$ZRTY&#mvl90Y)ckTbOS9)cvhfFC zS%q}B{;bui4j1pbY=4Bg-d+_RAvuodXuOVHn#%TOm0 znMyYH3Z^#@=jh;H@V_FKMSh#E|NrFb|Lo+-!pV-|QyjstM)eI{7^~Lk$1wN$ZmxADMS^#5} z!iS_n4j_XOD=ZYe7hvhj8(@g`A!%(3q6K<|=}`i`e90=?gUP-+D!;h@UAn7ax3W24lrGN&s2!uTPsRJ6gaTsHb@OT1&Xb4>PUvl)UFECAlNa6Hav@l_`9@=7)(M2z)CjM*HXhw0Db%7Bn zg6v~T3kv)r^pU-*Aww1rj*mdwE7nac7K8+ce=qyvs!7fqut!7)yyaImtnvm!az z+1SB(6fQn7em))^K1tElf?{&g3i7hjXtWZ}KvM~$i$SCD)&yN+qPc~+f`*-ot*Ntt znK_YhDut7ai;ss-f}dZ4sEk%7{@1^G#;Fv0$Q}U$2QrpUrHF!0(<@G-fDe(3LnqAO zV22XpJPMo{&H|31Fn;tXJ_#m%0j!~emt+XDAUKTDgi|%5Jp}O8zn=l23oM)i0dcY* zML3KJ{4p5#-uw_-&XqG=T6LziI;T{r>coq+J9n}Sm8XHd zyqs%<2Owm%=IyHcb4OJ35_i|$cWRgw%d0q%Ad$|8$}z~_{fuL3))hx~u&4@r`1+9_ zI?qi);ozQ4r|XC6CZFdd8_R##n{_VCzc;IvmOI>bP)07WO}MDb|wB((B&Sr@ZQ%s$Bd%J8Uz}` zHwS-HR>LIJof~ZkJTR#8Y1g?5v+B2MSMzl?ZwhXjtkgRO_6^QqtA=#VN~3XVmuTm9 zf9aF$*pgc#DkxsxAO3Sg(bqY+l1{_Mxm8jptGv0T`4TNZyfVLSZyy*QKmJHLPVcmP zozZsf=|{2X+Vfm%ZZs!E`MR-`=H0!L{Jp<>hiimpTGouVt}Xt}K-;Y{ruwj=o~oje zpu^o3#a#!E!*&Mqm*}3XinBhEy*V>D*?%TB3NwWdc|+@5XB&6wMs|1IkkZ#L5wrTH z_s%@#N|3_AujeCDvtI~C+}#u$TjbDwas122@HXNLjhRoOu{Pn3?UFW%NgJQ2@mINi ztJl?=x~K77%i&en?7AD4)4|Qpa$GK2RNR_WQ9E)Z-Z?zhT~u95#@XnT1m~)2hr&Aw zip{^zWfZMj>z-rgK&kw8*~;{+@}&(GhwY@Tu=b~iS~hxQ`wHE4%y$V3pAt1%9lLsC zn?-YY_1#x5e1m)h>?Ivoo8RMT;zu@r@;vw4d-wB6vPY+G z6lQaniebr2Meqv9g&={7I$lB+viVw%C>L)iWafTY>Hf&0MnSOXu;<1s@%K(&N@3Gs z!qVX~mo0gVsRnWK5zPKPlV`5ui;Xfj!fGG61tx29%GK#KxLDi2m95@ieZ^(KV73{t z>v)7@{#~2Wfv3bE$YsFjrIVlb-r2kZCj(3mZF&)`7i8O%ET8r*y8AVJWEkbxSc4j9 zCo0-oH*!}WkuaLn{M_1AnvQ~XqOU;_yOSiFONn8S!y@pFuy{o%^RBzz!6l^=_Xqa43VG?S5~wpQqU&$Eom3Uk1d-^-eJE%>#|b zm^6{{2ZF~%S*B!#lg3{=9N zt-cClvsWzp>Uc*%I|V@+8MxRF@i!a{65pP`>zM(qdCL8~_y{XgUqPZ|Tp9er)e{Fi za-qD=y2g^w3@_5q>3k`q$nL;Om;1XzTzjRT#Zl@x1kH{4RY&uS5{3A=%d0gn84j76 zRp_fbXTYvu;O7Ra{Y37V!h|9b1#4T9KliCQ7;hCtT=TKl;HSx_Gb<{q4wWD080hv4 z;C7y1agoV#=Nk?m<#JMT$gt4eo|pg|YmY1@`c3EW;_^<-!d=)Bd)4J6;s(By_g2EF zUJvZjqtEW}D{%e&EG1IM*WWgLHE@nI%--C*FLWr&xqS^%C?G5F=Ge4clj!`@{{dz> B?}GpU literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile31.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile31.jpg new file mode 100644 index 0000000000000000000000000000000000000000..31455ca32170a853e876648cf24663dcb6493cf0 GIT binary patch literal 15567 zcmeHOc|26n+rKk|vG0V$C>gt%vCY`G!Ps|MDvZH|!Pu1~rIKugq=>RbS}kcKqD7QY zX;VmAsi-L4Gh<2hTi^fQ&*#0|bD#U1@AI7JJolXEoO|Y;xrcLu5Wkf<$sB^gV2~yF zgXUg{?k2`j10aY*QiRwc2;zkJU~q^HP^sYL2lNVXO$VnS3<1Hwm2pbISee)3fL^&k z*8p060Rsm#7RYjg>v?eE04)rz&EOP*&+B1esJmF0dU^p}gMc7YUs@P#K1C7jolzPX zj2X&|WI=KfOI=KrhfYfOaSg39*0+28Y8UvgU181$dSP+62(B?0F1~QI8x>rbTXm z56_t|YXbwnzz+a4GH1RlR-l(-f#wG^=K`JvFsV66h#fdK2GG#a1skv!4D9H!{;&uz2tU{^s=_XS698bTk0Tyv~6Gm;(te=mD4iKY$0e zIqO2949Dz4L*}3jz=6-;5ThkJ5%7Te)gl<~VuKJ1{fyC17yIHgkYDVJa3H$8FTwzW z@eDZuAHG1xFdhOp7JJj;b#8f_U_3k^IQJMbh1l8HkZi2%NFRi}CP? z;bbIbaR2o&*9-};Lw-;j0=6203&0Qpu(>u!8raDSq8cNxEgYbGvaqrt**Q450HBT^ zg2NC9I17T6l?6BfmdNP7ECQ^8GFW3aAxB^2>Ih+6a&`&(8j~|EB2M?lW!3%Y+c`Kz zSBQyAtd&E{D=2DcYH8yMI;Lhsa|=t7m9vYhoBKKsPqIHHATWr!DKaWLCN?fUA!WzT z)Lpyxq~+{Ckeio(u;5VXvEyas6_r&d&YnA8SKrWhq3K#{+x7O28#izD-0$t{fAH|p zz>Akd!y~UoUyr?+`1JY9UC`+T8PV&=j@!(H!)y??k0{2VsglW*bZ%TQ75Z4hlFH%r~_u*UX;K zl~_1%`1IK#R$3u!0mw>bGNqZcb_nc^Fn`ZlZ>ymL)#gJG|K`Uh{lN#xph^?8$@SYh?Q(pVWq_ zqi;-=@%#Iz8PwI}JpQXpvmZycHXYXhy1)6y=!rN z-hFjNWBl^jQyIYkWm6Ca7bTkNAYY6{(B z%{IJd(d!VRU%jqt6-V>acM98IoVI4ap@IVh$C*X6~hl>Lyi zZiCIP9oAJw4>vpW?5f-7mc8oWmB=^v^8DOwKVQy_1QR_D>M7xhN#@AZA8(}JS*9R9 zkNzC;96Wm^b~~S|?B^eETnZxd)5%x*=OBqXkCVHj&~=@$J;%1tY+fE1Yvx^%_7Dfh zoslk~6_;suc1zjM>M6{`i(k@9x47R?W;7lCrFQoYrQ&>P&*Wi-99?iMDne+8jN4HW3J)xK8XMf3k79N|VC=z2p5<}V{rBx$sgEe;e zHcIacdx><@ZQ?bztZd7YZyx5O+rPDctfi~rqjgyJd*ArwGwCjVz2%S>+o7@S~Mn9kSDO%+_48?LE1E zRX6tJX7TLry2vUhw))UuYL9iOXg`}km7vv@jvx3zz74sfLlY;I9=5(r+OBWYz@eD= z*!soBz3N`A_pRD>gEMij+hi>_a=qex5jWF%zp*A$ZcOZ$#hC+x8H3aA*40F1?7%Zq zmKUWhJg*2tZwKl}@2>RfdEk2gy`*KUx{R{5)F}Gu(4C0ENM37`>U-~I47zW;?F{F6 zR<`!U)7a0GcmeKRHL%QDj*7DSgyxD_#ClGTi)sQ{tts6aAJ18`S$+PvVJOESctG-7 zy>my_)%B&xL5(-}8O=fA?q)t&0-xlNB~@;E(Zr5~q9_CMp&5hcO?X)uFo4ssVKXE07?{3;IhaR=gk!#+g1+NUJdV9xPBe4|HlhZ6iUEZ^Z zX}89n;Rfd^>#Egj2)@0;!=r1^N2QI|VZZske7cUPN3%fGVL$2}(pjCF2~s2r)e)3E7PJ1-`p zh3k!y+#E#ezce|cKcxZ}4tBjIC${C`$nKjRi2K`>6l&_OqThW@`|LGw=F&3~3q&Gx z++rUp-dfr?>Pg@FMc}F3K8=$SnICmo>+?if9~p5`&Z!n6goG2ozs=4;-;hx`ll7CWd^EjM#BWDP;A49gxLFWSevs>z1Q&PrmA||e@Jv@-R2J#uieG!Qp zU_}PQy1$Zs*yAfo@vk|gQZhlX5PU(#Q`0!m)Qv6nuM}GCykF$m#40w=L7sOpG~FY7 zwbqEA3Nfq0#hbs#W?MAKTtBcgEuygz|HiA%Y)Y#$W*3x^nVkKtKM#Xjbv98r$&<|5 zA(Ppg{G+PISTDUb5#ALmMa>DDve}U`J@L({n0Mfr@R^LKhtGU>Tq)gESKxY(Pr+V~ zdsOpkMN4qGnYK#D2?QvUJ8ljrYUKd(GtdF;D@=d666UZcTnZk?v{Dbkm=!RDZ&4-C0q zpE+){>9(9fn2eF(G>7Jp=^-mu^hJX+M3cLFE>v#1f15HDwfn{$fswMO#jT0%|E*^;~Cb z(yFfNz*pWHC1mc=m_Fm}`W9UsZ#{0qBvtQy@!W6Mbk2)naliIXS4^I^k<<~F4~9K; zYCh&W1J5@U;Scy3b)DL-I~q}YNe<`uv(MsnTzrxHikWGpzOCi>bKG>&*O!^6+pyV1 zlWN6clKZEfRyHOZVm2i3E8Uw)AMcaJhB`#q>F>n8-$vVI(1$6GGsI;jvZ_wfbb6a= z>PR9U!v6e094oe@)msg2DNnCL!M<_YQRH(kRc4mSG&tW?xZ`>4G+x@#xWS1aHdXT^ zyVT}rh`T;C&8hhD5r2lgp)=7Qr}1^4v~q)P#Ji-QQStQj=w^efJqJh%(n-iM(uF-T z2Zb_*H1E83w1eaDVTJoY+G2{y-&}5am4w|++aH|T{Pg~XiY_!)t{EDY(((m=v?clG znu%)e^n?!`{fC@0w&g^0YedEiy}Reqwxa6&vxDUHjN{X;-WK{6n5OonZUr~_(vg^qE~D=IB4q3HyGaLM zt}mDshoOdgIq$`(>NQ};x`sd11ezG;1()0BDas37dQy-nvTxw=JFh(>h}j~^h<6yp zx+k$%yK>86ldDOQIY}EQYIFK;JJ6EOxUrMn zI;w`M1!hGqOKum>rc98sDha7ve7TuN^(S04RkXyiYeUzPB|f7M7HxPk?LP+u}Z@uGq-+$PwLf8AJ@fzW8581zUtoj;o{4*WC-H>xETBY~X z1!s9oeuQ%8v*G)tMZ_T}`AO#N<@HFkWbw7seMs~~S@)AIJAz|c!o!gv61xjj*NPQ% zh&AJd^;Q-VPzf%$+f%NerFDt+IvVNXQC^Zs9j#mC(oXS~NZ#8;T8X^tXkBn$fZ=2ZyCD!(Sfy^a69eX~I!Goo!`CqJ?LU*!A9$)z^mS zpp`y+30t1Fl|I|OGiURKy#D727pnpiD>$!i(;H5ARVauWvptZX)v6J=ZSBLh%_G|k zb7Qns$Ys>3hOZfPfn=65lihXngs@#TA8RRB3W|(6hO-~OGJSfH{J35zr{;SZ`FV?j zOVtKXk>SI>B?pw+U2@w>ZW?YJucf_5bcLT3U&HFkW@s=u5G<`Rb^a@8WQ>%7q#oor9efcxZ>C<$DPv>p4ad(PoGen-hQmhXTf2ipc za2{)*_w@Q^?x6|IK>M>z*#)vf*;irp6Pay&l|S^v3*U2R<=uGVA~`Ewl}>Xh3+*zz zvrn#FQs@WKLi6aMbOGTQzl*%)(!M|Cd(VfRS#P0q-f1V{)5AgOT6V*e?rCca3JSK} z)KfJ#gbg&s(`q^iH6Jo^9(dK~+60UK$ZkCN=-mBN;+kh9^YeXE2tVd&;?`qPyysfQ%&p))$#KG}sUlV+8VrrwDvd9D*q3~? zd3Kmv=v&`~HOX@8*dM+jyyHin~3#CIpgSzVh9im}>ZDNI!9(+Ah^v*;{&_&ZeT+=oVJ; zwN{(Yy{=B;G<;x^zju9#oLI)?H$~~BeS2gN^PYEH-#dku-Q;nx@HCf}L0y8m5_cI1 zS!6Lp6gj+?xC)h%gGI*{#ea0Rd*5Vu@Y9daMBzb&56=CB2I`E0(Hrj6}M zSxb6loy(oP9j(Iw9$Jz0yy^MauKAe8;BidOOiQX-X9(FRWiuESpQkLO!YGt zzM%UmmJTS??|J|BPJkRvj{=KD4gjg{Pl+T0dIz9GVxz(t{Bb}F`vo&-#)=yxOa}%6 zT8u#l&eO_F%sg$xp#4K>{s63Ht>rl zdlamO?|`8C?q&56Wwe)|sHkuqHMQ8-SXC;8tjcic((!KrOUQptEGtiyQQng8P-c_> z-{_Di6vL_Hu#m85Iw~^UmrOya{Mm^A*MiH;T4sl`6D5E`r_exAZlIM>X@THz)BLGX z)G!)~O8aL|_v~6bbJ;P-lz)kcMC&^_ni=n5Eq2-E&@5QrRQeD;RZ2! zp1btue7grU$Y-X%CGbpe3y-7*q8MybCl?esnjXWTL7!kGtc;(v!0#sFkQB5Al800v z9Hb5DLPih~vV!a(7svzJ0Fj{}C={YYvCtMM8T|aU7s`Tip+itHbR4RJYN4}G19S;$ zf!d+l&|RnxdJGLhBhXuD9GZe=z#=UY#sd?AiNmB}Yhg+-91IULfDvIfFejJ?%m)?- zqrswKTVN@$y|5hEAy^5l3U&r|0oDS$3A+b-1bYD+gMETc!x3<9xG;PbTn?@V$HR@` z)^Hd2dUzl_0-gZh0ndORgqOl=;Pvnp_-%L}d=UN?K8b)3TnJHw3_=BgN0=h)5uS(u z1Rb#zk%q`alp<;oO$hLoj2J|`LwsjpV-aFOv8b@xqho6VBV zgKZPrX0~*;BW$PGn%VBL4Y5svUkJpJN=SXAJ<=B$g-k^jAZw6Ukv+%}AT zyE(fjJB@uidmj4<_ABf??4#^69Q+(>IdnMeIs7@|IWjqpb2M>ubBu7za0+muIl+1X z=O)f%&V!t_oYy&@aDL|E;#$o`;Bw@ma&6-}$aR|Q2Gd|lY_;sbT8_2&wLxq1*LJM^ zDun2s-mhks@qi?R6nT6s(Gqqt97W&Vl*)! znB$noSV626c00BS`w6FrBjXBjz3M#b=ITl6jp`E`N*WZ6B8`WdLYlUkyELz9{?yXe zqHEP^jcLnj`)D85et;LoJK)ptHwdf*6G9T<65+d!mQIw;S)Gr%D!ReCCv;!yq4g+w z$MuHvSL^%gm*@`~pbUHrjv5Ra${6|@mKwe^T4UsIRBkkCtYEy!xW@Q{3C4tOQfKnj zRL3;YwAl<{W?`0Q)V4>YefAGgr7*lf{k$!ckBnQi%iw2DL~RguQ6 zw5<}Y+N`;)ovja9zqC=bq1!as!fmZ=_uD?PTWd$NYp{pxN%s5gpE{r&A{;I{vN<|9 z7COFi!a8kn>Tniv-spV7dCJAaCDY}xtGsKp>oqq%H*dEpx3BJI?m6y*>(tgIuDk6a z;X(Ci^hA2Pd6s)ldJ(;Hy+*t>y;HsW*Q3|Rt-rZJVnfJ=%NzMN`ffbu!|LPibHe9` zuf1=n@1!5e@37xE*_515e(P`Kf53l?qEE@8j0WfjT;JFa@kW-=Tp+2D(Xo9q0T3eV@SX|h>aHa6n@WBY3h}?*Ax;4E# z5*E2WvN1{+Or|=c<)e2-zlbr2DUA6R>lS-1P9TmJ*BP%EzbAe)!91aCGs|Z3=4)GI zwj^(Pk!YM)vK6+~Z|k+BHAy>?Mz&dOt4!uh4o<$a9kV@W`9_V__Z{3fml2TBm8q3k zl*N`s%j(ZI%&yE4$Vtc<+HbqR{s8Jg+JP^*8*^{wY2+2>bLL0q4<58R*jON2kX7*G zP|%^iLX*N%hb0g1IsEmA|B;>|qoUg4RmFRYr;k#PJ}j{)sV|i)%{#_+Eaup&<8H@0 z%J5}XCy{Dnm z(Wl>>@j26f*81$VbNF+$=hvP;TqjhQRu8L>uODv+Y8Y(vXuNm9>cX`q-KM&WsuwFR z$y_SDEOI&X3g?v_SLUuJT%BwVZ+_Pj*fMl&sou;9NW6CTVKD?Zr z_M-Ep^UKFWbIl<0_oDfVV_@fOCIQ8Om;zGkiDN(+t zxX=(MGg3+?6-J=E!AVfYY2lP{+8W>lJiLaoraA_kSU>|lUR@bS&{9^%V3pOe zcyJPwHF4ln#{nMDqm2P4z-lw@H9^JiV-qZqXo|&|5&?xJ60~uqSaZCo z2_B1Lg-% z3qU60S9X{`C1gJTuTP^E1(=W}iT^XtTu+VBO_nwBf6?qgpIhoezqR_s?mXY<0Zhj> z%%2(%Z{{0CF#u1QpaTAJ8ZO`?B~2|I3|0wzET#^g`5#Dz_ZTcwIt2*+C&J5+i^8^` z`}_Mw`Ti&3zhM^@Fm;B}UBbdb42&5O3*`_L78Mp56c&y$byi2&`jV+M#{Ing%i$nk z{1@(|fRF$Fu|F9(%c2P5_V*~Fu7%gqP}aZ@v@pyR#0PCGZa&2^IrFJH;A>*%F&fGY zDQ#v7VhM;7fCEy%(ZFkBm??-OfCf#6k@$0qA6; z3|Ru6AxpqBFa$gULm)6Q1T2$gXd^JS5eSSD2~3>?h9%%5MJ;6zFO@-zRn}70R>p(r zFK`VOtBl1dW7Uqu?VKjvaBVU#a0J8A(+|vhA;*~ zyb2bpf+M&rez>cI#V`j#rm=qs{G|%aA;DZLf#Uxk>-r_Jq>LCB9u^r*pYJqSHSn4D z(t}u*u>?1_iw^ap&<(KaYKv7~WG!J9RXWha0_nb?jOiA$Mt|~`1e}ASqoOF_VNodl zZoVPW6a!5yEig!^X=!SzYb{9s4O)^n4yVwgKpo~~8N(OuciG>FZ3p#??-8DR0he721JJJun@mquA0 z9@O!8JY!;v*9P-+rXQI)|HAzPHOoUXc)MUsu+=odC%}KIS&sV$YJLTq#o6;8X8ZwI zQehe$85I`lM4|bEWp00i#g_F4dkMbyIGAo-1pmfdg1QHR@ND7>d954|3A6z&FB-+bTVcBC1|0?F@`Z<#eef!;7?w9DF~0IhcH(u z{K;yR5DFNvq9Vb20hY160fuOQvQ9u4J=8bKfEwx>NKxAqP6^ag`^Ek5$_-z9%ULS^ zw|Bgy>}4+|OFSLsK+EjLAe#OoHpAXueDG^C7%Kv(ArynZe%f3vzYO=g0!u0gH1v3s z+JEU>@Oo)$`;RNQ_!1i$y5L?WR)<83jA8^Gl9@qtG}T{+XimUrYHONn8yg#&5wTcf zyeW9&FvXhS@dN_V7=-M<tl|&E5!kFK)}F;jKy6k;^3$0Wjj(BzeF;3Z-9** zD*O-(>_%Z_frD)*%!9chLWV^UYb>Y(|kS5ngLzIsxDkzwZHA(!>Vx6hMh^ z7z^WXK@})P0Kvl8*}@8Twt#&tCMBzrv*&)>b}?zkn=N7Vnet$hfw87|YDr*!X$K55 z%Eg*5Z&4rHQGO?P177ROe!qz0T%H5HEwS}>S45sl)r`Cw-sCj!Mt|bL=-}apF+FPd zJR^*^BueDSc=fqp>w-JR#viuy>7Hsivc)U>%WH$elt*qy0|G9R3TN}sxkR}O4Hgx) zW1sIRXXReMrt0cF;T>0fE`TgCq4B&eNFqfh%0p;GO1bf@U*41Kh@?#!Ar{qF_L0Ax zBOQl-rWU0{wTUd=C>o?rq3lD?C&9;=-bx+=Jk3?F44;% zfLmNsTvU0}@Xc!pS?#{JZ%Yz(U;g58{fO;)sZ$R3^hLv{w_`b;508Z=Bxs&_W>b5D zR0Kb=q2^Oh(T8b z5rT8nC;s(=P~-Jou$r7_6OGQ~xbODos{8yh&b&M98P_w?ljN2EJ}EnW?WRU=T}1~> zgi)44Tm6r=kfJAL?B!SUJ5tn~Q=}@CCx%A{g1@e}&RBzfe(z~=%b|6b{7>JnI4pYG zXJyF6mpiSz!?z?kcAcYs`51kZ_*`q|Wv4zdca!0cCz|eVXH@iYRAERo5rfH1hM4T&EI#NM0NIa=Fm{PSktJqTJL-OdqQop(5>*#H;IN z5iO#nA4iHe_M^EAUJl;(B0n(TZEoM2B4wufLmIWk)1SY2>`1=;jWIJxeWagZmT6-F zJ>Wg=+*y^6pQ3-7+Xp_pI&tw-)2VLL;lcpy*}mi9YI(Gt%?gc(C(uaPz{PFJRcH1* zTIml>pVaxG&}2lJ^j3XWb>TaFKW2JIM$U;OA6n>|kLba&Q>c3Ifx8EncA(MP<2etc z_pZ(gN`JSnL%}S&hn*2iBgul=<_2C|u1LjV;m3K?h{OipuqPO4k zFf)68t=+Bg>qK&~>GCaF%r*|HyT>WHlD!r|YDDp|UGlgfy?S^%lbR(HpuCgGmpa5dgV(J=_$rj zc^xb+RkJ!KmEt#cKl1YD+M6wCSgpA+;$BVa}2vVPW^gmp=&SwAs literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile32.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile32.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f6a2a2947dfe8993531873723df82cb0c4a67029 GIT binary patch literal 15660 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 TY<;S`p_+3uG3;5({L}vd$29$2 literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile33.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile33.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8362cf44a5d16375feead0c2c7798febfb04bbc1 GIT binary patch literal 16331 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 literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile34.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile34.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2b856a0dc773d8c366d39352ab5317e55416e7a1 GIT binary patch literal 16060 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 X@$LBwTRC5}X;kC13(I^07oPnOeBrVe literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile35.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile35.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4e59e21a91257f01a33006bbbb5f6123a2df39bf GIT binary patch literal 16062 zcmeHOc|26#`@b`TvF{35CS>ep#xgVZZ7}w|q6K3xVKCOTh!!Q;LRq3zq(!Mn+9*s~ z>{QxBl2TEjD1K*#q|fKO{{H*DUcbvd_qoq`KhJs2bIy6rxo7U3dpy?<3ENqdtsxi; z2HAo?Xztary`*?rAOw-gY7jRBLA;O<3=VMtY7aPt0lget)4(YTLqITaWu4M6PWJT# zpjRx=X92CUfPn)V3uO7h^)fhdfEEYWdT@%t=k>5Kv=k1uo^C*&ML>|HA3dBtpQ1?4 z?kHUh#tLOcwjq0%J3uUh+2~(a8?vJd3a3TT!eSsC2CHj;AsS%uC@kJU7iWMYKr5hv zdz# zvV0CdI`1o%BN4x7UI^k`pwof>1WV^_*bZo+1$qylg-d_e7j|Nv1}Y)=5*o3D=2}9t zaIEq(K_aj~<1iS40Tye3BciZ620AzcECIB@-+A39SbYix9SuaXuXEr4=0KtgdcfuX z58#0v?uJkp%P|MgkTvK7aNsjI#OjG|L;|3G_Xw7|xFE#BIAe{|Ut@6^$p0FPa3H#L zEW!YT)rP!)4_}~TS&aaWU!&>Qb#7^#U^N~PoO=pcLfl+jNG?upBofKP!_CVtF2K*n z$1f!+CL}H^EiWf4EhB?c(n6yYG!$iIRPd@AU_|Qb%A<+K1e}qUjxLU+1jfU|!_UVr zDIg$;TPd>=_g^1#^^gcRU%ZLHzTA;KxT5^K&S=IV!3h!n>qWgX*Iw5V#3aO)UT((z{` z^YAWPE-59wN*S%9s-~-_k0%ffEUie^HnwCtcMngm)oa#zQv#@gLBX_5QPDB6aq*iI zcJA7}CuQ%x)a-*fxp{{U=NFV7KXJ0G{8Yv1ia4sh0?-7o39w!GUDe3kHt^ zCqjgSb0wBb)Z7*67b&KIOX3!{$U0Wlz@w<+HYVZE=-^$Zr2A^sIIC*xn*C=LOa5Qg zY_Vd$>(vMGBS8NaL5M)6P)(TgN!`8@yAhLy4~{d)>TRagHm&R49H@}3I1uW*X~e1I zgM&e0wVo#u`=HQ$#9;sMZwg949-2u*Iv zwQ?r5ZY%67x^S_WlU~RlBi`k2LwdN{z<(Hfm$A8}tZv!c9bWa-$WzU#=lp#>4$9kU z3s0uq4t73i)jQi$*XzUuwM^)Vtu-yz+e8c9b8_30{jayUZPvRSn*M@I0DJCmgrZVU z)U(QP9rWGFlfnVM+9uU!^PZ-2LqBN`gLbA1bsl?YtmWCaI=Cr>Vd!p@+NU4XSr8_e z;921l8TIV$X94Xj?=Z*~*nVBCk^^N&QCJD>}JeruRn1p1?Y@&-lEQrv?5# zwd*QzLhE{}i${c2vL@5(d-c$+=Oa3`)CWYiF5j%MC*4YI+o_5!_gnj7kB@&M`E7NM z-l!+m!I@=x+oszk)cDMrR#~3<=cB60uP)eg-!-mzlv*Wbdf)A9`h% zGGRtpkI&mhy}ZlRfFZggJty|XF7F=fIsDLX_Mz^`vmifarMi=S{yKr(&>R$d4A<%- zq@MMdw|av^$}al~v&UQ91yZUvdS%HTz8Uq7PpOs_1bfLqtVq5;yRCSr8^AdjMNJ* zPkoGo|e6JpP#ugrXV z*;l}{FJp9k#K$m+ux4gLIdgNcpi7YHofnrUJJDH(Up~1rQ_9Yl^B^lXBMb^UPmjV-)yCz);POh!UC-QW58>REVPqMAgk30V^9gp}8c zatzVkFg35X1Ss^HY68U0?korSCmsH>%P+{JX6iua95& z8tOHAu=(8Qv~or~bHw#$CC}!RVI!fO*U@bg5*vCBL`m_{3vF5teB=iHPFPTiB9J0hf#7peUXOLo3)4^1t&B` zgs~|%1wCTE&PG2<^4#~i1;qVRmGVJ+FPctYtgdXR{^^6>XlO9-mj;mDg}CDMyY!)VX)H z;ek{6TN3T#B>He@TwRX)=KV3suUa?0`LX$w^7WhdbsVEshF=#?Pt@@;D*uABNZ8?n zoG8~jligq$guEdg&1pzCOWk_?a=OrQyac_?@oHW2mKy$=%(4xEgyBcCW_vEh zu|v66%ZodyUMlOmA+iFBJ5$iVr_(-cSudAJg{a+D<_|)@(1zUM!SU1TkM9g5CL23k z;Ze(YYX55EejT4XU3SfeAsM*0jY_r~`Q8Y=il4dDReLr=c|`KKO;t{RdjIrl`!ggB zY~Kq@j#s4(0&j?e@B3KJx7PAY*$+XQhU{+%Wpq;KRuNC_#IRGaaKdCT(}$ zKZp=`adOq^=W$;q2qOF`XJHwYJmn{A66(um5$k!^UegxIytA`S_tPa?F1s(EHVkH) zg!IXLt8r&$-dbOp6kOYKz-$hRSZ(E-De_qvd91?AD2BvLD2_Iv6wH{stkWNC_fL{& zw!WWV|5a32y^L=K_JN(L(CiJ{n(>>lLhY$3E}hyBhHkGL&VOS%+1)*MCkjg?KfAy& z*t%{OG3`~``TC0cq=R0M-+EB3f5a#g-*;>+C4>LA@A*qbK~Fo+^_d&A3~3%Y`|!%9 zH_d{W$OgW5>dJEvx%c|SjPax2VDvb$ZDwe+_`h41u@>F{WvFA5%TTF zeU*ss<3xtQ+NQ`qoC)Qngtt6$J2ODA5Pd}<&{BEOw2ckUZ&dF%t$UT{5T{l@2YI(+ z=!QpxD(w+JRbv$*r0TyaW!YR=*_5+8HL|vr@Xn{&YEu6}YzmZ~k(BkVHxGl8y|_(0 z(VN1_T$$0G^rPaexl!7kZSdAOIa+r3q{FVA)8pUVN(B2}h*zaQFRJ?Px_h(Wr3Y1`kF}<$)bJV)l-|g?o%hD_uhRdGIa8J$(?Ph z#}4h4dqGNmKeMWT-;;m0qdEqGR)3WDd+1Bkvu1;#? zrd)S>vTW;_pf~Guk5TxCV|&b#jcr<)@7Lak$!K+a^*-oScgcrp(^dJfH8u}#CU?Z+ zqiJWgwy*W>pvzZ^2|4~|t>=>sha)SmE8|>$_Sn3Q-(0+U`OLI>&-OCHC4L5ZY9Ql6 zBQ~peLc2s#=HRs3irOSo%!UME^^VE3u^uICm`k*i@owyg9rP5F9!yERDK2vxr`81B zpu6sDHCbYWuc)tN(9l}|*D zkgx7rc~~rcQ19UfS0^}*5MFrjlOv{t^3CI(&$00PsRu*$)IaaKTHcE0%e6wIb~bz^ z9BoKyQ5-+RpO)~E*<0YAz9T!bO*d+@*l34G9Y}QLb{TDUhucC%`ZwEwuSI{)4X4b>gOgCg>4(J zv=3Z=cj$~3P2O?ONonENJF%R(H$I+iynIhMY{h-CZ$YWrU#pk%Q?4J0O>Z@8>yn_@ zU*1bTJg`20Rtkn1?B?x=*D|_-9cg|2`D~DdXBQ#roJM{ek$kCX`NhG=X;|(A9{~3G*=Y=_L%z{Q+6uw#1{rU*_3xA zMzj0#Rd*H4p-7DfFJ5<*7Lx{{q-PnkH`XK3G9|b796+MSPqsbVx+^5MAtC}9D!n&f zYn5a^Q?i~QZnUD1h)VFl-JkUQB5z1?HqcFziuRF7WZv1XoO)jHm`q0sc?GiF)jq#q zM&~3CDk}R#obLHsapugE%Fi2jyl+kzqaD=|lp@O?h^#OfVq|#vBAsk3XpPy{yj;e6 zo2 zxBThIr(MfFXFqsMNQ?c@TdjQIdLH(Tv$|w+_l5{$`MT}7QL6*YAGfC5PnUT4My)0y z;<27v;AO0d(etJ){Db3qLCzQJvhtP0vTng@#xojwPW>>FD*V8onRoY@hs>;0MH=1X zWLT@|!vo6AGGaeSHhM=3(nQ2#{jUjH%lrLQ>AoCZwcbYkvfFOr=g0l>mE5N1R;RAY z&(Gh{Vx(nl3hS%eOh5a8c=ls@_9LH~T!)ZlKeB2MKe^O(UP`Y@=FlPE6Bvt3jM}S# zci8si1nHJ`zbj8)8(=8+YMj443JK8o`WB`b>KC{CcIkQZbnKcLJ;rlKeZoj|hpxk< z$lZ%pSk2?w4{i>2dwJ^*!0t^Ke56;a^R;mH=0=7-8au}A>h17l%7@l0X!!O0+Uc4i z?GJt5HG7-KSI0D6&BruO$^1+hB#d}lcy-#8?7Lk)b1$SvW{fC1Sr0#0>NKY3v<{AXS?B#*&hgcmg%L$?8pnA$^1-VQWD^ z#L04>lZBI7?b z{*z>6vCSY!qG&%!7L}ciMaLCy{^aiTq0aR1=O154;{B>0-FwAXL}m)f755|wIZTzN zj_ljHiu}etmrwmvpCCfy#SQq2-scCS%td_haF-yXD3XNev+64UjoDQ72F(_U{qYa% zMajz6mThA{u5Tk6ZRsFIF%$DIK7H*uF5}vC%3s$*&bvs3xv9x$H@Y)bz5LiK`vK}z z$S2-bvW3V@h_$`4Ra&KDi-ahb66fo$rKxI;&T~)Z2Kd*IEiE^?xH(&s9Y|n{8iM$3 z9cc7$IM^%=qenB`Y%EYe94zL81Q4`?q%@ln6#N*er4ZNSy#`6a~<`038|^9l_#{0b1NYghjJ<+#qoV zFc8p^EIMeO)?j1iX)_ib5JnFG7?#Zu0bv0wx)IQOV`Hd*Mu2^8XiqGS8VBfZKr4mD zgwX)~4bb9YRKF;&yUf~FSBj=mf&q;IG(W?`%@WW?5X8+Nw1oCwLPt~Mft_HFJv?GF zgBBDVjZ&egqOe3F0cA^#3#CR!Yr6PRLi`v3D9iA$2tWE}2%4`mO9~QY`4$D7tdAw? z>uch)fcqDZf0?<6`qu=jZRZhRT-j{~TlU~L@3-u4UU(@45hj3dGJo^@^C9TUZU~a> z`^{523U1!?ocA zxH;S&?g3v94}wR+6X3hx>F~qwQutYT4ZHzEipWEhA}SGe2=J7Q=tqnqzH@MKh;g7eG&u}8Y&kqRC>)U-+d0xW3OG)2 z)NnL%baA}m7~`1b6yTKMROd9{By+Ce4C0LA+{2m2S;kq-*~HnyIm9{1#l^LZ3(bY+ zvgKOKwTWvBR~pw5uJc^=Tpe74Tod3I0x6_A(irKC^g~7?_aO6;XOXv%oyZ~NH*P*| zd2SrHHMci6ojaL3kNY(DP3}(aVeT0oVV+ey20YF@0X&;|GI&n#)bX_O4DrnHitwU& z!F~bnCf+38!@QNeO}x)|zwq(#Dew{bT={5xJNORsUEsUR_loa3zbL;Nza{@V{uusr z{&N0${wMrj1Ox<>1Gg1u6vY2)q#ZCMYJTB}f(w5KI&-6s!^K6#OW}BeY7$ zTxf$(f>55&C80K2iDoJGP!(nL;+w1~VFMT#nm zl0*YUcZnVsy(2m(h7eN}vlOF7v zqEO<7ME^3xGUa9DWueP5mR(%dyKH*7>~izv)aCn@S1#{dJ|($A(oB*nnJRf+vPW`S z3MEC73X#f^x+3*lnnPMm+EqGEx>&kNdUS>O3d0qY6{#yOuIQ72%c#k?$!w81F7rTU zLRL=JMm9n=U-pjdsN6C+Gr3K2IdV7UM&!lhP2_3vIr6vU-=QQ>=BQBAVN@e(4E!8p zyE10wv6ZbWXB5y1o(jnd=M|nS@+uM(0~K==Z!3-|$tyW3ZB;s>)VGRf6=7BIsza-o ztEQCE%Bz)Al&>hiMN6W|=mhi`^fMI!6;qW+l~R>%RW4P6YKZC))pj+wnx0y)TA^B- zI$T{}ou+<7y+eajgQ!8*DAjnR$**ay8LN3l^Oe>zEeEY+tt(m|wUxBJwX?LD+Orrv zOep3A<|$SbYllt7)?q*6)NmABA+B3TK*w4qQKwdCTvuI}s#~o4SWisPQ7=XBw%$*D zygoy}Qhx-mg!jc4;U5vi2`+>*!d)UK(Sn#ryiWXXpl=XuaM9qCp{8Mo;c3ISMrb3d z(FvnLV+CVB<73ACCMXkMlcOg6rYlYTOiN7%%oNQ6%*xD$%~j1enV&WPXo0a{SX5g~ zSsGYwv#hs5SlL*mTD6iyNuHzv(hF+^>mchh)?+q$Hd}1!Z8>f2ZL@42k!8sgas_$J z4sW;3uF;;~-rc^ye!xM^f#Fc+2zRt|Jm~n$X_XV*>54PtOm;r#{M-fY66tcymCMz| zwb1pA8`f>B8`E9PeWUwn_el>6j|`8eo+_R(p0~Y(yw-VDculRgTAjVRe~tEBacZn&{gXrtf8OTL`Gt9?)V z{_u15EA^Z3C;J!qk5McshbZp@%mQ)(MySTrZ0c~JabR}f+aTkhgFz#~ronl^qclre z0d0Jf?WU4VQz6bFaJLXlaNo-;4w>Yo3OYtJ{^!Nvx)i&?jJe**iaB>UB7Rr{} zTUTyP+WKmn`L<)*VcY$;-%eCa+?6=A!)C{+B;KTuq=(6v5 z^M4cs7xWZb6rL}VDcV;wbtK?OXR%pvWr=Lb{*vjVw4;xY*&M4WRW8jt&UHNY_?r`6 zCzvM*Co9UN%l4Q3ET@&~{HvpQE>sabjK{Ic`= zFF+S!F1)Mqt?IpKfARJu!llZ~t1cH+i&dxAz-l(vj9m%7(qFr_w&SYZ)!TK3b=B9j zu9aV3dA;z4#Ep!byf=5;% zoO$c6@!jh!1}#_b5$;{NuYbShf!>4ahk6gITlHIO+VE|)?Zo!F4x^5nou-`)T~=Mq z-Q@23Jx)Cxy`H^~AFX@T|2W|B+b5w<#`|LXW}fbN#`!Gux$yJ67cwtOU#h-5->=_) zTx45_0N32KM-}$^7dLQwAW^~U7kq<>5 zRX$$+WcKO7*qX7SamM)E=d>?UU&_Dge!V^6GVx+EY;tBQ^_$eUQ{M^SnbWJMhiBq` zAb%YGsrs{S)_(TcTo`!a;FZ|pg>@c1O3N=G+@GpN2@lhb_lwZRYGJe?Bhz@W@(Q6w zqx`8sG`cbR)3sY@6fM9Qy;{!^;}~H{4W`*9FsSYcP9Btm5DGB>ZEC`66mJk877<2` z_Cv*oh0>!8;*HU4;rfPgz=^pZ7aoQ_sFBS;&p!HL=!oHh=p z2@sl5o9WSh@tX7~MWBHf#nxd39zGbf2=*dvzPM;w;BRGrRE(9wt{D(Vp^R9!22Os_ zv{m4z^Q`+JfH`U0VlxXS@(J%ADlY6IyggpqJ_BymPE3|;w(vk!jg!1oF&$p zU}-_X=vw2AwEu3|qP@-x8WnhfRbLbA0&*#D5xCaU-;&`H9>re2g2k)}ZUOzbj75<( zZq%so&=|IEJa}sbgV6@B+F&pXf=ja(W!C;{nWfo_GQUMU=``T=1^s_vFTx%DLZ~L} z7oZk^Y{u{G@BnJ)eE#2Fjrt|PhAc|_pJ{U=ZPqYZ(#8Kpw+CZxaR~j<>wgXB`A!dD z29DtYw7|_)e$iAD(8@$j@Q>5=052)&=^J3M>fmKD9nj`~L9)EZVp%e%K=3~iUV{83 z>;R;LCTf|kxLh=3vyX0uok5A96XyQuu>wh6QpbmQ4i2y_m!OzSt*Exx>zF6 z$x2zWL;_2eNMK=z1Qv!!WMha}HqFvTWNRZ5StSzLI*BYxz)Ony8X#V3fEcTxuYuPf zfaNc64Hm0`#c5!5G_blFAbtW67Ow$Eo2nG=t1ZO=Er$Lwn5g7zpLp^hAODii2 zOLI$-4uM3}!Qibea2OnkXs)Y+F~?dOY5%>&SiOtQT^eeBGk$0P%TO~fuo!CCw@X8_ zM|gy0C^ay8!Gfh(izO_Lv3Q);eDq~&TAH&6Um9NjgfGomg#RVNlIavMPT1Q2lCvn` z9v&DS=f|M_yTQg*vIt%jXN_|YTG)J?VZDWl(?$J?GYcgzLKcPoHbGg2Yl3Nv8n7Vo zxBNv(*7h8O8WqKw9PM04Heds3k+C4W6tXC{)VFvr9sQ>c|7Fl`@39va|Hz~Nfv;Jn zh10?89tHND0%<`pjQMFEm^r`Jai>PI^e^0n(oEK(Ed5cI43y@4WCI*FmL296z)PzA zPjt`sd=dy_UQ`C#b*z_s7vz?*7u{tY>K7FpL5p9+gH^%;{;ye!GL~Q~kKxYR$YU*A z*dAKKT7UIzvtSlRl8 zvL-@;CKjuSBYOOLaaSFSVNZl?WB(@bH&tK_3D#N()PVn3*KdhMWu*9s@TeHZ{Gh>V zgV($l8)8YuBHY?3Cd{A8Fv03*|ElsY)*|MYN*6|W5W_EwwcKLY=&$@m0r%jT=x8cv zEGjj?%P%yBYNDsF4<-q1eLX!L{RQd2gBIn@BdCmMP=|S0*7Sw@Q}$25qTDZoENFgF z^Rk*)ZJobZ`#W<{4lER?46wol%Nk(n0{#!?BGfvZ;uq>br3Xa^n_vmQeYS-CC)Op@ zk4{}09&`u<0&8JRz=L%<+mCFWf5ZI)HA_P>c)DOMu(kETE5LtMvlRCa)cg)MzgEwG zG2<_QMHQAYQPJUHZd7^z*yau}`PH-j!d`^`Y7Vwre}VsCE<#ragYazO2YdyF_b=!l z%tfd>g-WMd`bDrhh{xuL-|)X>EQt;Y)&JSW71`aH9aG%Ev_=n#nxCs!4>*6D z*TB&Dw=>#5&^AAlv8NyuOK*f9BZ}S0jM1zy5yPNR=O2O=MjUGz16KSuj|Bmg#fO53 z7)B_2mm+|oO%0`j87n#pJQrYD+Z$kt4xktWhBLzaqD^RFenC|2O%c=}BkkYZ|E}DK zUr#xU#sBe)x0t=;!DNwVz@BK?!x%)TWsK4Ds!9J^HJV-B zMO|PBX>l1U$WL3@m z@%_aFU+hUA$EL$iBaY8J;%xlbyq@oVw3 zJt@>UxjHxN^YP$~DQ<=5H_SG#pAy-3ac!?hD^5Y$ye#cd)uD_td6L(DNS$jsH_%>1 z5`4`C$6om4H0&{>dh*o`8^Vnu_w9B43gcF-%h9tcY8B;g2fI6q3oA}1Mx~@1)Ycz5 zw1bZeev&xwGc@koEPsq|K|#w?`|+p!5eZgfjg9vSYB(q-?Wbyt7H^04a#F}UrXyq1 zSh11T#PzJ4tmv%MZ*QK@T;U;Jvg&$?v~-w_)7tF?b5Pld>tVt>x=^y$tx9(52ca9M zjVP8)c{qn<jqhCJ8iA? z5z7qe%0&n7byp3Wd30NE8QAS#+FV>&e7Sm1YE_=z7R?(u1)bmh#%)gU@N|_&Z26(0 z#+)?lO?@;SP|&3L96i(T`Jv3%iHFctE;-e8Zs6d$g9&vJz1=zJS)sz0C%#Ku_ywe!}Y++Loo^7}Zt z+A!d5)XZupq~;?yJ@otSjTKV@2z;^T)a`9+4OSsI$l5PZe;U0&x|4T z9HiT-nSHH4@Xf%zH6F!;n_~)he_paHy*&qUtvlrPYK@@E90cQ;RojR4nSs*ZEDuxo z82hD_d#1-@EnbOGl=9N!L;6t4i$W0{LB;BK9ZqIHnBR}3!V9gouL|;y+0RpWwOh@t z>te@|&!SrRJK;}UYeQei{t%^1p1MEMf1QGUXCdP4Ede{HkFJz1B=RLA(E`ng4B4E)q0m@-8=aq0NqwK-Qk|Qg-hg`VCZP9vXtOYourK3yrIVr6%Ib_vqpZi z)_fT?Rpzulb^I;A+vy@3F;rXT=~Y@SSL9N13sGMJWa}d*`ts3F(5u=#wkgJiUAtP= z0CTFfLc1TaePHmBEJ39Gyl3O$-#X~9%Sgi`rc+<9jj-JPvIcX%q2Ey`2BG-L+13ZP zs!hx-$+dg9C9cfX%;5lf`BgVJoAm@a4XqFrW+pLOQ0QIV<}gKq`-fX4_20Y5Hao1H zvkfuFAwv_{#1l^zY?X)KSXe2+XjtI#g4xs1PVO z+8Z3uAhtqQ(@;zB+w3OMeH}b>6ZCy*QLLQw-Wl24L5q5UwvJ01~SZ{t!6IrwA1qQ}pvh{~0Z~9VQsNTn;nn4Th<+L6!|VQI8mu{lxTT;8}haDi|T?Mm&wN; z?@zoxB?%ytMRR|b^#3}yS()T!e<2o>+oLRi`2+y?i=~2Vx%S*(?PqTP`7Jm%Yj38IrSdy{v7?&n9JGjWbh-; zf*l6^l&PZwGrX?blkq3Ua}7qDk$< zFTBZ!5y$2@=#+41m%zTQg7clhI}0#)k!x(58He=rz`(9`<#-s^@8GE!xNw%U$ME0c3mNrV37D}jy zkFBIsRF)`yXNIIt-}U$3@Adj!?zzu>&ii@JbDnd~bIv_;@8XNa0f^Vy0&fAqU@*uE z{6UL@!bi*_$vYqjk5_0zj`_ zrpo~>zl?zc8VzJQ!L<^c7(fexYdtsx;7fYw7;-!lLr*85%MlP{N}vQ$mQs|tg9}PS zP0b8thPTAK8rwp2gBj?bS4+H|BMPH}RY9vk7&Wwpjv7t}t&Ku!>u6wfFj#0UlyzXq z&UK)$v&*zBpw}PyWrN%@-2${jDM*M3T+}caG$LimW)*;ETBc0^4NG0Z!07czV`o_8 z4*2l2rLwls@yq-jfJUY*mBkG7vMtlRfM#FDQvfC|4GFOV$A$wMI=5^CT1^dhZb_Ez zbNIO>U(p?j_(`)v5c4vf1pLRHzhuK6K=Ulqae(H{|5aaD!4eHrLhw~IVinD@il*b} z<)?szYnjHVsbO`{XdMg=g;v*5$LOH3pauTqbr#V36bw4I1If59f&-WX@h|HEm;XP2 z2llz>LVaT9BRFiw79su_C_TzA~iH5<#75~jGl_R28ON##>U3R$-ybY z#U+A~5|_gK*T-T##K#KxK#d64dI-)3L-4^C8zD(xCo_m@^uV@!fZ@r+%z|WPW9I;X zDqaWVIp zc<69k{E?#xX(vvmXPi2nnU#OOps=X8q_phnwaTjMn%e7ickeacZ)$F7ebDi&v#b00 zi&Ob^j&>{c z3ZcDf`OKx7>KO>RLo-ta!Xr7{d)e*t%@uDY4pQ*-;@!8B<95^;eb&y1@6Gb@s@+n7 z;n~tvl{3aGpE{jX->oUPKa>`-L5I{ zlsVP#u4SiVfc}NeZ4zwted8Ms3|_WjZPBlOo^XXUl{35Mtlahp0CzNT}y>-1y7!efoGSAK-%3PpRH%q?7pp+zV>57XwwqnP@F zy=t3n{2`lCqZhkfxZD7l5&kJ{HcEa+{Vj}pD9muUH{xzK1X_-n<@#f^^uL17k zCz>vPPAsNIHjg>|s9=kVAJyYIITG4FCA6*UI8Bs;a>lal_(xXY4@Jj^y9~R1wQD5X z683Z@r)QT%HoH_?A{@h&o9snWKcqHypT9NfHr3HQ@|d=+t0zR4bh{SuJ*z;Oj~5+( zo7*+)>q6-B{ccA;w}QA|QX%Qb(SN_}YE?x;)epDi?|c5@F{)89v`uh*9(H;J&p-G2 zKy`>rhxS5ya$E8>sfPilq|;cNKbMfIIK|^ry!lz~H4nSBuBl^oDn2iJqA}}+&d}Uz zd-mg3wMFM|5Pp@ityX^# zQm7IM^rmIZAfoo99d#ZWz~V&p-|i6ZZUH5vHFAS4Ug=L z-F=(+Nsg}Yh!O8Kp0n!jTQc($dF4o{XTzPLBE_kg}_4Vyx8 zug&20W9nY_o>@2P`X^)FHp*CS=Xk?C7&(9MS#5c;?3l=T%PS`bk_P5B*<3JJLihBW zG7aW8aJ|6|zwfCYeX`c8)OM_YSsee7a`EV*TaQw&64b{~qz# zYM17eJ6rSj@2qV-ZnOvmZ!+^v;rlF$%qw-*3o~zy&IvUjX3ZP?Ri`!l#Am-ylf}c# z`mg-FibWi2(T}VRc@}P2RZrdy=XsJ4@7SUG==I$#qnU3Er#m|*?$OXB{Hx1M!);p@ z5OeOe9V0a^(>A5+rE!GLk&#hpxpR`no6)mALw%dg^(gjxpBt^c6f*W9k=9-q(s{`v zQ1bnRKD%p)O?2+zJCCZg&g!LT_vCpHlR58tU%Hm<*V}Qi$5^NJwes2W$2Gg&G;yni zG;q9AlwE}I-8ZM^^{18Lg8ps~WJO|Lygt&}jCgiHabtPa9l7zDgfCu`S8n#>nIJr_ z`2p*2?w$2BgBC4bUouFep;_tJ=ibWP<9LLfIh8Jl10`|M-F8yoD| zqlU57sqOPhu&Fln$3nSP3q$kA_gs?vHoLG0%_2*MPr^kTn7k!V`lK`^rw34@pozWK zPrNi`cyC=fxc>*)E`!ba2yz}*@hKo?5xTa+H?^V4?cl+&R>b6e)fXqzr6KPD%vYhv z9%iIJtbGRm-2q#ikA2Iw?qD(q7W{)mEIENqj=a6W;myW-_FD!sY$Fuv7a`9lY82hG zJQX&G9~;Bh2aDE!l}WX%k-C5Ka6(9JE%u#PmD#k`qwshrDS3bDYdU_J&1tP-d!p-#kf6&`ine`6xt?6nP516e zMPkh7Yg`@Ll;QnbVs96HDtqJpJlKj#pAZ{3~i%zXI(!U zc2uc)TO9VQtjWQi^f79?bU=4Bq~fM5#_30w<=e=poK0)y=M=m46k)G%Qt>lG$(I|^ zsX0@sxgz2x=A75o?l)B17R{^pbUJaOO9mb27;3M782w=%CElP*EjQ8-lM>6UGDXqp ztShg=3wa3o^6q3?6O&MFJrGlrSc-zpvfGp7({Gj}7fRK*JlXiz^X_G=q?2)tGfrf> z{8ehc?YV$W`p_J^!l#$KNe+fC<_;K*nd6d5HM$|=dw+yRQ4_=J4eoTD#BY?`iyXsW zKPq)vAZb|h@dqb+I0hSZ=ENsEwOrz?>jSU6poa-3{Nw8To?S0)ljBG?lS3VB_=-K( zu)kG$@&adK^vCAzESIExX(8>1_Nr0M7vHY;lEz6A-ROn(xHV%3=4EtKg>Y*FdxcRl_69M^Sko^|!lO-W^*E!zyScSZL&ha7Ca?ro^>h7WG#!*AJ) zkXv6J+W4||FZpG!`1f=54#PA1inpZ&Wi>jN7AumD^+wv&=JeaWGwfbpZJk@G5`+lTIKvtr#XKa8aXXJ4?+HcQOxDELB!A24Je3sdg= zeBDJ}?No@;qyCX+`8npp(EeA+3%9l+<-~LE#vMn>O%}GliaF#T-Vhv&3=lh#sj@*N zvst7bE2y{j3=S3Tig`Hg_C-?H+(AbpQ8d&`d~fr;J+cXxxbwuH#^cu_pE%iMX3eV? zvY}{c1rZvT?nD?j7s~cE?t9-9JwZOF&Mk_UJRVYN@S2+J?v1p!G$A*pS+KL{AGvQP zI560tUoZXy)|b@xrJ?Li?EME;;sYb=Zo!ALJ`bwhubXsIPh?q}6l>|3CUU;<=$ z9&-7Nio1nNb_(l4`bBwvYO#}-sdba2LTByau2b{%iF4GApDS&(F<2kWX0)8)k>cL5 zPltp*r#*UsO$`6gT_t<@W(N9=gQ7@N=eA&E@s>U5v`xOoFWTZCCJFuZMxi=5_=TqP zj!LwFUf=!QoWqlvehydbQZr=)Qt!a3CzBhyO1|rfp83F;lF{{#ei4clqHmaArH z<@2hKZVz|5duk2A9?WHZq*N(#v@&<6hXg#I$YXW#wEZ&UMQ-BO{rcWxt~y)wL(ezm z?xx92VfU|Rsx{7t|A-#Oj(M86cUa~gy<0s0z`sj;0w*z@!;F*zQ{%o8<69mMd7o_i zvpEH3yRJ*`m)*?z;vH_BYhxnjYHD^Ti;4RKxjBX1I32d~;tJ`W}Q8%};n^N<6u@05!hL>`kyPWz1a2$^?sukYK$i z0ZVuna4w}tt3^U5LB9A&NBkt;{-HO7-LY|o?}qhbkE_P3RLDHg`+PMH#X_~Tmankh zeeLaqz3hh1O->!#dQes*>DIfPMEvoiGTGdfPFp*tu`;_nZk)Nyp=D4NZK23nh)3pF z4x0;QA2XLgrKO?eB66ZWx!8ZGGd%tI`xkS;fsG$sx&_yUr0}fE>Dtd@JCmOWa_yG?HS2a;wm>$VWE(cv%b6 z_KEK|V{v-BpPJK}_hw%09dVl!ce-EVqv5*FGh4oS*L}UiavceZ#d(7^L!|4FSEQ9l zE3P>@+^Qx6y^T^ly1O+>X#g~gioSX5crrRBz9W3y+=3t8&f;g>g$&?^C z*engCgi@U?O;BLF1%+$_8)9r=6ATSO1R^ci&c(u&zBPh3H$~9^XnFr<`7+(j*!Y5; zDO)+BP`~H>+dDpDFf|lx7C8c>x-W@F1oR<52SkJh)As6h)(+kQhWi1Zga42pf8@QwWJ_KQ>fNxTM@q98NsOB&PiS+#9$(#eb z;fElox_woBgz5cdXJ}}!j;d-zM1%^NL{yVo*R(7!^eeCJ;#|<-d30|Fz&MvsT%mgjW zSPbkS>=-N!mIcd$mBOyTuEQE&t+1!Cm#{(D80<4_4vv6x!Uf?Ha9Ox291Ayw+rVAn zTj7525O_5F5IhNf8lDd?hgZWJ;1A(l@B#RH_!I&{a3F*cQV3-P7Ga8TKzJf{AgG8v zhy+9iA|FwKs6&9KWW)es9Py2bg-L)3#iY!n%Vfpm#zbTaVcNr#$dtuY!c@)F$n=b9 zkZFQxftib0oLP}shZ)bjnc0syf;o;kgSm*giupcs7xQc8X%-e1VHP+Y`kn6*mT$&*nHWd*pk@_*y`BY*CzA%(0K-G{p5R? ze&OQclI1eu^5hETO5!Tzy2sVeHOno)t-_7x_T}EoeTKW5yMy~94;#-09%G(uJkdNE zJlA;IdB%C!cx8D_c?rCGc+c=&=Y7sQ#V5dr=5yc+IumD0pTEJ9*C~#1qK;XW>Ye5!4IYGSOF2Q4h7X{k{CxrNgG=$uRB8AQf-4YrQ zMhMFarSq_y>3iWK+-^xEO}D$j^sO(5Xu-8fI5w8L`{I7W2~gYr1GTN zq~_Plt#@00VEv`_ebVgGSm_Vlf4T7X)C zS}&R(ZH+#Fu0wyuC}4<~Gnh_wE_Dm_z3R2N{|f_|R#dlrY^+kPp5rC(M0_cJ!dlxp z*1FM#)5gUn%Vx+{!Io-UX9u^lwmV_>%6@}A#lFS?a=<&BaOiWCa}063;l$$P=yb;E zjWgOg#<|%=z-7Bjnai}RiEFZJubaGEnA=@<9``NorS3DE%r>QM8rZD5Id=0y4>1q2 zN3AE))7`VkbIQxyE8Xk$7R@bjTe`Q(ZH?U8x=n0bz_wf4dA1X_U-M@6-sD~8{hi=I z$R|ws;C-@vCWxlQQ^fbaM!qL~$4L65G}7n}{T*pL-umhLo$wpmX}B|E=Q!DvoJF48 zWwk4J*Nne|f3g2!z@~spfvkbvf!8Vg6n{!%(7K?=pr^r#!EwO@Avz)HArn*^Y7q@a z+e)hq6$Fc^M`7||hrMq2>D3cQT3ylM;8)&6CNB>JCF5POVaq#qSJ;?*qx|8i8`5Z@@x9`^wtcGjNDV~r@~GRoVGn(n<Qs312#P8M+*H z`P~)oE8SOZuHL2>Stck6WPs&1&|DFW#U3*T%mt4QLJA8Z;Yx zG~_bWJ4_sYKSCS%{`$}xt~VK@lB1<>F>h~X-%tP8 z_@i#YX5rOhAb8A z(U<#ldP^d>j0Y0R+R%Fs_zaRXETds2)NUiTeaqlP*(|}`l*67I8Id!ql&>O1B5ay ziV{kQRHo3Rfd+OILx&l7_@I)58H==~;zG$gekuE{V)PtF&44%xrANQju_uI*!%1dj zKN2m}0Hdj;gwfOnCl;JIC5#qE38Sq6PQb%zC~2yzffEgAz{jd9VQ^YX>S|~ubu<>7 zI3-OCIMp$L2lQyGffHc0>Gzr-ADrqM>KI)uoQbgs+T7d}jWIO`6xtl8jWI=AU`Wda8d~wqmaXl}rL&px4&`y^LJVTLF5Q`j}E3gJ_KPD_G1LV3yH;WUPp6b|%q+ z0>T)&wZU5}YHF(BRU0+6Wx>_iD>5GcT4r_jip(z&HwqbeeOdqC*eh^5f71+RbBjFbbB!7R))}Tz5eHLUh4D!rehc6 zOWqM>MhGPtfL6vSgMW;MD|kssQ%gq;tq5KgQwMGSHzeJAbe1WV1O)#R;Z?|=!ggTz z`w~J4|B3i-*q;g*I)kXLK|uiq#`K7Va@-jd8bsR}6pS);QAgPkh-3==e#!pTa1b#5 z3wM&h%YXmapY)tnQG|Z`dlXUE!fI(KX{h0})EFs<587zVQi@@4mQr=V*F-O2G?eI4 z+Kd#$5)dZ<2c&?bfz?!Fq#%v}8blG0g1Dot4x$o01$dC+=qVN;K}s)69mFVlN-sqn z#4dUYWI@W11(8bwtpRfBDXZxI$xA%bzj(Ehia6$zK1 z9ib5fD(T-1HinWF@QOHnoV$_(m*NclEmVvK>Svr;E_nsABJ{@uWg4UmrZJN5vcwIVy=pqfd_3j^>tN18Ie^EW8@BBDdPN+F&~RPaXc#pkLl&EG+(!NB;v~(@hJa zfZ3e}_MLW+{lchA(>yS9X|3Zz3Zd&?z6&55c%V#uP^MIr@=|0290ryV=9a;$s{K!N zFZFzL5XRg|REF#5FZnLZt!A&d%OZe4+ZjxbT)~4?!ZQBvSt~N8U@MR6Lf^=vFIyNM zTE$v{Ig%qu0h`T0m!U9B(NfdEF2nzx^}CG6FBz~>`&k4-W>waT%+Fp21|eA45(4NG zAyyfUR>t65f4;b@h*o1vgbZW<5coqCSVMxfRy4`?Ki2h2VnvyGWN;8IjJh;v(5m1y z@0EsFm9YZ1un!CLAyEy`>Z(7h{FAkU`Ki*88stYM1k#sVj2iu&zarqWGb}We1R9G( z@^vQ!gpmw1wY0z_p{k{+sjjsw{U>Ne-Z+><4Fz>rlBG{ynBQf82dv2bG{}TZpe@NN zqgB=aX6>)c6*;g_AW^{z7c6Ulsmu7^m@80=AR-~amPGLj-D!Zv{_@!>_U~B700M=y zIy|Uju~_=T7^@A|=?p(IbpC<+2WnP_WbkxBUtp_hf>(h5u4XmvAE@~iY<{ku|7OPD z04pj?!)T#FfzBj~FWBbxHTc=H{>EN`|7;G1TYrLoW3E6q?F8Z3gaCX6hWAhCZ_E{_ z3z0-2nG%BO9mF*%_!s<-j1`gJmh1n&xcWc4xWcC4f_L>QGwT6zdt9&z+(3|R5sJQny8R~`z2!>9p_ zT?${KDk*>jW~@*ecrHNGw>Q8P?Mu|z5kw6ngc^_o34SEiUBM(jJ=I^_|E}ENpHDd} z#sBt@IXSE?PT$nr3U8&T<=|#- z;c8@QWzP62g_DbmkB3i+pI^#cMNY;1zdjZjU!^!gjtCg|Amis(DPrJPN$~jw{TImP z1HlA?Gea!k%MPuj4^m)ENLKLu9R2w9K?;k2v6J9>LdgESr$W*g6X(<`4L(R=7vdn0 z69XSk{H_yxT8Cg}qU)3cWrMF=nBV}hTp;~p5Pm6RCIPh5dIIx)laRcL#os=^n92=& z@k;h(^=`z4SbyD3*G#K647~lM5!M{pT^5qN`EdQ`k&c=5@yT{E89rxzAalOpGo9g+ zA1RK~oZl4`&m&8P+6$^%^lh3?@Kq=eb9(NqE^Y80s(klOF*{6T`m2O_czCDR)U_72 zPlJb1VUF*W^7VUbq0!uQy^n{S{bOz-i{2{Lxtn+#JGAA>@qToTNSmolcLj&#`dR}8 zntl1fKJ}yr4L#TdhEIVFy^@$O#?H#>ZJwvtL>Q||DHMJ=M z@loUQRcc$|{<+&(WubKeE)HM2^(zc+#bYdU4oSuuH1oMVEB99D(6H4DZ=rpEp4+io zWqcO%vFQv;ExwvO@HJrEqP4GfKBh$D>aHgrPT?y%w^f7>oF3N~<0rtG%sGMVDSm6&)DDWxP&ugURc$xy;7b(ntG#ntiQa~d^F~ct}jbozinTSS8KmCg7aYd zM5}?D+tU+~PJhKW^v+g1=KW~hm5(pI|3M}Al&axokuK$R{=$3`ZE8mAu$qBWIOD>R zf$xSc+a0m2j^qRz9FvGGHcX<;Ye{ud?}P<%68l*Ugmgd5TIv?6Dt)pKrs<{7p81~s zw&Q%YN!j^^+%lzz!gabQG0pAa8<5fug-GEKFb^kO!q{7|m%#gPCX!oHo8P$Pp54`C zZ8P*p)Wk6|tpPH@GbeG0o0-%mXA3u{^Ss3>*vjSg>>?qNyydqmXvX`x@qrI%JIDKx zgoXE#l9m@9PH%Y_aDI1h#o$~j)sSX0aOSqLRv!6=j6$x`xe^JC@%fvF0@1y~2D|Tk zE-FBJO^06FenZmoV_vJsCpNvPJz^xymMr^>j_DCaj90wmSsSW;qg(ox_feMmU+=yc z{H89`Cw^S~LS@o<5zUf|iQP`|I)z&*FWX+ZGax>{OAoEac|P)ZeI%ddNb4bqo|a$QLHJ>_|F&2kv9j>c7=VW{WBF zy^uu+ywvM+#vUtl=c?*ppoyMl_K~rc)Mu}|%MWEs9yqh>I2c)a-#80Trl7m5a22!G z7!TXltEI+6RwXHjeb97*?}N*mAL&`OU*1t1B7jeND)K76?#w!olL-T9+mbEL#3Y#= z7~-rP?27E^2rIknk&-eegIr5cUW7Jt8NZIF*hOAazAS9pwAZ?6&&lgC$BLggR0Rg^ z`y>|3*3P=85?`3D`B;aqPNR-$Z^JCIu*(nn(E1riz9i~;`a|Zo;hQ9T1JV5AIK>O& z!g+U`O~f8x3ndSCY;ByMOt|6^f5mky@qAoV_tEs1TLMW~soM2*&mV>o+qi4utlXT^ z9^SXZ^0uWtKGG*vG1v)n)mvMd&Jm^AM2M4-G8tU6bExcu@@CVgo8Ms@NN%5961#SD zm2z4ZD`dA*kMv6=%bp7};O!w_5%<|+nKo(4${qA1q@eL!hU&p5f$tr~a$)wl6agQ$ zq`_8h;o`>3lp*p2>%NDScJS&h`RbuZS8aQQ%D;srmXCPjr@mZ}_B}t4|6o}2=sL=q z{@u8qjn6ps!Y3?GSJbw)in_ZGOf(b(Shb+mU1uqY)Z(LsFc-vj7NO(%t~;KZ5i6Zj zPV|ZriTl`C;&;pH)H!bWa6gacwpYnVqt_AKf z>;_)@p;OXZ|9`l=`Hd-ci zGmA8RW<&Y)T86besHGYr!EaJdnon(KAzWE+?v)#E69-;dWtEj5Rc=j%^)wP3E+-%M z+FHRQr`e~Ibq~#avyEl6{)_TfyY&SRY>FPDUWsYdx8h_|lM0-BuO;i{hEE<$^dpKp zYZ%-VbDFjo;X8BWGrH;CvzfetvxDj5TPlpTvy+T^(BGdvQM{fmu`hGv)$=?Hbk3bF zAtvtmujn;%u`GK&PaR!^1ogg;RZ8vt{1vOqQ2^KdMoPU2i>-9^+24O9(fFl=u#%$J z4WZVxg+44XWic@vuX3}KEc3}Ig0c@`h)wx>Ru=JX67BW1R!b>VphR?tdwQI}NrP&Z zn70}_gup40^D$O$q3?}DrMrbUobU+SYPc)zdc47$Sc)!HC95eX{^)+mp@PoNBZKQd zTzYY?Z6+xAzTM8V*U|XhoBS)wjAh~_IAEsRpSW%J37I}~U~+Tg#T*+%?to+WlfKGt viVN@R(x;;ho(&$x{`g}3T53Mn_>||3wKXi;FuEnp9S5^-9juX*TYULHQu26H literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile37.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile37.jpg new file mode 100644 index 0000000000000000000000000000000000000000..31844113b96179c964361adc7a9d2d6c82ed034a GIT binary patch literal 15823 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 azSpRJD`9izE&c+NEsfGwx|NM*pZyO7Xa9@< literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile39.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile39.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2f7a636b4a6827f73e03d70357c72674c304dd8d GIT binary patch literal 15609 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 UXFMZGJ05sSWvfhPIn6!!AB;oJG5`Po literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile41.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile41.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d25a4e4db4e4b2c2749452247112708301ec6dbe GIT binary patch literal 15664 zcmeHOc|26#`@b`TvG0V)STc4qW1F#WgR$>I3&zld!PrTqMJ3q^NhQiIrLyGHLMdrc zWGQX7NU3O1A%16uq)*@V_uudJ`d#k1&wbAOdG2$bbIxzyXZ~vfSW$5u7+c3xjJjIECPgdKeh$UKXaFZa`NfAjsU07Dii4QA9^q zlokeKfwCZ3k=#t}AcnzA^v|mm$=(TtQ^%`gF%S-e)zZTd^su@ptgfCGP7jBNRzpX3 zF50;UJnZ-qtpI4*eZOo#FVVMwb|@1Gv49H(hr=Q=7j0Gtc$OvF4A8KwMGTBlj%-e* zMIL|;&t81iMh1R~9|&k<_TsZxfnJU!njg@dOL!W(ko+eHYiw8CEC$IZ7qfNn}lYvO)bpaf}97u3U54imQ z0X(q7RUZmvIOZT4vIK1a4txfO7%kC-fCtpC7Qt{A8-!TuXN-ROvoFp7`Ja6e4n$Y> zMHpZ(>W~xg;Y)N3qY}XJvp4;`F070bjLHLo3r`_)h@FiM$;Qf#L?Ssj*g3g{dAPZ_ zxWxs9_=Ke-q}NDENJ^sQ)zK(fRXIsXMO`IT&?B|9q|pRJJkCH}QwzsX0^{J|;O62M zsn7G7R1+=1)vX-`vE}o!gZb7uPvL@NMy19FJuJiIH2T%foHdD7mMn%WO#>FS> z+Px=b@4o%1*@q72ZH9Gcs{LR$IPoKX`fBp7-hEXmUsOq1`V!{4aE&)(3I13Ac1<5EE3?2(k zga8Yx43}5)n8~cZq)M5eim7mWL#&}v3~Smg;d4C5XUX!4u$XR z^tRV&yCbpp3S7tSX0bIn7Z;#uYT=UwXriaGe0>{XnmleFOdj7Lb8Z0&JQd70y(P!O zka9x@45MAFkRo(Ahlm7sOxAbZ-RS; zPekOi+aGx}vb;kf59sTc)1o6Mc(-#p6cbghqz%$Y&62%WGExGYOg`%7?tOaH-=}eX z4UTVpPu+=ee#NZm^yXe|wDZ~UE_Ib5fvv0JWmD2El#|LU9=TrIA9HHz1JQ4*W7H;X zp-$E;>pG-3nx9W7?HoL3%YNIizAv>_P&ba4bnldJlG2&7tU2w{3z;y3 ztjA|wQ~8+>VA&W;~Lv#)}QUPla6aD^mGWXjwd=?Uvy zh)-ibhP?;QUya?#=Pv(g?u}c1WL_HiYVQIhQRj7LUlh9TUToK?tu(u#!{g1ot5P51 z;J8}pB3faI?!5Cr1YAZ3B3IANPZ?{Tep0s!J^@NzJbeYW87kzn* z`!gn{#(ngY@#|*4C}hTO=5-1(zVYJXbQe17$jc`;W{a8mYy8L7c(uG|ZyPp^=G$4Q zt~Z(yDtk#hHEKS=&9Zqc`${6`>*=-!DsA+HZkoU2WzTtd>~>|57$cGx(g7*09%&z} zwcD>z`e4`)(nG(A*V4MYHB+&9gpcm{*72#1zLu}fG5PO3lUHif-2A&OTQ5(Y`yApi zap=~Wk7;G}xVCZUA2l5Dd&dm;4v$1V_#(2g=U}8b7p=gm3*iwvqdhYkK-4^eI;x5pzdRs|oO9 z_g>|7i~c+x)tBtP|6@Cd`{gy#L0m6xR-Uh`X{r0+o-w!WC7(%yy0KlVTVM2%5t87{ ztDW@`@?E;~4>CG3F38*s$&<@wZ~ItIspFR1o9QdacB5_By?s>^t8>{Yg+r}J|I!

    >|NX@T%!#mQqPy6GRaUV>}{iE&JKE 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 R7h+Ug59(uTrHU4w{0|N?X1M?W literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile44.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile44.jpg new file mode 100644 index 0000000000000000000000000000000000000000..46ad6a1f20ddcba4483eab331ec802e095b0e0d6 GIT binary patch literal 15861 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 literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile45.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile45.jpg new file mode 100644 index 0000000000000000000000000000000000000000..89d2d325d1a424dd79615716be9bfe0b11ebd75f GIT binary patch literal 15843 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 X{kijq4t+}M>Z{j55|M=|3y=N>IaN&L literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile47.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile47.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b702178926843c09a5482df0996793b79a4d3a76 GIT binary patch literal 16214 zcmeHOc|26#`@b`TvG2-mluY)SvCY`G!DQdFR2YK^gRzS&l}h%aloDl2X|*I3CDNh@ zl}bcOD+)#NJ2NDGKHv5C-|zMMUGBNhea`!N?sJ}V&U4N^bI-z)g+YjagE`3@g27;r zCHR9DhF0w%#!~$uh(uC^*dYkwg!o`^hz(F_;N%DNYH-Z}ryvXg!N8Sqio;l$*OP#j zSfWn@di@dx4rnZpKcJC$i_c;OdO4P8en4|B;b{PqmWPDcfn%cq4IN*y0gJ)FjxWkG zd=5Xp=qrXJ5x;0o2x48Lvw;72OBZd}4rsn5It|eLrN7GyJGn>$l@NRdjaWgmt)Lk= z#`AMP!m~uNp)N9@N0!c|9i?Z3+e*_eV0X3*Z3eK!Qtpz~%oB z;DKZ(T_}X%m`pTe4%z@5_zVs)TB0KX52)WQg5fSU2(i@982$8DUz`QSejPA=Kz$z$o=os$KFj$e)&2zJ@f7RETdd7P}RSV#e)5%Tmqn6a26H>3zAVT7(51? z2muyWSuC5Nu>;a4Txbn0gm77w0NDjiGfDjG{41_Ma6j^?y~e z<%0b#S3ks!0PR}!%>4L=GJK{8EJ%_Eyzq9~NQ;VN2Kp%QfReH4(rpaSALFBPbvgZ~c{}Vxc(_8b+ z>nGI_E z(-}7c?aIvh=6f6aY}ufWDNP}F!wSu<)ZnzT#Hag4wmHUWUJA|{VB^7_ITW^5t~cU& zO{hBh_H-G)pSPMp?dgJNS?thH>ivKnS$y3k_w`j>`rQI=1<`e#^wRsa0=f@}@W#7T zd4@+kzdgyLmg^A$xk58jCs&Oe<4xkUEhQ>l%^0GQnkDw+Tm>e zP#0^i;SGx(`(XX*jh#{)%`ZMEqz;|4V!y3l_c;B$pmr=V@!kpVM1?aIxj!_^FXq7X za-W>t5b^SMyD|*X6Yes#H+pr?aPOh}KJ)iA#-0cGwAU!vS`~Wn?1C1c=n`C~C!bR8 z6VBSr*1LCFRT(|m=ESqRc8hDS)S+t;@9^aZ^OJwRntL5YbU&o0gexYQBh!Aollfq| z1My|_=dj1%g=;aXd@gcde!O!oj5wGcCht@9Oj? zI5_USObM;HO#9vgX}ftng}FGfCcO-cN9|=svteIr_Uu$DJ}Bdnaw9&vI!iX^?Imv> z!~X0K6JwsbDfo?ZQ}Q`+fxPwshBpT;O?RVn550VPbFP$`FYP-j?cVZ5=pC&gRXdc~`e_zMXD=pwv!}@1gnHHMz~hW0Dj_q76u*NL!?gYJ^RY z#!jCGnat2vNLSrPUUSRJ)|~atBYbqb_jb>;bTz!SisZicj$b{W;q2RE-r6*A?rX5? zhy875CNnDNvF&3HKWjMRc8}`u9TT(|c8gY3@ii?Vlv53bE)Z=bBB)N)Ji&hZvxyV*YM(y_Xc)uG~q{QlO%S9D&@ zzP9Deyr3;MJ2TTsl#8FP+dXgi`3OCCuVh2Z`-uDdOxY*$=?oVc%k=W@$E z+lseD>ZfV+q2QRt15R=KqU47}!v-nPnFZsB^vI}|&2^HIa;Z22+K6Bg$W3}y|^x>;2dm9hN; zrYu9HEj(`s!|(g+MjuLec0YD`^ik6Crn;=MmGmh3`tbem!3bU}lj^Pya|RD?zrPp8 zGf=kf)QgxeQ+NUH-KSyMH5?UXb@9y=^N3BH?pM?Va&GQ;pz-;lCEJECpEnQZ83gr9 zeyek8&$+&-G$pX1BhzRB3Uf2_&JmcDN0wB%>O~RT)jFz|qupQPPR{1O;eGaEQNXkAGyTRo9j{f6p1xnd^-UWu zCcK5~os#?lMCxmrn$w?FfeQz@+>sYec=CErM?2zCs*=L#+Uw{KGwEMEC(btwkXRrR zq5Tf~aPjuihEWgt_OAjjY%?{^h-ZJ+Wvwd^x%t$Hi*ix*2tuehIvZcw;P>cL`Wq|! z(Y=O=b-54blwnhy>i0#8Yv*6hWo|z!^ZncW0`v`8wdw#|tcAr}=74WbTXudhJq{XA z+VId*Q;z@Y`5h@gsWt^1j(d=EgqqL62@B9gf4|(8T9+L=#ySuax73~-$X^S258}Rx z#P+iygJ2J4NI&fG6{YyM9MU_oL9h@UBIBv)9BAs67P~hJH*LL!3an!kn-?IDhZvgf zQN9{0#7~9jHDO}SU*&Qw>Sb>o*p(jM(13sES!*_}buW51l$D*5`>n44gOj?DD4gU$ zW^I?v?n(Jkb=p`j<7OhfGe(-47dmadbI0t&H^*Y${sH0hSucvte|M0O>8veuImD-6 zr^h|2dA*_~sN76jrTtlPUO`v2@D^L+)78V|4chNwyvKXbiwHY)(X?&5PwSz}n_T-Q zl*kE_7kRq1X~QXAiPy?MpL+iCqsteGQ_zd-M7QyS zd!z@5sYA}@?SX@XGD!oG71(<02|LxR0_bXm1&FgbY5jl=zh_6VP+18YN`z(db^|RDFxi_V@00VUns{Up@BQHeU3kSUjq^-x*z?Z6tlv`IBLH zt(v#_u7FGR$M6SyjXKY!>W+rjG|A%}e)d|tjg33zwt8+>sdsxh{vtP>H1jI^Tq`#B z*pynasO0`xM~Q|ML(JxQex0iNt^O|T$7I@WgI-K=tRXHZkyUkyrqk1S zx|Ss3F6_r2$gw&hy>7!`LU~3N3igfDma;y-sWQ7vw%+NX!hMe$=kPKP#`TT_(dpCA zb4#s{2fOJ*vz&^bpYmtf89EW|a2hk2GRpP3;UAKIM#j-IqM8k^cOM`r$Rr`hNSF7@ z9umqL*1Z4G!4{6ghaTDg*#=Wg{^oqgvn2Fx`u?D_<`<7HS9GGe^3Bkw9W7t+$6Hc5 z)=pG&XT*PM?>p?2m7Ev;KqDef=tGxt>*}hH1Bb{NStn;*%8gFs*1yzvAGB>-TWon_ z?8L)8h56a#AIRfnibbbBbQg&A9#kfMt+t{#MMdO8^Pk^vEkN**FW(%uc)BB$GZR%e z#q~c(y?)GagJ<==>df=co{yR1v*fG_0>6E28z{C;4C3;jdb||W%1s zV6`e$#wM*yoPT5omNozCr_-&M?(m05+!gv3kgoQ%b~QJ->1cFTr_qB)B4n#edq{^~ zZ7Q4>gQ13dIJ;t1_3E)>ogDi z(}G?m*PC%$wR^z%(2wIhm%aI2-hw%sv&NBd=OL&Y}&a7$l7i*^Kh zOoD&I>)<>R45wMA`Q&)~4aufXzF@zBP3liYx666+7f{ z?N!6o0`nqQB~!(6DHEieN|7Uq3Rr z^3Csol0=Ht%z=Za)bkze?tI|**jHp$q3iY2c&+faC+y$arDpt3exbut4LQf6RC*>a zJFUkY3|GE4F!HGM7;zX%d7eFgbrTXTS$rcc6N#QEd+)XO+f z5qT$LG|pa+F>WuDf6&2IrQ*RP)-LaG$9Fe{Xe|30r2y?4(!a+TQO(HAN!Xr=gR!_;A8zT#l zgg0M&!i(0@fjztOwp}jhdl`SF%0ID!^Ln!0NQR3-VdR+2frB|WH3E{?J!#$cI@vHk zT3dx&My;x!$)XFSu$-TIP)m;w-F^CV4dq(lF{Ad8+$V2LUtA$St5eE5{k@F*vc=xH zYO{yPNRdy;0i`zQ{MM2V!!6@Aw2z3+urp$7SzXu+4W{~oR%!J`pI*~KZg(h}ce&k9 zJw2o9YVK5&!#gLBOpvvh^YOV-*ryf1De7Hznv;@Q~e zovSAE?mfY0M1Sn7l|R>1fPG`9B-+-qISg6hwLL$=&CmEr=kB{%A}`-4)`f*V(RB2` zgf-B6acdj*@PuZ7-G#>7LOG$_>#(|s?AG4OA9`X(K62+2+e3^n+-jdHisOfN-?$6<%{0pP%b{E`^@oWTAA)aTj6o$)HRPyWttP^mT=Wg~=Uy zs^*5U{>C`k>3f9JpR)2Ed)DP!2d(;%+i>XV#YbnwG|x*OJm`HAW0HeW9D4N*`!F?L zyyKxy{j(7r4Eau--M7aLaRNp0iR{rcX0wysF+WB+%R zzP1Uss9To{F|9L_KjVk-V;&~1-4?}rZ&b|P3F?&`CrC{nV@0CD(3o6leAV5q}30~(eTjZk1s^wL4{9FeZmsqIegN`dQ3m zdSjK(rBvC^ix3#N3Lofuu|L9Cz*`$`AD|aO6cK!0d){|T9!04|r9)(2>^&<%lDxU; zgYh3ti3GiEUBrm?q{0i&MqDN&9d1?nYB)=K6s>RHdP{E?x;tH|qGZVG73DJI8EYxp zL1>SUwyaMOUthINM37C6b>wSlx}uHU!qbIU+#5-zrd#YC?aWEmM6g5+LEM(sR9Yw; zES84QBI%A6CMdAnf}9MFA&kVF_AVDmYc( z{^jFeW-g=tHNmLcMZ^~eW}U%S-TTe^E&H1nS_(nm99unT73h_S>h7cd`K`cUC5W?#i$bl`_n>B|k#OQf$ zGNX&_9?&44nf{T$v%xJqf*OEguuUDEQRFCkG=m0xf|0N?e%1oNn}|Wu&{}9cqypg} zZAcd~f{4%t$PRLb+@Z}78483#AUYHSB|s_Q=dXQG4wMfahKiw+P!&`IU4ZJLCa48! zgYH5PpLlz=WBd;U7k*|^8*tytc*m3OU z>>lhi_Eh!)_EYTF*t^+B+2=U;Io5ILaM*G9al~s zk-=4nRr0GytAbZ$uez|RZ`JH-sny1-DXaIcu36o^dPY=2)JT*fnl5@)v{!Ui3?)Vs z3lhr}s~3AA&LXZT?jRl`eoXw9_y-AL30(=YM7qQUiGE4Aq@tvwAa(;b72AlN#3|y)xFfh8bslwd^(6HM^$86n4T{DwjVGEynl_rdHE(GC)Y8_X zYt?9tY0GJQYZqxh#tY-^@frBr1Xh9xA&JmL_^zX+6RC4S=d-SgZjkON-M4yZJ&N8* zyyz4IR zPIYhaKzg`(lzU8h5h0!z%KL|p zolmLHlrPD*$akDq@Hnb~DDJ(5)FkB}*KYW~SMK6zl zMQn;_h!h5sse4iDqjp6NMH@sPiT)Pj8gnsLAeI(;FHSLTZ`^3Sd3@P6mTly1HxgtM zQWAy|jT1|@!?ycwzmc>yX=l>wWQ*j=6wZ{Ol>4ce)V$QMJKT0O?v&b@ymNS$#jff! z-ZXmJ5z?|iyn`kj54eFgh|WRNrNWMVT9WiDj-XLV+4Wgp97 z%c14;_c5bmHN{fJ`-*3eQ;$C>u_&o4l`kzg!FD40#G8|@C)>;L zWmVXtIqB_2c3&L z_wKy+`MwKQ7j9g{U#z*b?ov^$P;Gi0tS+u@ygsmgu))2d>+*)nHyU*tYp9~9cf2aPg*4?^$n)hn&Yu>Nz)atBzp#7lXA>m+Job7#_{8tY+o!=#C;FrN=bj}$XMLXjg8xOqfaE~wONEzb2ek&T4w((z zd*$@%*)Vzd{Yb>fkJmfj@VqG)l^Lyii+kHNW&LEgn$nYhJ>Ch>pxsy=?9z7$n*Xoy;@Pna536{7~}8ODN{R}du<j0X;Rzm|2-Opdg(ad}a0`BzDE9RUr>Z+mi05vcNC#Yd?YB-zLN`Jhw7n3J@{$3;^8e=GZ=V2m7Q$$&TtrN_9{vGs|hMpMkF z0hEYH1DvLoGEP$)oOo~&lyO=(Wt_GKH~|l@p{%Kn0VfvFfR9&K#u2oX)iGFQbu1p7 z1Z7PeIMs202lQxTzzMM0jC)Ox4^DLrb)2pi!Nk}EOC*|Nai&B-VTlB7oGI2EZ)$?a zXqap3sr}uuWqa-DR0{9{qr3*#CFDxpGSJ=B*OYD_8o`{ug2}7_ZVCOjjAfCHj+BVd z;3%eUZSd6!2BQW(wZUMP1XpG+%een*nU&egGQUM!XjI_!CH;S4FT-tof+z;e51^KS zOvdl*P(MoWV*cMgjrt|Pge*(^pQ&>_HAXjC(Zv5nvj=@{xeNW#>VI|T#YPWcIyRwx zRR1_LpGb-UsAYl*_{V8DgO8LnwRA98CGfGBI;iu%AQ|3cuuSO`Ao!mMuR#71wgKJW z&nMF7KN0@}`%3{+XDHn{G&I=2m=Up1_JN_1p%H|&%q7NksB5VnPydIIRWe`6B2&=6Of-MeUK?nvB7zAfc5T`+y1rZqpT3tPP{(MSo8T}wBEeWg9b=3&)l>U>jWJpmle;q1{AT>l{+FRh}W%XlzLSi=7`Ygxt=EalOi7z=rfX$#Xs zD_F}gdul8tc%vC;GBlKFpJ2v7 zh*!a4Rd597Umxx&VKK~skZJ7S1pcN9%pt*CE1u%_AM5%pv8;?38x|T7MPKYRST*pO z_i{z7$XJG(+eU@>Qs@R)b+unb{>56x{8DL84-KIEgfOOC%o6>TzbxPs7!?^w0hL9e z___K7M^OwkwY0z>p{Av&sjjso{ddr^ym1(X9tp~@D9aeWaDU4F30RiZu`2m8<{ zE5n029*<{CjPcrFp3d|mQ|I4s|3J;kkPO}~7!zzYP4EfuU)8L{{R1_>gUzqm^Iy#P z3t(A=X;egHXow?)<_DI!{S1D!tiQ0A;lHYb>DFK1KbXrBfxtBma)76hG;*sj(;dU#3#~#8sZZ`QQI0u3D8sf&HeA58}{oh zXZi7eyyGoruXr(8=IJm8T4px}(exj&8TS6>gI}A$Sm93%rWpL~r_Gh}D{y}*u%v=O zLytqL{g=)Kua`D9|G0p^USdN+mfXw4>X2v=k&K{2GBb#ZqWb9&%?TJyZB27+V`F18 zA{J|mHwAATrdSg^obvOZPQ`H2=PR>yy|Y^A7}do20xD z>G=A*Pub)7Waqh&TXnrZN!>rzboi#Gy;l0cwFd=-MQrj^SeM+i-=vF!+VQk*_7Q(i zWipj5N|Z33(HE4iDfeQInCXO4A$(g$+GaPZ(Zhlf5zIMYbKBHB07 z_9@!DS&{R8Yfp-X)>d)@$>C_4SeVuWq=C!B$371VtA@+-5_CVmlny$)GCCkN31txr4=A^+U=U$el@> z6*R&_RlP1cKPww={CP9?%=z@MoC!+rluBRZOTWgSJf7Rb)>Z<4ZIwye%bmC5d`Gjv z)IsXY=X;I>HQYFLizV*Gm5wN>%?bXQYRVGVT5>GtP;Z2I8*=(Iq5ZOkA}VB-yY5QH z_2>&H?hwSpzLnb;BoD8EbV!nPsBrF6zM-Ac0CeX$ zZo*CS>2O#=YCaS?R4ME3&vRX-49hh&ES{+bD^)d>;b56{*uvuTusw$EUVhXix?9$3 zG|9=mKqGp<&QeizzZku{>7wK)pKN<`bGd^h$#46O;R{;#ShVJ3G^M`=N_?(c`##6Z zV|(IB9Y5W?)pC9A6{|uX)vC&EdKmv+&sV8_|MfP@Fr#YO+#1o#<7%hmvlaBr6@*0+ z?;O4O#qEd4bD6ZAachHh`iyBA| z+$nNyVB?7f;lMNUZlWYd>(Af4i(Jr~j<^2s+GQ4YXLWD0QPOlKHkK!Az~5ST*UYOu zYF(ZiO=@6*n9*hkzQgiJ@p&1Kx3969dGNuJOFQ$FYvG3(K^CO>{2dmcK3M1Ue*G=WwYTepZ@LuZ3#-8V2r#d8>5{fJ5 z6&|TS5TR$>waQ4A7|1*z@%D`Gj@CWG-*d3eU5`Fo=Jp}spY@gH?bE&0<`(YQwSK55 zrCG-HRr#xsHL9L0@XM0P>ZTF$J;|HSo{agFE^j#@S(I?}YSrfHjdiN`vIFBqKB&%A zE8ULRpLyG$H!HS?5a7gCj`I{he(nDBMHbJf)jAhbWWUVLIt|~g-`%zOgk}Fr1no?f zsluJv^yl=$o!FW@-gx#O}Ks?Y$h5mS@#UE}jRONwfhn`Lf}j7Hnm6ZU$m z_J%6ybVSJQ?sT_GY^b!_exp`6s<#En?y){zSiMu3>pQ_$E+fQtex?f?{*syo{vy=(?WxU%RF}dP|pF8Q(=AK9P zsyfTXXLT8ANq_v|V%TARtER}ct2W!Lq0dmbhPM4FjSpqwL|X07Ss1Zc<&5l z4#};0xshF^Ef*PbzF_l%!P|LN6bCEr1=dxc=E-T zO!duPcGBiiot`okWvV0lL>`lm9qBrwoWEIvk|P;Y6!Ot>@;d5??mp}47X{pUZ8#pC zXchYnnB>^6sCL3FiIT0eu=j34TnPPr)gsRa)6Z%g^-z7(rn=1!`EYh_Q;F4OtU;KF zTj&{iy{hrM5o2{r$ja}+HXt90?;IIV zHq%op7-h>XZxsKzCDTuN8@Qs!ca3o2U#XHH#=@nq9B*ytR6JJ%3J9 zW%xvTU|aI%4s5(?Pm}A#&ddc!MI8I-S>o3FXFFWZ26(?)oxb~8)>C1r{EcSyGBpQX)d4 zMYd8ZDk_Ti%#c*S_5JVteBR4F_qoscKF@v5bIy6rxo7TK=vx?sc&#mn77z>ugRH;?As`sI(oZoMGvj&&(BeyU z1)$}ZFmOO)fh;GuUIr%)(1PIF2u=a`q8>VivX_aWryI}}2naIup@vcyQtL}uI06c*uA`3A!Qw#;{LSk=LvK?s=%hcAaa{lhFbCpa(gQC4e*h2c zaM6WA=#DvvhAcoEfCHbwA$m)6Cg1_}t3}Y=#R4Ig`Wd~S{_KmhK>lZ6gagr)eGvv2 z^g3h*eE1R_O|JxS{OnCXuL~>V1ikWr;KBf83bC@VAX%7Mkw_#P8!J1fAQvYG2d5~% z0FR)gnAB=XF$oEjtSTBMtt=xUA+N2V40@!7h7_8hkH_h$s%zlrN?>ekY@8gNB3xV| zxHS@MaR2qO&*g`WT1?*%7QH>thmJZN8nV4CStZeKY08q^f z!C?ploC(3q%mkbOOQ3gOCO&5VHCSU70Vf}%bhsccDf1Moj7e3KkaOpxthz5PnT=g| zm58X=S~;}5f})0|mNuTCV`^q@VQEFQc5!ucU$@@FljKMC4+x}eiHMAfj){$n-??k| zp1u3_r(_*EoSkzdH!r{N^qHdKlG3uX7cX6|uBolNQh)PS^X)q=cU$juJ?if1ecbn? z|K-r|$mrOs*W+)dK7RT^Q4e3t&?3?R^#Q`DSFWuCHv0`mi)ge*>b^t zm8&1(M1c0qhv0(@p_&kfB8~n6>v4mocXo5g>IB1T%k~Xp2g@YO4hB1H8MiNZXRDJ~ zt?7ouwjFgDx1GaQW?x=_W+(+u7NCiqv!xqa2s5N{yCBl|#x)lfApg@rJTqIe%^V2r z2}k>nUATCHnR=8nO7M}drTK$(I=-*4cWH60#r49kcepoJBTMfnob&a1KP+Xf#ygXG zGti;PtarYrzSo`wYMs^;@GvaV+(HT7Qu;+5aWMq3Ho>qpc zqwmfX@%nkI8B|x~45YC_KPV3ZcBb)koqC|J>ejz5@OBVQ*F`U-Un`(1KZHBpt;{Pt z;_2OwTxyw~A&@)tW%`Wp$O-O5cKbqerH0g(RAQqb-&Pl{z15rDGbMAcN4&uoAIaYFV`y$P$lN1WPYG8*v_S6p{zhuTYA52; zs~^LjgBP#IB=fk*e)|5#H80{wD(QOf0wh-Lac*BEy1Ff<>-09N?a<-zM($N9eK^90fx7pU!LhgXXd_ma%--Tk-ysa)oPEX_pB|$#!)=U1*$q@ z=^@frL{lQ?<6SKpUu89HWq&=>(xKEsi|?lTI$T>f506Py6pA(=iXiQgQmPSlK^nV! z>ZA^a4k6uj>$xqgN}Ds}8%KC(4sRU>v~)GRwT{Vt>zQn*N_F+^wrIXKb>VZc`^2F; z=RT&E&|+K0oqklZ#qE8i$8&fjvSV6kQ_sN&Q4Z=+%l3osS%E*49Pe*2?Df;Gm1}+g{ON}MMF=pHboC#qPb% z?HcuYKJsyr+y0NOAnun|N(FE{zkT*%b!Aia54ZI1+h6b))v6lWrno+i${iu{&yFV7 zgv)km&v&G^r(argKlq4D7HiALQgSt?#NG^VewJG;!)~ps>Y1HNPRku?&cCWNG&^d~ ze(<8U=5RP~{33Nzo&Ex( zSS=Fb9g*_|5w|^Szw^)_o*=6KhUfAdr0mlTWgTw8t{>Oll~Q&nC_zp=P``Jjsm;FR zwK?Ve3_3SBrv9)?+<_>$m+hO!zQ>iyUAul?-7aEH=rzH#M0Iz)l2163_#Ixz=@QNI ztR~X{WP?~Fvo6&rW!tsOX*{R3g{U2NSL&0u)^OHj6mRm!zbfTuk)5Sswr7*LvnO)-7I z=H=!C>Rz`VS>MqOO2@r!mbKc3}5>~g=*C^Ypeucg<{2+WVg4@QVymMmCpyTe_wlJ>e zMQhJKi}^H-=i}U40ZXrBD=DgpZ!DQdY-IPis>YXbYiEbXhf7v0)}KCX8qP8Z>X-Oh zDynQySFnYtd$^Dt$vW0zXn=*z*UQlEKjlG6=e+5C{?f64fv$7?#yYK|D#t4x)NUEO!;J}V z;&`JZw*V1)uT9VCr!gWT@PiEQf|-PhWJc$BQ9P*HsYJ@F;wlh;($wdX`8h)8I; z$2wfFy|C_;CvE#@zGwCaHO`5pf6!&F$q~Bs#E64@N%bf~pddOOUs&h&=zYqV4gUCk z!-Sg5jyYx6bi4Wkp@Qo9p}B+G&r5y#I==vYMV1L4hKn{ac}pGk&A5}E9ZZXZCKIh6 zdTGk?HdO6Q`a!YFVRPPxoFi0z2;Q~;UGn$KY^rwKxpTY~F?Cz5?{KyZ=nGS*l|4R~{lEQ|jY;JNGQ@CZn;bo3(jAG*go+Rd$ zHR;_+-^(hD^-^yoz}sV1Q?f#5Y7;7gcU9=2i?6x&PAQSL zeZ0igrA-}9+K_O)_`}(!FW$L*5(V}Msw-VM(9@)40 zxq0$SSBsXw!9l6S=aD7YTI^{D)doIvxxxa(-k2!=+>Y0)b)C zCELpb#x`i2B5}To?lDf*w`^~D>v10@q1yS`^N@Z0B`>n&qsj;E(K*^itBaJlvb{;;o6`}t(uSK*b{?^xHSw8z(X?oF`T9=0k4?J&Pz)Lw9*E$nKW-6X$ z7TTT+UZ)SuvMYXg!kgw`=wj}G)A({wO1V}yd?N8jWE?Fus?p#^*I}Z9R3dVmcxC^Z zT!FM<%?IzC?BO_k=+Q$T>@WqSudesJPKDl2ITW;~@!6v*CGBXAY%?@!XVYi=$)==M znW=Kl)cE%;z4z%G$P^zCOTc4SCzeco=ZwiJ2UH6Y;-!a_JzjVpsic)#1_}b zPCeY0mz`cbL7FU5Ja%@XD@U~Fh%)hWxeeJRDk2-2|1id}0KrE-eRbaK<$+K>n4r2b zuD>Js#tB1fukr)s2df62j$7cydyiY%-Id^E1ECg;|&qNFYq-WMeTF-Do)b1>rr$+CPezKOqr5fl;h} z8iTbjwi+?Hkrn zX+^RE@WWU}@M|CI*A~ey{%1bX;K_#U<54QzAFsH` zV~&I?w>=+uRCvOC7)pAYKHsnriIyn1x#u7fJyq24bla|==%%nRWU$!2Jk_-#c`YK1 zctJh!qXbmEEAIY`+b1bqa|a!bRMAK;iNuy$+vQTubDxsv+)ETkK6J9l%b!y(Vnan_ zor%#neV-EqxsKGkZtd9srF4$&TVPE>~3jm6Mlx<5!*t2eqrx{1`{bZp-3Za<~s zi>kYY%drgB`RsG@L9`MlFH`F~Qi|PmyL*nzHKxwe6h2Y!+j+K;~#bj zf6Qv@!>2~S>#df%a4iQr=Aa~Ur+ZTvvSh>d?1*)K#(nL3@23g97*nhX3+vN#_P>lZ z(0g`!E9dZ(W`M)R`pi68fy^7QnyK{Wp3?7nqDS9xX5`#`>MAiWT9!(6EedHjd~i_i zj)cH>b4$&W`Kf$@(Y{x?Eu?&Y$ah~3t=ed*blG_~;bY&RR3)q7xpgUP^YZd`wCbr^ z7{dDN=U!Tle!@kJ*}IYVZ2Lsr25N zS{HTuN*<>9i^PxkVf?tKiF=o2!Ty^ibN7OJBqj-xGbfmlXfQPHC^c^Ia5(kB_W3bR zfv-JRWRm38v-Z6qOmHcr;x1+$>t->+xTZUE;jOw_a%9oP3K0f-GEI}cO74}i=<7H+ zh7s|hbFVaj;)Jc&qdDyjgiZ5ws%x7!D9>X`o`UwZs_NrFO-qZVdaSw`xW@#;7X}$H* z>+(c)!^b8^4s6^hCz96i=0qy-;C|U-+?Sm;cF*8tw|HDVdVxdBpgP_{iL;1^JYhL( zE_CdGxg;tp3yY395%UGwc(BddJ*PA{7(AXQj|(gy|fu3UxB=0twdT0 zE%DJ-wcEtx%eD&fv&b@!d@f8;v~yT^vM|KCo@i>i+0og-f@o_FmZ%|!)5?}Y4TXcn z(hzDS&Dqif1(sV-$ab(G#s(I_un^=!iU_lFv2dj?jS$UEQ4s*NwEnYnndxOLd_j*? ztQ=9O-}C$)rF)V*t%bb9FWav>pVpat5rReOJ(t$Ve4OA5xGH%@1W78WQG1jf0@YGSj6Xe!6c_z{y%zf|iyFP8GO+ z`S_Qa%cy@%(Cc;)@yUr%XE5QmU%X$kzj&dA5QLuwzRCE-^UZ^x+T9Q&(*KJmdlIaM z?}DJ3juqt*rni^C$jC4qHMN+S7*z_Hq)K<_^6_s0%gBFDtazR({dvp2Lz$8NeWHRR zQFN!0LW4u2XsC!V9}*d*@@FIdUkk1$4BdF!NJ zEw+0=gM3E%TLMo9x9|u`0E*5wb#_IOqG-`{8uSTz!c6~J3;b>(3ay4@AbCgy!a>@Q zE@T9mL)MT3xCg=`y zA9@J&Km*VqGzz_iCZQQ<4lL3lVO%f)m?%sNwic!Y!@=+{1DH9?7Um4|fO*3LU{qKX zY#VGR>;NnamJd4xD}zA7LI^(!Uf@ya5=ad91k~!+rVAn z8{q-)aCkg?7d#D~3onFMz-!=5@cZx{_#pf(d>R2EI1s{!H3$_19$|`bKzJhj5j4bh zL<%AYQHZES)FZ%KGGY)hf%wM6!X&_iVp3t!WwK&&ViQI$CLslSfAiI#G$giv%tWvBvRtr{7 zRw`>UYYyvK*6XZYtgl$-*m&92vgxonu=%mYv8A(}VXJ5BU>jwdW9MT>vxD^l_ATs5 z?78ff?6=vUvVY>>;E?7Za5!;LICgO4a$MlJ%kh%q8z(=fBBv?m2F@tXG|m#vM$RXk zpSZZVvd(zwdFZgD;5`pPZ9t;$X0_Tx_EKFVFg-NpT$hmB_~k1@|Co_L-d zo=ZF(JQKWZymGvzygt0!d5`j5;eE_I%_qQz<#XT*;Y;N^%h$^Hnjgt8$8XN>$G?mJ zH2*FBVF84IjDV>CNnoeI8G+jZqk=4gXhEXj7Qq98=LFjYCx!ThG=$uRVug+hH3$s~ zBZTFIiNe9c>B1LCa7MlOKg=mEzu@1ExB6KQZh_3 zPx6-J#A@NyMyt21KD_$+>TxLnDFZ2r)M2R`Qg2W~C}UJGDi_s^ngl<`Sgna#b81cd znmK8-w3~FY^m*xLGVC&V8Go6>GB;%=Wu;{8WVgwd%l5BjTZ>;Cxc11}mbG8x&~oeK z_R7`Dy+(_miRgHAIr^zQm%O2TxO|~}w*rd-ULi=~xWYq4xT2zUgS`MCx4B`Jk(!8>D+y_q85ckF0k_Z&+Vi z-$(zH{-6QMz}w)Y!Jy$9Lm$IJ!yzLXBR`{JqgTcX##@XljNhAJOlT(6CSOc-OcP8S z%@AgmW+`Uv=KSVv=K1E&Eu<|1EXpk=Ej2B-S~glSTiIA;T0JI85=q1|;-s~Lt)eZ>w%!hIXKi=L?y3D+d#Zh{1LQz-IOOom5$zc6c-4u;$Ki-5~!m$NQ2t|qSOt^;oJZc%PG-Fe(MxR<$qS!cE`Yu(^_we<<>?|X=OP(129 zk)H0J#h%k%=3d!eqZ>3g?Ag$}5xp^XW9ufdO~IQQHuG%u*?h^H*?XP$S?}*Y4nBoG z)4oLCW4@ClQ_>OATR$Ve!+zsreR3B0mA}4!mjCMj{eVLOLLZfWU4JnK5BQ=%V>k>qtRbu++!}q^2JhP+u{`C_Q$=7w}>y=%CwcV_2#xU+mg1u zOfXJ3wH>zIcl*smnZ#X*qdP2jlqRt!1tmR5#w2GYf8M!nXZ|mldBie8}!l&0*Bxl*6C1H)przXyg}qFR865=Va{om)6u8L&bXgxDZ&?( z6^j)gDE?7GEg39zE^RK;E-O1LaW=giULI9GUa_I#;W@K&b(JcWr_KwXKX3uM5Ov{A zm3LL|MVpH^FX1m$US50oShYZPN)4u+^8J2ki8wz+-xj>Vl@EygW3?&{yY z)~eHbx_@q9$5ZB~DbIMHoUid=c#rZ+4!G@P+FWZJ( zh6aX7!*53-M!t{k8si$vc_sC#>^1K7wQ-B_hi|;zjJ^$fJ2$cC9pAfS@8#cL{$TW> zZF2qO=oD>g;bZD2(N86xH9p^*cAS1b6EZXRCFQH=*V1qJZ!NRyW?#+4d`EuI{h{!q ze%@yO=|Twj;NYb|-_ksf8lmdr7wSt^C548l#rlM)VO23|ke*>In0W<}BT>HO018zf z{o(2jG>YP*LxQOh zIo}WPEJ47XK6=HRGe%uClop@{=HLW13{DM)QvnE-h&XDb zPpk?xLI!AHM=^AmftL>&C5$miTYOw3#s8PG-wH<0VU!GrqfmPETOE6!NJ=!>j1oYO zh%~@yYANG1wZVx8CqWsfg;U09Yk(8*@EXdR>KJfh0S)+gb!8kuOIaO*RaVF1!AVfo z#DP;C2Y5h_HU^vkt4+Vx1o_}p*HFjlY7tC~O|a(XrdXV*IiRrS1Z|ut)&g&8g2!lB zXzQu{ZP~KD4m1iGc!6GC1MCuVC2tw%Vd`s2a}14O%wNG|)&RGJ{wrfyWW6&vA~ZOP zp<5e#wSvK@flqBPm?goL*~>B>|5|2c_Oi?`5jQFYczsF#AM9ngolg+ifbjv;5|F|8 zl^yCw4qnXv>(i*80u0Eq#Q&K(*Hfc+lNC+;Uo?Br=a#$BZ>|1kcV2At0H$LX>PPX9 zGxLcg8-Q9SsDOW*hAa3;NmEM)gH-|_i>ZS;{{u<)9-U=MBLl(zM0f@Ar?4I9{(e4@ zKL3gMZ`hv-7&=2~uA!m92FCP=g>noGjSP(l3=Kn>x~QYoW)cf@HMfE7!75*lr|#; zu>`~kzyT@XXy7$5j19Pbo zU6z2SV+eRUhCpCo2v`P9*G6DyBM|6MBrtRm=$3$w6t$E=yi^7;R#{6~TNw|gzrZzE ztTGm-j8#|0YAA#F2|!qFWe{v}01HAeh`=B?Yl1ip!Yqi$AkgY+8e5o}nVFaxo0_ZR z%?aulZ3`0|24_w%)=tc{;n~4>tb+MhMHfDU)ldM)GP`thZ@H1%Fyf@8m1CV z_K#e$U}e_w6IRApZJg?2^kryTnX?RE8D4*fugqD7{}Ew{R1)YX4DEm9EK9hA`bWn2 z(8&L;voVw`gO|nW{oIujvKVLRU!meOP(S0$(vz1V%R+w*P^O_OU>GC&ElK>9zbr{# zo}-Z?BItvowWGNuSU_54ED5iKEX%F*tu`2r{!@qlH0YQ27!!+sX z9E34i3kj%#4h8(EMW=%XV$WeDOk#*xzHE#=+hR4hgPtb zVUCnoa`1XH&}66#Q?xJ|_$BzCS-;D8{E`7PwV#h*$gId(migK0Kqmwa zR%9&0E$pL0e91Heth(CIBL8G9V}7c1q=g31d_w5cEk=p{$f7w&i2-vP^VKMgXW_(Uwqs$kXB z|FHH~=CT}^D3EDjh6|=Oz|Ias&{S1D#tUuVx@SoMeaO+R-Z_H)rx?d zWK*9odINEd3;PBCD`Q#Yx9R%-PpG z|3KT~NX8g~P;|XvKC}o%BhyFI`$QCtL|%LeTIzB1VGLOD-@F$1k(OTy!lG!wj8zIh zk{UUf3`VTT2=HEjr7v%QA=;0m;~z>3@rg8`g!lxI)wYC@1N78>asRvLhW&iYS$_O) z?|94ED_%^Nc{+@NmeGwtH2p_xy1l>n;MZm_R`^qb$p(M@w7F7#1@3nRRum9uXmKdD z|I)eO_0rDn9~bcFOKeEUl6x6g9U?U%k{)!3W(HAF6h9qv3j#({Thl_@*x1<29E&x^ zn}RnEQ>+OdPav2ZgOL4~9NqLqrdcQ{ie8IEGe+y7Ej1Z^^rC9>f2|tLDDJW@Fd{{h z{VZsqA^)Z+ELP1tZQC=2}R>2u*t6=moXf)oIpl@n!MYK}Va&WV^a5b{D zGH2{c;pF1tsFgBeq zvw)o)%JdB>a3&Tw91iwE&<_}#4?-}3{V2==#!f!r(n&0tf;baX=R$xM`+W}x9St$| zw}3oF@I*L_iN3vszQv4w096Q}$cf3=hgmvYAnBA~=F7>ch2QpFd|+RUv?Kg)!;%7&g-wb|p7-3B z${$sB3g5iFQY@-uH2A~URcVKhPi?v?-7D&FVmM` zXGZIFl>uJ+v1rM+>DeQucbY%mfBbP)la+AaT7XXrtL7i!=ZmJC^S(d(_4E-t&kOmF z(EMN1RtFD6ugZSj2_G7jMpwPv#3S`aO=EZ!yK{r;xAWUD4-@YA@|O z!_R%`{pB)VLGDY__HA`C1(-|^MCYZj6CAXM^}<8X9%SdUtsnogSzT6Q`zeWh6@1TQ z?jO@tuk9zRx3@oan_FAAe@sS6vk>x*u$X#&J?7v}Tzcx|%U#Fc-JVL248TAA@)|#3 z^7PWywF8_-tVcz2Yu`kckIyTg&Ji1y9gewZGuLx(0g}m~l-PyN&$np7=f!z~2kP4@ zCJd~Mx}w#)8eTX~T^ijI+fPAXy1rfMSs3j$-jcFaCT*)jGgY%Dz@#e*`84*t!;XtB z&8Y#G%jY+D_~YuX9jxTBluW4!du*vQaOV74*EhNkwiO^+S~(}<#kRf;-P^PMhvdfx z`1%B=;lP28`X6ggVEDwvN`v{Pzqm=8^>~+&u>(bvlRAVvi+p#LwC3mIx^JhgzZs!O z-7d98ZjQL-?GOACQNn$;F!#*9=G8aODyBRv8YJ^8q9ht^ACd1OvNYl4id^OQgM;O2 zMw(&*#VA$tS<)*PA_wYx9*Jd|&CyoV~t+_0RyY0DNmzt3y zhfS5i194UklaA)@5tE}kX(y%NmqNS5Fmbq+*KTF1)_auedByF=j(bzR#XBnk^;*YW zGCJ=`_LYGn{C(Jvf>xU}32r`~56@alCJHtX#(lsSJkUxxO?Dw*fdxCXb8IygBnki4_z(7Ig;QMplS)DAyy77zX zg)|6SHrx)+Jtkgnpzw@3rqyG_jPylX*BTrS+S#&h74d4k`tb)|mu?+jtEKex^2t%6 zbpFH;@%{9stL&u}&R?Wl&Qpq09vnMJ^pKQ0s2mLUIG{0mHh15ImSE?4r;~POF1fhZ zfi=2p2fpXeuM+IrWYjPyreGPJORH4oJ7pb01>cj0>;|Ny zzDaQov8!yTxx3CSOg7!*XaK=GHCqB~RZS&Jn_3KI-$15>g+;a=P0{K0Bg(!emTzw? jO)!4zbS^2=qu4juXSBoVO}-4r7BSK433)NYg(v?5O=!83 literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile49.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile49.jpg new file mode 100644 index 0000000000000000000000000000000000000000..26f2a44a56bd1fee0080e80ee471df08d49e123b GIT binary patch literal 15517 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 literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/etc/keystore.p12 b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/etc/keystore.p12 new file mode 100644 index 0000000000000000000000000000000000000000..e4657280ca540596f3abd3ab6c786355d0a24276 GIT binary patch literal 2565 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 NnR_g#fmC7He*w>()F}V} literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/etc/realm.properties b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/etc/realm.properties new file mode 100644 index 00000000000..f4b3490e910 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/embedded/src/main/resources/exampleserver.xml b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/exampleserver.xml new file mode 100644 index 00000000000..0a32e6d0465 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/exampleserver.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + /hello + + org.eclipse.jetty.ee10.demos.HelloServlet + / + + + + + + + + + + + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/fileserver.xml b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/fileserver.xml new file mode 100644 index 00000000000..dd3a4dae9cb --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/fileserver.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + true + + index.html + + + + + + + + + + + + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/java-util-logging.properties b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/java-util-logging.properties new file mode 100644 index 00000000000..4aaa236d96c --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/embedded/src/main/resources/jetty-logging.properties b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/jetty-logging.properties new file mode 100644 index 00000000000..606ea8870d8 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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.ee9.servlets.LEVEL=DEBUG +#org.eclipse.jetty.alpn.LEVEL=DEBUG +#org.eclipse.jetty.jmx.LEVEL=DEBUG diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/logback-access.xml b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/logback-access.xml new file mode 100644 index 00000000000..6cd90623677 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/AbstractEmbeddedTest.java b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/AbstractEmbeddedTest.java new file mode 100644 index 00000000000..cfa09bb58b0 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/AbstractEmbeddedTest.java @@ -0,0 +1,59 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.client.http.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-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/ExampleServerTest.java b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/ExampleServerTest.java new file mode 100644 index 00000000000..26968077546 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/ExampleServerTest.java @@ -0,0 +1,85 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import java.net.URI; + +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.client.util.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-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/ExampleServerXmlTest.java b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/ExampleServerXmlTest.java new file mode 100644 index 00000000000..23c10f7b71f --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/ExampleServerXmlTest.java @@ -0,0 +1,62 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import java.net.URI; + +import org.eclipse.jetty.client.api.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-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/FastFileServerTest.java b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/FastFileServerTest.java new file mode 100644 index 00000000000..0ae8356a329 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/FastFileServerTest.java @@ -0,0 +1,90 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import java.io.BufferedWriter; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.eclipse.jetty.client.api.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"; + public WorkDir workDir; + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + Path baseDir = workDir.getEmptyPathDir(); + + Path textFile = baseDir.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(); + } + + @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-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/FileServerTest.java b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/FileServerTest.java new file mode 100644 index 00000000000..06d44a6fdf0 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/FileServerTest.java @@ -0,0 +1,88 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import java.io.BufferedWriter; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.eclipse.jetty.client.api.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.PathResource; +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 WorkDir workDir; + private Server server; + + @BeforeEach + public void startServer() 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 = FileServer.createServer(0, new PathResource(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-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/FileServerXmlTest.java b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/FileServerXmlTest.java new file mode 100644 index 00000000000..daa131ea690 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/FileServerXmlTest.java @@ -0,0 +1,87 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import java.io.BufferedWriter; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.eclipse.jetty.client.api.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"; + public WorkDir workDir; + private Server server; + + @BeforeEach + public void startServer() 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-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/JarServerTest.java b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/JarServerTest.java new file mode 100644 index 00000000000..50c88e034c1 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/JarServerTest.java @@ -0,0 +1,81 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import java.net.URI; + +import org.eclipse.jetty.client.api.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.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; + +public class JarServerTest extends AbstractEmbeddedTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = JarServer.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Disabled //TODO + @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")); + } + + @Disabled //TODO + @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-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/LikeJettyXmlTest.java b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/LikeJettyXmlTest.java new file mode 100644 index 00000000000..ec4b615eb05 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/LikeJettyXmlTest.java @@ -0,0 +1,93 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import java.net.URI; +import java.util.Map; + +import org.eclipse.jetty.client.api.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; + +public class LikeJettyXmlTest extends AbstractEmbeddedTest +{ + private Server server; + private URI serverPlainUri; + private URI serverSslUri; + + @BeforeEach + public void startServer() throws Exception + { + //TODO fix me + //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); + } + + @Disabled //TODO + @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")); + } + + @Disabled //TODO + @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-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/ManyConnectorsTest.java b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/ManyConnectorsTest.java new file mode 100644 index 00000000000..b9152f597a7 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/ManyConnectorsTest.java @@ -0,0 +1,90 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import java.net.URI; +import java.util.Map; + +import org.eclipse.jetty.client.api.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-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/ManyContextsTest.java b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/ManyContextsTest.java new file mode 100644 index 00000000000..8f57235d25e --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/ManyContextsTest.java @@ -0,0 +1,112 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import java.net.URI; + +import org.eclipse.jetty.client.api.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-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/ManyHandlersTest.java b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/ManyHandlersTest.java new file mode 100644 index 00000000000..7aa31e717a5 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/ManyHandlersTest.java @@ -0,0 +1,105 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import java.net.URI; +import java.util.Map; + +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.HttpHeaderValue; +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"); + + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .headers(headers -> headers.put(HttpHeader.ACCEPT_ENCODING, HttpHeaderValue.GZIP)) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test gzip + // Test that Gzip was used to produce the response + String contentEncoding = response.getHeaders().get(HttpHeader.CONTENT_ENCODING); + assertThat("Content-Encoding", contentEncoding, 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"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .headers(headers -> headers.put(HttpHeader.ACCEPT_ENCODING, HttpHeaderValue.GZIP)) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test gzip + // Test that Gzip was used to produce the response + String contentEncoding = response.getHeaders().get(HttpHeader.CONTENT_ENCODING); + assertThat("Content-Encoding", contentEncoding, 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-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/ManyServletContextsTest.java b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/ManyServletContextsTest.java new file mode 100644 index 00000000000..8b4ed10919e --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/ManyServletContextsTest.java @@ -0,0 +1,110 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import java.net.URI; + +import org.eclipse.jetty.client.api.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-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/MinimalServletsTest.java b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/MinimalServletsTest.java new file mode 100644 index 00000000000..377e0b92095 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/MinimalServletsTest.java @@ -0,0 +1,62 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import java.net.URI; + +import org.eclipse.jetty.client.api.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-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/OneConnectorTest.java b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/OneConnectorTest.java new file mode 100644 index 00000000000..1f180a572fd --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/OneConnectorTest.java @@ -0,0 +1,62 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import java.net.URI; + +import org.eclipse.jetty.client.api.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-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/OneContextTest.java b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/OneContextTest.java new file mode 100644 index 00000000000..c4d60087411 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/OneContextTest.java @@ -0,0 +1,62 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import java.net.URI; + +import org.eclipse.jetty.client.api.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-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/OneHandlerTest.java b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/OneHandlerTest.java new file mode 100644 index 00000000000..7f6e6ee4464 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/OneHandlerTest.java @@ -0,0 +1,62 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import java.net.URI; + +import org.eclipse.jetty.client.api.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-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/OneServletContextJmxStatsTest.java b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/OneServletContextJmxStatsTest.java new file mode 100644 index 00000000000..3c0cd5550f0 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/OneServletContextJmxStatsTest.java @@ -0,0 +1,96 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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.api.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.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 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; + +@ExtendWith(WorkDirExtension.class) +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-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/OneServletContextTest.java b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/OneServletContextTest.java new file mode 100644 index 00000000000..ab03d9eac74 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/OneServletContextTest.java @@ -0,0 +1,153 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import java.io.BufferedWriter; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.eclipse.jetty.client.api.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.PathResource; +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; + private Server server; + + @BeforeEach + public void startServer() 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 = OneServletContext.createServer(0, new PathResource(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-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/OneServletContextWithSessionTest.java b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/OneServletContextWithSessionTest.java new file mode 100644 index 00000000000..43b2736bb5e --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/OneServletContextWithSessionTest.java @@ -0,0 +1,90 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import java.io.BufferedWriter; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.eclipse.jetty.client.api.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.PathResource; +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"; + public WorkDir workDir; + private Server server; + + @BeforeEach + public void startServer() 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 = OneServletContextWithSession.createServer(0, new PathResource(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-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/OneWebAppTest.java b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/OneWebAppTest.java new file mode 100644 index 00000000000..7ab885c186c --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/OneWebAppTest.java @@ -0,0 +1,68 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import java.net.URI; + +import org.eclipse.jetty.client.api.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-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/OneWebAppWithJspTest.java b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/OneWebAppWithJspTest.java new file mode 100644 index 00000000000..9e99d64cac5 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/OneWebAppWithJspTest.java @@ -0,0 +1,106 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import java.net.URI; + +import org.eclipse.jetty.client.api.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-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/ProxyServerTest.java b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/ProxyServerTest.java new file mode 100644 index 00000000000..18bfd4f1773 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/ProxyServerTest.java @@ -0,0 +1,71 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import java.net.URI; + +import org.eclipse.jetty.client.HttpProxy; +import org.eclipse.jetty.client.api.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.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().getProxies().add(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-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/RewriteServerTest.java b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/RewriteServerTest.java new file mode 100644 index 00000000000..f10454ddb7f --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/RewriteServerTest.java @@ -0,0 +1,78 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import java.net.URI; + +import org.eclipse.jetty.client.api.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-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/SecuredHelloHandlerTest.java b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/SecuredHelloHandlerTest.java new file mode 100644 index 00000000000..7379ad8b3bc --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/SecuredHelloHandlerTest.java @@ -0,0 +1,80 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import java.net.URI; +import java.util.Base64; + +import org.eclipse.jetty.client.api.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-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/ServerUtil.java b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/ServerUtil.java new file mode 100644 index 00000000000..c1a6c5b0093 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/ServerUtil.java @@ -0,0 +1,83 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/ServerWithAnnotationsTest.java b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/ServerWithAnnotationsTest.java new file mode 100644 index 00000000000..ed0481518ba --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/ServerWithAnnotationsTest.java @@ -0,0 +1,64 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import java.net.URI; + +import org.eclipse.jetty.client.api.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-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/ServerWithJMXTest.java b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/ServerWithJMXTest.java new file mode 100644 index 00000000000..837e981d6bf --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/ServerWithJMXTest.java @@ -0,0 +1,59 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/ServerWithJNDITest.java b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/ServerWithJNDITest.java new file mode 100644 index 00000000000..3fc8ed0c8a6 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/ServerWithJNDITest.java @@ -0,0 +1,72 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import java.net.URI; + +import org.eclipse.jetty.client.api.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.allOf; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; + +@Disabled //TODO +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-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/SimplestServerTest.java b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/SimplestServerTest.java new file mode 100644 index 00000000000..6d840dd0bc9 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/SimplestServerTest.java @@ -0,0 +1,52 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import java.net.URI; + +import org.eclipse.jetty.client.api.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-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/SplitFileServerTest.java b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/SplitFileServerTest.java new file mode 100644 index 00000000000..a0103e7b66b --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/SplitFileServerTest.java @@ -0,0 +1,92 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import java.net.URI; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.resource.PathResource; +import org.eclipse.jetty.util.resource.Resource; +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 = new PathResource(path0); + Resource resource1 = new PathResource(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-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/WebSocketServerTest.java b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/WebSocketServerTest.java new file mode 100644 index 00000000000..f01895c8528 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/test/java/org/eclipse/jetty/ee10/demos/WebSocketServerTest.java @@ -0,0 +1,106 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.demos; + +import java.net.URI; +import java.time.Duration; +import java.util.concurrent.Future; +import java.util.concurrent.LinkedBlockingQueue; + +import org.eclipse.jetty.ee10.websocket.api.Session; +import org.eclipse.jetty.ee10.websocket.api.annotations.OnWebSocketClose; +import org.eclipse.jetty.ee10.websocket.api.annotations.OnWebSocketError; +import org.eclipse.jetty.ee10.websocket.api.annotations.OnWebSocketMessage; +import org.eclipse.jetty.ee10.websocket.api.annotations.WebSocket; +import org.eclipse.jetty.ee10.websocket.api.util.WSURI; +import org.eclipse.jetty.ee10.websocket.client.WebSocketClient; +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 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.getRemote().sendString("Hello World"); + + 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-ee10/jetty-ee10-demos/embedded/src/test/resources/dir0/test0.txt b/jetty-ee10/jetty-ee10-demos/embedded/src/test/resources/dir0/test0.txt new file mode 100644 index 00000000000..a39c44cc614 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/test/resources/dir0/test0.txt @@ -0,0 +1 @@ +test0 \ No newline at end of file diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/test/resources/dir1/test1.txt b/jetty-ee10/jetty-ee10-demos/embedded/src/test/resources/dir1/test1.txt new file mode 100644 index 00000000000..f079749c42f --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/embedded/src/test/resources/dir1/test1.txt @@ -0,0 +1 @@ +test1 \ No newline at end of file diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/test/resources/jetty-logging.properties b/jetty-ee10/jetty-ee10-demos/embedded/src/test/resources/jetty-logging.properties new file mode 100644 index 00000000000..a3472974ab9 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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.ee9.servlets.LEVEL=DEBUG +#org.eclipse.jetty.alpn.LEVEL=DEBUG +#org.eclipse.jetty.jmx.LEVEL=DEBUG diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/test/resources/realm.properties b/jetty-ee10/jetty-ee10-demos/embedded/src/test/resources/realm.properties new file mode 100644 index 00000000000..556117fcdbc --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/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-ee10/jetty-ee10-demos/pom.xml b/jetty-ee10/jetty-ee10-demos/pom.xml new file mode 100644 index 00000000000..d4afc267eb4 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/pom.xml @@ -0,0 +1,45 @@ + + + + org.eclipse.jetty.ee10 + jetty-ee10 + 12.0.0-SNAPSHOT + + + 4.0.0 + org.eclipse.jetty.ee10.demos + jetty-ee10-demos + EE10 :: Jetty Demos :: Parent + pom + + + true + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + bogus.* + + + + + + + demo-async-rest + demo-jaas-webapp + demo-jndi-webapp + demo-jetty-webapp + demo-proxy-webapp + demo-simple-webapp + demo-jsp-webapp + demo-mock-resources + demo-spec + demo-template + embedded + + diff --git a/jetty-ee10/jetty-ee10-glassfish-jstl/pom.xml b/jetty-ee10/jetty-ee10-glassfish-jstl/pom.xml new file mode 100644 index 00000000000..9265d07e364 --- /dev/null +++ b/jetty-ee10/jetty-ee10-glassfish-jstl/pom.xml @@ -0,0 +1,79 @@ + + + org.eclipse.jetty.ee10 + jetty-ee10 + 12.0.0-SNAPSHOT + + 4.0.0 + jetty-ee10-glassfish-jstl + EE10 :: Glassfish :: JSTL module + https://projects.eclipse.org/projects/ee4j.glassfish + jar + + ${project.groupId}.glassfish.jstl + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + false + + + + org.jacoco + jacoco-maven-plugin + + true + + + + + + + + + jakarta.servlet.jsp.jstl + jakarta.servlet.jsp.jstl-api + + + + org.glassfish.web + jakarta.servlet.jsp.jstl + + + + org.eclipse.jetty.toolchain + jetty-test-helper + test + + + + org.eclipse.jetty.ee10 + jetty-ee10-apache-jsp + test + + + + org.eclipse.jetty.ee10 + jetty-ee10-annotations + test + + + + org.eclipse.jetty.ee10 + jetty-ee10-webapp + test + + + + org.slf4j + slf4j-simple + test + + + + + diff --git a/jetty-ee10/jetty-ee10-glassfish-jstl/src/main/config/modules/glassfish-jstl.mod b/jetty-ee10/jetty-ee10-glassfish-jstl/src/main/config/modules/glassfish-jstl.mod new file mode 100644 index 00000000000..1b6a0b01562 --- /dev/null +++ b/jetty-ee10/jetty-ee10-glassfish-jstl/src/main/config/modules/glassfish-jstl.mod @@ -0,0 +1,7 @@ +# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html + +[description] +Enables the glassfish version of JSTL for all webapps. + +[lib] +lib/glassfish-jstl/*.jar diff --git a/jetty-ee10/jetty-ee10-glassfish-jstl/src/main/resources/readme.txt b/jetty-ee10/jetty-ee10-glassfish-jstl/src/main/resources/readme.txt new file mode 100644 index 00000000000..a516023c352 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-glassfish-jstl/src/test/java/org/eclipse/jetty/ee10/jstl/JspConfig.java b/jetty-ee10/jetty-ee10-glassfish-jstl/src/test/java/org/eclipse/jetty/ee10/jstl/JspConfig.java new file mode 100644 index 00000000000..ce3ce236911 --- /dev/null +++ b/jetty-ee10/jetty-ee10-glassfish-jstl/src/test/java/org/eclipse/jetty/ee10/jstl/JspConfig.java @@ -0,0 +1,36 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.jstl; + +import java.io.File; +import java.net.URI; +import java.nio.file.Path; + +import org.eclipse.jetty.ee10.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("jakarta.servlet.context.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.setResourceBase(Path.of(baseUri)); + } +} diff --git a/jetty-ee10/jetty-ee10-glassfish-jstl/src/test/java/org/eclipse/jetty/ee10/jstl/JspIncludeTest.java b/jetty-ee10/jetty-ee10-glassfish-jstl/src/test/java/org/eclipse/jetty/ee10/jstl/JspIncludeTest.java new file mode 100644 index 00000000000..afb764904eb --- /dev/null +++ b/jetty-ee10/jetty-ee10-glassfish-jstl/src/test/java/org/eclipse/jetty/ee10/jstl/JspIncludeTest.java @@ -0,0 +1,167 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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 org.eclipse.jetty.ee10.annotations.AnnotationConfiguration; +import org.eclipse.jetty.ee10.webapp.Configurations; +import org.eclipse.jetty.ee10.webapp.JettyWebXmlConfiguration; +import org.eclipse.jetty.ee10.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.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +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; + +public class JspIncludeTest +{ + 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); + + // Setup WebAppContext + File testWebAppDir = MavenTestingUtils.getProjectDir("src/test/webapp"); + + // Prepare WebApp libs + File libDir = new File(testWebAppDir, "WEB-INF/lib"); + FS.ensureDirExists(libDir); + File testTagLibDir = MavenTestingUtils.getProjectDir("src/test/taglibjar"); + JAR.create(testTagLibDir, new File(libDir, "testtaglib.jar")); + + // Configure WebAppContext + Configurations.setServerDefault(server).add(new JettyWebXmlConfiguration(), new AnnotationConfiguration()); + + WebAppContext context = new WebAppContext(); + context.setContextPath("/"); + + File scratchDir = MavenTestingUtils.getTargetFile("tests/" + JspIncludeTest.class.getSimpleName() + "-scratch"); + FS.ensureEmpty(scratchDir); + JspConfig.init(context, testWebAppDir.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(); + } + + @Disabled //TODO + @Test + public void testTopWithIncluded() throws IOException + { + URI uri = baseUri.resolve("/top.jsp"); + // System.out.println("GET (String): " + uri.toASCIIString()); + + 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-ee10/jetty-ee10-glassfish-jstl/src/test/java/org/eclipse/jetty/ee10/jstl/JstlTest.java b/jetty-ee10/jetty-ee10-glassfish-jstl/src/test/java/org/eclipse/jetty/ee10/jstl/JstlTest.java new file mode 100644 index 00000000000..021e3c9cb3c --- /dev/null +++ b/jetty-ee10/jetty-ee10-glassfish-jstl/src/test/java/org/eclipse/jetty/ee10/jstl/JstlTest.java @@ -0,0 +1,139 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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.ee10.annotations.AnnotationConfiguration; +import org.eclipse.jetty.ee10.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.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 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); + + // Setup WebAppContext + File testWebAppDir = MavenTestingUtils.getProjectDir("src/test/webapp"); + + // Prepare WebApp libs + File libDir = new File(testWebAppDir, "WEB-INF/lib"); + FS.ensureDirExists(libDir); + File testTagLibDir = MavenTestingUtils.getProjectDir("src/test/taglibjar"); + JAR.create(testTagLibDir, new File(libDir, "testtaglib.jar")); + + // Configure WebAppCont + WebAppContext context = new WebAppContext(); + context.setContextPath("/"); + + File scratchDir = MavenTestingUtils.getTargetFile("tests/" + JstlTest.class.getSimpleName() + "-scratch"); + FS.ensureEmpty(scratchDir); + JspConfig.init(context, testWebAppDir.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-ee10/jetty-ee10-glassfish-jstl/src/test/resources/jetty-logging.properties b/jetty-ee10/jetty-ee10-glassfish-jstl/src/test/resources/jetty-logging.properties new file mode 100644 index 00000000000..c1f44baf179 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-glassfish-jstl/src/test/taglibjar/META-INF/etag.tld b/jetty-ee10/jetty-ee10-glassfish-jstl/src/test/taglibjar/META-INF/etag.tld new file mode 100644 index 00000000000..de59ff05a3a --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-glassfish-jstl/src/test/taglibjar/META-INF/tags/errorhandler.tag b/jetty-ee10/jetty-ee10-glassfish-jstl/src/test/taglibjar/META-INF/tags/errorhandler.tag new file mode 100644 index 00000000000..ddd44dd4910 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-glassfish-jstl/src/test/webapp/WEB-INF/lib/testtaglib.jar b/jetty-ee10/jetty-ee10-glassfish-jstl/src/test/webapp/WEB-INF/lib/testtaglib.jar new file mode 100644 index 0000000000000000000000000000000000000000..d7f1724d9e3859e50b960a2b75b556e6fd5115cc GIT binary patch literal 791 zcmWIWW@Zs#;Nak3_!vAVgaHW%GO#fCx`sIFdiuHPry_lB{5!?K&^o~||B!(| zYk6JkRNH+M^hAx^WV=E*cTMrSJE3shk-$eTcFqs(UpJd_sq<{z_j})K#JYp8Z&J20 z+B-KrI4*@}?K|z{@1NfkZ$2~Mc-sWwhgbKRT8rJ8_AK)4UjychD}jYp5$k^Q9pcb& z3|2}LzyHSN*)!SW6Z`%5lvitXCvnBUzS_QSWAM(6Po`d-;VZ%2DXCg{HT&u1#d1?N zvKLDfPB%A+F)KJVcl*$`y3A;>R+ZvAcjANVpIfJc_47|`|0C7WcZBQx ziT%E_iyqDrcoFkHD|NNl+=lq98GNU%3td}3d-|WZwVaCH1^Le$1t-Kz2w9@Zc&Ooykbwv6Q_WkuG3r@UOIIj znj2flJkM3Le$h>>nL-@Qo@IO93(2UIG;jT!6hG^$)`jZl*;hry{)!e{x-q#>mZKv3 zGrvVWOV25EpdD;r2X6A< + + Test webapp for JSTL + \ No newline at end of file diff --git a/jetty-ee10/jetty-ee10-glassfish-jstl/src/test/webapp/catch-basic.jsp b/jetty-ee10/jetty-ee10-glassfish-jstl/src/test/webapp/catch-basic.jsp new file mode 100644 index 00000000000..06e2bcfd2bc --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-glassfish-jstl/src/test/webapp/catch-taglib.jsp b/jetty-ee10/jetty-ee10-glassfish-jstl/src/test/webapp/catch-taglib.jsp new file mode 100644 index 00000000000..28f7488e2ab --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-glassfish-jstl/src/test/webapp/included.jsp b/jetty-ee10/jetty-ee10-glassfish-jstl/src/test/webapp/included.jsp new file mode 100644 index 00000000000..dde5f29c45d --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-glassfish-jstl/src/test/webapp/ref.jsp b/jetty-ee10/jetty-ee10-glassfish-jstl/src/test/webapp/ref.jsp new file mode 100644 index 00000000000..0debf754890 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-glassfish-jstl/src/test/webapp/top.jsp b/jetty-ee10/jetty-ee10-glassfish-jstl/src/test/webapp/top.jsp new file mode 100644 index 00000000000..53d8d0c3c16 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-glassfish-jstl/src/test/webapp/urls.jsp b/jetty-ee10/jetty-ee10-glassfish-jstl/src/test/webapp/urls.jsp new file mode 100644 index 00000000000..bc18e9ac5f5 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-http-spi/pom.xml b/jetty-ee10/jetty-ee10-http-spi/pom.xml new file mode 100644 index 00000000000..d12257c5ecf --- /dev/null +++ b/jetty-ee10/jetty-ee10-http-spi/pom.xml @@ -0,0 +1,114 @@ + + + org.eclipse.jetty.ee10 + jetty-ee10 + 12.0.0-SNAPSHOT + + 4.0.0 + jetty-ee10-http-spi + EE10 :: Jetty :: Http Service Provider Interface + + ${project.groupId}.http.spi + org.eclipse.jetty.http.spi.* + + + + org.eclipse.jetty.toolchain + jetty-test-helper + test + + + org.eclipse.jetty + jetty-client + test + + + org.eclipse.jetty + jetty-server + provided + + + org.eclipse.jetty.toolchain + jetty-jakarta-servlet-api + provided + + + org.slf4j + slf4j-api + provided + + + org.slf4j + jul-to-slf4j + test + + + org.eclipse.jetty + jetty-slf4j-impl + test + + + jakarta.ws.rs + jakarta.ws.rs-api + + + com.sun.xml.ws + jaxws-rt + test + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + false + + true + + + + org.apache.felix + maven-bundle-plugin + true + + + Jetty Http SPI + osgi.extender; filter:="(osgi.extender=osgi.serviceloader.registrar)";resolution:=optional + osgi.serviceloader; osgi.serviceloader=com.sun.net.httpserver.spi.HttpServerProvider + <_nouses>true + + + + + org.jacoco + jacoco-maven-plugin + + true + + + + + + + jdk16 + + [16,) + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + --add-opens java.base/jdk.internal.misc=ALL-UNNAMED + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-http-spi/src/main/java/module-info.java b/jetty-ee10/jetty-ee10-http-spi/src/main/java/module-info.java new file mode 100644 index 00000000000..7a4b12af785 --- /dev/null +++ b/jetty-ee10/jetty-ee10-http-spi/src/main/java/module-info.java @@ -0,0 +1,23 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 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.ee10.http.spi +{ + requires jetty.servlet.api; + requires transitive jdk.httpserver; + requires transitive java.xml; + requires transitive org.eclipse.jetty.server; + requires transitive org.eclipse.jetty.util; + + exports org.eclipse.jetty.ee10.http.spi; +} diff --git a/jetty-ee10/jetty-ee10-http-spi/src/main/java/org/eclipse/jetty/ee10/http/spi/DelegatingThreadPool.java b/jetty-ee10/jetty-ee10-http-spi/src/main/java/org/eclipse/jetty/ee10/http/spi/DelegatingThreadPool.java new file mode 100644 index 00000000000..da22d7d1ecf --- /dev/null +++ b/jetty-ee10/jetty-ee10-http-spi/src/main/java/org/eclipse/jetty/ee10/http/spi/DelegatingThreadPool.java @@ -0,0 +1,130 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.http.spi; + +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jetty.util.component.ContainerLifeCycle; +import org.eclipse.jetty.util.component.LifeCycle; +import org.eclipse.jetty.util.thread.ThreadPool; +import org.eclipse.jetty.util.thread.TryExecutor; + +public class DelegatingThreadPool extends ContainerLifeCycle implements ThreadPool, TryExecutor +{ + private Executor _executor; // memory barrier provided by start/stop semantics + private TryExecutor _tryExecutor; + + public DelegatingThreadPool(Executor executor) + { + _executor = executor; + _tryExecutor = TryExecutor.asTryExecutor(executor); + addBean(_executor); + } + + public Executor getExecutor() + { + return _executor; + } + + public void setExecutor(Executor executor) + { + if (isRunning()) + throw new IllegalStateException(getState()); + updateBean(_executor, executor); + _executor = executor; + _tryExecutor = TryExecutor.asTryExecutor(executor); + } + + @Override + public void execute(Runnable job) + { + _executor.execute(job); + } + + @Override + public boolean tryExecute(Runnable task) + { + return _tryExecutor.tryExecute(task); + } + + @Override + public int getIdleThreads() + { + final Executor executor = _executor; + if (executor instanceof ThreadPool) + return ((ThreadPool)executor).getIdleThreads(); + + if (executor instanceof ThreadPoolExecutor) + { + final ThreadPoolExecutor tpe = (ThreadPoolExecutor)executor; + return tpe.getPoolSize() - tpe.getActiveCount(); + } + return -1; + } + + @Override + public int getThreads() + { + final Executor executor = _executor; + if (executor instanceof ThreadPool) + return ((ThreadPool)executor).getThreads(); + + if (executor instanceof ThreadPoolExecutor) + { + final ThreadPoolExecutor tpe = (ThreadPoolExecutor)executor; + return tpe.getPoolSize(); + } + return -1; + } + + @Override + public boolean isLowOnThreads() + { + final Executor executor = _executor; + if (executor instanceof ThreadPool) + return ((ThreadPool)executor).isLowOnThreads(); + + if (executor instanceof ThreadPoolExecutor) + { + final ThreadPoolExecutor tpe = (ThreadPoolExecutor)executor; + // getActiveCount() locks the thread pool, so execute it last + return tpe.getPoolSize() == tpe.getMaximumPoolSize() && + tpe.getQueue().size() >= tpe.getPoolSize() - tpe.getActiveCount(); + } + return false; + } + + @Override + public void join() throws InterruptedException + { + final Executor executor = _executor; + if (executor instanceof ThreadPool) + ((ThreadPool)executor).join(); + else if (executor instanceof ExecutorService) + ((ExecutorService)executor).awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS); + else + throw new IllegalStateException(); + } + + @Override + protected void doStop() throws Exception + { + super.doStop(); + if (!(_executor instanceof LifeCycle) && (_executor instanceof ExecutorService)) + ((ExecutorService)_executor).shutdownNow(); + } +} diff --git a/jetty-ee10/jetty-ee10-http-spi/src/main/java/org/eclipse/jetty/ee10/http/spi/HttpSpiContextHandler.java b/jetty-ee10/jetty-ee10-http-spi/src/main/java/org/eclipse/jetty/ee10/http/spi/HttpSpiContextHandler.java new file mode 100644 index 00000000000..a517434f87e --- /dev/null +++ b/jetty-ee10/jetty-ee10-http-spi/src/main/java/org/eclipse/jetty/ee10/http/spi/HttpSpiContextHandler.java @@ -0,0 +1,159 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.http.spi; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.List; +import java.util.Map; + +import com.sun.net.httpserver.Authenticator; +import com.sun.net.httpserver.Authenticator.Result; +import com.sun.net.httpserver.HttpContext; +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpPrincipal; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.util.StringUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Jetty handler that bridges requests to {@link HttpHandler}. + */ +public class HttpSpiContextHandler extends ContextHandler +{ + public static final Logger LOG = LoggerFactory.getLogger(HttpSpiContextHandler.class); + + private HttpContext _httpContext; + + private HttpHandler _httpHandler; + + public HttpSpiContextHandler(HttpContext httpContext, HttpHandler httpHandler) + { + this._httpContext = httpContext; + this._httpHandler = httpHandler; + } + + //TODO needs rewriting + /* + @Override + public void doScope(String target, Request baseRequest, HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException + { + if (!target.startsWith(getContextPath())) + { + return; + } + + HttpExchange jettyHttpExchange; + if (baseRequest.isSecure()) + { + jettyHttpExchange = new JettyHttpsExchange(_httpContext, req, resp); + } + else + { + jettyHttpExchange = new JettyHttpExchange(_httpContext, req, resp); + } + + // TODO: add filters processing + + try + { + Authenticator auth = _httpContext.getAuthenticator(); + if (auth != null) + { + handleAuthentication(resp, jettyHttpExchange, auth); + } + else + { + _httpHandler.handle(jettyHttpExchange); + } + } + catch (Exception ex) + { + LOG.debug("Failed to handle", ex); + PrintWriter writer = new PrintWriter(jettyHttpExchange.getResponseBody()); + + resp.setStatus(500); + writer.println("

    HTTP ERROR: 500

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

    RequestURI=" + StringUtil.sanitizeXmlString(req.getRequestURI()) + "

    "); + + if (LOG.isDebugEnabled()) + { + writer.println("
    ");
    +                ex.printStackTrace(writer);
    +                writer.println("
    "); + } + + baseRequest.getHttpChannel().getHttpConfiguration().writePoweredBy(writer, "

    ", "

    "); + + writer.close(); + } + finally + { + baseRequest.setHandled(true); + } + } +*/ + private void handleAuthentication(HttpServletResponse resp, HttpExchange httpExchange, Authenticator auth) throws IOException + { + Result result = auth.authenticate(httpExchange); + if (result instanceof Authenticator.Failure) + { + int rc = ((Authenticator.Failure)result).getResponseCode(); + for (Map.Entry> header : httpExchange.getResponseHeaders().entrySet()) + { + for (String value : header.getValue()) + { + resp.addHeader(header.getKey(), value); + } + } + resp.sendError(rc); + } + else if (result instanceof Authenticator.Retry) + { + int rc = ((Authenticator.Retry)result).getResponseCode(); + for (Map.Entry> header : httpExchange.getResponseHeaders().entrySet()) + { + for (String value : header.getValue()) + { + resp.addHeader(header.getKey(), value); + } + } + resp.setStatus(rc); + resp.flushBuffer(); + } + else if (result instanceof Authenticator.Success) + { + HttpPrincipal principal = ((Authenticator.Success)result).getPrincipal(); + ((JettyExchange)httpExchange).setPrincipal(principal); + _httpHandler.handle(httpExchange); + } + } + + public HttpHandler getHttpHandler() + { + return _httpHandler; + } + + public void setHttpHandler(HttpHandler handler) + { + this._httpHandler = handler; + } +} diff --git a/jetty-ee10/jetty-ee10-http-spi/src/main/java/org/eclipse/jetty/ee10/http/spi/JettyExchange.java b/jetty-ee10/jetty-ee10-http-spi/src/main/java/org/eclipse/jetty/ee10/http/spi/JettyExchange.java new file mode 100644 index 00000000000..e028e72bd19 --- /dev/null +++ b/jetty-ee10/jetty-ee10-http-spi/src/main/java/org/eclipse/jetty/ee10/http/spi/JettyExchange.java @@ -0,0 +1,27 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.http.spi; + +import com.sun.net.httpserver.HttpPrincipal; + +/** + * + */ +public interface JettyExchange +{ + + HttpPrincipal getPrincipal(); + + void setPrincipal(HttpPrincipal principal); +} diff --git a/jetty-ee10/jetty-ee10-http-spi/src/main/java/org/eclipse/jetty/ee10/http/spi/JettyHttpContext.java b/jetty-ee10/jetty-ee10-http-spi/src/main/java/org/eclipse/jetty/ee10/http/spi/JettyHttpContext.java new file mode 100644 index 00000000000..9d5de006d9c --- /dev/null +++ b/jetty-ee10/jetty-ee10-http-spi/src/main/java/org/eclipse/jetty/ee10/http/spi/JettyHttpContext.java @@ -0,0 +1,104 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.http.spi; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.sun.net.httpserver.Authenticator; +import com.sun.net.httpserver.Filter; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; + +/** + * Jetty implementation of {@link com.sun.net.httpserver.HttpContext} + */ +public class JettyHttpContext extends com.sun.net.httpserver.HttpContext +{ + + private HttpSpiContextHandler _jettyContextHandler; + + private HttpServer _server; + + private Map _attributes = new HashMap(); + + private List _filters = new ArrayList(); + + private Authenticator _authenticator; + + protected JettyHttpContext(HttpServer server, String path, + HttpHandler handler) + { + this._server = server; + _jettyContextHandler = new HttpSpiContextHandler(this, handler); + _jettyContextHandler.setContextPath(path); + } + + protected HttpSpiContextHandler getJettyContextHandler() + { + return _jettyContextHandler; + } + + @Override + public HttpHandler getHandler() + { + return _jettyContextHandler.getHttpHandler(); + } + + @Override + public void setHandler(HttpHandler h) + { + _jettyContextHandler.setHttpHandler(h); + } + + @Override + public String getPath() + { + return _jettyContextHandler.getContextPath(); + } + + @Override + public HttpServer getServer() + { + return _server; + } + + @Override + public Map getAttributes() + { + return _attributes; + } + + @Override + public List getFilters() + { + return _filters; + } + + @Override + public Authenticator setAuthenticator(Authenticator auth) + { + Authenticator previous = _authenticator; + _authenticator = auth; + return previous; + } + + @Override + public Authenticator getAuthenticator() + { + return _authenticator; + } +} diff --git a/jetty-ee10/jetty-ee10-http-spi/src/main/java/org/eclipse/jetty/ee10/http/spi/JettyHttpExchange.java b/jetty-ee10/jetty-ee10-http-spi/src/main/java/org/eclipse/jetty/ee10/http/spi/JettyHttpExchange.java new file mode 100644 index 00000000000..a2837951635 --- /dev/null +++ b/jetty-ee10/jetty-ee10-http-spi/src/main/java/org/eclipse/jetty/ee10/http/spi/JettyHttpExchange.java @@ -0,0 +1,164 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.http.spi; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.net.URI; + +import com.sun.net.httpserver.Headers; +import com.sun.net.httpserver.HttpContext; +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpPrincipal; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +public class JettyHttpExchange extends HttpExchange implements JettyExchange +{ + private JettyHttpExchangeDelegate _delegate; + + public JettyHttpExchange(HttpContext jaxWsContext, HttpServletRequest req, HttpServletResponse resp) + { + super(); + _delegate = new JettyHttpExchangeDelegate(jaxWsContext, req, resp); + } + + @Override + public int hashCode() + { + return _delegate.hashCode(); + } + + @Override + public Headers getRequestHeaders() + { + return _delegate.getRequestHeaders(); + } + + @Override + public Headers getResponseHeaders() + { + return _delegate.getResponseHeaders(); + } + + @Override + public URI getRequestURI() + { + return _delegate.getRequestURI(); + } + + @Override + public String getRequestMethod() + { + return _delegate.getRequestMethod(); + } + + @Override + public HttpContext getHttpContext() + { + return _delegate.getHttpContext(); + } + + @Override + public void close() + { + _delegate.close(); + } + + @Override + public boolean equals(Object obj) + { + return _delegate.equals(obj); + } + + @Override + public InputStream getRequestBody() + { + return _delegate.getRequestBody(); + } + + @Override + public OutputStream getResponseBody() + { + return _delegate.getResponseBody(); + } + + @Override + public void sendResponseHeaders(int rCode, long responseLength) throws IOException + { + _delegate.sendResponseHeaders(rCode, responseLength); + } + + @Override + public InetSocketAddress getRemoteAddress() + { + return _delegate.getRemoteAddress(); + } + + @Override + public int getResponseCode() + { + return _delegate.getResponseCode(); + } + + @Override + public InetSocketAddress getLocalAddress() + { + return _delegate.getLocalAddress(); + } + + @Override + public String getProtocol() + { + return _delegate.getProtocol(); + } + + @Override + public Object getAttribute(String name) + { + return _delegate.getAttribute(name); + } + + @Override + public void setAttribute(String name, Object value) + { + _delegate.setAttribute(name, value); + } + + @Override + public void setStreams(InputStream i, OutputStream o) + { + _delegate.setStreams(i, o); + } + + @Override + public HttpPrincipal getPrincipal() + { + return _delegate.getPrincipal(); + } + + @Override + public void setPrincipal(HttpPrincipal principal) + { + _delegate.setPrincipal(principal); + } + + @Override + public String toString() + { + return _delegate.toString(); + } +} diff --git a/jetty-ee10/jetty-ee10-http-spi/src/main/java/org/eclipse/jetty/ee10/http/spi/JettyHttpExchangeDelegate.java b/jetty-ee10/jetty-ee10-http-spi/src/main/java/org/eclipse/jetty/ee10/http/spi/JettyHttpExchangeDelegate.java new file mode 100644 index 00000000000..2e7cc1c9657 --- /dev/null +++ b/jetty-ee10/jetty-ee10-http-spi/src/main/java/org/eclipse/jetty/ee10/http/spi/JettyHttpExchangeDelegate.java @@ -0,0 +1,226 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.http.spi; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Enumeration; +import java.util.List; +import java.util.Map; + +import com.sun.net.httpserver.Headers; +import com.sun.net.httpserver.HttpContext; +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpPrincipal; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +/** + * Jetty implementation of {@link com.sun.net.httpserver.HttpExchange} + */ +public class JettyHttpExchangeDelegate extends HttpExchange +{ + + private HttpContext _httpContext; + + private HttpServletRequest _req; + + private HttpServletResponse _resp; + + private Headers _responseHeaders = new Headers(); + + private int _responseCode = 0; + + private InputStream _is; + + private OutputStream _os; + + private HttpPrincipal _httpPrincipal; + + JettyHttpExchangeDelegate(HttpContext jaxWsContext, HttpServletRequest req, HttpServletResponse resp) + { + this._httpContext = jaxWsContext; + this._req = req; + this._resp = resp; + try + { + this._is = req.getInputStream(); + this._os = resp.getOutputStream(); + } + catch (IOException ex) + { + throw new RuntimeException(ex); + } + } + + @Override + public Headers getRequestHeaders() + { + Headers headers = new Headers(); + Enumeration en = _req.getHeaderNames(); + while (en.hasMoreElements()) + { + String name = (String)en.nextElement(); + Enumeration en2 = _req.getHeaders(name); + while (en2.hasMoreElements()) + { + String value = (String)en2.nextElement(); + headers.add(name, value); + } + } + return headers; + } + + @Override + public Headers getResponseHeaders() + { + return _responseHeaders; + } + + @Override + public URI getRequestURI() + { + try + { + String uriAsString = _req.getRequestURI(); + if (_req.getQueryString() != null) + { + uriAsString += "?" + _req.getQueryString(); + } + + return new URI(uriAsString); + } + catch (URISyntaxException ex) + { + throw new RuntimeException(ex); + } + } + + @Override + public String getRequestMethod() + { + return _req.getMethod(); + } + + @Override + public HttpContext getHttpContext() + { + return _httpContext; + } + + @Override + public void close() + { + try + { + _resp.getOutputStream().close(); + } + catch (IOException ex) + { + throw new RuntimeException(ex); + } + } + + @Override + public InputStream getRequestBody() + { + return _is; + } + + @Override + public OutputStream getResponseBody() + { + return _os; + } + + @Override + public void sendResponseHeaders(int rCode, long responseLength) throws IOException + { + this._responseCode = rCode; + + for (Map.Entry> stringListEntry : _responseHeaders.entrySet()) + { + String name = stringListEntry.getKey(); + List values = stringListEntry.getValue(); + + for (String value : values) + { + _resp.setHeader(name, value); + } + } + if (responseLength > 0) + { + _resp.setHeader("content-length", "" + responseLength); + } + _resp.setStatus(rCode); + } + + @Override + public InetSocketAddress getRemoteAddress() + { + return new InetSocketAddress(_req.getRemoteAddr(), _req.getRemotePort()); + } + + @Override + public int getResponseCode() + { + return _responseCode; + } + + @Override + public InetSocketAddress getLocalAddress() + { + return new InetSocketAddress(_req.getLocalAddr(), _req.getLocalPort()); + } + + @Override + public String getProtocol() + { + return _req.getProtocol(); + } + + @Override + public Object getAttribute(String name) + { + return _req.getAttribute(name); + } + + @Override + public void setAttribute(String name, Object value) + { + _req.setAttribute(name, value); + } + + @Override + public void setStreams(InputStream i, OutputStream o) + { + _is = i; + _os = o; + } + + @Override + public HttpPrincipal getPrincipal() + { + return _httpPrincipal; + } + + public void setPrincipal(HttpPrincipal principal) + { + this._httpPrincipal = principal; + } +} diff --git a/jetty-ee10/jetty-ee10-http-spi/src/main/java/org/eclipse/jetty/ee10/http/spi/JettyHttpServer.java b/jetty-ee10/jetty-ee10-http-spi/src/main/java/org/eclipse/jetty/ee10/http/spi/JettyHttpServer.java new file mode 100644 index 00000000000..d982955e231 --- /dev/null +++ b/jetty-ee10/jetty-ee10-http-spi/src/main/java/org/eclipse/jetty/ee10/http/spi/JettyHttpServer.java @@ -0,0 +1,314 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.http.spi; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Executor; + +import com.sun.net.httpserver.HttpContext; +import com.sun.net.httpserver.HttpHandler; +import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.HttpConnectionFactory; +import org.eclipse.jetty.server.NetworkConnector; +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.util.thread.ThreadPool; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Jetty implementation of {@link com.sun.net.httpserver.HttpServer}. + */ +public class JettyHttpServer extends com.sun.net.httpserver.HttpServer +{ + private static final Logger LOG = LoggerFactory.getLogger(JettyHttpServer.class); + + private final HttpConfiguration _httpConfiguration; + + private final Server _server; + + private boolean _serverShared; + + private InetSocketAddress _addr; + + private Map _contexts = new HashMap<>(); + + private Map _connectors = new HashMap<>(); + + public JettyHttpServer(Server server, boolean shared) + { + this(server, shared, new HttpConfiguration()); + } + + public JettyHttpServer(Server server, boolean shared, HttpConfiguration configuration) + { + this._server = server; + this._serverShared = shared; + this._httpConfiguration = configuration; + } + + public HttpConfiguration getHttpConfiguration() + { + return _httpConfiguration; + } + + @Override + public void bind(InetSocketAddress addr, int backlog) throws IOException + { + this._addr = addr; + // check if there is already a connector listening + Collection connectors = _server.getBeans(NetworkConnector.class); + if (connectors != null) + { + for (NetworkConnector connector : connectors) + { + if (connector.getPort() == addr.getPort() || connector.getLocalPort() == addr.getPort()) + { + if (LOG.isDebugEnabled()) + LOG.debug("server already bound to port {}, no need to rebind", addr.getPort()); + return; + } + } + } + + if (_serverShared) + throw new IOException("jetty server is not bound to port " + addr.getPort()); + + if (LOG.isDebugEnabled()) + LOG.debug("binding server to port {}", addr.getPort()); + ServerConnector connector = new ServerConnector(_server); + connector.setPort(addr.getPort()); + connector.setHost(addr.getHostName()); + + _server.addConnector(connector); + + _connectors.put(addr.getHostName() + addr.getPort(), connector); + } + + protected Server getServer() + { + return _server; + } + + protected ServerConnector newServerConnector(InetSocketAddress addr, int backlog) + { + ServerConnector connector = new ServerConnector(_server, new HttpConnectionFactory(_httpConfiguration)); + connector.setPort(addr.getPort()); + connector.setHost(addr.getHostName()); + return connector; + } + + @Override + public InetSocketAddress getAddress() + { + if (_addr.getPort() == 0 && _server.isStarted()) + return new InetSocketAddress(_addr.getHostString(), _server.getBean(NetworkConnector.class).getLocalPort()); + return _addr; + } + + @Override + public void start() + { + if (_serverShared) + return; + + try + { + _server.start(); + } + catch (Exception ex) + { + throw new RuntimeException(ex); + } + } + + @Override + public void setExecutor(Executor executor) + { + if (executor == null) + throw new IllegalArgumentException("missing required 'executor' argument"); + ThreadPool threadPool = _server.getThreadPool(); + if (threadPool instanceof DelegatingThreadPool) + { + try + { + if (_server.isRunning()) + { + _server.stop(); + } + ((DelegatingThreadPool)_server.getThreadPool()).setExecutor(executor); + _server.start(); + } + catch (Exception e) + { + throw new RuntimeException(e.getMessage(), e); + } + } + else + { + throw new UnsupportedOperationException("!DelegatingThreadPool"); + } + } + + @Override + public Executor getExecutor() + { + ThreadPool threadPool = _server.getThreadPool(); + if (threadPool instanceof DelegatingThreadPool) + return ((DelegatingThreadPool)_server.getThreadPool()).getExecutor(); + return threadPool; + } + + @Override + public void stop(int delay) + { + cleanUpContexts(); + cleanUpConnectors(); + + if (_serverShared) + return; + + try + { + _server.stop(); + } + catch (Exception ex) + { + throw new RuntimeException(ex); + } + } + + private void cleanUpContexts() + { + for (Map.Entry stringJettyHttpContextEntry : _contexts.entrySet()) + { + JettyHttpContext context = stringJettyHttpContextEntry.getValue(); + _server.removeBean(context.getJettyContextHandler()); + } + _contexts.clear(); + } + + private void cleanUpConnectors() + { + for (Map.Entry stringConnectorEntry : _connectors.entrySet()) + { + Connector connector = stringConnectorEntry.getValue(); + try + { + connector.stop(); + } + catch (Exception ex) + { + LOG.warn("Unable to stop connector {}", connector, ex); + } + _server.removeConnector(connector); + } + _connectors.clear(); + } + + @Override + public HttpContext createContext(String path, HttpHandler httpHandler) + { + checkIfContextIsFree(path); + + JettyHttpContext context = new JettyHttpContext(this, path, httpHandler); + HttpSpiContextHandler jettyContextHandler = context.getJettyContextHandler(); + + ContextHandlerCollection chc = _server.getDescendant(ContextHandlerCollection.class); + + if (chc == null) + throw new RuntimeException("could not find ContextHandlerCollection, you must configure one"); + + chc.addHandler(jettyContextHandler); + if (chc.isStarted()) + { + try + { + jettyContextHandler.start(); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + } + _contexts.put(path, context); + return context; + } + + @Override + public HttpContext createContext(String path) + { + return createContext(path, null); + } + + private void checkIfContextIsFree(String path) + { + Handler serverHandler = _server.getHandler(); + if (serverHandler instanceof ContextHandler) + { + ContextHandler ctx = (ContextHandler)serverHandler; + if (ctx.getContextPath().equals(path)) + throw new RuntimeException("another context already bound to path " + path); + } + + List handlers = _server.getHandlers(); + if (handlers == null) + return; + + for (Handler handler : handlers) + { + if (handler instanceof ContextHandler) + { + ContextHandler ctx = (ContextHandler)handler; + if (ctx.getContextPath().equals(path)) + throw new RuntimeException("another context already bound to path " + path); + } + } + } + + @Override + public void removeContext(String path) throws IllegalArgumentException + { + JettyHttpContext context = _contexts.remove(path); + if (context == null) + return; + HttpSpiContextHandler handler = context.getJettyContextHandler(); + + ContextHandlerCollection chc = _server.getDescendant(ContextHandlerCollection.class); + try + { + handler.stop(); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + chc.removeHandler(handler); + } + + @Override + public void removeContext(HttpContext context) + { + removeContext(context.getPath()); + } +} diff --git a/jetty-ee10/jetty-ee10-http-spi/src/main/java/org/eclipse/jetty/ee10/http/spi/JettyHttpServerProvider.java b/jetty-ee10/jetty-ee10-http-spi/src/main/java/org/eclipse/jetty/ee10/http/spi/JettyHttpServerProvider.java new file mode 100644 index 00000000000..0626ce31ea7 --- /dev/null +++ b/jetty-ee10/jetty-ee10-http-spi/src/main/java/org/eclipse/jetty/ee10/http/spi/JettyHttpServerProvider.java @@ -0,0 +1,71 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.http.spi; + +import java.io.IOException; +import java.net.InetSocketAddress; + +import com.sun.net.httpserver.HttpServer; +import com.sun.net.httpserver.HttpsServer; +import com.sun.net.httpserver.spi.HttpServerProvider; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.ContextHandlerCollection; +import org.eclipse.jetty.server.handler.DefaultHandler; +import org.eclipse.jetty.server.handler.HandlerList; +import org.eclipse.jetty.util.thread.QueuedThreadPool; +import org.eclipse.jetty.util.thread.ThreadPool; + +/** + * Jetty implementation of
    Java HTTP Server SPI + */ +public class JettyHttpServerProvider extends HttpServerProvider +{ + + private static Server _server; + + public static void setServer(Server server) + { + _server = server; + } + + @Override + public HttpServer createHttpServer(InetSocketAddress addr, int backlog) + throws IOException + { + Server server = _server; + boolean shared = true; + + if (server == null) + { + ThreadPool threadPool = new DelegatingThreadPool(new QueuedThreadPool()); + server = new Server(threadPool); + + HandlerList handlerCollection = new HandlerList(new ContextHandlerCollection(), new DefaultHandler()); + server.setHandler(handlerCollection); + + shared = false; + } + + JettyHttpServer jettyHttpServer = new JettyHttpServer(server, shared); + if (addr != null) + jettyHttpServer.bind(addr, backlog); + return jettyHttpServer; + } + + @Override + public HttpsServer createHttpsServer(InetSocketAddress addr, int backlog) throws IOException + { + throw new UnsupportedOperationException(); + } +} diff --git a/jetty-ee10/jetty-ee10-http-spi/src/main/java/org/eclipse/jetty/ee10/http/spi/JettyHttpsExchange.java b/jetty-ee10/jetty-ee10-http-spi/src/main/java/org/eclipse/jetty/ee10/http/spi/JettyHttpsExchange.java new file mode 100644 index 00000000000..61917d6419a --- /dev/null +++ b/jetty-ee10/jetty-ee10-http-spi/src/main/java/org/eclipse/jetty/ee10/http/spi/JettyHttpsExchange.java @@ -0,0 +1,174 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.http.spi; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.net.URI; +import javax.net.ssl.SSLSession; + +import com.sun.net.httpserver.Headers; +import com.sun.net.httpserver.HttpContext; +import com.sun.net.httpserver.HttpPrincipal; +import com.sun.net.httpserver.HttpsExchange; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +/** + * + */ +public class JettyHttpsExchange extends HttpsExchange implements JettyExchange +{ + private JettyHttpExchangeDelegate _delegate; + + public JettyHttpsExchange(HttpContext jaxWsContext, HttpServletRequest req, HttpServletResponse resp) + { + super(); + _delegate = new JettyHttpExchangeDelegate(jaxWsContext, req, resp); + } + + @Override + public int hashCode() + { + return _delegate.hashCode(); + } + + @Override + public Headers getRequestHeaders() + { + return _delegate.getRequestHeaders(); + } + + @Override + public Headers getResponseHeaders() + { + return _delegate.getResponseHeaders(); + } + + @Override + public URI getRequestURI() + { + return _delegate.getRequestURI(); + } + + @Override + public String getRequestMethod() + { + return _delegate.getRequestMethod(); + } + + @Override + public HttpContext getHttpContext() + { + return _delegate.getHttpContext(); + } + + @Override + public void close() + { + _delegate.close(); + } + + @Override + public boolean equals(Object obj) + { + return _delegate.equals(obj); + } + + @Override + public InputStream getRequestBody() + { + return _delegate.getRequestBody(); + } + + @Override + public OutputStream getResponseBody() + { + return _delegate.getResponseBody(); + } + + @Override + public void sendResponseHeaders(int rCode, long responseLength) throws IOException + { + _delegate.sendResponseHeaders(rCode, responseLength); + } + + @Override + public InetSocketAddress getRemoteAddress() + { + return _delegate.getRemoteAddress(); + } + + @Override + public int getResponseCode() + { + return _delegate.getResponseCode(); + } + + @Override + public InetSocketAddress getLocalAddress() + { + return _delegate.getLocalAddress(); + } + + @Override + public String getProtocol() + { + return _delegate.getProtocol(); + } + + @Override + public Object getAttribute(String name) + { + return _delegate.getAttribute(name); + } + + @Override + public void setAttribute(String name, Object value) + { + _delegate.setAttribute(name, value); + } + + @Override + public void setStreams(InputStream i, OutputStream o) + { + _delegate.setStreams(i, o); + } + + @Override + public HttpPrincipal getPrincipal() + { + return _delegate.getPrincipal(); + } + + @Override + public void setPrincipal(HttpPrincipal principal) + { + _delegate.setPrincipal(principal); + } + + @Override + public String toString() + { + return _delegate.toString(); + } + + @Override + public SSLSession getSSLSession() + { + return null; + } +} diff --git a/jetty-ee10/jetty-ee10-http-spi/src/main/resources/META-INF/services/com.sun.net.httpserver.spi.HttpServerProvider b/jetty-ee10/jetty-ee10-http-spi/src/main/resources/META-INF/services/com.sun.net.httpserver.spi.HttpServerProvider new file mode 100644 index 00000000000..51958d50ce6 --- /dev/null +++ b/jetty-ee10/jetty-ee10-http-spi/src/main/resources/META-INF/services/com.sun.net.httpserver.spi.HttpServerProvider @@ -0,0 +1 @@ +org.eclipse.jetty.http.spi.JettyHttpServerProvider diff --git a/jetty-ee10/jetty-ee10-http-spi/src/test/java/org/eclipse/jetty/ee10/http/spi/LoggingUtil.java b/jetty-ee10/jetty-ee10-http-spi/src/test/java/org/eclipse/jetty/ee10/http/spi/LoggingUtil.java new file mode 100644 index 00000000000..cbbc4d76390 --- /dev/null +++ b/jetty-ee10/jetty-ee10-http-spi/src/test/java/org/eclipse/jetty/ee10/http/spi/LoggingUtil.java @@ -0,0 +1,29 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.http.spi; + +public final class LoggingUtil +{ + /** + * It's easier to setup logging in code for this test project, + * then it is to setup the various system properties and files for every test + * execution (maven, CI, and IDE). + */ + public static void init() + { + // Wire up java.util.logging (used by javax.xml.soap others) to slf4j. + org.slf4j.bridge.SLF4JBridgeHandler.removeHandlersForRootLogger(); + org.slf4j.bridge.SLF4JBridgeHandler.install(); + } +} diff --git a/jetty-ee10/jetty-ee10-http-spi/src/test/java/org/eclipse/jetty/ee10/http/spi/SPIServerTest.java b/jetty-ee10/jetty-ee10-http-spi/src/test/java/org/eclipse/jetty/ee10/http/spi/SPIServerTest.java new file mode 100644 index 00000000000..db1f3230c37 --- /dev/null +++ b/jetty-ee10/jetty-ee10-http-spi/src/test/java/org/eclipse/jetty/ee10/http/spi/SPIServerTest.java @@ -0,0 +1,138 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.http.spi; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.Authenticator; +import java.net.HttpURLConnection; +import java.net.InetSocketAddress; +import java.net.PasswordAuthentication; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.Base64; + +import com.sun.net.httpserver.BasicAuthenticator; +import com.sun.net.httpserver.Headers; +import com.sun.net.httpserver.HttpContext; +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; +import org.eclipse.jetty.util.IO; +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 SPIServerTest +{ + static + { + LoggingUtil.init(); + } + + String host = "localhost"; + HttpServer server; + int port; + + @BeforeEach + public void before() throws Exception + { + server = new JettyHttpServerProvider().createHttpServer(new InetSocketAddress(host, 0), 10); + + server.start(); + port = server.getAddress().getPort(); + System.err.println(port); + } + + @AfterEach + public void after() throws Exception + { + server.stop(0); + } + + @Test + public void testSimple() throws Exception + { + server.createContext("/", new HttpHandler() + { + public void handle(HttpExchange exchange) throws IOException + { + Headers responseHeaders = exchange.getResponseHeaders(); + responseHeaders.set("Content-Type", "text/plain"); + exchange.sendResponseHeaders(200, 0); + + OutputStream responseBody = exchange.getResponseBody(); + responseBody.write("Hello".getBytes(StandardCharsets.ISO_8859_1)); + responseBody.close(); + } + }); + + URL url = new URL("http://localhost:" + port + "/"); + assertThat(IO.toString(url.openConnection().getInputStream()), is("Hello")); + } + + @Test + public void testAuth() throws Exception + { + final HttpContext httpContext = server.createContext("/", new HttpHandler() + { + public void handle(HttpExchange exchange) throws IOException + { + Headers responseHeaders = exchange.getResponseHeaders(); + responseHeaders.set("Content-Type", "text/plain"); + exchange.sendResponseHeaders(200, 0); + + OutputStream responseBody = exchange.getResponseBody(); + responseBody.write("Hello".getBytes(StandardCharsets.ISO_8859_1)); + responseBody.close(); + } + }); + + httpContext.setAuthenticator(new BasicAuthenticator("Test") + { + @Override + public boolean checkCredentials(String username, String password) + { + if ("username".equals(username) && password.equals("password")) + return true; + return false; + } + }); + + URL url = new URL("http://localhost:" + port + "/"); + HttpURLConnection client = (HttpURLConnection)url.openConnection(); + client.connect(); + assertThat(client.getResponseCode(), is(401)); + + Authenticator.setDefault(new Authenticator() + { + protected PasswordAuthentication getPasswordAuthentication() + { + return new PasswordAuthentication("username", "password".toCharArray()); + } + }); + + client = (HttpURLConnection)url.openConnection(); + String userpass = "username:password"; + String basicAuth = "Basic " + Base64.getEncoder().encodeToString(userpass.getBytes(StandardCharsets.ISO_8859_1)); + client.setRequestProperty("Authorization", basicAuth); + + client.connect(); + assertThat(client.getResponseCode(), is(200)); + assertThat(IO.toString(client.getInputStream()), is("Hello")); + } +} diff --git a/jetty-ee10/jetty-ee10-http-spi/src/test/java/org/eclipse/jetty/ee10/http/spi/TestEndpointMultiplePublishProblem.java b/jetty-ee10/jetty-ee10-http-spi/src/test/java/org/eclipse/jetty/ee10/http/spi/TestEndpointMultiplePublishProblem.java new file mode 100644 index 00000000000..6af9b42a4da --- /dev/null +++ b/jetty-ee10/jetty-ee10-http-spi/src/test/java/org/eclipse/jetty/ee10/http/spi/TestEndpointMultiplePublishProblem.java @@ -0,0 +1,160 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.http.spi; + +import java.net.URL; +import javax.xml.namespace.QName; + +import jakarta.jws.WebMethod; +import jakarta.jws.WebService; +import jakarta.xml.ws.BindingProvider; +import jakarta.xml.ws.Endpoint; +import jakarta.xml.ws.Service; +import jakarta.xml.ws.WebEndpoint; +import jakarta.xml.ws.WebServiceClient; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.handler.ContextHandlerCollection; +import org.eclipse.jetty.util.thread.QueuedThreadPool; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class TestEndpointMultiplePublishProblem +{ + static + { + LoggingUtil.init(); + } + + private static String default_impl = System.getProperty("com.sun.net.httpserver.HttpServerProvider"); + + @BeforeAll + public static void changeImpl() + { + System.setProperty("com.sun.net.httpserver.HttpServerProvider", JettyHttpServerProvider.class.getName()); + } + + @AfterAll + public static void restoreImpl() + { + if (default_impl != null) + { + System.setProperty("com.sun.net.httpserver.HttpServerProvider", default_impl); + } + } + + @Test + public void mainJetty() + throws Exception + { + + Server jettyWebServer = new Server(new DelegatingThreadPool(new QueuedThreadPool())); + ServerConnector connector = new ServerConnector(jettyWebServer); + connector.setHost("localhost"); + connector.setPort(0); + connector.setReuseAddress(true); + jettyWebServer.addConnector(connector); + jettyWebServer.setHandler(new ContextHandlerCollection()); + + JettyHttpServerProvider.setServer(jettyWebServer); + + jettyWebServer.start(); + + Endpoint.publish(String.format("http://%s:%d/hello", "localhost", 0), new WsHello()); + Endpoint.publish(String.format("http://%s:%d/hello2", "localhost", 0), new WsHello()); + + int port = connector.getLocalPort(); + + HttpClient httpClient = new HttpClient(); + httpClient.start(); + + { + String url = String.format("http://localhost:%d/hello", port); + String urlWsdl = url + "?wsdl"; + + ContentResponse contentResponse = httpClient.newRequest(url).send(); + Assertions.assertEquals(200, contentResponse.getStatus()); + + HelloMessengerService helloMessengerService = new HelloMessengerService(new URL(urlWsdl)); + Hello hello = helloMessengerService.getHelloMessengerPort(); + ((BindingProvider)hello).getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, url); + String helloResponse = hello.hello(); + Assertions.assertEquals("G'Day mate!", helloResponse); + } + + { + + String url2 = String.format("http://localhost:%d/hello2", port); + String url2Wsdl = url2 + "?wsdl"; + + ContentResponse contentResponse = httpClient.newRequest(url2Wsdl).send(); + Assertions.assertEquals(200, contentResponse.getStatus()); + + HelloMessengerService helloMessengerService = new HelloMessengerService(new URL(url2Wsdl)); + Hello hello = helloMessengerService.getHelloMessengerPort(); + ((BindingProvider)hello).getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, url2); + String helloResponse = hello.hello(); + Assertions.assertEquals("G'Day mate!", helloResponse); + } + httpClient.stop(); + jettyWebServer.stop(); + } + + @WebService(targetNamespace = "http://org.eclipse.jetty.ws.test", name = "HelloService") + public interface Hello + { + @WebMethod + String hello(); + } + + @WebService(targetNamespace = "http://org.eclipse.jetty.ws.test", name = "HelloService") + public static class WsHello + implements Hello + { + @WebMethod + public String hello() + { + return "G'Day mate!"; + } + } + + @WebServiceClient(name = "HelloService", targetNamespace = "http://org.eclipse.jetty.ws.test") + public static class HelloMessengerService + extends Service + { + + public HelloMessengerService(URL wsdlLocation) + { + super(wsdlLocation, // + new QName("http://org.eclipse.jetty.ws.test", "WsHelloService")); + } + + @WebEndpoint(name = "HelloServicePort") + public Hello getHelloMessengerPort() + { + return super.getPort(new QName("http://org.eclipse.jetty.ws.test", "HelloServicePort"), // + Hello.class); + } + } + + private void assertWsdl(String wsdl) + throws Exception + { + + } +} diff --git a/jetty-ee10/jetty-ee10-http-spi/src/test/java/org/eclipse/jetty/ee10/http/spi/TestSPIServer.java b/jetty-ee10/jetty-ee10-http-spi/src/test/java/org/eclipse/jetty/ee10/http/spi/TestSPIServer.java new file mode 100644 index 00000000000..b980df8287a --- /dev/null +++ b/jetty-ee10/jetty-ee10-http-spi/src/test/java/org/eclipse/jetty/ee10/http/spi/TestSPIServer.java @@ -0,0 +1,202 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.http.spi; + +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.net.URI; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import com.sun.net.httpserver.BasicAuthenticator; +import com.sun.net.httpserver.Headers; +import com.sun.net.httpserver.HttpContext; +import com.sun.net.httpserver.HttpServer; +import jakarta.servlet.http.HttpServletResponse; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.client.api.Request; +import org.eclipse.jetty.client.util.BasicAuthentication; +import org.eclipse.jetty.server.NetworkConnector; +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class TestSPIServer +{ + static + { + LoggingUtil.init(); + } + + /** + * Create a server that has a null InetSocketAddress, then + * bind before using. + */ + @Test + public void testUnboundHttpServer() throws Exception + { + + HttpServer server = null; + + try + { + //ensure no InetSocketAddress is passed + server = new JettyHttpServerProvider().createHttpServer(null, 10); + + final HttpContext httpContext = server.createContext("/", + exchange -> + { + Headers responseHeaders = exchange.getResponseHeaders(); + responseHeaders.set("Content-Type", "text/plain"); + exchange.sendResponseHeaders(200, 0); + + OutputStream responseBody = exchange.getResponseBody(); + Headers requestHeaders = exchange.getRequestHeaders(); + Set keySet = requestHeaders.keySet(); + Iterator iter = keySet.iterator(); + while (iter.hasNext()) + { + String key = iter.next(); + List values = requestHeaders.get(key); + String s = key + " = " + values.toString() + "\n"; + responseBody.write(s.getBytes()); + } + responseBody.close(); + }); + + httpContext.setAuthenticator(new BasicAuthenticator("Test") + { + @Override + public boolean checkCredentials(String username, String password) + { + if ("username".equals(username) && password.equals("password")) + return true; + return false; + } + }); + + //now bind one. Use port '0' to let jetty pick the + //address to bind so this test isn't port-specific + //and thus is portable and can be run concurrently on CI + //environments + server.bind(new InetSocketAddress("localhost", 0), 10); + + server.start(); + + //find out the port jetty picked + Server jetty = ((JettyHttpServer)server).getServer(); + int port = ((NetworkConnector)jetty.getConnectors()[0]).getLocalPort(); + + HttpClient client = new HttpClient(); + client.start(); + + try + { + Request request = client.newRequest("http://localhost:" + port + "/"); + client.getAuthenticationStore().addAuthentication(new BasicAuthentication(URI.create("http://localhost:" + port), "Test", "username", "password")); + ContentResponse response = request.send(); + assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + } + finally + { + client.stop(); + } + } + finally + { + if (server != null) + server.stop(5); + } + } + + /** + * Test using a server that is created with a given InetSocketAddress + */ + @Test + public void testBoundHttpServer() throws Exception + { + + HttpServer server = null; + + try + { + //use an InetSocketAddress, but use port value of '0' to allow + //jetty to pick a free port. Ensures test is not tied to specific port number + //for test portability and concurrency. + server = new JettyHttpServerProvider().createHttpServer(new + InetSocketAddress("localhost", 0), 10); + + final HttpContext httpContext = server.createContext("/", + exchange -> + { + Headers responseHeaders = exchange.getResponseHeaders(); + responseHeaders.set("Content-Type", "text/plain"); + exchange.sendResponseHeaders(200, 0); + + OutputStream responseBody = exchange.getResponseBody(); + Headers requestHeaders = exchange.getRequestHeaders(); + Set keySet = requestHeaders.keySet(); + Iterator iter = keySet.iterator(); + while (iter.hasNext()) + { + String key = iter.next(); + List values = requestHeaders.get(key); + String s = key + " = " + values.toString() + "\n"; + responseBody.write(s.getBytes()); + } + responseBody.close(); + }); + + httpContext.setAuthenticator(new BasicAuthenticator("Test") + { + @Override + public boolean checkCredentials(String username, String password) + { + if ("username".equals(username) && password.equals("password")) + return true; + return false; + } + }); + + server.start(); + + //find out the port jetty picked + Server jetty = ((JettyHttpServer)server).getServer(); + int port = ((NetworkConnector)jetty.getConnectors()[0]).getLocalPort(); + + HttpClient client = new HttpClient(); + client.start(); + + try + { + Request request = client.newRequest("http://localhost:" + port + "/"); + client.getAuthenticationStore().addAuthentication(new BasicAuthentication(URI.create("http://localhost:" + port), "Test", "username", "password")); + ContentResponse response = request.send(); + assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + } + finally + { + client.stop(); + } + } + finally + { + if (server != null) + server.stop(5); + } + } +} diff --git a/jetty-ee10/jetty-ee10-http-spi/src/test/java/org/eclipse/jetty/ee10/http/spi/util/Pool.java b/jetty-ee10/jetty-ee10-http-spi/src/test/java/org/eclipse/jetty/ee10/http/spi/util/Pool.java new file mode 100644 index 00000000000..b5c414406a0 --- /dev/null +++ b/jetty-ee10/jetty-ee10-http-spi/src/test/java/org/eclipse/jetty/ee10/http/spi/util/Pool.java @@ -0,0 +1,43 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.http.spi.util; + +/** + * This class holds the default pool constants + */ +public enum Pool +{ + + DEFAULT_SIZE(0), + + CORE_POOL_SIZE(15), + + MAXIMUM_POOL_SIZE(20), + + KEEP_ALIVE_TIME(300), + + DEFAULT_WORK_QUEUE_SIZE(20); + + private final int value; + + private Pool(int value) + { + this.value = value; + } + + public int getValue() + { + return value; + } +} diff --git a/jetty-ee10/jetty-ee10-http-spi/src/test/java/org/eclipse/jetty/ee10/http/spi/util/PrintTask.java b/jetty-ee10/jetty-ee10-http-spi/src/test/java/org/eclipse/jetty/ee10/http/spi/util/PrintTask.java new file mode 100644 index 00000000000..bd50dfe16c2 --- /dev/null +++ b/jetty-ee10/jetty-ee10-http-spi/src/test/java/org/eclipse/jetty/ee10/http/spi/util/PrintTask.java @@ -0,0 +1,27 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.http.spi.util; + +/** + * This is a sample task. Test cases uses this for testing purpose + */ +public class PrintTask implements Runnable +{ + @Override + public void run() + { + System.out.println("Started print task execution "); + System.out.println("Completed print task execution"); + } +} diff --git a/jetty-ee10/jetty-ee10-http-spi/src/test/java/org/eclipse/jetty/ee10/http/spi/util/SpiConstants.java b/jetty-ee10/jetty-ee10-http-spi/src/test/java/org/eclipse/jetty/ee10/http/spi/util/SpiConstants.java new file mode 100644 index 00000000000..62c451891bf --- /dev/null +++ b/jetty-ee10/jetty-ee10-http-spi/src/test/java/org/eclipse/jetty/ee10/http/spi/util/SpiConstants.java @@ -0,0 +1,78 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.http.spi.util; + +/** + * This class holds the constant required for test cases. + */ +public class SpiConstants +{ + + public static final int[] poolInfo = + {Pool.MAXIMUM_POOL_SIZE.getValue(), Pool.KEEP_ALIVE_TIME.getValue(), Pool.DEFAULT_WORK_QUEUE_SIZE.getValue()}; + + public static final String LOCAL_HOST = "localhost"; + + public static final int DEFAULT_PORT = 0; + + public static final int ONE = 1; + + public static final int MINUS_ONE = -1; + + public static final int TWO = 2; + + public static final int ZERO = 0; + + public static final int BACK_LOG = 10; + + public static final int DELAY = 1; + + public static final String PROTOCOL = "HTTP"; + + public static final String QUERY_STRING = "key1=value1,key2=value2"; + + public static final String REQUEST_URI = "/cp/helloworld"; + + public static final String ACCEPT_LANGUAGE = "Accept-Language"; + + public static final String EN_US = "en-US"; + + public static final String ACCEPT = "Accept"; + + public static final String TEXT_PLAIN = "text/plain"; + + public static final String ACCEPT_CHARSET = "Accept-Charset"; + + public static final String UTF_8 = "utf-8"; + + public static final String REQUEST_METHOD = "POST"; + + public static final String USER_NAME = "USER NAME"; + + public static final String PASSWORD = "PASSWORD"; + + public static final String VALID_USER = "user1"; + + public static final String VALID_PASSWORD = "pswd"; + + public static final Integer FAILURE_STATUS = 500; + + public static final Integer RETRY_STATUS = 300; + + public static final Integer TWO_HUNDRED = 200; + + public static final Integer HUNDRED = 100; + + public static final Integer THOUSAND = 1000; +} diff --git a/jetty-ee10/jetty-ee10-http-spi/src/test/resources/jetty-logging.properties b/jetty-ee10/jetty-ee10-http-spi/src/test/resources/jetty-logging.properties new file mode 100644 index 00000000000..f602bee5c31 --- /dev/null +++ b/jetty-ee10/jetty-ee10-http-spi/src/test/resources/jetty-logging.properties @@ -0,0 +1,5 @@ +# Jetty Logging using jetty-slf4j-impl +org.eclipse.jetty.logging.appender.NAME_CONDENSE=false +org.eclipse.jetty.logging.appender.MESSAGE_ESCAPE=false +# org.eclipse.jetty.LEVEL=WARN +log.LEVEL=INFO diff --git a/jetty-ee10/jetty-ee10-jaas/pom.xml b/jetty-ee10/jetty-ee10-jaas/pom.xml new file mode 100644 index 00000000000..e3e1e60280d --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaas/pom.xml @@ -0,0 +1,169 @@ + + + org.eclipse.jetty.ee10 + jetty-ee10 + 12.0.0-SNAPSHOT + + + 4.0.0 + jetty-ee10-jaas + EE10 :: Jetty :: JAAS + Jetty JAAS support + + + ${project.groupId}.jaas + 2.0.0.AM26 + 2.1.0 + org.eclipse.jetty.ee10.jaas.* + + + + + + + org.apache.maven.plugins + maven-source-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + + false + false + + + + + + + + org.eclipse.jetty.ee10 + jetty-ee10-servlet + + + org.slf4j + slf4j-api + + + org.eclipse.jetty + jetty-slf4j-impl + test + + + org.eclipse.jetty.toolchain + jetty-test-helper + test + + + org.apache.directory.server + apacheds-test-framework + ${apacheds.version} + test + + + junit + junit + + + + org.apache.directory.shared + shared-ldap-schema + + + org.apache.directory.api + api-ldap-schema-data + + + + + org.apache.directory.server + apacheds-server-integ + ${apacheds.version} + test + + + + org.apache.directory.shared + shared-ldap-schema + + + org.apache.directory.api + api-ldap-schema-data + + + + + org.apache.directory.server + apacheds-core-integ + ${apacheds.version} + test + + + + org.apache.directory.shared + shared-ldap-schema + + + org.apache.directory.api + api-ldap-schema-data + + + + + org.apache.directory.api + api-ldap-schema-data + ${apache.directory.api.version} + test + + + org.apache.directory.api + api-ldap-model + ${apache.directory.api.version} + + + org.apache.directory.api + api-util + ${apache.directory.api.version} + + + org.apache.directory.api + api-asn1-api + ${apache.directory.api.version} + + + + org.junit.vintage + junit-vintage-engine + ${junit.version} + test + + + + + + + jdk16 + + [16,) + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + --add-opens java.base/sun.security.x509=ALL-UNNAMED --add-opens java.base/sun.security.util=ALL-UNNAMED + + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-jaas/src/main/config/etc/jetty-jaas.xml b/jetty-ee10/jetty-ee10-jaas/src/main/config/etc/jetty-jaas.xml new file mode 100644 index 00000000000..881b23292a0 --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaas/src/main/config/etc/jetty-jaas.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + java.security.auth.login.config + + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-jaas/src/main/config/modules/jaas.mod b/jetty-ee10/jetty-ee10-jaas/src/main/config/modules/jaas.mod new file mode 100644 index 00000000000..9816e7b487c --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaas/src/main/config/modules/jaas.mod @@ -0,0 +1,18 @@ +# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html + +[description] +Enables JAAS for deployed web applications. + +[depend] +server + +[lib] +lib/jetty-jaas-${jetty.version}.jar + +[xml] +etc/jetty-jaas.xml + +[ini-template] +## The file location (relative to $jetty.base) for the +## JAAS "java.security.auth.login.config" system property +# jetty.jaas.login.conf=etc/login.conf diff --git a/jetty-ee10/jetty-ee10-jaas/src/main/java/module-info.java b/jetty-ee10/jetty-ee10-jaas/src/main/java/module-info.java new file mode 100644 index 00000000000..fc468a2b44d --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaas/src/main/java/module-info.java @@ -0,0 +1,27 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 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.ee10.jaas +{ + requires org.slf4j; + requires org.eclipse.jetty.util; + + requires transitive org.eclipse.jetty.ee10.servlet; + + // Only required if using JDBCLoginModule. + requires static java.sql; + + exports org.eclipse.jetty.ee10.jaas; + exports org.eclipse.jetty.ee10.jaas.callback; + exports org.eclipse.jetty.ee10.jaas.spi; +} diff --git a/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/JAASLoginService.java b/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/JAASLoginService.java new file mode 100644 index 00000000000..eb6fdbbf5f1 --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/JAASLoginService.java @@ -0,0 +1,310 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.jaas; + +import java.io.IOException; +import java.security.Principal; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.List; +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.NameCallback; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.auth.login.Configuration; +import javax.security.auth.login.FailedLoginException; +import javax.security.auth.login.LoginContext; +import javax.security.auth.login.LoginException; + +import jakarta.servlet.ServletRequest; +import org.eclipse.jetty.ee10.jaas.callback.DefaultCallbackHandler; +import org.eclipse.jetty.ee10.servlet.ServletContextRequest; +import org.eclipse.jetty.ee10.servlet.security.DefaultIdentityService; +import org.eclipse.jetty.ee10.servlet.security.IdentityService; +import org.eclipse.jetty.ee10.servlet.security.LoginService; +import org.eclipse.jetty.ee10.servlet.security.UserIdentity; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.util.ArrayUtil; +import org.eclipse.jetty.util.Loader; +import org.eclipse.jetty.util.component.ContainerLifeCycle; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * JAASLoginService + * + * + * Implementation of jetty's LoginService that works with JAAS for + * authorization and authentication. + */ +public class JAASLoginService extends ContainerLifeCycle implements LoginService +{ + private static final Logger LOG = LoggerFactory.getLogger(JAASLoginService.class); + + public static final String DEFAULT_ROLE_CLASS_NAME = "org.eclipse.jetty.ee10.jaas.JAASRole"; + public static final String[] DEFAULT_ROLE_CLASS_NAMES = {DEFAULT_ROLE_CLASS_NAME}; + public static final ThreadLocal INSTANCE = new ThreadLocal<>(); + + protected String[] _roleClassNames = DEFAULT_ROLE_CLASS_NAMES; + protected String _callbackHandlerClass; + protected String _realmName; + protected String _loginModuleName; + protected JAASUserPrincipal _defaultUser = new JAASUserPrincipal(null, null, null); + protected IdentityService _identityService; + protected Configuration _configuration; + + public JAASLoginService() + { + } + + /** + * @param name the name of the realm + */ + public JAASLoginService(String name) + { + this(); + _realmName = name; + _loginModuleName = name; + } + + /** + * Get the name of the realm. + * + * @return name or null if not set. + */ + @Override + public String getName() + { + return _realmName; + } + + /** + * Set the name of the realm + * + * @param name a String value + */ + public void setName(String name) + { + _realmName = name; + } + + /** + * @return the configuration + */ + public Configuration getConfiguration() + { + return _configuration; + } + + /** + * @param configuration the configuration to set + */ + public void setConfiguration(Configuration configuration) + { + _configuration = configuration; + } + + /** + * Get the identityService. + * + * @return the identityService + */ + @Override + public IdentityService getIdentityService() + { + return _identityService; + } + + /** + * Set the identityService. + * + * @param identityService the identityService to set + */ + @Override + public void setIdentityService(IdentityService identityService) + { + _identityService = identityService; + } + + /** + * Set the name to use to index into the config + * file of LoginModules. + * + * @param name a String value + */ + public void setLoginModuleName(String name) + { + _loginModuleName = name; + } + + public void setCallbackHandlerClass(String classname) + { + _callbackHandlerClass = classname; + } + + public void setRoleClassNames(String[] classnames) + { + if (classnames == null || classnames.length == 0) + { + _roleClassNames = DEFAULT_ROLE_CLASS_NAMES; + return; + } + + _roleClassNames = ArrayUtil.addToArray(classnames, DEFAULT_ROLE_CLASS_NAME, String.class); + } + + public String[] getRoleClassNames() + { + return _roleClassNames; + } + + @Override + protected void doStart() throws Exception + { + if (_identityService == null) + _identityService = new DefaultIdentityService(); + addBean(new PropertyUserStoreManager()); + super.doStart(); + } + + @Override + public UserIdentity login(final String username, final Object credentials, final ServletRequest request) + { + try + { + CallbackHandler callbackHandler = null; + if (_callbackHandlerClass == null) + callbackHandler = new DefaultCallbackHandler(); + else + { + Class clazz = Loader.loadClass(_callbackHandlerClass); + callbackHandler = (CallbackHandler)clazz.getDeclaredConstructor().newInstance(); + } + + if (callbackHandler instanceof DefaultCallbackHandler) + { + DefaultCallbackHandler dch = (DefaultCallbackHandler)callbackHandler; + dch.setRequest(ServletContextRequest.getBaseRequest(request)); + dch.setCredential(credentials); + dch.setUserName(username); + } + + //set up the login context + Subject subject = new Subject(); + INSTANCE.set(this); + LoginContext loginContext = + (_configuration == null ? new LoginContext(_loginModuleName, subject, callbackHandler) + : new LoginContext(_loginModuleName, subject, callbackHandler, _configuration)); + + loginContext.login(); + + //login success + JAASUserPrincipal userPrincipal = new JAASUserPrincipal(getUserName(callbackHandler), subject, loginContext); + subject.getPrincipals().add(userPrincipal); + + return _identityService.newUserIdentity(subject, userPrincipal, getGroups(subject)); + } + catch (FailedLoginException e) + { + if (LOG.isDebugEnabled()) + LOG.debug("Login failed", e); + } + catch (Exception e) + { + if (LOG.isDebugEnabled()) + LOG.debug("Login error", e); + } + finally + { + INSTANCE.remove(); + } + + return null; + } + + @Override + public boolean validate(UserIdentity user) + { + // TODO optionally check user is still valid + return true; + } + + private String getUserName(CallbackHandler callbackHandler) throws IOException, UnsupportedCallbackException + { + NameCallback nameCallback = new NameCallback("foo"); + callbackHandler.handle(new Callback[]{nameCallback}); + return nameCallback.getName(); + } + + @Override + public void logout(UserIdentity user) + { + Set userPrincipals = user.getSubject().getPrincipals(JAASUserPrincipal.class); + LoginContext loginContext = userPrincipals.iterator().next().getLoginContext(); + try + { + loginContext.logout(); + } + catch (LoginException e) + { + LOG.warn("Failed to logout {}", user, e); + } + } + + /** + * Get all of the groups for the user. + * + * @param subject the Subject representing the user + * @return all the names of groups that the user is in, or 0 length array if none + */ + protected String[] getGroups(Subject subject) + { + Collection groups = new LinkedHashSet<>(); + for (Principal principal : subject.getPrincipals()) + { + if (isRoleClass(principal.getClass(), Arrays.asList(getRoleClassNames()))) + groups.add(principal.getName()); + } + + return groups.toArray(new String[groups.size()]); + } + + /** + * Check whether the class, its superclasses or any interfaces they implement + * is one of the classes that represents a role. + * + * @param clazz the class to check + * @param roleClassNames the list of classnames that represent roles + * @return true if the class is a role class + */ + private static boolean isRoleClass(Class clazz, List roleClassNames) + { + Class c = clazz; + + //add the class, its interfaces and superclasses to the list to test + List classnames = new ArrayList<>(); + while (c != null) + { + classnames.add(c.getName()); + Arrays.stream(c.getInterfaces()).map(Class::getName).forEach(classnames::add); + c = c.getSuperclass(); + } + + return roleClassNames.stream().anyMatch(classnames::contains); + } +} diff --git a/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/JAASPrincipal.java b/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/JAASPrincipal.java new file mode 100644 index 00000000000..95529aec999 --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/JAASPrincipal.java @@ -0,0 +1,63 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.jaas; + +import java.io.Serializable; +import java.security.Principal; + +/** + * JAASPrincipal + *

    + * Impl class of Principal interface. + */ +public class JAASPrincipal implements Principal, Serializable +{ + private static final long serialVersionUID = -5538962177019315479L; + + private final String _name; + + public JAASPrincipal(String userName) + { + this._name = userName; + } + + @Override + public boolean equals(Object p) + { + if (!(p instanceof JAASPrincipal)) + return false; + + return getName().equals(((JAASPrincipal)p).getName()); + } + + @Override + public int hashCode() + { + return getName().hashCode(); + } + + @Override + public String getName() + { + return this._name; + } + + @Override + public String toString() + { + return getName(); + } +} + + diff --git a/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/JAASRole.java b/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/JAASRole.java new file mode 100644 index 00000000000..1a36228a899 --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/JAASRole.java @@ -0,0 +1,33 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.jaas; + +public class JAASRole extends JAASPrincipal +{ + private static final long serialVersionUID = 3465114254970134526L; + + public JAASRole(String name) + { + super(name); + } + + @Override + public boolean equals(Object o) + { + if (!(o instanceof JAASRole)) + return false; + + return getName().equals(((JAASRole)o).getName()); + } +} diff --git a/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/JAASUserPrincipal.java b/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/JAASUserPrincipal.java new file mode 100644 index 00000000000..475049b65fa --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/JAASUserPrincipal.java @@ -0,0 +1,68 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.jaas; + +import java.security.Principal; +import javax.security.auth.Subject; +import javax.security.auth.login.LoginContext; + +/** + * JAASUserPrincipal + *

    + * Implements the JAAS version of the + * org.eclipse.jetty.security.UserPrincipal interface. + */ +public class JAASUserPrincipal implements Principal +{ + private final String _name; + private final Subject _subject; + private final LoginContext _loginContext; + + public JAASUserPrincipal(String name, Subject subject, LoginContext loginContext) + { + this._name = name; + this._subject = subject; + this._loginContext = loginContext; + } + + /** + * Get the name identifying the user + */ + @Override + public String getName() + { + return _name; + } + + /** + * Provide access to the Subject + * + * @return subject + */ + public Subject getSubject() + { + return this._subject; + } + + LoginContext getLoginContext() + { + return this._loginContext; + } + + @Override + public String toString() + { + return getName(); + } +} diff --git a/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/PropertyUserStoreManager.java b/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/PropertyUserStoreManager.java new file mode 100644 index 00000000000..68e19a1a9e6 --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/PropertyUserStoreManager.java @@ -0,0 +1,94 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.jaas; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import org.eclipse.jetty.ee10.servlet.security.PropertyUserStore; +import org.eclipse.jetty.util.component.AbstractLifeCycle; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * PropertyUserStoreManager + * + * Maintains a map of PropertyUserStores, keyed off the location of the property file containing + * the authentication and authorization information. + * + * This class is used to enable the PropertyUserStores to be cached and shared. This is essential + * for the PropertyFileLoginModules, whose lifecycle is controlled by the JAAS api and instantiated + * afresh whenever a user needs to be authenticated. Without this class, every PropertyFileLoginModule + * instantiation would re-read and reload in all the user information just to authenticate a single user. + */ +public class PropertyUserStoreManager extends AbstractLifeCycle +{ + private static final Logger LOG = LoggerFactory.getLogger(PropertyUserStoreManager.class); + /** + * Map of user authentication and authorization information loaded in from a property file. + * The map is keyed off the location of the file. + */ + private Map _propertyUserStores; + + public PropertyUserStore getPropertyUserStore(String file) + { + synchronized (this) + { + if (_propertyUserStores == null) + return null; + + return _propertyUserStores.get(file); + } + } + + public PropertyUserStore addPropertyUserStore(String file, PropertyUserStore store) + { + synchronized (this) + { + Objects.requireNonNull(_propertyUserStores); + PropertyUserStore existing = _propertyUserStores.get(file); + if (existing != null) + return existing; + + _propertyUserStores.put(file, store); + return store; + } + } + + @Override + protected void doStart() throws Exception + { + _propertyUserStores = new HashMap(); + super.doStart(); + } + + @Override + protected void doStop() throws Exception + { + for (Map.Entry entry : _propertyUserStores.entrySet()) + { + try + { + entry.getValue().stop(); + } + catch (Exception e) + { + LOG.warn("Error stopping PropertyUserStore at {}", entry.getKey(), e); + } + } + _propertyUserStores = null; + super.doStop(); + } +} diff --git a/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/callback/AbstractCallbackHandler.java b/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/callback/AbstractCallbackHandler.java new file mode 100644 index 00000000000..1c3e5f792a2 --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/callback/AbstractCallbackHandler.java @@ -0,0 +1,51 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.jaas.callback; + +import java.io.IOException; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.UnsupportedCallbackException; + +public abstract class AbstractCallbackHandler implements CallbackHandler +{ + protected String _userName; + protected Object _credential; + + public void setUserName(String userName) + { + _userName = userName; + } + + public String getUserName() + { + return _userName; + } + + public void setCredential(Object credential) + { + _credential = credential; + } + + public Object getCredential() + { + return _credential; + } + + @Override + public void handle(Callback[] callbacks) + throws IOException, UnsupportedCallbackException + { + } +} diff --git a/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/callback/DefaultCallbackHandler.java b/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/callback/DefaultCallbackHandler.java new file mode 100644 index 00000000000..cfcb12702ae --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/callback/DefaultCallbackHandler.java @@ -0,0 +1,77 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.jaas.callback; + +import java.io.IOException; +import java.util.Arrays; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; + +import org.eclipse.jetty.ee10.servlet.ServletContextRequest; +import org.eclipse.jetty.server.Request; + +/** + * DefaultCallbackHandler + * + * An implementation of the JAAS CallbackHandler. Users can provide + * their own implementation instead and set the name of its class on the JAASLoginService. + */ +public class DefaultCallbackHandler extends AbstractCallbackHandler +{ + private Request _request; + + public void setRequest(Request request) + { + _request = request; + } + + @Override + public void handle(Callback[] callbacks) + throws IOException, UnsupportedCallbackException + { + for (Callback callback : callbacks) + { + if (callback instanceof NameCallback) + { + ((NameCallback)callback).setName(getUserName()); + } + else if (callback instanceof ObjectCallback) + { + ((ObjectCallback)callback).setObject(getCredential()); + } + else if (callback instanceof PasswordCallback) + { + ((PasswordCallback)callback).setPassword(getCredential().toString().toCharArray()); + } + else if (callback instanceof RequestParameterCallback) + { + ServletContextRequest servletContextRequest = Request.as(_request, ServletContextRequest.class); + if (servletContextRequest != null) + { + RequestParameterCallback rpc = (RequestParameterCallback)callback; + rpc.setParameterValues(Arrays.asList(servletContextRequest.getServletApiRequest().getParameterValues(rpc.getParameterName()))); + } + } + else if (callback instanceof ServletRequestCallback) + { + ServletContextRequest servletContextRequest = Request.as(_request, ServletContextRequest.class); + ((ServletRequestCallback)callback).setRequest(servletContextRequest.getServletApiRequest()); + } + else + throw new UnsupportedCallbackException(callback); + } + } +} diff --git a/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/callback/ObjectCallback.java b/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/callback/ObjectCallback.java new file mode 100644 index 00000000000..943c7098eda --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/callback/ObjectCallback.java @@ -0,0 +1,44 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.jaas.callback; + +import javax.security.auth.callback.Callback; + +/** + * ObjectCallback + *

    + * Can be used as a LoginModule Callback to + * obtain a user's credential as an Object, rather than + * a char[], to which some credentials may not be able + * to be converted + */ +public class ObjectCallback implements Callback +{ + protected Object _object; + + public void setObject(Object o) + { + _object = o; + } + + public Object getObject() + { + return _object; + } + + public void clearObject() + { + _object = null; + } +} diff --git a/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/callback/RequestParameterCallback.java b/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/callback/RequestParameterCallback.java new file mode 100644 index 00000000000..d6d5e523da3 --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/callback/RequestParameterCallback.java @@ -0,0 +1,50 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.jaas.callback; + +import java.util.List; +import javax.security.auth.callback.Callback; + +/** + * RequestParameterCallback + *

    + * Allows a JAAS callback handler to access any parameter from the j_security_check FORM. + * This means that a LoginModule can access form fields other than the j_username and j_password + * fields, and use it, for example, to authenticate a user. + */ +public class RequestParameterCallback implements Callback +{ + private String _paramName; + private List _paramValues; + + public void setParameterName(String name) + { + _paramName = name; + } + + public String getParameterName() + { + return _paramName; + } + + public void setParameterValues(List values) + { + _paramValues = values; + } + + public List getParameterValues() + { + return _paramValues; + } +} diff --git a/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/callback/ServletRequestCallback.java b/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/callback/ServletRequestCallback.java new file mode 100644 index 00000000000..e6f107414ad --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/callback/ServletRequestCallback.java @@ -0,0 +1,38 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.jaas.callback; + +import javax.security.auth.callback.Callback; + +import jakarta.servlet.ServletRequest; + +/** + * ServletRequestCallback + * + * Provides access to the request associated with the authentication. + */ +public class ServletRequestCallback implements Callback +{ + protected ServletRequest _request; + + public void setRequest(ServletRequest request) + { + _request = request; + } + + public ServletRequest getRequest() + { + return _request; + } +} diff --git a/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/callback/package-info.java b/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/callback/package-info.java new file mode 100644 index 00000000000..1e1114c3901 --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/callback/package-info.java @@ -0,0 +1,18 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 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 Jaas : Jaas Callbacks + */ +package org.eclipse.jetty.ee10.jaas.callback; + diff --git a/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/package-info.java b/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/package-info.java new file mode 100644 index 00000000000..281a3a2e622 --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/package-info.java @@ -0,0 +1,18 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 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 Jaas : Support for Jaas + */ +package org.eclipse.jetty.ee10.jaas; + diff --git a/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/spi/AbstractDatabaseLoginModule.java b/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/spi/AbstractDatabaseLoginModule.java new file mode 100644 index 00000000000..ab5015e6317 --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/spi/AbstractDatabaseLoginModule.java @@ -0,0 +1,156 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.jaas.spi; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import javax.security.auth.Subject; +import javax.security.auth.callback.CallbackHandler; + +import org.eclipse.jetty.ee10.servlet.security.UserPrincipal; +import org.eclipse.jetty.util.security.Credential; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * AbstractDatabaseLoginModule + * + * Abstract base class for LoginModules that interact with a + * database to retrieve authentication and authorization information. + * Used by the JDBCLoginModule and DataSourceLoginModule. + */ +public abstract class AbstractDatabaseLoginModule extends AbstractLoginModule +{ + private static final Logger LOG = LoggerFactory.getLogger(AbstractDatabaseLoginModule.class); + + private String userQuery; + private String rolesQuery; + private String dbUserTable; + private String dbUserTableUserField; + private String dbUserTableCredentialField; + private String dbUserRoleTable; + private String dbUserRoleTableUserField; + private String dbUserRoleTableRoleField; + + /** + * @return a java.sql.Connection from the database + * @throws Exception if unable to get the connection + */ + public abstract Connection getConnection() throws Exception; + + public class JDBCUser extends JAASUser + { + public JDBCUser(UserPrincipal user) + { + super(user); + } + + @Override + public List doFetchRoles() + throws Exception + { + return getRoles(getUserName()); + } + } + + /** + * Load info from database + * + * @param userName user info to load + * @throws Exception if unable to get the user info + */ + @Override + public JAASUser getUser(String userName) + throws Exception + { + try (Connection connection = getConnection()) + { + + //query for credential + String dbCredential = null; + try (PreparedStatement statement = connection.prepareStatement(userQuery)) + { + statement.setString(1, userName); + try (ResultSet results = statement.executeQuery()) + { + if (results.next()) + { + dbCredential = results.getString(1); + } + } + } + + if (dbCredential == null) + return null; + + return new JDBCUser(new UserPrincipal(userName, Credential.getCredential(dbCredential))); + } + } + + public List getRoles(String userName) + throws Exception + { + List roles = new ArrayList(); + + try (Connection connection = getConnection()) + { + //query for role names + + try (PreparedStatement statement = connection.prepareStatement(rolesQuery)) + { + statement.setString(1, userName); + try (ResultSet results = statement.executeQuery()) + { + while (results.next()) + { + String roleName = results.getString(1); + roles.add(roleName); + } + } + } + } + + return roles; + } + + @Override + public void initialize(Subject subject, + CallbackHandler callbackHandler, + Map sharedState, + Map options) + { + super.initialize(subject, callbackHandler, sharedState, options); + + //get the user credential query out of the options + dbUserTable = (String)options.get("userTable"); + dbUserTableUserField = (String)options.get("userField"); + dbUserTableCredentialField = (String)options.get("credentialField"); + + userQuery = "select " + dbUserTableCredentialField + " from " + dbUserTable + " where " + dbUserTableUserField + "=?"; + + //get the user roles query out of the options + dbUserRoleTable = (String)options.get("userRoleTable"); + dbUserRoleTableUserField = (String)options.get("userRoleUserField"); + dbUserRoleTableRoleField = (String)options.get("userRoleRoleField"); + + rolesQuery = "select " + dbUserRoleTableRoleField + " from " + dbUserRoleTable + " where " + dbUserRoleTableUserField + "=?"; + + if (LOG.isDebugEnabled()) + LOG.debug("userQuery = {} rolesQuery = {}", userQuery, rolesQuery); + } +} diff --git a/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/spi/AbstractLoginModule.java b/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/spi/AbstractLoginModule.java new file mode 100644 index 00000000000..27c5b0fd79e --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/spi/AbstractLoginModule.java @@ -0,0 +1,291 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.jaas.spi; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import javax.security.auth.Subject; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.auth.login.FailedLoginException; +import javax.security.auth.login.LoginException; +import javax.security.auth.spi.LoginModule; + +import org.eclipse.jetty.ee10.jaas.JAASRole; +import org.eclipse.jetty.ee10.jaas.callback.ObjectCallback; +import org.eclipse.jetty.ee10.servlet.security.UserPrincipal; + +/** + * AbstractLoginModule + * + * Abstract base class for all LoginModules. Subclasses should + * just need to implement getUserInfo method. + */ +public abstract class AbstractLoginModule implements LoginModule +{ + private CallbackHandler callbackHandler; + + private boolean authState = false; + private boolean commitState = false; + private JAASUser currentUser; + private Subject subject; + + public abstract static class JAASUser + { + private final UserPrincipal _user; + private List _roles; + + public JAASUser(UserPrincipal u) + { + _user = u; + } + + public String getUserName() + { + return _user.getName(); + } + + /** + * @param subject The subject + */ + public void setJAASInfo(Subject subject) + { + if (_user == null) + return; + + _user.configureSubject(subject); + if (_roles != null) + subject.getPrincipals().addAll(_roles); + } + + /** + * @param subject The subject + */ + public void unsetJAASInfo(Subject subject) + { + if (_user == null) + return; + _user.deconfigureSubject(subject); + if (_roles != null) + subject.getPrincipals().removeAll(_roles); + } + + public boolean checkCredential(Object suppliedCredential) + { + return _user.authenticate(suppliedCredential); + } + + public void fetchRoles() throws Exception + { + List rolenames = doFetchRoles(); + if (rolenames != null) + _roles = rolenames.stream().map(JAASRole::new).collect(Collectors.toList()); + } + + public abstract List doFetchRoles() throws Exception; + } + + public abstract JAASUser getUser(String username) throws Exception; + + public Subject getSubject() + { + return this.subject; + } + + public void setSubject(Subject s) + { + this.subject = s; + } + + public JAASUser getCurrentUser() + { + return this.currentUser; + } + + public void setCurrentUser(JAASUser u) + { + this.currentUser = u; + } + + public CallbackHandler getCallbackHandler() + { + return this.callbackHandler; + } + + public void setCallbackHandler(CallbackHandler h) + { + this.callbackHandler = h; + } + + public boolean isAuthenticated() + { + return this.authState; + } + + public boolean isCommitted() + { + return this.commitState; + } + + public void setAuthenticated(boolean authState) + { + this.authState = authState; + } + + public void setCommitted(boolean commitState) + { + this.commitState = commitState; + } + + @Override + public boolean abort() throws LoginException + { + this.currentUser = null; + return (isAuthenticated() && isCommitted()); + } + + /** + * @return true if committed, false if not (likely not authenticated) + * @throws LoginException if unable to commit + * @see javax.security.auth.spi.LoginModule#commit() + */ + @Override + public boolean commit() throws LoginException + { + if (!isAuthenticated()) + { + currentUser = null; + setCommitted(false); + return false; + } + + setCommitted(true); + currentUser.setJAASInfo(subject); + return true; + } + + public Callback[] configureCallbacks() + { + Callback[] callbacks = new Callback[3]; + callbacks[0] = new NameCallback("Enter user name"); + callbacks[1] = new ObjectCallback(); + callbacks[2] = new PasswordCallback("Enter password", false); //only used if framework does not support the ObjectCallback + return callbacks; + } + + public boolean isIgnored() + { + return false; + } + + /** + * @return true if is authenticated, false otherwise + * @throws LoginException if unable to login + * @see javax.security.auth.spi.LoginModule#login() + */ + @Override + public boolean login() throws LoginException + { + try + { + if (isIgnored()) + return false; + + if (callbackHandler == null) + throw new LoginException("No callback handler"); + + Callback[] callbacks = configureCallbacks(); + callbackHandler.handle(callbacks); + + String webUserName = ((NameCallback)callbacks[0]).getName(); + Object webCredential = null; + + webCredential = ((ObjectCallback)callbacks[1]).getObject(); //first check if ObjectCallback has the credential + if (webCredential == null) + webCredential = ((PasswordCallback)callbacks[2]).getPassword(); //use standard PasswordCallback + + if ((webUserName == null) || (webCredential == null)) + { + setAuthenticated(false); + throw new FailedLoginException(); + } + + JAASUser user = getUser(webUserName); + + if (user == null) + { + setAuthenticated(false); + throw new FailedLoginException(); + } + + currentUser = user; + setAuthenticated(currentUser.checkCredential(webCredential)); + + if (isAuthenticated()) + { + currentUser.fetchRoles(); + return true; + } + else + throw new FailedLoginException(); + } + catch (IOException e) + { + throw new LoginException(e.toString()); + } + catch (UnsupportedCallbackException e) + { + throw new LoginException(e.toString()); + } + catch (Exception e) + { + if (e instanceof LoginException) + throw (LoginException)e; + throw new LoginException(e.toString()); + } + } + + /** + * @return true always + * @throws LoginException if unable to logout + * @see javax.security.auth.spi.LoginModule#logout() + */ + @Override + public boolean logout() throws LoginException + { + this.currentUser.unsetJAASInfo(this.subject); + this.currentUser = null; + return true; + } + + /** + * @param subject the subject + * @param callbackHandler the callback handler + * @param sharedState the shared state map + * @param options the option map + * @see javax.security.auth.spi.LoginModule#initialize(javax.security.auth.Subject, javax.security.auth.callback.CallbackHandler, java.util.Map, java.util.Map) + */ + @Override + public void initialize(Subject subject, CallbackHandler callbackHandler, + Map sharedState, Map options) + { + this.callbackHandler = callbackHandler; + this.subject = subject; + } +} diff --git a/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/spi/DataSourceLoginModule.java b/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/spi/DataSourceLoginModule.java new file mode 100644 index 00000000000..feb695927a2 --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/spi/DataSourceLoginModule.java @@ -0,0 +1,83 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.jaas.spi; + +import java.sql.Connection; +import java.util.Map; +import javax.naming.InitialContext; +import javax.naming.NamingException; +import javax.security.auth.Subject; +import javax.security.auth.callback.CallbackHandler; +import javax.sql.DataSource; + +/** + * DataSourceLoginModule + * + * A LoginModule that uses a DataSource to retrieve user authentication + * and authorisation information. + * + * @see JDBCLoginModule + */ +public class DataSourceLoginModule extends AbstractDatabaseLoginModule +{ + + private String dbJNDIName; + private DataSource dataSource; + + /** + * Init LoginModule. + *

    + * Called once by JAAS after new instance created. + * + * @param subject the subject + * @param callbackHandler the callback handler + * @param sharedState the shared state map + * @param options the option map + */ + @Override + public void initialize(Subject subject, + CallbackHandler callbackHandler, + Map sharedState, + Map options) + { + try + { + super.initialize(subject, callbackHandler, sharedState, options); + + //get the datasource jndi name + dbJNDIName = (String)options.get("dbJNDIName"); + + InitialContext ic = new InitialContext(); + dataSource = (DataSource)ic.lookup("java:comp/env/" + dbJNDIName); + } + catch (NamingException e) + { + throw new IllegalStateException(e.toString()); + } + } + + /** + * Get a connection from the DataSource + * + * @return the connection for the datasource + * @throws Exception if unable to get the connection + * @see AbstractDatabaseLoginModule#getConnection() + */ + @Override + public Connection getConnection() + throws Exception + { + return dataSource.getConnection(); + } +} diff --git a/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/spi/JDBCLoginModule.java b/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/spi/JDBCLoginModule.java new file mode 100644 index 00000000000..6001d11a1dc --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/spi/JDBCLoginModule.java @@ -0,0 +1,102 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.jaas.spi; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.util.Map; +import javax.security.auth.Subject; +import javax.security.auth.callback.CallbackHandler; + +import org.eclipse.jetty.util.Loader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + *

    JAAS LoginModule to retrieve user information from + * a database and authenticate the user.

    + *

    Notes

    + *

    This version uses plain old JDBC connections NOT DataSources.

    + */ +public class JDBCLoginModule extends AbstractDatabaseLoginModule +{ + private static final Logger LOG = LoggerFactory.getLogger(JDBCLoginModule.class); + + private String dbDriver; + private String dbUrl; + private String dbUserName; + private String dbPassword; + + /** + * Get a connection from the DriverManager + * + * @return the connection for this datasource + * @throws Exception if unable to get the connection + */ + @Override + public Connection getConnection() + throws Exception + { + if (!((dbDriver != null) && (dbUrl != null))) + throw new IllegalStateException("Database connection information not configured"); + + if (LOG.isDebugEnabled()) + LOG.debug("Connecting using dbDriver={} dbUserName={}, dbPassword={}", dbDriver, dbUserName, dbUrl); + + return DriverManager.getConnection(dbUrl, + dbUserName, + dbPassword); + } + + /** + * Init LoginModule. + *

    + * Called once by JAAS after new instance created. + * + * @param subject the subject + * @param callbackHandler the callback handler + * @param sharedState the shared state map + * @param options the options map + */ + @Override + public void initialize(Subject subject, + CallbackHandler callbackHandler, + Map sharedState, + Map options) + { + try + { + super.initialize(subject, callbackHandler, sharedState, options); + + //get the jdbc username/password, jdbc url out of the options + dbDriver = (String)options.get("dbDriver"); + dbUrl = (String)options.get("dbUrl"); + dbUserName = (String)options.get("dbUserName"); + dbPassword = (String)options.get("dbPassword"); + + if (dbUserName == null) + dbUserName = ""; + + if (dbPassword == null) + dbPassword = ""; + + if (dbDriver != null) + Loader.loadClass(dbDriver).getDeclaredConstructor().newInstance(); + } + catch (Exception e) + { + throw new IllegalStateException(e.toString()); + } + } +} diff --git a/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/spi/LdapLoginModule.java b/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/spi/LdapLoginModule.java new file mode 100644 index 00000000000..d9138d967ba --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/spi/LdapLoginModule.java @@ -0,0 +1,756 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.jaas.spi; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Base64; +import java.util.Hashtable; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import javax.naming.AuthenticationException; +import javax.naming.Context; +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.Attribute; +import javax.naming.directory.Attributes; +import javax.naming.directory.DirContext; +import javax.naming.directory.InitialDirContext; +import javax.naming.directory.SearchControls; +import javax.naming.directory.SearchResult; +import javax.security.auth.Subject; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.auth.login.FailedLoginException; +import javax.security.auth.login.LoginException; + +import org.eclipse.jetty.ee10.jaas.callback.ObjectCallback; +import org.eclipse.jetty.ee10.servlet.security.UserPrincipal; +import org.eclipse.jetty.util.TypeUtil; +import org.eclipse.jetty.util.security.Credential; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A LdapLoginModule for use with JAAS setups + *

    + * The jvm should be started with the following parameter: + *

    + * -Djava.security.auth.login.config=etc/ldap-loginModule.conf
    + * 
    + * and an example of the ldap-loginModule.conf would be: + *
    + * ldaploginmodule {
    + *    org.eclipse.jetty.server.server.plus.jaas.spi.LdapLoginModule required
    + *    debug="true"
    + *    useLdaps="false"
    + *    contextFactory="com.sun.jndi.ldap.LdapCtxFactory"
    + *    hostname="ldap.example.com"
    + *    port="389"
    + *    bindDn="cn=Directory Manager"
    + *    bindPassword="directory"
    + *    authenticationMethod="simple"
    + *    forceBindingLogin="false"
    + *    userBaseDn="ou=people,dc=alcatel"
    + *    userRdnAttribute="uid"
    + *    userIdAttribute="uid"
    + *    userPasswordAttribute="userPassword"
    + *    userObjectClass="inetOrgPerson"
    + *    roleBaseDn="ou=groups,dc=example,dc=com"
    + *    roleNameAttribute="cn"
    + *    roleMemberAttribute="uniqueMember"
    + *    roleObjectClass="groupOfUniqueNames";
    + *    };
    + * 
    + */ +public class LdapLoginModule extends AbstractLoginModule +{ + private static final Logger LOG = LoggerFactory.getLogger(LdapLoginModule.class); + + /** + * hostname of the ldap server + */ + private String _hostname; + + /** + * port of the ldap server + */ + private int _port; + + /** + * Context.SECURITY_AUTHENTICATION + */ + private String _authenticationMethod; + + /** + * Context.INITIAL_CONTEXT_FACTORY + */ + private String _contextFactory; + + /** + * root DN used to connect to + */ + private String _bindDn; + + /** + * password used to connect to the root ldap context + */ + private String _bindPassword; + + /** + * object class of a user + */ + private String _userObjectClass = "inetOrgPerson"; + + /** + * attribute that the principal is located + */ + private String _userRdnAttribute = "uid"; + + /** + * attribute that the principal is located + */ + private String _userIdAttribute = "cn"; + + /** + * name of the attribute that a users password is stored under + *

    + * NOTE: not always accessible, see force binding login + */ + private String _userPasswordAttribute = "userPassword"; + + /** + * base DN where users are to be searched from + */ + private String _userBaseDn; + + /** + * base DN where role membership is to be searched from + */ + private String _roleBaseDn; + + /** + * object class of roles + */ + private String _roleObjectClass = "groupOfUniqueNames"; + + /** + * name of the attribute that a username would be under a role class + */ + private String _roleMemberAttribute = "uniqueMember"; + + /** + * the name of the attribute that a role would be stored under + */ + private String _roleNameAttribute = "roleName"; + + private boolean _debug; + + /** + * if the getUserInfo can pull a password off of the user then + * password comparison is an option for authn, to force binding + * login checks, set this to true + */ + private boolean _forceBindingLogin = false; + + /** + * When true changes the protocol to ldaps + */ + private boolean _useLdaps = false; + + private DirContext _rootContext; + + public class LDAPUser extends JAASUser + { + Attributes attributes; + + public LDAPUser(UserPrincipal user, Attributes attributes) + { + super(user); + this.attributes = attributes; + } + + @Override + public List doFetchRoles() throws Exception + { + return getUserRoles(_rootContext, getUserName(), attributes); + } + } + + public class LDAPBindingUser extends JAASUser + { + DirContext _context; + String _userDn; + + public LDAPBindingUser(UserPrincipal user, DirContext context, String userDn) + { + super(user); + _context = context; + _userDn = userDn; + } + + @Override + public List doFetchRoles() throws Exception + { + return getUserRolesByDn(_context, _userDn); + } + } + + /** + * get the available information about the user + *

    + * for this LoginModule, the credential can be null which will result in a + * binding ldap authentication scenario + *

    + * roles are also an optional concept if required + * + * @param username the user name + * @return the userinfo for the username + * @throws Exception if unable to get the user info + */ + @Override + public JAASUser getUser(String username) throws Exception + { + Attributes attributes = getUserAttributes(username); + String pwdCredential = getUserCredentials(attributes); + + if (pwdCredential == null) + return null; + + pwdCredential = convertCredentialLdapToJetty(pwdCredential); + Credential credential = Credential.getCredential(pwdCredential); + return new LDAPUser(new UserPrincipal(username, credential), attributes); + } + + protected String doRFC2254Encoding(String inputString) + { + StringBuffer buf = new StringBuffer(inputString.length()); + for (int i = 0; i < inputString.length(); i++) + { + char c = inputString.charAt(i); + switch (c) + { + case '\\': + buf.append("\\5c"); + break; + case '*': + buf.append("\\2a"); + break; + case '(': + buf.append("\\28"); + break; + case ')': + buf.append("\\29"); + break; + case '\0': + buf.append("\\00"); + break; + default: + buf.append(c); + break; + } + } + return buf.toString(); + } + + /** + * attempts to get the users LDAP attributes from the users context + *

    + * NOTE: this is not an user authenticated operation + * + * @return the {@link Attributes} from the user + */ + private Attributes getUserAttributes(String username) throws LoginException + { + SearchResult result = findUser(username); + Attributes attributes = result.getAttributes(); + return attributes; + } + + private String getUserCredentials(Attributes attributes) throws LoginException + { + String ldapCredential = null; + + Attribute attribute = attributes.get(_userPasswordAttribute); + if (attribute != null) + { + try + { + byte[] value = (byte[])attribute.get(); + + ldapCredential = new String(value); + } + catch (NamingException e) + { + LOG.debug("no password available under attribute: {}", _userPasswordAttribute); + } + } + + if (LOG.isDebugEnabled()) + LOG.debug("user cred is: {}", ldapCredential); + + return ldapCredential; + } + + /** + * attempts to get the users roles from the root context + *

    + * NOTE: this is not an user authenticated operation + */ + private List getUserRoles(DirContext dirContext, String username, Attributes attributes) throws LoginException, NamingException + { + String rdnValue = username; + Attribute attribute = attributes.get(_userRdnAttribute); + if (attribute != null) + { + try + { + rdnValue = (String)attribute.get(); // switch to the value stored in the _userRdnAttribute if we can + } + catch (NamingException ignored) + { + } + } + + String filter = "({0}={1})"; + + Object[] filterArguments = new Object[]{ + _userRdnAttribute, + rdnValue + }; + + SearchResult searchResult = findUser(dirContext, filter, filterArguments); + + return getUserRolesByDn(dirContext, searchResult.getNameInNamespace()); + } + + private List getUserRolesByDn(DirContext dirContext, String userDn) throws NamingException + { + List roleList = new ArrayList<>(); + + if (dirContext == null || _roleBaseDn == null || _roleMemberAttribute == null || _roleObjectClass == null) + { + return roleList; + } + + SearchControls ctls = new SearchControls(); + ctls.setDerefLinkFlag(true); + ctls.setSearchScope(SearchControls.SUBTREE_SCOPE); + ctls.setReturningAttributes(new String[]{_roleNameAttribute}); + + String filter = "(&(objectClass={0})({1}={2}))"; + Object[] filterArguments = {_roleObjectClass, _roleMemberAttribute, userDn}; + NamingEnumeration results = dirContext.search(_roleBaseDn, filter, filterArguments, ctls); + + if (LOG.isDebugEnabled()) + LOG.debug("Found user roles?: {}", results.hasMoreElements()); + + while (results.hasMoreElements()) + { + SearchResult result = results.nextElement(); + + Attributes attributes = result.getAttributes(); + + if (attributes == null) + { + continue; + } + + Attribute roleAttribute = attributes.get(_roleNameAttribute); + + if (roleAttribute == null) + { + continue; + } + + NamingEnumeration roles = roleAttribute.getAll(); + while (roles.hasMore()) + { + roleList.add(roles.next().toString()); + } + } + + return roleList; + } + + /** + * since ldap uses a context bind for valid authentication checking, we override login() + *

    + * if credentials are not available from the users context or if we are forcing the binding check + * then we try a binding authentication check, otherwise if we have the users encoded password then + * we can try authentication via that mechanic + * + * @return true if authenticated, false otherwise + * @throws LoginException if unable to login + */ + @Override + public boolean login() throws LoginException + { + try + { + if (getCallbackHandler() == null) + { + throw new LoginException("No callback handler"); + } + + Callback[] callbacks = configureCallbacks(); + getCallbackHandler().handle(callbacks); + + String webUserName = ((NameCallback)callbacks[0]).getName(); + Object webCredential = ((ObjectCallback)callbacks[1]).getObject(); + + if (webUserName == null || webCredential == null) + { + setAuthenticated(false); + return isAuthenticated(); + } + + boolean authed = false; + + if (_forceBindingLogin) + { + authed = bindingLogin(webUserName, webCredential); + } + else + { + // This sets read and the credential + JAASUser userInfo = getUser(webUserName); + + if (userInfo == null) + { + setAuthenticated(false); + return false; + } + + setCurrentUser(userInfo); + + if (webCredential instanceof String) + authed = credentialLogin(Credential.getCredential((String)webCredential)); + else + authed = credentialLogin(webCredential); + } + + //only fetch roles if authenticated + if (authed) + getCurrentUser().fetchRoles(); + + return authed; + } + catch (UnsupportedCallbackException e) + { + throw new LoginException("Error obtaining callback information."); + } + catch (IOException e) + { + if (_debug) + LOG.info("Login failure", e); + throw new LoginException("IO Error performing login."); + } + catch (AuthenticationException e) + { + if (_debug) + LOG.info("Login failure", e); + return false; + } + catch (LoginException e) + { + throw e; + } + catch (Exception e) + { + if (_debug) + LOG.info("Login failure", e); + throw new LoginException("Error obtaining user info"); + } + } + + /** + * password supplied authentication check + * + * @param webCredential the web credential + * @return true if authenticated + * @throws LoginException if unable to login + */ + protected boolean credentialLogin(Object webCredential) throws LoginException + { + setAuthenticated(getCurrentUser().checkCredential(webCredential)); + return isAuthenticated(); + } + + /** + * binding authentication check + * This method of authentication works only if the user branch of the DIT (ldap tree) + * has an ACI (access control instruction) that allow the access to any user or at least + * for the user that logs in. + * + * @param username the user name + * @param password the password + * @return true always + * @throws LoginException if unable to bind the login + */ + public boolean bindingLogin(String username, Object password) throws LoginException + { + SearchResult searchResult = findUser(username); + + String userDn = searchResult.getNameInNamespace(); + + LOG.info("Attempting authentication: {}", userDn); + + Hashtable environment = getEnvironment(); + + if (userDn == null || "".equals(userDn)) + { + throw new FailedLoginException("username may not be empty"); + } + environment.put(Context.SECURITY_PRINCIPAL, userDn); + // RFC 4513 section 6.3.1, protect against ldap server implementations that allow successful binding on empty passwords + if (password == null || "".equals(password)) + { + throw new FailedLoginException("password may not be empty"); + } + environment.put(Context.SECURITY_CREDENTIALS, password); + + try + { + DirContext dirContext = new InitialDirContext(environment); + setCurrentUser(new LDAPBindingUser(new UserPrincipal(username, null), dirContext, userDn)); + setAuthenticated(true); + return true; + } + catch (javax.naming.AuthenticationException e) + { + throw new FailedLoginException(e.getMessage()); + } + catch (NamingException e) + { + throw new FailedLoginException(e.getMessage()); + } + } + + private SearchResult findUser(String username) throws LoginException + { + String filter = "(&(objectClass={0})({1}={2}))"; + + if (LOG.isDebugEnabled()) + LOG.debug("Searching for user {} with filter: \'{}\' from base dn: {}", username, filter, _userBaseDn); + + Object[] filterArguments = new Object[]{ + _userObjectClass, + _userIdAttribute, + username + }; + + return findUser(_rootContext, filter, filterArguments); + } + + private SearchResult findUser(DirContext dirContext, String filter, Object[] filterArguments) throws LoginException + { + SearchControls ctls = new SearchControls(); + ctls.setDerefLinkFlag(true); + ctls.setSearchScope(SearchControls.SUBTREE_SCOPE); + + NamingEnumeration results; + try + { + results = _rootContext.search(_userBaseDn, filter, filterArguments, ctls); + } + catch (NamingException ex) + { + throw new FailedLoginException(ex.getMessage()); + } + + if (LOG.isDebugEnabled()) + LOG.debug("Found user?: {}", results.hasMoreElements()); + + if (!results.hasMoreElements()) + throw new FailedLoginException("User not found."); + + SearchResult searchResult = (SearchResult)results.nextElement(); + if (results.hasMoreElements()) + throw new FailedLoginException("Search result contains ambiguous entries"); + + return searchResult; + } + + /** + * Init LoginModule. + *

    + * Called once by JAAS after new instance is created. + * + * @param subject the subect + * @param callbackHandler the callback handler + * @param sharedState the shared state map + * @param options the option map + */ + @Override + public void initialize(Subject subject, + CallbackHandler callbackHandler, + Map sharedState, + Map options) + { + super.initialize(subject, callbackHandler, sharedState, options); + + _hostname = (String)options.get("hostname"); + _port = Integer.parseInt((String)options.get("port")); + _contextFactory = (String)options.get("contextFactory"); + _bindDn = (String)options.get("bindDn"); + _bindPassword = (String)options.get("bindPassword"); + _authenticationMethod = (String)options.get("authenticationMethod"); + + _userBaseDn = (String)options.get("userBaseDn"); + + _roleBaseDn = (String)options.get("roleBaseDn"); + + if (options.containsKey("forceBindingLogin")) + { + _forceBindingLogin = Boolean.parseBoolean((String)options.get("forceBindingLogin")); + } + + if (options.containsKey("useLdaps")) + { + _useLdaps = Boolean.parseBoolean((String)options.get("useLdaps")); + } + + _userObjectClass = getOption(options, "userObjectClass", _userObjectClass); + _userRdnAttribute = getOption(options, "userRdnAttribute", _userRdnAttribute); + _userIdAttribute = getOption(options, "userIdAttribute", _userIdAttribute); + _userPasswordAttribute = getOption(options, "userPasswordAttribute", _userPasswordAttribute); + _roleObjectClass = getOption(options, "roleObjectClass", _roleObjectClass); + _roleMemberAttribute = getOption(options, "roleMemberAttribute", _roleMemberAttribute); + _roleNameAttribute = getOption(options, "roleNameAttribute", _roleNameAttribute); + _debug = Boolean.parseBoolean(String.valueOf(getOption(options, "debug", Boolean.toString(_debug)))); + + try + { + _rootContext = new InitialDirContext(getEnvironment()); + } + catch (NamingException ex) + { + throw new IllegalStateException("Unable to establish root context", ex); + } + } + + @Override + public boolean commit() throws LoginException + { + try + { + _rootContext.close(); + } + catch (NamingException e) + { + throw new LoginException("error closing root context: " + e.getMessage()); + } + + return super.commit(); + } + + @Override + public boolean abort() throws LoginException + { + try + { + _rootContext.close(); + } + catch (NamingException e) + { + throw new LoginException("error closing root context: " + e.getMessage()); + } + + return super.abort(); + } + + private String getOption(Map options, String key, String defaultValue) + { + Object value = options.get(key); + + if (value == null) + { + return defaultValue; + } + + return (String)value; + } + + /** + * get the context for connection + * + * @return the environment details for the context + */ + public Hashtable getEnvironment() + { + Properties env = new Properties(); + + env.put(Context.INITIAL_CONTEXT_FACTORY, _contextFactory); + + if (_hostname != null) + { + env.put(Context.PROVIDER_URL, (_useLdaps ? "ldaps://" : "ldap://") + _hostname + (_port == 0 ? "" : ":" + _port) + "/"); + } + + if (_authenticationMethod != null) + { + env.put(Context.SECURITY_AUTHENTICATION, _authenticationMethod); + } + + if (_bindDn != null) + { + env.put(Context.SECURITY_PRINCIPAL, _bindDn); + } + + if (_bindPassword != null) + { + env.put(Context.SECURITY_CREDENTIALS, _bindPassword); + } + + return env; + } + + public static String convertCredentialLdapToJetty(String encryptedPassword) + { + if (encryptedPassword == null) + { + return null; + } + + if (encryptedPassword.toUpperCase(Locale.ENGLISH).startsWith("{MD5}")) + { + String src = encryptedPassword.substring("{MD5}".length(), encryptedPassword.length()); + return "MD5:" + base64ToHex(src); + } + + if (encryptedPassword.toUpperCase(Locale.ENGLISH).startsWith("{CRYPT}")) + { + return "CRYPT:" + encryptedPassword.substring("{CRYPT}".length(), encryptedPassword.length()); + } + + return encryptedPassword; + } + + private static String base64ToHex(String src) + { + byte[] bytes = Base64.getDecoder().decode(src); + return TypeUtil.toString(bytes, 16); + } + + private static String hexToBase64(String src) + { + byte[] bytes = TypeUtil.fromHexString(src); + return Base64.getEncoder().encodeToString(bytes); + } +} diff --git a/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/spi/PropertyFileLoginModule.java b/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/spi/PropertyFileLoginModule.java new file mode 100644 index 00000000000..ece0690ebcf --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/spi/PropertyFileLoginModule.java @@ -0,0 +1,138 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.jaas.spi; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import javax.security.auth.Subject; +import javax.security.auth.callback.CallbackHandler; + +import org.eclipse.jetty.ee10.jaas.JAASLoginService; +import org.eclipse.jetty.ee10.jaas.PropertyUserStoreManager; +import org.eclipse.jetty.ee10.servlet.security.PropertyUserStore; +import org.eclipse.jetty.ee10.servlet.security.RolePrincipal; +import org.eclipse.jetty.ee10.servlet.security.UserPrincipal; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * PropertyFileLoginModule + */ +public class PropertyFileLoginModule extends AbstractLoginModule +{ + public static final String DEFAULT_FILENAME = "realm.properties"; + private static final Logger LOG = LoggerFactory.getLogger(PropertyFileLoginModule.class); + + private PropertyUserStore _store; + + /** + * Use a PropertyUserStore to read the authentication and authorizaton information contained in + * the file named by the option "file". + * + * @param subject the subject + * @param callbackHandler the callback handler + * @param sharedState the shared state map + * @param options the options map + * @see javax.security.auth.spi.LoginModule#initialize(javax.security.auth.Subject, javax.security.auth.callback.CallbackHandler, java.util.Map, + * java.util.Map) + */ + @Override + public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) + { + super.initialize(subject, callbackHandler, sharedState, options); + setupPropertyUserStore(options); + } + + /** + * Get an existing, or create a new PropertyUserStore to read the + * authentication and authorization information from the file named by + * the option "file". + * + * @param options configuration options + */ + private void setupPropertyUserStore(Map options) + { + String filename = (String)options.get("file"); + filename = (filename == null ? DEFAULT_FILENAME : filename); + + PropertyUserStoreManager mgr = JAASLoginService.INSTANCE.get().getBean(PropertyUserStoreManager.class); + if (mgr == null) + throw new IllegalStateException("No PropertyUserStoreManager"); + + _store = mgr.getPropertyUserStore(filename); + if (_store == null) + { + boolean hotReload = false; + String tmp = (String)options.get("hotReload"); + if (tmp != null) + hotReload = Boolean.parseBoolean(tmp); + else + { + //refreshInterval is deprecated, use hotReload instead + tmp = (String)options.get("refreshInterval"); + if (tmp != null) + { + LOG.warn("Use 'hotReload' boolean property instead of 'refreshInterval'"); + try + { + hotReload = (Integer.parseInt(tmp) > 0); + } + catch (NumberFormatException e) + { + LOG.warn("'refreshInterval' is not an integer"); + } + } + } + PropertyUserStore newStore = new PropertyUserStore(); + newStore.setConfig(filename); + newStore.setHotReload(hotReload); + _store = mgr.addPropertyUserStore(filename, newStore); + try + { + _store.start(); + } + catch (Exception e) + { + LOG.warn("Exception starting propertyUserStore {} ", filename, e); + } + } + } + + /** + * @param userName the user name + * @throws Exception if unable to get the user information + */ + @Override + public JAASUser getUser(String userName) throws Exception + { + if (LOG.isDebugEnabled()) + LOG.debug("Checking PropertyUserStore {} for {}", _store.getConfig(), userName); + UserPrincipal up = _store.getUserPrincipal(userName); + if (up == null) + return null; + + List rps = _store.getRolePrincipals(userName); + List roles = rps == null ? Collections.emptyList() : rps.stream().map(RolePrincipal::getName).collect(Collectors.toList()); + return new JAASUser(up) + { + @Override + public List doFetchRoles() + { + return roles; + } + }; + } +} diff --git a/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/spi/package-info.java b/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/spi/package-info.java new file mode 100644 index 00000000000..c4e9295218b --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaas/src/main/java/org/eclipse/jetty/ee10/jaas/spi/package-info.java @@ -0,0 +1,18 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 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 Jaas : Various Jaas Implementations for Jetty + */ +package org.eclipse.jetty.ee10.jaas.spi; + diff --git a/jetty-ee10/jetty-ee10-jaas/src/test/java/org/eclipse/jetty/ee10/jaas/JAASLdapLoginServiceTest.java b/jetty-ee10/jetty-ee10-jaas/src/test/java/org/eclipse/jetty/ee10/jaas/JAASLdapLoginServiceTest.java new file mode 100644 index 00000000000..8a5ca89c9a1 --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaas/src/test/java/org/eclipse/jetty/ee10/jaas/JAASLdapLoginServiceTest.java @@ -0,0 +1,314 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.jaas; + +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 javax.security.auth.login.AppConfigurationEntry; +import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag; +import javax.security.auth.login.Configuration; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.directory.server.annotations.CreateLdapServer; +import org.apache.directory.server.annotations.CreateTransport; +import org.apache.directory.server.core.annotations.ApplyLdifs; +import org.apache.directory.server.core.annotations.CreateDS; +import org.apache.directory.server.core.annotations.CreatePartition; +import org.apache.directory.server.core.integ.FrameworkRunner; +import org.apache.directory.server.ldap.LdapServer; +import org.eclipse.jetty.ee10.jaas.spi.LdapLoginModule; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.security.ConstraintSecurityHandler; +import org.eclipse.jetty.ee10.servlet.security.DefaultIdentityService; +import org.eclipse.jetty.ee10.servlet.security.authentication.BasicAuthenticator; +import org.eclipse.jetty.server.LocalConnector; +import org.eclipse.jetty.server.Server; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static java.nio.charset.StandardCharsets.ISO_8859_1; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.startsWith; + +/** + * JAASLdapLoginServiceTest + */ +@RunWith(FrameworkRunner.class) +@CreateLdapServer(transports = {@CreateTransport(protocol = "LDAP")}) +@CreateDS(allowAnonAccess = false, partitions = { + @CreatePartition(name = "Users Partition", suffix = "ou=people,dc=jetty,dc=org"), + @CreatePartition(name = "Groups Partition", suffix = "ou=groups,dc=jetty,dc=org") +}) +@ApplyLdifs({ + // Entry 1 + "dn: ou=people,dc=jetty,dc=org", + "objectClass: organizationalunit", + "objectClass: top", + "ou: people", + // Entry # 2 + "dn:uid=someone,ou=people,dc=jetty,dc=org", + "objectClass: inetOrgPerson", + "cn: someone", + "sn: sn test", + "userPassword: complicatedpassword", + // Entry # 3 + "dn:uid=someoneelse,ou=people,dc=jetty,dc=org", + "objectClass: inetOrgPerson", + "cn: someoneelse", + "sn: sn test", + "userPassword: verycomplicatedpassword", + // Entry 4 + "dn: ou=groups,dc=jetty,dc=org", + "objectClass: organizationalunit", + "objectClass: top", + "ou: groups", + // Entry 5 + "dn: ou=subdir,ou=people,dc=jetty,dc=org", + "objectClass: organizationalunit", + "objectClass: top", + "ou: subdir", + // Entry # 6 + "dn:uid=uniqueuser,ou=subdir,ou=people,dc=jetty,dc=org", + "objectClass: inetOrgPerson", + "cn: uniqueuser", + "sn: unique user", + "userPassword: hello123", + // Entry # 7 + "dn:uid=ambiguousone,ou=people,dc=jetty,dc=org", + "objectClass: inetOrgPerson", + "cn: ambiguous1", + "sn: ambiguous user", + "userPassword: foobar", + // Entry # 8 + "dn:uid=ambiguousone,ou=subdir,ou=people,dc=jetty,dc=org", + "objectClass: inetOrgPerson", + "cn: ambiguous2", + "sn: ambiguous subdir user", + "userPassword: barfoo", + // Entry 9 + "dn: cn=developers,ou=groups,dc=jetty,dc=org", + "objectClass: groupOfUniqueNames", + "objectClass: top", + "ou: groups", + "description: People who try to build good software", + "uniquemember: uid=someone,ou=people,dc=jetty,dc=org", + "uniquemember: uid=uniqueuser,ou=subdir,ou=people,dc=jetty,dc=org", + "cn: developers", + // Entry 10 + "dn: cn=admin,ou=groups,dc=jetty,dc=org", + "objectClass: groupOfUniqueNames", + "objectClass: top", + "ou: groups", + "description: People who try to run software build by developers", + "uniquemember: uid=someone,ou=people,dc=jetty,dc=org", + "uniquemember: uid=someoneelse,ou=people,dc=jetty,dc=org", + "uniquemember: uid=uniqueuser,ou=subdir,ou=people,dc=jetty,dc=org", + "cn: admin" +}) +public class JAASLdapLoginServiceTest +{ + public static class TestConfiguration extends Configuration + { + private boolean forceBindingLogin; + + public TestConfiguration(boolean forceBindingLogin) + { + this.forceBindingLogin = forceBindingLogin; + } + + @Override + public AppConfigurationEntry[] getAppConfigurationEntry(String name) + { + Map options = new HashMap<>(); + options.put("hostname", "localhost"); + options.put("port", Integer.toString(_ldapServer.getTransports()[0].getPort())); + options.put("contextFactory", "com.sun.jndi.ldap.LdapCtxFactory"); + options.put("bindDn", "uid=admin,ou=system"); + options.put("bindPassword", "secret"); + options.put("userBaseDn", "ou=people,dc=jetty,dc=org"); + options.put("roleBaseDn", "ou=groups,dc=jetty,dc=org"); + options.put("roleNameAttribute", "cn"); + options.put("forceBindingLogin", Boolean.toString(forceBindingLogin)); + AppConfigurationEntry entry = new AppConfigurationEntry(LdapLoginModule.class.getCanonicalName(), LoginModuleControlFlag.REQUIRED, options); + + return new AppConfigurationEntry[]{entry}; + } + } + + public static LdapServer getLdapServer() + { + return _ldapServer; + } + + public static void setLdapServer(LdapServer ldapServer) + { + _ldapServer = ldapServer; + } + + private static LdapServer _ldapServer; + private Server _server; + private LocalConnector _connector; + private ServletContextHandler _context; + + public void setUp() throws Exception + { + _server = new Server(); + _connector = new LocalConnector(_server); + _server.addConnector(_connector); + + _context = new ServletContextHandler(); + _context.setContextPath("/ctx"); + _server.setHandler(_context); + ConstraintSecurityHandler security = new ConstraintSecurityHandler(); + security.setAuthenticator(new BasicAuthenticator()); + _context.setSecurityHandler(security); + } + + private JAASLoginService jaasLoginService(String name) + { + JAASLoginService ls = new JAASLoginService("foo"); + ls.setCallbackHandlerClass("org.eclipse.jetty.ee10.jaas.callback.DefaultCallbackHandler"); + ls.setIdentityService(new DefaultIdentityService()); + ls.setConfiguration(new TestConfiguration(true)); + return ls; + } + + private String doLogin(String username, String password, List hasRoles, List hasntRoles) throws Exception + { + JAASLoginService ls = jaasLoginService("foo"); + _server.addBean(ls, true); + + _context.setServletHandler(new ServletHandler()); + ServletHolder holder = new ServletHolder(); + holder.setServlet(new TestServlet(hasRoles, hasntRoles)); + _context.getServletHandler().addServletWithMapping(holder, "/"); + + _server.start(); + + return _connector.getResponse("GET /ctx/test HTTP/1.0\n" + "Authorization: Basic " + + Base64.getEncoder().encodeToString((username + ":" + password).getBytes(ISO_8859_1)) + "\n\n"); + } + + @Test + public void testLdapUserIdentity() throws Exception + { + setUp(); + _context.setServletHandler(new ServletHandler()); + ServletHolder holder = new ServletHolder(); + holder.setServlet(new TestServlet(Arrays.asList("developers", "admin"), Arrays.asList("blabla"))); + _context.getServletHandler().addServletWithMapping(holder, "/"); + + JAASLoginService ls = new JAASLoginService("foo"); + ls.setCallbackHandlerClass("org.eclipse.jetty.ee10.jaas.callback.DefaultCallbackHandler"); + ls.setIdentityService(new DefaultIdentityService()); + ls.setConfiguration(new TestConfiguration(false)); + + _server.addBean(ls, true); + _server.start(); + + String response = _connector.getResponse("GET /ctx/test HTTP/1.0\n" + "Authorization: Basic " + + Base64.getEncoder().encodeToString("someone:complicatedpassword".getBytes(ISO_8859_1)) + "\n\n"); + assertThat(response, startsWith("HTTP/1.1 200 OK")); + + _server.stop(); + + _context.setServletHandler(new ServletHandler()); + holder = new ServletHolder(); + holder.setServlet(new TestServlet(Arrays.asList("admin"), Arrays.asList("developers, blabla"))); + _context.getServletHandler().addServletWithMapping(holder, "/"); + + _server.start(); + + response = _connector.getResponse("GET /ctx/test HTTP/1.0\n" + "Authorization: Basic " + + Base64.getEncoder().encodeToString("someoneelse:verycomplicatedpassword".getBytes(ISO_8859_1)) + "\n\n"); + assertThat(response, startsWith("HTTP/1.1 200 OK")); + } + + @Test + public void testLdapUserIdentityBindingLogin() throws Exception + { + setUp(); + _context.setServletHandler(new ServletHandler()); + ServletHolder holder = new ServletHolder(); + holder.setServlet(new TestServlet(Arrays.asList("developers", "admin"), Arrays.asList("blabla"))); + _context.getServletHandler().addServletWithMapping(holder, "/"); + JAASLoginService ls = new JAASLoginService("foo"); + ls.setCallbackHandlerClass("org.eclipse.jetty.ee10.jaas.callback.DefaultCallbackHandler"); + ls.setIdentityService(new DefaultIdentityService()); + ls.setConfiguration(new TestConfiguration(true)); + _server.addBean(ls, true); + _server.start(); + + + String response = _connector.getResponse("GET /ctx/test HTTP/1.0\n" + "Authorization: Basic " + + Base64.getEncoder().encodeToString("someone:complicatedpassword".getBytes(ISO_8859_1)) + "\n\n"); + assertThat(response, startsWith("HTTP/1.1 200 OK")); + + _server.stop(); + _context.setServletHandler(new ServletHandler()); + _context.addServlet(new HttpServlet() + { + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException + { + //check authentication status + if (req.getUserPrincipal() == null) + req.authenticate(resp); + } + + }, "/"); + _server.start(); + + //TODO this test shows response already committed! + response = _connector.getResponse("GET /ctx/test HTTP/1.0\n" + "Authorization: Basic " + + Base64.getEncoder().encodeToString("someone:wrongpassword".getBytes(ISO_8859_1)) + "\n\n"); + System.err.println(response); + assertThat(response, startsWith("HTTP/1.1 " + HttpServletResponse.SC_UNAUTHORIZED)); + } + + @Test + public void testLdapBindingSubdirUniqueUserName() throws Exception + { + setUp(); + String response = doLogin("uniqueuser", "hello123", Arrays.asList("developers", "admin"), Arrays.asList("blabla")); + assertThat(response, startsWith("HTTP/1.1 200 OK")); + } + + //TODO test is failing, needs more work + @Test + public void testLdapBindingAmbiguousUserName() throws Exception + { + setUp(); + String response = doLogin("ambiguousone", "foobar", null, null); + assertThat(response, startsWith("HTTP/1.1 " + HttpServletResponse.SC_UNAUTHORIZED)); + } + + //TODO test is failing, needs more work + @Test + public void testLdapBindingSubdirAmbiguousUserName() throws Exception + { + setUp(); + String response = doLogin("ambiguousone", "barfoo", null, null); + assertThat(response, startsWith("HTTP/1.1 " + HttpServletResponse.SC_UNAUTHORIZED)); + } +} diff --git a/jetty-ee10/jetty-ee10-jaas/src/test/java/org/eclipse/jetty/ee10/jaas/JAASLoginServiceTest.java b/jetty-ee10/jetty-ee10-jaas/src/test/java/org/eclipse/jetty/ee10/jaas/JAASLoginServiceTest.java new file mode 100644 index 00000000000..5fc1db310e2 --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaas/src/test/java/org/eclipse/jetty/ee10/jaas/JAASLoginServiceTest.java @@ -0,0 +1,245 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.jaas; + +import java.io.IOException; +import java.security.Principal; +import java.util.Arrays; +import java.util.Base64; +import java.util.Collections; +import javax.security.auth.Subject; +import javax.security.auth.login.AppConfigurationEntry; +import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag; +import javax.security.auth.login.Configuration; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.security.ConstraintMapping; +import org.eclipse.jetty.ee10.servlet.security.ConstraintSecurityHandler; +import org.eclipse.jetty.ee10.servlet.security.DefaultIdentityService; +import org.eclipse.jetty.ee10.servlet.security.authentication.BasicAuthenticator; +import org.eclipse.jetty.server.LocalConnector; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.security.Constraint; +import org.junit.jupiter.api.AfterEach; +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.containsInAnyOrder; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.startsWith; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * JAASLoginServiceTest + */ +public class JAASLoginServiceTest +{ + interface SomeRole + { + + } + + public class TestRole implements Principal, SomeRole + { + String _name; + + public TestRole(String name) + { + _name = name; + } + + public String getName() + { + return _name; + } + } + + public class AnotherTestRole extends TestRole + { + public AnotherTestRole(String name) + { + super(name); + } + } + + public class NotTestRole implements Principal + { + String _name; + + public NotTestRole(String n) + { + _name = n; + } + + public String getName() + { + return _name; + } + } + + public static class TestServlet extends HttpServlet + { + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException + { + resp.setStatus(200); + resp.setContentType("text/plain"); + resp.getWriter().println("All OK"); + resp.getWriter().println("requestURI=" + req.getRequestURI()); + } + } + + 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(); + _context.setContextPath("/ctx"); + _context.addServlet(new TestServlet(), "/"); + _server.setHandler(_context); + ConstraintSecurityHandler security = new ConstraintSecurityHandler(); + security.setAuthenticator(new BasicAuthenticator()); + Constraint constraint = new Constraint("All", "users"); + constraint.setAuthenticate(true); + ConstraintMapping mapping = new ConstraintMapping(); + mapping.setPathSpec("/jaspi/*"); + mapping.setConstraint(constraint); + security.addConstraintMapping(mapping); + _context.setSecurityHandler(security); + } + + @AfterEach + public void tearDown() throws Exception + { + _server.stop(); + } + + @Test + public void testServletRequestCallback() throws Exception + { + Configuration config = new Configuration() + { + @Override + public AppConfigurationEntry[] getAppConfigurationEntry(String name) + { + return new AppConfigurationEntry[] { + new AppConfigurationEntry(TestLoginModule.class.getCanonicalName(), + LoginModuleControlFlag.REQUIRED, + Collections.emptyMap()) + }; + } + }; + + //Test with the DefaultCallbackHandler + JAASLoginService ls = new JAASLoginService("foo"); + ls.setCallbackHandlerClass("org.eclipse.jetty.ee10.jaas.callback.DefaultCallbackHandler"); + ls.setIdentityService(new DefaultIdentityService()); + ls.setConfiguration(config); + _server.addBean(ls, true); + _server.start(); + + String response = _connector.getResponse("GET /ctx/jaspi/test HTTP/1.0\n" + "Authorization: Basic " + + Base64.getEncoder().encodeToString("aaardvaark:aaa".getBytes(ISO_8859_1)) + "\n\n"); + assertThat(response, startsWith("HTTP/1.1 200 OK")); + + _server.stop(); + _server.removeBean(ls); + + //Test with the fallback CallbackHandler + ls = new JAASLoginService("foo"); + ls.setIdentityService(new DefaultIdentityService()); + ls.setConfiguration(config); + _server.addBean(ls, true); + _server.start(); + + response = _connector.getResponse("GET /ctx/jaspi/test HTTP/1.0\n" + "Authorization: Basic " + + Base64.getEncoder().encodeToString("aaardvaark:aaa".getBytes(ISO_8859_1)) + "\n\n"); + assertThat(response, startsWith("HTTP/1.1 200 OK")); + } + + @Test + public void testLoginServiceRoles() throws Exception + { + JAASLoginService ls = new JAASLoginService("foo"); + + //test that we always add in the DEFAULT ROLE CLASSNAME + ls.setRoleClassNames(new String[]{"arole", "brole"}); + String[] roles = ls.getRoleClassNames(); + assertEquals(3, roles.length); + assertEquals(JAASLoginService.DEFAULT_ROLE_CLASS_NAME, roles[2]); + + ls.setRoleClassNames(new String[]{}); + assertEquals(1, ls.getRoleClassNames().length); + assertEquals(JAASLoginService.DEFAULT_ROLE_CLASS_NAME, ls.getRoleClassNames()[0]); + + ls.setRoleClassNames(null); + assertEquals(1, ls.getRoleClassNames().length); + assertEquals(JAASLoginService.DEFAULT_ROLE_CLASS_NAME, ls.getRoleClassNames()[0]); + + //test a custom role class where some of the roles are subclasses of it + ls.setRoleClassNames(new String[]{TestRole.class.getName()}); + Subject subject = new Subject(); + subject.getPrincipals().add(new NotTestRole("w")); + subject.getPrincipals().add(new TestRole("x")); + subject.getPrincipals().add(new TestRole("y")); + subject.getPrincipals().add(new AnotherTestRole("z")); + + String[] groups = ls.getGroups(subject); + assertThat(Arrays.asList(groups), containsInAnyOrder("x", "y", "z")); + + //test a custom role class + ls.setRoleClassNames(new String[]{AnotherTestRole.class.getName()}); + Subject subject2 = new Subject(); + subject2.getPrincipals().add(new NotTestRole("w")); + subject2.getPrincipals().add(new TestRole("x")); + subject2.getPrincipals().add(new TestRole("y")); + subject2.getPrincipals().add(new AnotherTestRole("z")); + String[] s2groups = ls.getGroups(subject2); + assertThat(s2groups, is(notNullValue())); + assertThat(Arrays.asList(s2groups), containsInAnyOrder("z")); + + //test a custom role class that implements an interface + ls.setRoleClassNames(new String[]{SomeRole.class.getName()}); + Subject subject3 = new Subject(); + subject3.getPrincipals().add(new NotTestRole("w")); + subject3.getPrincipals().add(new TestRole("x")); + subject3.getPrincipals().add(new TestRole("y")); + subject3.getPrincipals().add(new AnotherTestRole("z")); + String[] s3groups = ls.getGroups(subject3); + assertThat(s3groups, is(notNullValue())); + assertThat(Arrays.asList(s3groups), containsInAnyOrder("x", "y", "z")); + + //test a class that doesn't match + ls.setRoleClassNames(new String[]{NotTestRole.class.getName()}); + Subject subject4 = new Subject(); + subject4.getPrincipals().add(new TestRole("x")); + subject4.getPrincipals().add(new TestRole("y")); + subject4.getPrincipals().add(new AnotherTestRole("z")); + assertEquals(0, ls.getGroups(subject4).length); + } +} diff --git a/jetty-ee10/jetty-ee10-jaas/src/test/java/org/eclipse/jetty/ee10/jaas/TestLoginModule.java b/jetty-ee10/jetty-ee10-jaas/src/test/java/org/eclipse/jetty/ee10/jaas/TestLoginModule.java new file mode 100644 index 00000000000..7db34c67d9f --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaas/src/test/java/org/eclipse/jetty/ee10/jaas/TestLoginModule.java @@ -0,0 +1,59 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.jaas; + +import java.util.Collections; +import java.util.List; +import javax.security.auth.callback.Callback; +import javax.security.auth.login.LoginException; + +import org.eclipse.jetty.ee10.jaas.callback.ServletRequestCallback; +import org.eclipse.jetty.ee10.jaas.spi.AbstractLoginModule; +import org.eclipse.jetty.ee10.servlet.security.UserPrincipal; +import org.eclipse.jetty.util.ArrayUtil; +import org.eclipse.jetty.util.security.Password; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +public class TestLoginModule extends AbstractLoginModule +{ + public ServletRequestCallback _callback = new ServletRequestCallback(); + + @Override + public JAASUser getUser(String username) throws Exception + { + return new JAASUser(new UserPrincipal(username, new Password("aaa"))) + { + @Override + public List doFetchRoles() throws Exception + { + return Collections.singletonList("users"); + } + }; + } + + @Override + public Callback[] configureCallbacks() + { + return ArrayUtil.addToArray(super.configureCallbacks(), _callback, Callback.class); + } + + @Override + public boolean login() throws LoginException + { + boolean result = super.login(); + assertNotNull(_callback.getRequest()); + return result; + } +} diff --git a/jetty-ee10/jetty-ee10-jaas/src/test/java/org/eclipse/jetty/ee10/jaas/TestServlet.java b/jetty-ee10/jetty-ee10-jaas/src/test/java/org/eclipse/jetty/ee10/jaas/TestServlet.java new file mode 100644 index 00000000000..302008e64a4 --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaas/src/test/java/org/eclipse/jetty/ee10/jaas/TestServlet.java @@ -0,0 +1,91 @@ + +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.jaas; + +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 +{ + private List _hasRoles; + private List _hasntRoles; + + public TestServlet(List hasRoles, List hasntRoles) + { + _hasRoles = hasRoles; + _hasntRoles = hasntRoles; + } + + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException + { + if (_hasRoles == null && _hasntRoles == null) + { + try + { + req.authenticate(resp); + } + catch (ServletException e) + { + //TODO: a ServletException should only be thrown here if the response has + //not been set, but currently it seems that the ServletException is thrown + //anyway by ServletContextRequest.ServletApiRequest.authenticate. + } + } + else + { + testHasRoles(req, resp); + testHasntRoles(req, resp); + + resp.setStatus(200); + resp.setContentType("text/plain"); + resp.getWriter().println("All OK"); + resp.getWriter().println("requestURI=" + req.getRequestURI()); + } + } + + private void testHasRoles(HttpServletRequest req, HttpServletResponse resp) throws ServletException + { + if (_hasRoles != null) + { + for (String role : _hasRoles) + { + //TODO: the response may already have been committed, because eg the + //BasicAuthenticator may have decided the authentication is invalid. + //Thus throwing ServletException here causes a problem because the HttpChannelState + //tries to set the response. + if (!req.isUserInRole(role)) + throw new ServletException("! in role " + role); + } + } + } + + private void testHasntRoles(HttpServletRequest req, HttpServletResponse resp) throws ServletException + { + if (_hasntRoles != null) + { + for (String role : _hasntRoles) + { + if (req.isUserInRole(role)) + throw new ServletException("in role " + role); + } + } + } +} \ No newline at end of file diff --git a/jetty-ee10/jetty-ee10-jaas/src/test/java/org/eclipse/jetty/ee10/jaas/spi/PropertyFileLoginModuleTest.java b/jetty-ee10/jetty-ee10-jaas/src/test/java/org/eclipse/jetty/ee10/jaas/spi/PropertyFileLoginModuleTest.java new file mode 100644 index 00000000000..f4ee0df5892 --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaas/src/test/java/org/eclipse/jetty/ee10/jaas/spi/PropertyFileLoginModuleTest.java @@ -0,0 +1,122 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.jaas.spi; + +import java.io.File; +import java.util.Arrays; +import java.util.Base64; +import java.util.Collections; +import javax.security.auth.login.AppConfigurationEntry; +import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag; +import javax.security.auth.login.Configuration; + +import org.eclipse.jetty.ee10.jaas.JAASLoginService; +import org.eclipse.jetty.ee10.jaas.PropertyUserStoreManager; +import org.eclipse.jetty.ee10.jaas.TestServlet; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.security.ConstraintSecurityHandler; +import org.eclipse.jetty.ee10.servlet.security.DefaultIdentityService; +import org.eclipse.jetty.ee10.servlet.security.PropertyUserStore; +import org.eclipse.jetty.ee10.servlet.security.authentication.BasicAuthenticator; +import org.eclipse.jetty.server.LocalConnector; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.junit.jupiter.api.AfterEach; +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.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; +import static org.hamcrest.Matchers.startsWith; + +public class PropertyFileLoginModuleTest +{ + private Server _server; + private LocalConnector _connector; + + @BeforeEach + public void setUp() throws Exception + { + _server = new Server(); + + _connector = new LocalConnector(_server); + _server.addConnector(_connector); + + } + + @AfterEach + public void tearDown() throws Exception + { + _server.stop(); + } + + @Test + public void testPropertyFileLoginModule() throws Exception + { + //configure for PropertyFileLoginModule + File loginProperties = MavenTestingUtils.getTestResourceFile("login.properties"); + + Configuration testConfig = new Configuration() + { + @Override + public AppConfigurationEntry[] getAppConfigurationEntry(String name) + { + return new AppConfigurationEntry[]{new AppConfigurationEntry(PropertyFileLoginModule.class.getName(), + LoginModuleControlFlag.REQUIRED, + Collections.singletonMap("file", loginProperties.getAbsolutePath()))}; + } + }; + + ServletContextHandler context = new ServletContextHandler(); + context.setContextPath("/ctx"); + context.addServlet(new TestServlet(Arrays.asList("role1", "role2", "role3"), Arrays.asList("role4")), "/"); + _server.setHandler(context); + + ConstraintSecurityHandler security = new ConstraintSecurityHandler(); + security.setAuthenticator(new BasicAuthenticator()); + context.setSecurityHandler(security); + + JAASLoginService ls = new JAASLoginService("foo"); + ls.setCallbackHandlerClass("org.eclipse.jetty.ee10.jaas.callback.DefaultCallbackHandler"); + ls.setIdentityService(new DefaultIdentityService()); + ls.setConfiguration(testConfig); + _server.addBean(ls, true); + + _server.start(); + + //test that the manager is created when the JAASLoginService starts + PropertyUserStoreManager mgr = ls.getBean(PropertyUserStoreManager.class); + assertThat(mgr, notNullValue()); + + //test the PropertyFileLoginModule authentication and authorization + String response = _connector.getResponse("GET /ctx/test HTTP/1.0\n" + "Authorization: Basic " + + Base64.getEncoder().encodeToString("fred:pwd".getBytes(ISO_8859_1)) + "\n\n"); + assertThat(response, startsWith("HTTP/1.1 200 OK")); + + //Test that the PropertyUserStore is created by the PropertyFileLoginModule + PropertyUserStore store = mgr.getPropertyUserStore(loginProperties.getAbsolutePath()); + assertThat(store, is(notNullValue())); + assertThat(store.isRunning(), is(true)); + assertThat(store.isHotReload(), is(false)); + + //test that the PropertyUserStoreManager is stopped and all PropertyUserStores stopped + _server.stop(); + assertThat(mgr.isStopped(), is(true)); + assertThat(mgr.getPropertyUserStore(loginProperties.getAbsolutePath()), is(nullValue())); + assertThat(store.isStopped(), is(true)); + } +} diff --git a/jetty-ee10/jetty-ee10-jaas/src/test/resources/jetty-logging.properties b/jetty-ee10/jetty-ee10-jaas/src/test/resources/jetty-logging.properties new file mode 100644 index 00000000000..f0773d90fa5 --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaas/src/test/resources/jetty-logging.properties @@ -0,0 +1,3 @@ +# Jetty Logging using jetty-slf4j-impl +org.eclipse.jetty.LEVEL=INFO +org.apache.directory.LEVEL=ERROR \ No newline at end of file diff --git a/jetty-ee10/jetty-ee10-jaas/src/test/resources/login.properties b/jetty-ee10/jetty-ee10-jaas/src/test/resources/login.properties new file mode 100644 index 00000000000..22a4bedc7b7 --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaas/src/test/resources/login.properties @@ -0,0 +1 @@ +fred=pwd,role1,role2,role3 \ No newline at end of file diff --git a/jetty-ee10/jetty-ee10-jaspi/pom.xml b/jetty-ee10/jetty-ee10-jaspi/pom.xml new file mode 100644 index 00000000000..5db8cbd404f --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaspi/pom.xml @@ -0,0 +1,74 @@ + + + org.eclipse.jetty.ee10 + jetty-ee10 + 12.0.0-SNAPSHOT + + + 4.0.0 + jetty-ee10-jaspi + EE10 :: Jetty :: JASPI Security + Jetty security infrastructure + + + ${project.groupId}.security.jaspi + org.eclipse.jetty.jaspi.* + + + + + + org.apache.felix + maven-bundle-plugin + true + + + + manifest + + + + osgi.extender; filter:="(osgi.extender=osgi.serviceloader.registrar)" + osgi.serviceloader;osgi.serviceloader=org.eclipse.jetty.ee10.servlet.security.Authenticator$Factory + + + + + + + + + + + org.eclipse.jetty.toolchain + jetty-jakarta-servlet-api + + + org.eclipse.jetty.ee10 + jetty-ee10-servlet + + + org.slf4j + slf4j-api + + + org.eclipse.jetty + jetty-slf4j-impl + test + + + org.eclipse.jetty.toolchain + jetty-test-helper + test + + + jakarta.authentication + jakarta.authentication-api + + + jakarta.activation + jakarta.activation-api + test + + + diff --git a/jetty-ee10/jetty-ee10-jaspi/src/main/config/etc/jaspi/jaspi-authmoduleconfig.xml b/jetty-ee10/jetty-ee10-jaspi/src/main/config/etc/jaspi/jaspi-authmoduleconfig.xml new file mode 100644 index 00000000000..189888d9d57 --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaspi/src/main/config/etc/jaspi/jaspi-authmoduleconfig.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/jetty-ee10/jetty-ee10-jaspi/src/main/config/etc/jaspi/jaspi-default.xml b/jetty-ee10/jetty-ee10-jaspi/src/main/config/etc/jaspi/jaspi-default.xml new file mode 100644 index 00000000000..9dbdd89ad0d --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaspi/src/main/config/etc/jaspi/jaspi-default.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + false + + diff --git a/jetty-ee10/jetty-ee10-jaspi/src/main/config/etc/jaspi/jaspi-demo.xml b/jetty-ee10/jetty-ee10-jaspi/src/main/config/etc/jaspi/jaspi-demo.xml new file mode 100644 index 00000000000..8c00a9cdb7d --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaspi/src/main/config/etc/jaspi/jaspi-demo.xml @@ -0,0 +1,48 @@ + + + + + + + + + org.eclipse.jetty.security.jaspi.provider.JaspiAuthConfigProvider + + + + + + + ServerAuthModule + org.eclipse.jetty.security.jaspi.modules.BasicAuthenticationAuthModule + + + + org.eclipse.jetty.security.jaspi.modules.RealmName + Test Realm + + + + + + HttpServlet + + + server /test + + + A simple provider using HTTP BASIC authentication. + + + \ No newline at end of file diff --git a/jetty-ee10/jetty-ee10-jaspi/src/main/config/modules/jaspi-default-auth-config-factory.mod b/jetty-ee10/jetty-ee10-jaspi/src/main/config/modules/jaspi-default-auth-config-factory.mod new file mode 100644 index 00000000000..70913c907be --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaspi/src/main/config/modules/jaspi-default-auth-config-factory.mod @@ -0,0 +1,16 @@ +# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html + +[description] +Provides a DefaultAuthConfigFactory for jaspi + +[tags] +security + +[depend] +security + +[provide] +auth-config-factory + +[xml] +etc/jaspi/jaspi-default.xml diff --git a/jetty-ee10/jetty-ee10-jaspi/src/main/config/modules/jaspi-demo.mod b/jetty-ee10/jetty-ee10-jaspi/src/main/config/modules/jaspi-demo.mod new file mode 100644 index 00000000000..b0e7fd303e4 --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaspi/src/main/config/modules/jaspi-demo.mod @@ -0,0 +1,16 @@ +# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html + +[description] +Enables JASPI basic authentication the /test context path. + +[tags] +security + +[depend] +jaspi + +[xml] +etc/jaspi/jaspi-demo.xml + +[files] +basehome:etc/jaspi/jaspi-demo.xml|etc/jaspi/jaspi-demo.xml diff --git a/jetty-ee10/jetty-ee10-jaspi/src/main/config/modules/jaspi.mod b/jetty-ee10/jetty-ee10-jaspi/src/main/config/modules/jaspi.mod new file mode 100644 index 00000000000..76b2e81406e --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaspi/src/main/config/modules/jaspi.mod @@ -0,0 +1,22 @@ +# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html + +[description] +Enables JASPI authentication for deployed web applications. + +[tags] +security + +[depend] +security +auth-config-factory + +[lib] +lib/jetty-jaspi-${jetty.version}.jar +lib/jaspi/*.jar + +[xml] +etc/jaspi/jaspi-authmoduleconfig.xml + +[files] +basehome:etc/jaspi/jaspi-authmoduleconfig.xml|etc/jaspi/jaspi-authmoduleconfig.xml + diff --git a/jetty-ee10/jetty-ee10-jaspi/src/main/java/module-info.java b/jetty-ee10/jetty-ee10-jaspi/src/main/java/module-info.java new file mode 100644 index 00000000000..f18245e3427 --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaspi/src/main/java/module-info.java @@ -0,0 +1,29 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 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.ee10.security.jaspi +{ + requires jetty.servlet.api; + requires org.slf4j; + + requires transitive jakarta.security.auth.message; + requires transitive org.eclipse.jetty.ee10.servlet; + + exports org.eclipse.jetty.ee10.security.jaspi; + exports org.eclipse.jetty.ee10.security.jaspi.callback; + exports org.eclipse.jetty.ee10.security.jaspi.modules; + exports org.eclipse.jetty.ee10.security.jaspi.provider; + + provides org.eclipse.jetty.ee10.servlet.security.Authenticator.Factory with + org.eclipse.jetty.ee10.security.jaspi.JaspiAuthenticatorFactory; +} diff --git a/jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/DefaultAuthConfigFactory.java b/jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/DefaultAuthConfigFactory.java new file mode 100644 index 00000000000..7d4a1fa4942 --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/DefaultAuthConfigFactory.java @@ -0,0 +1,248 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.security.jaspi; + +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 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) + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(AuthConfigFactory.providerRegistrationSecurityPermission); + + 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) + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(AuthConfigFactory.providerRegistrationSecurityPermission); + + 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) + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(AuthConfigFactory.providerRegistrationSecurityPermission); + + DefaultRegistrationContext registrationContext = _registrations.remove(registrationID); + if (registrationContext == null) + return false; + + registrationContext.notifyListeners(); + return true; + } + + @Override + public String[] detachListener(RegistrationListener listener, String layer, String appContext) + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(AuthConfigFactory.providerRegistrationSecurityPermission); + + 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() + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(AuthConfigFactory.providerRegistrationSecurityPermission); + + // TODO: maybe we should re-construct providers created from classname. + } + + 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-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/JaspiAuthenticator.java b/jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/JaspiAuthenticator.java new file mode 100644 index 00000000000..b85f620072e --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/JaspiAuthenticator.java @@ -0,0 +1,304 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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.ee10.servlet.ServletContextRequest; +import org.eclipse.jetty.ee10.servlet.security.Authentication; +import org.eclipse.jetty.ee10.servlet.security.Authentication.User; +import org.eclipse.jetty.ee10.servlet.security.EmptyLoginService; +import org.eclipse.jetty.ee10.servlet.security.IdentityService; +import org.eclipse.jetty.ee10.servlet.security.LoginService; +import org.eclipse.jetty.ee10.servlet.security.ServerAuthException; +import org.eclipse.jetty.ee10.servlet.security.UserAuthentication; +import org.eclipse.jetty.ee10.servlet.security.UserIdentity; +import org.eclipse.jetty.ee10.servlet.security.WrappedAuthConfiguration; +import org.eclipse.jetty.ee10.servlet.security.authentication.DeferredAuthentication; +import org.eclipse.jetty.ee10.servlet.security.authentication.LoginAuthenticator; +import org.eclipse.jetty.ee10.servlet.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.ee10.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(AuthConfiguration 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 JaspiAuthConfiguration(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.getInitParameterNames()) + { + _authProperties.put(key, configuration.getInitParameter(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 getAuthMethod() + { + return "JASPI"; + } + + @Override + public UserIdentity login(String username, Object password, Request request) + { + ServletContextRequest servletContextRequest = Request.as(request, ServletContextRequest.class); + + UserIdentity user = _loginService.login(username, password, servletContextRequest == null ? null : servletContextRequest.getServletApiRequest()); + if (user != null) + { + renewSession((HttpServletRequest)request, null); + HttpSession session = ((HttpServletRequest)request).getSession(true); + if (session != null) + { + SessionAuthentication sessionAuth = new SessionAuthentication(getAuthMethod(), user, password); + session.setAttribute(SessionAuthentication.__J_AUTHENTICATED, sessionAuth); + } + } + return user; + } + + @Override + public Authentication validateRequest(Request request, Response response, Callback callback, boolean mandatory) throws ServerAuthException + { + JaspiMessageInfo info = new JaspiMessageInfo(request, response, callback, mandatory); + request.setAttribute("org.eclipse.jetty.ee10.security.jaspi.info", info); + + Authentication a = validateRequest(info); + + //if its not mandatory to authenticate, and the authenticator returned UNAUTHENTICATED, we treat it as authentication deferred + if (_allowLazyAuthentication && !info.isAuthMandatory() && a == Authentication.UNAUTHENTICATED) + a = new DeferredAuthentication(this); + return a; + } + + public Authentication 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 Authentication.SEND_CONTINUE; + if (authStatus == AuthStatus.SEND_FAILURE) + return Authentication.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 Authentication.UNAUTHENTICATED; + } + 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) + { + return Authentication.UNAUTHENTICATED; + } + } + GroupPrincipalCallback groupPrincipalCallback = _callbackHandler.getThreadGroupPrincipalCallback(); + String[] groups = groupPrincipalCallback == null ? null : groupPrincipalCallback.getGroups(); + userIdentity = _identityService.newUserIdentity(clientSubject, principal, groups); + } + + HttpSession session = ((HttpServletRequest)messageInfo.getRequestMessage()).getSession(false); + Authentication cached = (session == null ? null : (SessionAuthentication)session.getAttribute(SessionAuthentication.__J_AUTHENTICATED)); + if (cached != null) + return cached; + + return new UserAuthentication(getAuthMethod(), userIdentity); + } + if (authStatus == AuthStatus.SEND_SUCCESS) + { + // we are processing a message in a secureResponse dialog. + return Authentication.SEND_SUCCESS; + } + if (authStatus == AuthStatus.FAILURE) + { + Response.writeError(messageInfo.getBaseRequest(), messageInfo.getBaseResponse(), messageInfo.getCallback(), HttpServletResponse.SC_FORBIDDEN); + return Authentication.SEND_FAILURE; + } + // should not happen + throw new IllegalStateException("No AuthStatus returned"); + } + catch (AuthException e) + { + throw new ServerAuthException(e); + } + } + + @Override + public boolean secureResponse(Request request, Response response, Callback callback, boolean mandatory, User validatedUser) throws ServerAuthException + { + ServletContextRequest servletContextRequest = Request.as(request, ServletContextRequest.class); + JaspiMessageInfo info = (JaspiMessageInfo)servletContextRequest.getServletApiRequest().getAttribute("org.eclipse.jetty.ee10.security.jaspi.info"); + if (info == null) + throw new NullPointerException("MessageInfo from request missing: " + request); + return secureResponse(info, validatedUser); + } + + public boolean secureResponse(JaspiMessageInfo messageInfo, Authentication 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); + // TODO + // authContext.cleanSubject(messageInfo,validatedUser.getUserIdentity().getSubject()); + AuthStatus status = authContext.secureResponse(messageInfo, _serviceSubject); + return (AuthStatus.SEND_SUCCESS.equals(status)); + } + catch (AuthException e) + { + throw new ServerAuthException(e); + } + } + + private static class JaspiAuthConfiguration extends WrappedAuthConfiguration + { + private final LoginService loginService = new EmptyLoginService(); + + public JaspiAuthConfiguration(AuthConfiguration configuration) + { + super(configuration); + } + + @Override + public LoginService getLoginService() + { + return loginService; + } + } +} diff --git a/jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/JaspiAuthenticatorFactory.java b/jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/JaspiAuthenticatorFactory.java new file mode 100644 index 00000000000..e4d6ff29932 --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/JaspiAuthenticatorFactory.java @@ -0,0 +1,155 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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 jakarta.servlet.ServletContext; +import org.eclipse.jetty.ee10.servlet.security.Authenticator; +import org.eclipse.jetty.ee10.servlet.security.Authenticator.AuthConfiguration; +import org.eclipse.jetty.ee10.servlet.security.DefaultAuthenticatorFactory; +import org.eclipse.jetty.ee10.servlet.security.IdentityService; +import org.eclipse.jetty.ee10.servlet.security.LoginService; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.StringUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 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 +{ + private static final Logger LOG = LoggerFactory.getLogger(JaspiAuthenticatorFactory.class); + public static final String MESSAGE_LAYER = "HttpServlet"; + + private Subject _serviceSubject; + private String _serverName; + + /** + * @return the serviceSubject + */ + public Subject getServiceSubject() + { + return _serviceSubject; + } + + /** + * @param serviceSubject the serviceSubject to set + */ + public void setServiceSubject(Subject serviceSubject) + { + _serviceSubject = serviceSubject; + } + + /** + * @return the serverName + */ + public String getServerName() + { + return _serverName; + } + + /** + * @param serverName the serverName to set + */ + public void setServerName(String serverName) + { + _serverName = serverName; + } + + @Override + public Authenticator getAuthenticator(Server server, ServletContext context, AuthConfiguration configuration, IdentityService identityService, LoginService loginService) + { + 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 + * + * @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(ServletContext context, Server server) + { + if (_serverName != null) + return _serverName; + + String virtualServerName = context.getVirtualServerName(); + if (virtualServerName != null) + return virtualServerName; + + 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-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/JaspiMessageInfo.java b/jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/JaspiMessageInfo.java new file mode 100644 index 00000000000..5eca763f999 --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/JaspiMessageInfo.java @@ -0,0 +1,274 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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.ee10.servlet.ServletContextRequest; +import org.eclipse.jetty.ee10.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 MANDATORY_KEY = "jakarta.security.auth.message.MessagePolicy.isMandatory"; + public static final String AUTH_METHOD_KEY = "jakarta.servlet.http.authType"; + private Request _request; + private Response _response; + private Callback _callback; + private final MIMap _map; + + public JaspiMessageInfo(Request request, Response response, Callback callback, boolean isAuthMandatory) + { + _request = request; + _response = response; + _callback = callback; + //JASPI 3.8.1 + _map = new MIMap(isAuthMandatory); + } + + 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.getBaseRequest((ServletRequest)request); + } + + @Override + public void setResponseMessage(Object response) + { + if (!(response instanceof ServletResponse)) + throw new IllegalStateException("Not a ServletResponse"); + _response = ServletContextResponse.getBaseResponse((ServletResponse)response); + } + + public String getAuthMethod() + { + return _map.getAuthMethod(); + } + + public boolean isAuthMandatory() + { + return _map.isAuthMandatory(); + } + + //TODO this has bugs in the view implementations. Changing them will not affect the hardcoded values. + private static class MIMap implements Map + { + private final boolean isMandatory; + private String authMethod; + private Map delegate; + + private MIMap(boolean mandatory) + { + isMandatory = mandatory; + } + + @Override + public int size() + { + return (isMandatory ? 1 : 0) + + (authMethod == null ? 0 : 1) + + (delegate == null ? 0 : delegate.size()); + } + + @Override + public boolean isEmpty() + { + return !isMandatory && authMethod == null && (delegate == null || delegate.isEmpty()); + } + + @Override + public boolean containsKey(Object key) + { + if (MANDATORY_KEY.equals(key)) + return isMandatory; + if (AUTH_METHOD_KEY.equals(key)) + return authMethod != null; + return delegate != null && delegate.containsKey(key); + } + + @Override + public boolean containsValue(Object value) + { + if (isMandatory && "true".equals(value)) + return true; + if (authMethod == value || (authMethod != null && authMethod.equals(value))) + return true; + return delegate != null && delegate.containsValue(value); + } + + @Override + public Object get(Object key) + { + if (MANDATORY_KEY.equals(key)) + return isMandatory ? "true" : null; + if (AUTH_METHOD_KEY.equals(key)) + return authMethod; + if (delegate == null) + return null; + return delegate.get(key); + } + + @Override + public Object put(Object key, Object value) + { + if (MANDATORY_KEY.equals(key)) + { + throw new IllegalArgumentException("Mandatory not mutable"); + } + if (AUTH_METHOD_KEY.equals(key)) + { + String authMethod = this.authMethod; + this.authMethod = (String)value; + if (delegate != null) + delegate.put(AUTH_METHOD_KEY, value); + return authMethod; + } + + return getDelegate(true).put(key, value); + } + + @Override + public Object remove(Object key) + { + if (MANDATORY_KEY.equals(key)) + { + throw new IllegalArgumentException("Mandatory not mutable"); + } + if (AUTH_METHOD_KEY.equals(key)) + { + String authMethod = this.authMethod; + this.authMethod = null; + if (delegate != null) + delegate.remove(AUTH_METHOD_KEY); + return authMethod; + } + 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() + { + authMethod = 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 (isMandatory) + delegate.put(MANDATORY_KEY, "true"); + if (authMethod != null) + delegate.put(AUTH_METHOD_KEY, authMethod); + } + return delegate; + } + + boolean isAuthMandatory() + { + return isMandatory; + } + + String getAuthMethod() + { + return authMethod; + } + } +} diff --git a/jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/ServletCallbackHandler.java b/jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/ServletCallbackHandler.java new file mode 100644 index 00000000000..0aac03801fd --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/ServletCallbackHandler.java @@ -0,0 +1,134 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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.ee10.security.jaspi.callback.CredentialValidationCallback; +import org.eclipse.jetty.ee10.servlet.security.LoginService; +import org.eclipse.jetty.ee10.servlet.security.UserIdentity; +import org.eclipse.jetty.ee10.servlet.security.authentication.LoginCallback; +import org.eclipse.jetty.ee10.servlet.security.authentication.LoginCallbackImpl; + +/** + * 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 passwordValidationCallback = (PasswordValidationCallback)callback; + @SuppressWarnings("unused") + Subject subject = passwordValidationCallback.getSubject(); + + UserIdentity user = _loginService.login(passwordValidationCallback.getUsername(), passwordValidationCallback.getPassword(), 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 credentialValidationCallback = (CredentialValidationCallback)callback; + Subject subject = credentialValidationCallback.getSubject(); + LoginCallback loginCallback = new LoginCallbackImpl(subject, + credentialValidationCallback.getUsername(), + credentialValidationCallback.getCredential()); + + UserIdentity user = _loginService.login(credentialValidationCallback.getUsername(), credentialValidationCallback.getCredential(), 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-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/SimpleAuthConfig.java b/jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/SimpleAuthConfig.java new file mode 100644 index 00000000000..888534520b4 --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/SimpleAuthConfig.java @@ -0,0 +1,77 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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; + +/** + * @deprecated use {@link org.eclipse.jetty.security.jaspi.provider.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-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/callback/CredentialValidationCallback.java b/jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/callback/CredentialValidationCallback.java new file mode 100644 index 00000000000..b3aad8cd0b3 --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/callback/CredentialValidationCallback.java @@ -0,0 +1,70 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/callback/package-info.java b/jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/callback/package-info.java new file mode 100644 index 00000000000..7c102ac09e1 --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/callback/package-info.java @@ -0,0 +1,18 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 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.ee10.security.jaspi.callback; + diff --git a/jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/modules/BaseAuthModule.java b/jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/modules/BaseAuthModule.java new file mode 100644 index 00000000000..d3daedce0c6 --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/modules/BaseAuthModule.java @@ -0,0 +1,139 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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.ee10.security.jaspi.JaspiMessageInfo; +import org.eclipse.jetty.ee10.security.jaspi.callback.CredentialValidationCallback; +import org.eclipse.jetty.ee10.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.ee10.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; + } + + /** + * @param messageInfo message info to examine for mandatory flag + * @return whether authentication is mandatory or optional + */ + protected boolean isMandatory(MessageInfo messageInfo) + { + String mandatory = (String)messageInfo.getMap().get(JaspiMessageInfo.MANDATORY_KEY); + if (mandatory == null) + return false; + return Boolean.parseBoolean(mandatory); + } + + protected boolean login(Subject clientSubject, String credentials, String authMethod, 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), authMethod, messageInfo); + } + + protected boolean login(Subject clientSubject, String username, Credential credential, String authMethod, 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.AUTH_METHOD_KEY, authMethod); + } + return credValidationCallback.getResult(); + } +} diff --git a/jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/modules/BasicAuthenticationAuthModule.java b/jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/modules/BasicAuthenticationAuthModule.java new file mode 100644 index 00000000000..982aaac1573 --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/modules/BasicAuthenticationAuthModule.java @@ -0,0 +1,101 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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.ee10.security.jaspi.JaspiMessageInfo; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Response; +import org.eclipse.jetty.util.security.Constraint; +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.ee10.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, Constraint.__BASIC_AUTH, messageInfo)) + { + return AuthStatus.SUCCESS; + } + } + + if (!isMandatory(messageInfo)) + { + return AuthStatus.SUCCESS; + } + response.setHeader(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-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/modules/package-info.java b/jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/modules/package-info.java new file mode 100644 index 00000000000..f9ef24a66a2 --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/modules/package-info.java @@ -0,0 +1,18 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 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.ee10.security.jaspi.modules; + diff --git a/jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/package-info.java b/jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/package-info.java new file mode 100644 index 00000000000..88856cff337 --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/package-info.java @@ -0,0 +1,18 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 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.ee10.security.jaspi; + diff --git a/jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/provider/JaspiAuthConfigProvider.java b/jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/provider/JaspiAuthConfigProvider.java new file mode 100644 index 00000000000..9557aaa039c --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/provider/JaspiAuthConfigProvider.java @@ -0,0 +1,132 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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.ee10.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-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/provider/SimpleAuthConfig.java b/jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/provider/SimpleAuthConfig.java new file mode 100644 index 00000000000..ed835d1462f --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/provider/SimpleAuthConfig.java @@ -0,0 +1,84 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/provider/SimpleServerAuthContext.java b/jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/provider/SimpleServerAuthContext.java new file mode 100644 index 00000000000..db4a5a9a490 --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaspi/src/main/java/org/eclipse/jetty/ee10/security/jaspi/provider/SimpleServerAuthContext.java @@ -0,0 +1,59 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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-ee10/jetty-ee10-jaspi/src/main/resources/META-INF/services/org.eclipse.jetty.security.Authenticator$Factory b/jetty-ee10/jetty-ee10-jaspi/src/main/resources/META-INF/services/org.eclipse.jetty.security.Authenticator$Factory new file mode 100644 index 00000000000..03cf3bbc1a2 --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaspi/src/main/resources/META-INF/services/org.eclipse.jetty.security.Authenticator$Factory @@ -0,0 +1 @@ +org.eclipse.jetty.security.jaspi.JaspiAuthenticatorFactory diff --git a/jetty-ee10/jetty-ee10-jaspi/src/test/java/org/eclipse/jetty/ee10/security/jaspi/DefaultAuthConfigFactoryTest.java b/jetty-ee10/jetty-ee10-jaspi/src/test/java/org/eclipse/jetty/ee10/security/jaspi/DefaultAuthConfigFactoryTest.java new file mode 100644 index 00000000000..1a7d2740191 --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaspi/src/test/java/org/eclipse/jetty/ee10/security/jaspi/DefaultAuthConfigFactoryTest.java @@ -0,0 +1,115 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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.ee10.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.ee10.security.jaspi.provider.JaspiAuthConfigProvider"; + private final String appContext = "server /test"; + + private final Map serverAuthModuleProperties = Map.of("ServerAuthModule", + "org.eclipse.jetty.ee10.security.jaspi.modules.BasicAuthenticationAuthModule", "AppContextID", appContext, + "org.eclipse.jetty.ee10.security.jaspi.modules.RealmName", "TestRealm"); + + private final String serverAuthModuleClassName = "org.eclipse.jetty.ee10.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-ee10/jetty-ee10-jaspi/src/test/java/org/eclipse/jetty/ee10/security/jaspi/HttpHeaderAuthModule.java b/jetty-ee10/jetty-ee10-jaspi/src/test/java/org/eclipse/jetty/ee10/security/jaspi/HttpHeaderAuthModule.java new file mode 100644 index 00000000000..2eb2599b1b2 --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaspi/src/test/java/org/eclipse/jetty/ee10/security/jaspi/HttpHeaderAuthModule.java @@ -0,0 +1,118 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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-ee10/jetty-ee10-jaspi/src/test/java/org/eclipse/jetty/ee10/security/jaspi/JaspiTest.java b/jetty-ee10/jetty-ee10-jaspi/src/test/java/org/eclipse/jetty/ee10/security/jaspi/JaspiTest.java new file mode 100644 index 00000000000..c8e73df3f50 --- /dev/null +++ b/jetty-ee10/jetty-ee10-jaspi/src/test/java/org/eclipse/jetty/ee10/security/jaspi/JaspiTest.java @@ -0,0 +1,225 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.security.AbstractLoginService; +import org.eclipse.jetty.ee10.servlet.security.ConstraintMapping; +import org.eclipse.jetty.ee10.servlet.security.ConstraintSecurityHandler; +import org.eclipse.jetty.ee10.servlet.security.RolePrincipal; +import org.eclipse.jetty.ee10.servlet.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.Constraint; +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.ee10.security.jaspi.provider.JaspiAuthConfigProvider", + Map.of("ServerAuthModule", "org.eclipse.jetty.ee10.security.jaspi.modules.BasicAuthenticationAuthModule", + "AppContextID", "server /ctx", + "org.eclipse.jetty.ee10.security.jaspi.modules.RealmName", "TestRealm"), + "HttpServlet", "server /ctx", "a test provider"); + + factory.registerConfigProvider("org.eclipse.jetty.ee10.security.jaspi.provider.JaspiAuthConfigProvider", + Map.of("ServerAuthModule", "org.eclipse.jetty.ee10.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.setHandler(security); + security.setAuthenticatorFactory(jaspiAuthFactory); + // security.setAuthenticator(new BasicAuthenticator()); + + Constraint constraint = new Constraint("All", "users"); + constraint.setAuthenticate(true); + 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.setHandler(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/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/test HTTP/1.0\n" + "X-Forwarded-User: user\n\n"); + assertThat(response, startsWith("HTTP/1.1 200 OK")); + } + + public class TestServlet extends HttpServlet + { + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException + { + resp.setStatus(200); + resp.setContentType("text/plain"); + resp.getWriter().println("All OK"); + resp.getWriter().println("requestURI=" + req.getRequestURI()); + } + } +} diff --git a/jetty-ee10/jetty-ee10-jspc-maven-plugin/pom.xml b/jetty-ee10/jetty-ee10-jspc-maven-plugin/pom.xml new file mode 100644 index 00000000000..5207e5693ce --- /dev/null +++ b/jetty-ee10/jetty-ee10-jspc-maven-plugin/pom.xml @@ -0,0 +1,169 @@ + + + + org.eclipse.jetty.ee10 + jetty-ee10 + 12.0.0-SNAPSHOT + + 4.0.0 + jetty-ee10-jspc-maven-plugin + maven-plugin + EE10 :: Jetty :: Jetty JSPC Maven Plugin + + ${project.groupId}.jspc.plugin + + + + + org.apache.maven.plugins + maven-surefire-plugin + + true + + + + org.apache.maven.plugins + maven-plugin-plugin + + + exec-plugin-doc + process-classes + + descriptor + helpmojo + + + + + + org.jacoco + jacoco-maven-plugin + + true + + + + org.apache.maven.plugins + maven-invoker-plugin + + + integration-test + + install + integration-test + verify + + + + + true + true + + ${maven.surefire.plugin.version} + + + clean + + + + + + + + org.eclipse.jetty + jetty-util + + + org.apache.maven + maven-plugin-api + + + javax.annotation + javax.annotation-api + + + + + org.apache.maven.plugin-tools + maven-plugin-annotations + provided + + + org.apache.maven + maven-artifact + + + org.apache.maven.plugin-tools + maven-plugin-tools-api + + + junit + junit + + + + + org.eclipse.jetty.ee10 + jetty-ee10-apache-jsp + ${project.version} + + + org.eclipse.jetty.ee10 + jetty-ee10-glassfish-jstl + ${project.version} + + + org.apache.ant + ant + + + + + + org.apache.maven.plugins + maven-project-info-reports-plugin + + false + + + + + team + mailing-lists + ci-management + issue-management + licenses + scm + + + + + + org.apache.maven.plugins + maven-plugin-plugin + + + + + + eclipse-release + + + + org.apache.maven.plugins + maven-site-plugin + + + site-jar + + jar + + package + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-jspc-maven-plugin/src/it/package-root/invoker.properties b/jetty-ee10/jetty-ee10-jspc-maven-plugin/src/it/package-root/invoker.properties new file mode 100644 index 00000000000..df6cbf2d0bc --- /dev/null +++ b/jetty-ee10/jetty-ee10-jspc-maven-plugin/src/it/package-root/invoker.properties @@ -0,0 +1,2 @@ +invoker.goals = test -fae +invoker.debug = true diff --git a/jetty-ee10/jetty-ee10-jspc-maven-plugin/src/it/package-root/pom.xml b/jetty-ee10/jetty-ee10-jspc-maven-plugin/src/it/package-root/pom.xml new file mode 100644 index 00000000000..13f54f4be56 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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 + + EE10 :: Jetty :: Simple Jsp + + + UTF-8 + UTF-8 + 1.8 + @project.version@ + + + + + + org.eclipse.jetty.ee10 + jetty-ee10-jspc-maven-plugin + ${jetty.version} + + + + jspc + + compile + + + org.eclipse.jetty.test + + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-jspc-maven-plugin/src/it/package-root/postbuild.groovy b/jetty-ee10/jetty-ee10-jspc-maven-plugin/src/it/package-root/postbuild.groovy new file mode 100644 index 00000000000..4a73995f2a0 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-jspc-maven-plugin/src/it/package-root/src/main/webapp/foo.jsp b/jetty-ee10/jetty-ee10-jspc-maven-plugin/src/it/package-root/src/main/webapp/foo.jsp new file mode 100644 index 00000000000..fb73b0b0002 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-jspc-maven-plugin/src/it/settings.xml b/jetty-ee10/jetty-ee10-jspc-maven-plugin/src/it/settings.xml new file mode 100644 index 00000000000..d64bdb89034 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-jspc-maven-plugin/src/it/simple-jsp-fail/invoker.properties b/jetty-ee10/jetty-ee10-jspc-maven-plugin/src/it/simple-jsp-fail/invoker.properties new file mode 100644 index 00000000000..8a7cae8ef84 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-jspc-maven-plugin/src/it/simple-jsp-fail/pom.xml b/jetty-ee10/jetty-ee10-jspc-maven-plugin/src/it/simple-jsp-fail/pom.xml new file mode 100644 index 00000000000..50986e51178 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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 + + EE10 :: Jetty :: Simple Jsp Fail + + + UTF-8 + UTF-8 + 1.8 + 3.0.0 + @project.version@ + + + + + + org.eclipse.jetty.ee10 + jetty-ee10-jspc-maven-plugin + ${jetty.version} + + + + jspc + + compile + + src/main/jsp + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-jspc-maven-plugin/src/it/simple-jsp-fail/postbuild.groovy b/jetty-ee10/jetty-ee10-jspc-maven-plugin/src/it/simple-jsp-fail/postbuild.groovy new file mode 100644 index 00000000000..cdc73a7cef1 --- /dev/null +++ b/jetty-ee10/jetty-ee10-jspc-maven-plugin/src/it/simple-jsp-fail/postbuild.groovy @@ -0,0 +1 @@ +System.out.println( "running postbuild.groovy" ) diff --git a/jetty-ee10/jetty-ee10-jspc-maven-plugin/src/it/simple-jsp-fail/src/main/jsp/foo.jsp b/jetty-ee10/jetty-ee10-jspc-maven-plugin/src/it/simple-jsp-fail/src/main/jsp/foo.jsp new file mode 100644 index 00000000000..00b636ccd93 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-jspc-maven-plugin/src/it/simple-jsp/invoker.properties b/jetty-ee10/jetty-ee10-jspc-maven-plugin/src/it/simple-jsp/invoker.properties new file mode 100644 index 00000000000..df6cbf2d0bc --- /dev/null +++ b/jetty-ee10/jetty-ee10-jspc-maven-plugin/src/it/simple-jsp/invoker.properties @@ -0,0 +1,2 @@ +invoker.goals = test -fae +invoker.debug = true diff --git a/jetty-ee10/jetty-ee10-jspc-maven-plugin/src/it/simple-jsp/pom.xml b/jetty-ee10/jetty-ee10-jspc-maven-plugin/src/it/simple-jsp/pom.xml new file mode 100644 index 00000000000..41163846886 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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 + + EE10 :: Jetty :: Simple Jsp + + + UTF-8 + UTF-8 + 1.8 + @project.version@ + + + + + + org.eclipse.jetty.ee10 + jetty-ee10-jspc-maven-plugin + ${jetty.version} + + + + jspc + + compile + + + + + + + diff --git a/jetty-ee10/jetty-ee10-jspc-maven-plugin/src/it/simple-jsp/postbuild.groovy b/jetty-ee10/jetty-ee10-jspc-maven-plugin/src/it/simple-jsp/postbuild.groovy new file mode 100644 index 00000000000..03f44370c6a --- /dev/null +++ b/jetty-ee10/jetty-ee10-jspc-maven-plugin/src/it/simple-jsp/postbuild.groovy @@ -0,0 +1,18 @@ +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-ee10/jetty-ee10-jspc-maven-plugin/src/it/simple-jsp/src/main/webapp/foo.jsp b/jetty-ee10/jetty-ee10-jspc-maven-plugin/src/it/simple-jsp/src/main/webapp/foo.jsp new file mode 100644 index 00000000000..fb73b0b0002 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-jspc-maven-plugin/src/main/java/org/eclipse/jetty/ee10/jspc/plugin/JspcMojo.java b/jetty-ee10/jetty-ee10-jspc-maven-plugin/src/main/java/org/eclipse/jetty/ee10/jspc/plugin/JspcMojo.java new file mode 100644 index 00000000000..73f0949ef87 --- /dev/null +++ b/jetty-ee10/jetty-ee10-jspc-maven-plugin/src/main/java/org/eclipse/jetty/ee10/jspc/plugin/JspcMojo.java @@ -0,0 +1,627 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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; +import org.eclipse.jetty.util.resource.Resource; + +/** + * 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.equals("") ? 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.equals("")) + { + 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.equals("") ? 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(); + String classesDir = classesDirectory.getCanonicalPath(); + classesDir = classesDir + (classesDir.endsWith(File.pathSeparator) ? "" : File.separator); + urls.add(Resource.toURL(new File(classesDir))); + + if (getLog().isDebugEnabled()) + getLog().debug("Adding to classpath classes dir: " + classesDir); + + //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(Resource.toURL(artifact.getFile())); + } + } + 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-ee10/jetty-ee10-jspc-maven-plugin/src/main/java/org/eclipse/jetty/ee10/jspc/plugin/package-info.java b/jetty-ee10/jetty-ee10-jspc-maven-plugin/src/main/java/org/eclipse/jetty/ee10/jspc/plugin/package-info.java new file mode 100644 index 00000000000..b13e735da71 --- /dev/null +++ b/jetty-ee10/jetty-ee10-jspc-maven-plugin/src/main/java/org/eclipse/jetty/ee10/jspc/plugin/package-info.java @@ -0,0 +1,18 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 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.ee10.jspc.plugin; + diff --git a/jetty-ee10/jetty-ee10-jspc-maven-plugin/src/main/resources/META-INF/m2e/lifecycle-mapping-metadata.xml b/jetty-ee10/jetty-ee10-jspc-maven-plugin/src/main/resources/META-INF/m2e/lifecycle-mapping-metadata.xml new file mode 100644 index 00000000000..8f66ced4e44 --- /dev/null +++ b/jetty-ee10/jetty-ee10-jspc-maven-plugin/src/main/resources/META-INF/m2e/lifecycle-mapping-metadata.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + jspc + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-jspc-maven-plugin/src/site/markdown/index.md b/jetty-ee10/jetty-ee10-jspc-maven-plugin/src/site/markdown/index.md new file mode 100644 index 00000000000..639a5a3fbf8 --- /dev/null +++ b/jetty-ee10/jetty-ee10-jspc-maven-plugin/src/site/markdown/index.md @@ -0,0 +1,4 @@ +Eclipse Jetty Jspc Maven Plugin +======================== + +This plugin will help you pre-compile your jsps and works in conjunction with the Maven war plugin to put them inside an assembled war. diff --git a/jetty-ee10/jetty-ee10-jspc-maven-plugin/src/site/site.xml b/jetty-ee10/jetty-ee10-jspc-maven-plugin/src/site/site.xml new file mode 100644 index 00000000000..d37c6e5f9dc --- /dev/null +++ b/jetty-ee10/jetty-ee10-jspc-maven-plugin/src/site/site.xml @@ -0,0 +1,44 @@ + + + + + + ${project.name} + https://www.eclipse.org/jetty/images/jetty-logo-80x22.png + https://eclipse.org/jetty/ + + + https://www.eclipse.org/eclipse.org-common/themes/solstice/public/images/logo/eclipse-426x100.png + + + + org.apache.maven.skins + maven-fluido-skin + 1.7 + + + + + true + true + + ${project.url} + + + eclipse/jetty.project + right + gray + + + + + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-maven-plugin/README_INTEGRATION_TEST.md b/jetty-ee10/jetty-ee10-maven-plugin/README_INTEGRATION_TEST.md new file mode 100644 index 00000000000..43a97b9161a --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/README_INTEGRATION_TEST.md @@ -0,0 +1,26 @@ +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 the tests +-------------------- +As they can be long to run, the tests do not run per default. So to run them you must activate a profile using the command line argument: ```-Prun-its``` + +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-ee10/jetty-ee10-maven-plugin/pom.xml b/jetty-ee10/jetty-ee10-maven-plugin/pom.xml new file mode 100644 index 00000000000..cff807c14f5 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/pom.xml @@ -0,0 +1,416 @@ + + + org.eclipse.jetty.ee10 + jetty-ee10 + 12.0.0-SNAPSHOT + + 4.0.0 + jetty-ee10-maven-plugin + maven-plugin + EE10 :: Jetty :: Jetty Maven Plugin + Jetty maven plugins + + ${project.groupId}.maven.plugin + FREEBEER + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + test-reserve-ports + process-test-classes + + reserve-network-port + + + + test.stopPort + test.jettyPort + + + + + reserve-ports + pre-integration-test + + reserve-network-port + + + + jetty.stopPort + + + + + + + maven-surefire-plugin + + -Dstop.port=@{test.stopPort} -Djetty.port=@{test.jettyPort} + + **/IntegrationTest*.java + + + + + org.apache.maven.plugins + maven-plugin-plugin + + + exec-plugin-doc + generate-sources + + helpmojo + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + test-jar + + + + + + org.apache.maven.plugins + maven-invoker-plugin + + + integration-test + integration-test + + install + integration-test + verify + + + + + true + true + true + + 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} + ${jetty.servlet.api.version} + + src/it/settings.xml + + clean + + + + + org.jacoco + jacoco-maven-plugin + + true + + + + + + + org.apache.maven.shared + maven-artifact-transfer + ${maven-artifact-transfer.version} + + + org.apache.maven + maven-plugin-api + + + javax.annotation + javax.annotation-api + + + + + org.apache.maven + maven-artifact + + + org.apache.maven + maven-core + + + org.apache.maven.plugin-tools + maven-plugin-tools-api + + + org.apache.maven.plugin-tools + maven-plugin-annotations + provided + + + org.eclipse.jetty + jetty-util + + + org.eclipse.jetty.ee10 + jetty-ee10-webapp + + + jakarta.servlet + servlet-api + + + + + org.eclipse.jetty.ee10 + jetty-ee10-quickstart + + + org.eclipse.jetty.ee10 + jetty-ee10-jaas + + + org.eclipse.jetty.ee10 + jetty-ee10-plus + + + org.eclipse.jetty + jetty-jndi + + + org.eclipse.jetty + jetty-server + + + org.eclipse.jetty.ee10 + jetty-ee10-servlet + + + org.eclipse.jetty + jetty-client + + + org.eclipse.jetty + jetty-http + + + org.eclipse.jetty + jetty-io + + + org.eclipse.jetty + jetty-jmx + + + org.eclipse.jetty.ee10 + jetty-ee10-annotations + + + org.eclipse.jetty.ee10 + jetty-ee10-websocket-jakarta-server + + + org.eclipse.jetty.ee10 + jetty-ee10-websocket-jetty-server + + + org.eclipse.jetty.ee10 + jetty-ee10-apache-jsp + + + org.eclipse.jetty.ee10 + jetty-ee10-glassfish-jstl + + + org.slf4j + slf4j-api + + + jakarta.transaction + jakarta.transaction-api + compile + + + org.eclipse.jetty + jetty-slf4j-impl + test + + + + org.eclipse.jetty.toolchain + jetty-test-helper + test + + + + + + org.apache.maven.plugins + maven-project-info-reports-plugin + + + + team + mailing-lists + ci-management + issue-management + licenses + scm + + + + + + org.apache.maven.plugins + maven-plugin-plugin + + + + + + eclipse-release + + + + org.apache.maven.plugins + maven-site-plugin + + + site-jar + + jar + + package + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/it-parent-pom/invoker.properties b/jetty-ee10/jetty-ee10-maven-plugin/src/it/it-parent-pom/invoker.properties new file mode 100644 index 00000000000..521301a6d91 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-maven-plugin/src/it/it-parent-pom/pom.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/it-parent-pom/pom.xml new file mode 100644 index 00000000000..07095191db7 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/it-parent-pom/pom.xml @@ -0,0 +1,157 @@ + + + 4.0.0 + + org.eclipse.jetty.its + it-parent-pom + 0.0.1-SNAPSHOT + pom + + + @project.version@ + UTF-8 + + + + + + commons-io + commons-io + 2.7 + + + org.eclipse.jetty.toolchain + jetty-jakarta-servlet-api + @jetty.servlet.api.version@ + provided + + + org.apache.commons + commons-lang3 + 3.8.1 + + + org.eclipse.jetty.toolchain + jetty-perf-helper + @jetty.perf-helper.version@ + + + com.fasterxml.jackson.core + jackson-databind + @jackson-databind.version@ + + + org.slf4j + slf4j-api + @slf4j.version@ + + + org.slf4j + slf4j-simple + @slf4j.version@ + + + org.eclipse.jetty + jetty-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 + jetty-maven-plugin + @project.version@ + tests + test-jar + test + + + org.junit.jupiter + junit-jupiter-engine + @junit.version@ + test + + + + + + + + + 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 + jetty-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-ee10/jetty-ee10-maven-plugin/src/it/jetty-cdi-start-forked/invoker.properties b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-cdi-start-forked/invoker.properties new file mode 100644 index 00000000000..2fc6409821b --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-maven-plugin/src/it/jetty-cdi-start-forked/pom.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-cdi-start-forked/pom.xml new file mode 100644 index 00000000000..266eee0eb1d --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-cdi-start-forked/pom.xml @@ -0,0 +1,94 @@ + + + 4.0.0 + + + org.eclipse.jetty.its + it-parent-pom + 0.0.1-SNAPSHOT + + + org.eclipse.jetty.its.jetty-cdi-start-forked-mojo-it + jetty-weld-minimal + 1.0-SNAPSHOT + war + + + ${project.build.directory}/jetty-cdi-start-forked-port.txt + FORK + + + + + org.eclipse.jetty.toolchain + jetty-jakarta-servlet-api + provided + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.eclipse.jetty + jetty-client + test + + + org.eclipse.jetty + jetty-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:jetty-maven-plugin + + + + + org.eclipse.jetty + jetty-maven-plugin + + + 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-ee10/jetty-ee10-maven-plugin/src/it/jetty-cdi-start-forked/postbuild.groovy b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-cdi-start-forked/postbuild.groovy new file mode 100644 index 00000000000..de050eea4eb --- /dev/null +++ b/jetty-ee10/jetty-ee10-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.ee10.maven.plugin.it.IntegrationTestGetContent') +assert buildLog.text.contains( 'helloServlet') + diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-cdi-start-forked/src/main/java/test/Greeter.java b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-cdi-start-forked/src/main/java/test/Greeter.java new file mode 100644 index 00000000000..a95fbc5e5e9 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-cdi-start-forked/src/main/java/test/Greeter.java @@ -0,0 +1,37 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.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-ee10/jetty-ee10-maven-plugin/src/it/jetty-cdi-start-forked/src/main/jetty/jetty-context.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-cdi-start-forked/src/main/jetty/jetty-context.xml new file mode 100644 index 00000000000..effcf47f4cf --- /dev/null +++ b/jetty-ee10/jetty-ee10-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.ee10.servlet.ServletContextHandler + + + + + + diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-cdi-start-forked/src/main/jetty/jetty.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-cdi-start-forked/src/main/jetty/jetty.xml new file mode 100644 index 00000000000..e2575774e66 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/api/pom.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/api/pom.xml new file mode 100755 index 00000000000..da37bc1e3d6 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/api/pom.xml @@ -0,0 +1,11 @@ + + 4.0.0 + + + test.jetty-maven-plugin-provided-module-dep + parent + 1.0-SNAPSHOT + + api + + diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/api/src/main/java/test/Api.java b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/api/src/main/java/test/Api.java new file mode 100755 index 00000000000..be76dbb147e --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/api/src/main/java/test/Api.java @@ -0,0 +1,22 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.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-ee10/jetty-ee10-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/invoker.properties b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/invoker.properties new file mode 100644 index 00000000000..816c3f38def --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/pom.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/pom.xml new file mode 100755 index 00000000000..d7e74fcaa00 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/pom.xml @@ -0,0 +1,20 @@ + + 4.0.0 + + + org.eclipse.jetty.its + it-parent-pom + 0.0.1-SNAPSHOT + + + test.jetty-maven-plugin-provided-module-dep + parent + 1.0-SNAPSHOT + pom + + + api + web + + + diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/postbuild.groovy b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/postbuild.groovy new file mode 100644 index 00000000000..bfadcee89c4 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/web/pom.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/web/pom.xml new file mode 100755 index 00000000000..c4cfcc69cb9 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/web/pom.xml @@ -0,0 +1,62 @@ + + 4.0.0 + + + test.jetty-maven-plugin-provided-module-dep + parent + 1.0-SNAPSHOT + + web + war + + + EMBED + + + + + ${project.groupId} + api + ${project.version} + provided + + + org.eclipse.jetty.toolchain + jetty-jakarta-servlet-api + provided + + + + + + + org.apache.maven.plugins + maven-war-plugin + + false + + + + + + + org.eclipse.jetty + jetty-maven-plugin + + + start-jetty + pre-integration-test + + start + + + + ${basedir}/src/config/jetty.xml + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/web/src/config/jetty.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/web/src/config/jetty.xml new file mode 100644 index 00000000000..0a3a743a2c4 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/web/src/main/java/test/ClassLoadingTestingServletContextListener.java b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/web/src/main/java/test/ClassLoadingTestingServletContextListener.java new file mode 100755 index 00000000000..979ed869961 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-maven-plugin-provided-module-dep/web/src/main/java/test/ClassLoadingTestingServletContextListener.java @@ -0,0 +1,70 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.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-ee10/jetty-ee10-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyLibrary/pom.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyLibrary/pom.xml new file mode 100644 index 00000000000..553abd1a486 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyLibrary/pom.xml @@ -0,0 +1,21 @@ + + + + jetty-issue + org.mehdi + 1.0-SNAPSHOT + + 4.0.0 + + MyLibrary + + + + org.eclipse.jetty.toolchain + jetty-jakarta-servlet-api + provided + + + diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyLibrary/src/main/java/jettyissue/MyAnnotation.java b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyLibrary/src/main/java/jettyissue/MyAnnotation.java new file mode 100644 index 00000000000..1fda1251206 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyLibrary/src/main/java/jettyissue/MyAnnotation.java @@ -0,0 +1,24 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.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-ee10/jetty-ee10-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyLibrary/src/main/java/jettyissue/MyServletContainerInitializer.java b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyLibrary/src/main/java/jettyissue/MyServletContainerInitializer.java new file mode 100644 index 00000000000..f7c3f64a964 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyLibrary/src/main/java/jettyissue/MyServletContainerInitializer.java @@ -0,0 +1,28 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.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-ee10/jetty-ee10-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyLibrary/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-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-ee10/jetty-ee10-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/pom.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/pom.xml new file mode 100644 index 00000000000..5c2229498f7 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/pom.xml @@ -0,0 +1,66 @@ + + + + jetty-issue + org.mehdi + 1.0-SNAPSHOT + + 4.0.0 + + MyWebApp + jar + + + ${project.build.directory}/jetty-run-mojo.txt + + + + + org.eclipse.jetty.toolchain + jetty-jakarta-servlet-api + provided + + + org.mehdi + MyLibrary + + + + + + + + + org.eclipse.jetty + jetty-maven-plugin + + + start-jetty + test-compile + + start + + + + + jetty.port.file + ${jetty.port.file} + + + true + ${basedir}/src/config/jetty.xml + ${basedir}/src/config/context.xml + true + + jar + + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/src/config/context.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/src/config/context.xml new file mode 100644 index 00000000000..2f12f68b140 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/src/config/context.xml @@ -0,0 +1,7 @@ + + + + + /setbycontextxml + + diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/src/config/jetty.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/src/config/jetty.xml new file mode 100644 index 00000000000..d6cbe3174e1 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/src/main/java/jettyissue/NormalClass.java b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/src/main/java/jettyissue/NormalClass.java new file mode 100644 index 00000000000..28e22b597d4 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/src/main/java/jettyissue/NormalClass.java @@ -0,0 +1,19 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.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-ee10/jetty-ee10-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/MyWebApp/src/main/webapp/index.html b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-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-ee10/jetty-ee10-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/invoker.properties b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/invoker.properties new file mode 100644 index 00000000000..ac620b04a8b --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/invoker.properties @@ -0,0 +1 @@ +invoker.goals = test -e diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/pom.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/pom.xml new file mode 100644 index 00000000000..1380e37256e --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/pom.xml @@ -0,0 +1,32 @@ + + + 4.0.0 + + org.eclipse.jetty.its + it-parent-pom + 0.0.1-SNAPSHOT + + + + org.mehdi + jetty-issue + pom + 1.0-SNAPSHOT + + MyLibrary + MyWebApp + + + + + + org.mehdi + MyLibrary + ${project.version} + + + + + diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/postbuild.groovy b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-run-mojo-jar-scan-it/postbuild.groovy new file mode 100644 index 00000000000..caf7a534eab --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-distro-mojo-it/invoker.properties b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-distro-mojo-it/invoker.properties new file mode 100644 index 00000000000..94591882576 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-base/pom.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-base/pom.xml new file mode 100644 index 00000000000..7637515ed1a --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-base/pom.xml @@ -0,0 +1,41 @@ + + + 4.0.0 + + + org.eclipse.jetty.its.jetty-start-distro-mojo-it + jetty-simple-project + 0.0.1-SNAPSHOT + + + jetty-simple-base + jar + + EE10 :: Jetty :: Simple :: Base + + + + org.eclipse.jetty.toolchain + jetty-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-ee10/jetty-ee10-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-ee10/jetty-ee10-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..71bc1c29f91 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-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-ee10/jetty-ee10-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..520705db15b --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-base/src/main/resources/META-INF/web-fragment.xml b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-webapp/pom.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-webapp/pom.xml new file mode 100644 index 00000000000..fdafa6ef703 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-webapp/pom.xml @@ -0,0 +1,114 @@ + + + 4.0.0 + + + org.eclipse.jetty.its.jetty-start-distro-mojo-it + jetty-simple-project + 0.0.1-SNAPSHOT + + + jetty-simple-webapp + war + + EE10 :: Jetty :: Simple :: Webapp + + + ${project.build.directory}/jetty-start-distro-port.txt + @jetty.jvmArgs@ + EXTERNAL + + + + + + org.eclipse.jetty.its.jetty-start-distro-mojo-it + jetty-simple-base + + + + org.eclipse.jetty + jetty-servlet + provided + + + + org.eclipse.jetty + jetty-maven-plugin + tests + test-jar + test + + + + org.junit.jupiter + junit-jupiter-engine + 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:jetty-maven-plugin + + + + + org.eclipse.jetty + jetty-maven-plugin + + @jetty.stopPort@ + @jetty.stopKey@ + + + + start-jetty + process-test-classes + + start + + + ${basedir}/src/base + ${java.home}/bin/java + + true + ${jetty.port.file} + 0 + + jsp,jstl,testmod + --debug + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-webapp/src/base/etc/test-jetty.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-webapp/src/base/etc/test-jetty.xml new file mode 100644 index 00000000000..7fa53cc3a33 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-webapp/src/base/etc/test-jetty.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-webapp/src/base/modules/testmod.mod b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-webapp/src/base/modules/testmod.mod new file mode 100644 index 00000000000..83d7fc98732 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-webapp/src/base/modules/testmod.mod @@ -0,0 +1,12 @@ +# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html + +[description] +Enables test setup + +[depend] +http + + +[xml] +etc/test-jetty.xml + diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-distro-mojo-it/jetty-simple-webapp/src/main/webapp/WEB-INF/web.xml b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-distro-mojo-it/pom.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-distro-mojo-it/pom.xml new file mode 100644 index 00000000000..d856312a59e --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-distro-mojo-it/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + + + org.eclipse.jetty.its + it-parent-pom + 0.0.1-SNAPSHOT + + + org.eclipse.jetty.its.jetty-start-distro-mojo-it + jetty-simple-project + 0.0.1-SNAPSHOT + pom + + EE10 :: Jetty :: Simple + + + UTF-8 + UTF-8 + 1.8 + @project.version@ + + + + jetty-simple-base + jetty-simple-webapp + + + + + + org.eclipse.jetty.its.jetty-start-distro-mojo-it + jetty-simple-base + ${project.version} + + + + + diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-distro-mojo-it/postbuild.groovy b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-distro-mojo-it/postbuild.groovy new file mode 100644 index 00000000000..4de41c7c0da --- /dev/null +++ b/jetty-ee10/jetty-ee10-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.ee10.maven.plugin.it.IntegrationTestGetContent') +assert buildLog.text.contains( 'pingServlet ok') +assert buildLog.text.contains( 'helloServlet') diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-forked-mojo-it/invoker.properties b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-forked-mojo-it/invoker.properties new file mode 100644 index 00000000000..850d38aa127 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-forked-mojo-it/invoker.properties @@ -0,0 +1 @@ +invoker.goals = verify -fae diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-base/pom.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-base/pom.xml new file mode 100644 index 00000000000..0ae83d84847 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-base/pom.xml @@ -0,0 +1,41 @@ + + + 4.0.0 + + + org.eclipse.jetty.its.jetty-start-forked-mojo-it + jetty-simple-project + 0.0.1-SNAPSHOT + + + jetty-simple-base + jar + + EE10 :: Jetty :: Simple :: Base + + + + org.eclipse.jetty.toolchain + jetty-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-ee10/jetty-ee10-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-ee10/jetty-ee10-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..4be59f83943 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-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-ee10/jetty-ee10-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..065b4f103d2 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-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-ee10/jetty-ee10-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..0ff4caa2433 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-base/src/main/resources/META-INF/web-fragment.xml b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-webapp/pom.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-webapp/pom.xml new file mode 100644 index 00000000000..b29ac18701f --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-webapp/pom.xml @@ -0,0 +1,114 @@ + + + 4.0.0 + + + org.eclipse.jetty.its.jetty-start-forked-mojo-it + jetty-simple-project + 0.0.1-SNAPSHOT + + + jetty-simple-webapp + war + + EE10 :: Jetty :: Simple :: Webapp + + + @jetty.jvmArgs@ + ${project.build.directory}/jetty-start-forked-port.txt + FORK + + + + + + org.eclipse.jetty.its.jetty-start-forked-mojo-it + jetty-simple-base + + + + org.eclipse.jetty + jetty-servlet + provided + + + + org.eclipse.jetty + jetty-maven-plugin + tests + test-jar + test + + + + org.junit.jupiter + junit-jupiter-engine + 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:jetty-maven-plugin + + + + + org.eclipse.jetty + jetty-maven-plugin + + @jetty.stopPort@ + @jetty.stopKey@ + + + + start-jetty + process-test-classes + + start + + + + ${basedir}/src/config/jetty.xml + + ${jetty.jvmArgs} + + ${jetty.port.file} + + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-webapp/src/config/jetty.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-webapp/src/config/jetty.xml new file mode 100644 index 00000000000..445b5961914 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-webapp/src/main/webapp/WEB-INF/web.xml b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-forked-mojo-it/jetty-simple-webapp/src/main/webapp/jsp/bean1.jsp b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-forked-mojo-it/pom.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-forked-mojo-it/pom.xml new file mode 100644 index 00000000000..7cd19b2bff7 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-forked-mojo-it/pom.xml @@ -0,0 +1,41 @@ + + + 4.0.0 + + + org.eclipse.jetty.its + it-parent-pom + 0.0.1-SNAPSHOT + + + org.eclipse.jetty.its.jetty-start-forked-mojo-it + jetty-simple-project + 0.0.1-SNAPSHOT + pom + + EE10 :: Jetty :: Simple + + + UTF-8 + UTF-8 + 1.8 + 3.0.0 + @project.version@ + + + + jetty-simple-base + jetty-simple-webapp + + + + + + org.eclipse.jetty.its.jetty-start-forked-mojo-it + jetty-simple-base + ${project.version} + + + + + diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-forked-mojo-it/postbuild.groovy b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-forked-mojo-it/postbuild.groovy new file mode 100644 index 00000000000..f48dadea2b5 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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.ee10.maven.plugin.it.IntegrationTestGetContent') +assert buildLog.text.contains( 'pingServlet ok') +assert buildLog.text.contains( 'helloServlet') diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-client/pom.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-client/pom.xml new file mode 100644 index 00000000000..d1602c95c72 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-client/src/main/java/org/olamy/App.java b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-client/src/main/java/org/olamy/App.java new file mode 100644 index 00000000000..539741ce175 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-client/src/main/java/org/olamy/App.java @@ -0,0 +1,184 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-client/src/main/module.gwt.xml b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-client/src/main/module.gwt.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-server/pom.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-server/pom.xml new file mode 100644 index 00000000000..857e9c6ce97 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-server/pom.xml @@ -0,0 +1,113 @@ + + + 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 + + + org.eclipse.jetty.toolchain + jetty-jakarta-servlet-api + provided + + + + org.eclipse.jetty + jetty-client + 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 + jetty-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:jetty-maven-plugin + + + + + org.eclipse.jetty + jetty-maven-plugin + + + run + test-compile + + start + + + + ${jetty.port.file} + + ${basedir}/src/main/jettyconf/context.xml + + ${basedir}/src/config/jetty.xml + + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-server/src/config/jetty.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-server/src/config/jetty.xml new file mode 100644 index 00000000000..4e43b5305df --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-server/src/main/java/org/olamy/GreetingServiceImpl.java b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-server/src/main/java/org/olamy/GreetingServiceImpl.java new file mode 100644 index 00000000000..fe5dc791eee --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-server/src/main/java/org/olamy/GreetingServiceImpl.java @@ -0,0 +1,46 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-server/src/main/jettyconf/context.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-server/src/main/jettyconf/context.xml new file mode 100644 index 00000000000..d9fc72818a6 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-server/src/main/jettyconf/context.xml @@ -0,0 +1,8 @@ + + + + + org.eclipse.jetty.ee10.servlet.Default.useFileMappedBuffer + false + + diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-server/src/main/webapp/WEB-INF/web.xml b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-server/src/main/webapp/beer.css b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-server/src/main/webapp/favicon.ico b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-server/src/main/webapp/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..858a707523f540b16b6bd2cd79d8e870e5be524e GIT binary patch literal 1082 zcmZ`%ZETZO6uxD(Y;;{a>DsOByS{zfwcXaP?KZm7ZL*C*$bz2`oQXsMV+;XBQWYWr zYPJc6iP}IxBVpqQ{7{GsCSoK&zqdJ}*@R%GfJNp6J26{7Sl7o}lPdi1+&uT3oR8<6 zb8`vd(IXKPdb32wCqzaFF;Ykmn-B47&A#g@=I{@kviGeTm7B8>BFG2x znvr(r&4;vx+4o87i{n`_doHasp1I3#$0pRw!NdB>cUIZNPvu5=NEoSyFw&lXd~E5? zS2&j+jhmBLP#(r2HUtUx4djL|AuJoXD_G2F>pSGS3Nf=cL+sl+FyDB6W8y76rG3697TyP3Ylpb+NM$PRZ*1cFF~W& z_c>=hF}blXfooG4)OUVI>jEJ15J<^I_#@L;SQ(}}1t4Jj;FSmPh$?_$?GQ>00cg}8 zd~0bwnf@aU3||GLRo_FT8HJF$i~>~{o7_KQ#dBj2RfQp^GpyNkmBykFaUn=+!{Bs> z;--dUzuulo!{bSzu`Y$ymJ}Ks@oaTN3@XzVtl980R&`y&lP~;)Uhgju>LTbjMc8pct~ zjzVrc2X%SR2_D%Z=92?1MXC3-D)sRr?T+plr+GCRYKM}}`q-?)GCNaWoygjZXJO#( zz-FF+$r72@>iZK4;hXjc|01>d&p*@F-`-dewVT(Su^P93Z87*F4$DU~%k7mcFN5b%ULc)s3%Ms*f*gs@bMx qKWr^5TrSP^$>&dkkc&AiBV_MF>di6z%a+gjPv4-iKWScu=Kl?kF{Cg6 literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-server/src/main/webapp/index.html b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-shared/pom.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-shared/pom.xml new file mode 100644 index 00000000000..054ba242e95 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-shared/src/main/java/org/olamy/FieldVerifier.java b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-shared/src/main/java/org/olamy/FieldVerifier.java new file mode 100644 index 00000000000..171c2ae60b3 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-shared/src/main/java/org/olamy/FieldVerifier.java @@ -0,0 +1,58 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-shared/src/main/java/org/olamy/GreetingResponse.java b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-shared/src/main/java/org/olamy/GreetingResponse.java new file mode 100644 index 00000000000..3ab940bc152 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-shared/src/main/java/org/olamy/GreetingResponse.java @@ -0,0 +1,54 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-shared/src/main/java/org/olamy/GreetingService.java b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-shared/src/main/java/org/olamy/GreetingService.java new file mode 100644 index 00000000000..cf736faa3b5 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-shared/src/main/java/org/olamy/GreetingService.java @@ -0,0 +1,26 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-shared/src/main/java/org/olamy/GreetingServiceAsync.java b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-shared/src/main/java/org/olamy/GreetingServiceAsync.java new file mode 100644 index 00000000000..501a2c67416 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/beer-shared/src/main/java/org/olamy/GreetingServiceAsync.java @@ -0,0 +1,25 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/invoker.properties b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/invoker.properties new file mode 100644 index 00000000000..26652d0273a --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/pom.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/pom.xml new file mode 100644 index 00000000000..9d310560684 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/pom.xml @@ -0,0 +1,82 @@ + + + 4.0.0 + + + org.eclipse.jetty.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 + jetty-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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/postbuild.groovy b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-gwt-it/postbuild.groovy new file mode 100644 index 00000000000..f9182cc833d --- /dev/null +++ b/jetty-ee10/jetty-ee10-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.ee10.maven.plugin.it.IntegrationTestGetContent') +assert buildLog.text.contains( 'contentCheck') diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-it/invoker.properties b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-it/invoker.properties new file mode 100644 index 00000000000..e0222d4d54e --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-it/invoker.properties @@ -0,0 +1 @@ +invoker.goals = test \ No newline at end of file diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-base/pom.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-base/pom.xml new file mode 100644 index 00000000000..aca1dceab18 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-base/pom.xml @@ -0,0 +1,39 @@ + + + 4.0.0 + + + org.eclipse.jetty.its.jetty-start-mojo-it + jetty-simple-project + 0.0.1-SNAPSHOT + + + jetty-simple-base + jar + + EE10 :: Jetty :: Simple :: Base + + + + org.eclipse.jetty.toolchain + jetty-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-ee10/jetty-ee10-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-ee10/jetty-ee10-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..d341ce67e55 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-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-ee10/jetty-ee10-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..115aa91ec09 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-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-ee10/jetty-ee10-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..5abc7bd6033 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-base/src/main/resources/META-INF/web-fragment.xml b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-webapp/pom.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-webapp/pom.xml new file mode 100644 index 00000000000..9fffa761d3f --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-webapp/pom.xml @@ -0,0 +1,110 @@ + + + 4.0.0 + + + org.eclipse.jetty.its.jetty-start-mojo-it + jetty-simple-project + 0.0.1-SNAPSHOT + + + jetty-simple-webapp + war + + EE10 :: Jetty :: Simple :: Webapp + + + ${project.build.directory}/jetty-start-port.txt + + + + + + org.eclipse.jetty.its.jetty-start-mojo-it + jetty-simple-base + + + + org.eclipse.jetty + jetty-servlet + provided + + + + org.eclipse.jetty + jetty-maven-plugin + tests + test-jar + test + + + + org.junit.jupiter + junit-jupiter-engine + 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:jetty-maven-plugin + + + + + org.eclipse.jetty + jetty-maven-plugin + + + start-jetty + test-compile + + start + + + ${basedir}/src/config/context.xml + + ${jetty.port.file} + EMBED + + + ${basedir}/src/config/jetty.xml + + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-webapp/src/config/context.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-webapp/src/config/context.xml new file mode 100644 index 00000000000..2f12f68b140 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-webapp/src/config/context.xml @@ -0,0 +1,7 @@ + + + + + /setbycontextxml + + diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-webapp/src/config/jetty.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-webapp/src/config/jetty.xml new file mode 100644 index 00000000000..445b5961914 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-webapp/src/main/webapp/WEB-INF/web.xml b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-it/jetty-simple-webapp/src/main/webapp/jsp/bean1.jsp b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-it/pom.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-it/pom.xml new file mode 100644 index 00000000000..e6c69758252 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-it/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + + + org.eclipse.jetty.its + it-parent-pom + 0.0.1-SNAPSHOT + + + org.eclipse.jetty.its.jetty-start-mojo-it + jetty-simple-project + 0.0.1-SNAPSHOT + pom + + EE10 :: Jetty :: Simple + + + UTF-8 + UTF-8 + 1.8 + @project.version@ + + + + jetty-simple-base + jetty-simple-webapp + + + + + + org.eclipse.jetty.its.jetty-start-mojo-it + jetty-simple-base + ${project.version} + + + + + diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-it/postbuild.groovy b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-it/postbuild.groovy new file mode 100644 index 00000000000..298c50f6092 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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.ee10.maven.plugin.it.IntegrationTestGetContent') +assert buildLog.text.contains( 'pingServlet ok') +assert buildLog.text.contains( 'helloServlet') diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/common/pom.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/common/pom.xml new file mode 100644 index 00000000000..8ac4e43a071 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/common/pom.xml @@ -0,0 +1,11 @@ + + + 4.0.0 + + test.jetty-start-mojo-multi-module-single-war-it + jetty-multi-module-project + 1.0-SNAPSHOT + + common + + diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/common/src/main/java/mca/common/CommonService.java b/jetty-ee10/jetty-ee10-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..52d941dd4ed --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/invoker.properties b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/invoker.properties @@ -0,0 +1 @@ +invoker.goals = test diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/module/module-api/pom.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/module/module-api/pom.xml new file mode 100644 index 00000000000..02b7893f051 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-start-mojo-multi-module-single-war-it + module + 1.0-SNAPSHOT + + module-api + + diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/module/module-api/src/main/java/mca/module/ModuleApi.java b/jetty-ee10/jetty-ee10-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..45d0203e3c6 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/module/module-impl/pom.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/module/module-impl/pom.xml new file mode 100644 index 00000000000..4cca9837cd9 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-start-mojo-multi-module-single-war-it + module + 1.0-SNAPSHOT + + module-impl + + + + test.jetty-start-mojo-multi-module-single-war-it + module-api + + + diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/module/module-impl/src/main/java/mca/module/ModuleImpl.java b/jetty-ee10/jetty-ee10-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..01b4659ee08 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/module/pom.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/module/pom.xml new file mode 100644 index 00000000000..fe53f4f610a --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/module/pom.xml @@ -0,0 +1,24 @@ + + + 4.0.0 + + test.jetty-start-mojo-multi-module-single-war-it + jetty-multi-module-project + 1.0-SNAPSHOT + + module + pom + + + module-api + module-impl + + + + + test.jetty-start-mojo-multi-module-single-war-it + common + + + + diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/pom.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/pom.xml new file mode 100644 index 00000000000..c63978a0126 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/pom.xml @@ -0,0 +1,51 @@ + + + 4.0.0 + + + org.eclipse.jetty.its + it-parent-pom + 0.0.1-SNAPSHOT + + + test.jetty-start-mojo-multi-module-single-war-it + jetty-multi-module-project + 1.0-SNAPSHOT + pom + + EE10 :: Jetty :: multi-module project + + + common + module + webapp-war + + + + UTF-8 + UTF-8 + 1.8 + @project.version@ + + + + + + test.jetty-start-mojo-multi-module-single-war-it + common + ${project.version} + + + test.jetty-start-mojo-multi-module-single-war-it + module-api + ${project.version} + + + test.jetty-start-mojo-multi-module-single-war-it + module-impl + ${project.version} + + + + + diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/postbuild.groovy b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/postbuild.groovy new file mode 100644 index 00000000000..0d0d1f5d90b --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/postbuild.groovy @@ -0,0 +1,36 @@ +/* + * 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( '/org/eclipse/jetty/toolchain/jetty-jakarta-servlet-api/'+servletApiVersion+'/jetty-jakarta-servlet-api-'+servletApiVersion+'.jar!/jakarta/servlet/ServletContextListener.class << (1b)' ) + +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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/webapp-war/pom.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/webapp-war/pom.xml new file mode 100644 index 00000000000..e27abbf8e31 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/webapp-war/pom.xml @@ -0,0 +1,60 @@ + + + 4.0.0 + + test.jetty-start-mojo-multi-module-single-war-it + jetty-multi-module-project + 1.0-SNAPSHOT + + webapp-war + war + + + + org.eclipse.jetty + jetty-servlet + + + org.eclipse.jetty.toolchain + jetty-jakarta-servlet-api + @jetty.servlet.api.version@ + provided + + + test.jetty-start-mojo-multi-module-single-war-it + module-impl + + + + + ${project.build.directory}/jetty-start-mojo.txt + EMBED + + + + + + org.eclipse.jetty + jetty-maven-plugin + + + start-jetty + test-compile + + start + + + + ${jetty.port.file} + + + ${basedir}/src/config/jetty.xml + + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/webapp-war/src/config/jetty.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/webapp-war/src/config/jetty.xml new file mode 100644 index 00000000000..445b5961914 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/webapp-war/src/main/java/mca/webapp/WebAppServletListener.java b/jetty-ee10/jetty-ee10-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..f08a736b773 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-mojo-multi-module-single-war-it/webapp-war/src/main/webapp/WEB-INF/web.xml b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-distro-mojo-it/invoker.properties b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-distro-mojo-it/invoker.properties new file mode 100644 index 00000000000..d2583001859 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-distro-mojo-it/invoker.properties @@ -0,0 +1,2 @@ +invoker.goals = verify -V -X +#test-compile failsafe:integration-test diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-distro-mojo-it/jetty-simple-base/pom.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-distro-mojo-it/jetty-simple-base/pom.xml new file mode 100644 index 00000000000..ad15ee747c3 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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.its.jetty-start-war-distro-mojo-it + jetty-simple-project + 0.0.1-SNAPSHOT + + + jetty-simple-base + jar + + EE10 :: Jetty :: Simple :: Base + + + + org.eclipse.jetty.toolchain + jetty-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-ee10/jetty-ee10-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-ee10/jetty-ee10-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..a094ed308c5 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-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-ee10/jetty-ee10-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..0a9a86f09b8 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-distro-mojo-it/jetty-simple-base/src/main/resources/META-INF/web-fragment.xml b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-distro-mojo-it/jetty-simple-webapp/pom.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-distro-mojo-it/jetty-simple-webapp/pom.xml new file mode 100644 index 00000000000..78cd5b4204c --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-distro-mojo-it/jetty-simple-webapp/pom.xml @@ -0,0 +1,130 @@ + + + 4.0.0 + + + org.eclipse.jetty.its.jetty-start-war-distro-mojo-it + jetty-simple-project + 0.0.1-SNAPSHOT + + + jetty-simple-webapp + war + + EE10 :: Jetty :: Simple :: Webapp + + + ${project.build.directory}/jetty-start-war-distro-port.txt + @jetty.jvmArgs@ + HOME + + + + + org.eclipse.jetty.its.jetty-start-war-distro-mojo-it + jetty-simple-base + + + org.eclipse.jetty + jetty-servlet + provided + + + org.eclipse.jetty + jetty-maven-plugin + tests + test-jar + test + + + org.junit.jupiter + junit-jupiter-engine + 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:jetty-maven-plugin + + + + + integration-test + + integration-test + + + + verify + + verify + + + + + + org.eclipse.jetty + jetty-maven-plugin + + + start-jetty + pre-integration-test + + start-war + + + @jetty.stopPort@ + @jetty.stopKey@ + + + + ${basedir}/src/base + ${java.home}/bin/java + + true + ${jetty.port.file} + 0 + + jsp,jstl,testmod + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-distro-mojo-it/jetty-simple-webapp/src/base/etc/test-jetty.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-distro-mojo-it/jetty-simple-webapp/src/base/etc/test-jetty.xml new file mode 100644 index 00000000000..6ba1c7f8e1f --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-distro-mojo-it/jetty-simple-webapp/src/base/etc/test-jetty.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-distro-mojo-it/jetty-simple-webapp/src/base/modules/testmod.mod b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-distro-mojo-it/jetty-simple-webapp/src/base/modules/testmod.mod new file mode 100644 index 00000000000..f88fe0a7f5d --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-distro-mojo-it/jetty-simple-webapp/src/base/modules/testmod.mod @@ -0,0 +1,11 @@ +# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html + +[description] +Enables test setup + +[depend] +http + + +[xml] +etc/test-jetty.xml diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-distro-mojo-it/jetty-simple-webapp/src/main/webapp/WEB-INF/web.xml b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-distro-mojo-it/pom.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-distro-mojo-it/pom.xml new file mode 100644 index 00000000000..db9455addaf --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-distro-mojo-it/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + + + org.eclipse.jetty.its + it-parent-pom + 0.0.1-SNAPSHOT + + + org.eclipse.jetty.its.jetty-start-war-distro-mojo-it + jetty-simple-project + 0.0.1-SNAPSHOT + pom + + EE10 :: Jetty :: Simple + + + UTF-8 + UTF-8 + 1.8 + @project.version@ + + + + jetty-simple-base + jetty-simple-webapp + + + + + + org.eclipse.jetty.its.jetty-start-war-distro-mojo-it + jetty-simple-base + ${project.version} + + + + + diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-distro-mojo-it/postbuild.groovy b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-distro-mojo-it/postbuild.groovy new file mode 100644 index 00000000000..f906562b212 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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.ee10.maven.plugin.it.IntegrationTestGetContent') +assert buildLog.text.contains( 'pingServlet ok') +assert buildLog.text.contains( 'helloServlet') diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-forked-mojo-it/invoker.properties b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-forked-mojo-it/invoker.properties new file mode 100644 index 00000000000..d2583001859 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-forked-mojo-it/invoker.properties @@ -0,0 +1,2 @@ +invoker.goals = verify -V -X +#test-compile failsafe:integration-test diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-forked-mojo-it/jetty-simple-base/pom.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-forked-mojo-it/jetty-simple-base/pom.xml new file mode 100644 index 00000000000..e76347d8c89 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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.its.jetty-start-war-forked-mojo-it + jetty-simple-project + 0.0.1-SNAPSHOT + + + jetty-simple-base + jar + + EE10 :: Jetty :: Simple :: Base + + + + org.eclipse.jetty.toolchain + jetty-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-ee10/jetty-ee10-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-ee10/jetty-ee10-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..6fb01a27c64 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-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-ee10/jetty-ee10-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..de5674f7ab1 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-forked-mojo-it/jetty-simple-base/src/main/resources/META-INF/web-fragment.xml b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-forked-mojo-it/jetty-simple-webapp/pom.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-forked-mojo-it/jetty-simple-webapp/pom.xml new file mode 100644 index 00000000000..8902b2d434c --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-forked-mojo-it/jetty-simple-webapp/pom.xml @@ -0,0 +1,128 @@ + + + 4.0.0 + + + org.eclipse.jetty.its.jetty-start-war-forked-mojo-it + jetty-simple-project + 0.0.1-SNAPSHOT + ../pom.xml + + + jetty-simple-webapp + war + + EE10 :: Jetty :: Simple :: Webapp + + + ${project.build.directory}/jetty-start-war-forked-port.txt + FORK + + + + + org.eclipse.jetty.its.jetty-start-war-forked-mojo-it + jetty-simple-base + + + org.eclipse.jetty + jetty-servlet + provided + + + org.eclipse.jetty + jetty-maven-plugin + tests + test-jar + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + + + + + + + org.apache.maven.plugins + maven-war-plugin + + false + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + true + + + + org.eclipse.jetty + jetty-maven-plugin + + + 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:jetty-maven-plugin + + + **/*TestGetContent* + + + + + integration-test + + integration-test + + + + verify + + verify + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-forked-mojo-it/jetty-simple-webapp/src/config/jetty.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-forked-mojo-it/jetty-simple-webapp/src/config/jetty.xml new file mode 100644 index 00000000000..445b5961914 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-forked-mojo-it/jetty-simple-webapp/src/main/webapp/WEB-INF/web.xml b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-forked-mojo-it/pom.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-forked-mojo-it/pom.xml new file mode 100644 index 00000000000..691ae81eeeb --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-forked-mojo-it/pom.xml @@ -0,0 +1,41 @@ + + + 4.0.0 + + + org.eclipse.jetty.its + it-parent-pom + 0.0.1-SNAPSHOT + ../it-parent-pom/pom.xml + + + org.eclipse.jetty.its.jetty-start-war-forked-mojo-it + jetty-simple-project + 0.0.1-SNAPSHOT + pom + + EE10 :: Jetty :: Simple + + + UTF-8 + UTF-8 + 1.8 + @project.version@ + + + + jetty-simple-base + jetty-simple-webapp + + + + + + org.eclipse.jetty.its.jetty-start-war-forked-mojo-it + jetty-simple-base + ${project.version} + + + + + diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-forked-mojo-it/postbuild.groovy b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-forked-mojo-it/postbuild.groovy new file mode 100644 index 00000000000..95f638f96de --- /dev/null +++ b/jetty-ee10/jetty-ee10-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.ee10.maven.plugin.it.IntegrationTestGetContent') +assert outputLog.text.contains( 'pingServlet ok') +assert outputLog.text.contains( 'helloServlet') diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-mojo-it/invoker.properties b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-mojo-it/invoker.properties new file mode 100644 index 00000000000..816c3f38def --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-mojo-it/pom.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-mojo-it/pom.xml new file mode 100644 index 00000000000..93350a818d3 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-mojo-it/pom.xml @@ -0,0 +1,139 @@ + + + 4.0.0 + + + org.eclipse.jetty.its + it-parent-pom + 0.0.1-SNAPSHOT + + + org.eclipse.jetty.its.jetty-start-war-mojo-it + jetty-simple-project + 0.0.1-SNAPSHOT + pom + + EE10 :: Jetty :: Simple start war mojo test + + + ${project.build.directory}/jetty-start-war-port.txt + + + + + org.eclipse.jetty + jetty-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:jetty-maven-plugin + + + **/*TestGetContent* + + + + + integration-test + + integration-test + + + + verify + + verify + + + + + + org.eclipse.jetty + jetty-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-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-mojo-it/postbuild.groovy b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-mojo-it/postbuild.groovy new file mode 100644 index 00000000000..f9182cc833d --- /dev/null +++ b/jetty-ee10/jetty-ee10-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.ee10.maven.plugin.it.IntegrationTestGetContent') +assert buildLog.text.contains( 'contentCheck') diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-mojo-it/src/config/jetty.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-start-war-mojo-it/src/config/jetty.xml new file mode 100644 index 00000000000..445b5961914 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-maven-plugin/src/it/settings.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/settings.xml new file mode 100644 index 00000000000..d64bdb89034 --- /dev/null +++ b/jetty-ee10/jetty-ee10-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-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/AbstractForker.java b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/AbstractForker.java new file mode 100644 index 00000000000..e88fb11f893 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/AbstractForker.java @@ -0,0 +1,250 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.maven.plugin; + +import java.io.File; +import java.util.List; +import java.util.Map; + +import org.eclipse.jetty.util.component.AbstractLifeCycle; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * AbstractForker + * + * Base class for forking jetty. + */ +public abstract class AbstractForker extends AbstractLifeCycle +{ + private static final Logger LOG = LoggerFactory.getLogger(AbstractForker.class); + + protected Map env; + + protected String jvmArgs; + + protected boolean exitVm; + + protected boolean stopAtShutdown; + + protected List jettyXmlFiles; + + protected Map jettyProperties; + + protected int stopPort; + + protected String stopKey; + + protected File jettyOutputFile; + + protected boolean waitForChild; + + protected int maxChildStartChecks = 10; //check up to 10 times for child to start + + protected long maxChildStartCheckMs = 200; //wait 200ms between checks + + protected File tokenFile; + + protected File workDir; + + protected Map systemProperties; + + protected abstract ProcessBuilder createCommand(); + + protected abstract void redeployWebApp() throws Exception; + + public File getWorkDir() + { + return workDir; + } + + public void setWorkDir(File workDir) + { + this.workDir = workDir; + } + + /** + * @return the systemProperties + */ + public Map getSystemProperties() + { + return systemProperties; + } + + /** + * @param systemProperties the systemProperties to set + */ + public void setSystemProperties(Map systemProperties) + { + this.systemProperties = systemProperties; + } + + public Map getEnv() + { + return env; + } + + public void setEnv(Map env) + { + this.env = env; + } + + public String getJvmArgs() + { + return jvmArgs; + } + + public void setJvmArgs(String jvmArgs) + { + this.jvmArgs = jvmArgs; + } + + public boolean isExitVm() + { + return exitVm; + } + + public void setExitVm(boolean exitVm) + { + this.exitVm = exitVm; + } + + public boolean isStopAtShutdown() + { + return stopAtShutdown; + } + + public void setStopAtShutdown(boolean stopAtShutdown) + { + this.stopAtShutdown = stopAtShutdown; + } + + public List getJettyXmlFiles() + { + return jettyXmlFiles; + } + + public void setJettyXmlFiles(List jettyXmlFiles) + { + this.jettyXmlFiles = jettyXmlFiles; + } + + public Map getJettyProperties() + { + return jettyProperties; + } + + public void setJettyProperties(Map jettyProperties) + { + this.jettyProperties = jettyProperties; + } + + public int getStopPort() + { + return stopPort; + } + + public void setStopPort(int stopPort) + { + this.stopPort = stopPort; + } + + public String getStopKey() + { + return stopKey; + } + + public void setStopKey(String stopKey) + { + this.stopKey = stopKey; + } + + public File getJettyOutputFile() + { + return jettyOutputFile; + } + + public void setJettyOutputFile(File jettyOutputFile) + { + this.jettyOutputFile = jettyOutputFile; + } + + public boolean isWaitForChild() + { + return waitForChild; + } + + public void setWaitForChild(boolean waitForChild) + { + this.waitForChild = waitForChild; + } + + public int getMaxChildtartChecks() + { + return maxChildStartChecks; + } + + public void setMaxChildStartChecks(int maxChildStartChecks) + { + this.maxChildStartChecks = maxChildStartChecks; + } + + public long getMaxChildStartCheckMs() + { + return maxChildStartCheckMs; + } + + public void setMaxChildStartCheckMs(long maxChildStartCheckMs) + { + this.maxChildStartCheckMs = maxChildStartCheckMs; + } + + public File getTokenFile() + { + return tokenFile; + } + + public void setTokenFile(File tokenFile) + { + this.tokenFile = tokenFile; + } + + public void doStart() + throws Exception + { + super.doStart(); + + //Create the command to fork + ProcessBuilder command = createCommand(); + Process process = command.start(); + + if (waitForChild) + { + //keep executing until the child dies + process.waitFor(); + } + else + { + //just wait until the child has started successfully + int attempts = maxChildStartChecks; + while (!tokenFile.exists() && attempts > 0) + { + Thread.sleep(maxChildStartCheckMs); + --attempts; + } + if (attempts <= 0) + LOG.info("Couldn't verify success of child startup"); + } + } +} diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/AbstractUnassembledWebAppMojo.java b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/AbstractUnassembledWebAppMojo.java new file mode 100644 index 00000000000..5e56a8cd85d --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/AbstractUnassembledWebAppMojo.java @@ -0,0 +1,243 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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.plugins.annotations.Parameter; +import org.eclipse.jetty.util.resource.Resource; + +/** + * 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(); + configureUnassembledWebApp(); + } + + /** + * Configure a webapp that has not been assembled into a war. + * + * @throws Exception + */ + 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 = Resource.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 + + //TODO: needs WebAppContext.setBaseResource sorted out + //webApp.setBaseResource(originalBaseResource); + + if (webApp.getWar() == null) + webApp.setWar(originalBaseResource.getURI().toURL().toExternalForm()); + + 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 = Resource.newResource(webXml); + if (r.exists() && !r.isDirectory()) + { + webApp.setDescriptor(r.toString()); + } + } + + //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) + { + Resource r = webApp.getBaseResource().addPath("WEB-INF/web.xml"); + if (r.exists() && !r.isDirectory()) + { + webApp.setDescriptor(r.toString()); + } + } + + //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)) + { + 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); + + 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-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/AbstractWebAppMojo.java b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/AbstractWebAppMojo.java new file mode 100644 index 00000000000..51c861cbbf0 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/AbstractWebAppMojo.java @@ -0,0 +1,878 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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.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.apache.maven.shared.transfer.artifact.resolve.ArtifactResolver; +import org.codehaus.plexus.util.StringUtils; +import org.eclipse.jetty.ee10.maven.plugin.utils.MavenProjectHelper; +import org.eclipse.jetty.ee10.servlet.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.ee10.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 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 ArtifactResolver artifactResolver; + + /** + * 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, artifactResolver, 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(mavenProjectHelper.resolveArtifact(JETTY_HOME_GROUPID, JETTY_HOME_ARTIFACTID, plugin.getVersion(), "zip")); + + jetty.version = plugin.getVersion(); + jetty.setJettyHome(jettyHome); + jetty.setJettyBase(jettyBase); + jetty.setBaseDir(target); + + return jetty; + } + + /** + * Used by subclasses. + * @throws MojoExecutionException + */ + 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 + */ + 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 + */ + 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 + */ + 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 + */ + 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 Exception + */ + 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 + */ + 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-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/ConsoleReader.java b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/ConsoleReader.java new file mode 100644 index 00000000000..5bafa316944 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/ConsoleReader.java @@ -0,0 +1,66 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.maven.plugin; + +import java.io.Console; +import java.util.EventListener; +import java.util.HashSet; +import java.util.Set; + +/** + * ConsoleReader + * + * Reads lines from the System console and supplies them + * to ConsoleReader.Listeners. + */ +public class ConsoleReader implements Runnable +{ + public interface Listener extends EventListener + { + public void consoleEvent(String line); + } + + public Set listeners = new HashSet<>(); + + public void addListener(ConsoleReader.Listener listener) + { + listeners.add(listener); + } + + public void removeListener(ConsoleReader.Listener listener) + { + listeners.remove(listener); + } + + public void run() + { + Console console = System.console(); + if (console == null) + return; + + String line = ""; + while (true && line != null) + { + line = console.readLine("%nHit to redeploy:%n%n"); + if (line != null) + signalEvent(line); + } + } + + private void signalEvent(String line) + { + for (ConsoleReader.Listener l:listeners) + l.consoleEvent(line); + } +} diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/JettyEffectiveWebXml.java b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/JettyEffectiveWebXml.java new file mode 100644 index 00000000000..3f0313b817f --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/JettyEffectiveWebXml.java @@ -0,0 +1,87 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.maven.plugin; + +import java.io.File; + +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; + +/** + * 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 + { + //Use a nominated war file for which to generate the effective web.xml, or + //if that is not set, try to use the details of the current project's + //unassembled webapp + super.configureWebApp(); + if (StringUtil.isBlank(webApp.getWar())) + super.configureUnassembledWebApp(); + } + + /** + * Override so we can call the parent's method in a different order. + */ + @Override + protected void configureUnassembledWebApp() throws Exception + { + } + + @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, webApp); + generator.generate(); + } + catch (Exception e) + { + throw new MojoExecutionException("Error generating effective web xml", e); + } + } +} diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/JettyEmbedder.java b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/JettyEmbedder.java new file mode 100644 index 00000000000..06f4cada218 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/JettyEmbedder.java @@ -0,0 +1,320 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.maven.plugin; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import org.eclipse.jetty.ee10.quickstart.QuickStartConfiguration; +import org.eclipse.jetty.ee10.quickstart.QuickStartConfiguration.Mode; +import org.eclipse.jetty.ee10.servlet.security.LoginService; +import org.eclipse.jetty.server.RequestLog; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ShutdownMonitor; +import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.util.component.AbstractLifeCycle; +import org.eclipse.jetty.util.resource.Resource; + +/** + * JettyEmbedded + * + * Starts jetty within the current process. + */ +public class JettyEmbedder extends AbstractLifeCycle +{ + protected List contextHandlers; + protected List loginServices; + protected RequestLog requestLog; + protected MavenServerConnector httpConnector; + protected Server server; + protected MavenWebAppContext webApp; + protected boolean exitVm; + protected boolean stopAtShutdown; + protected List jettyXmlFiles; + protected Map jettyProperties; + protected ShutdownMonitor shutdownMonitor; + protected int stopPort; + protected String stopKey; + private String contextXml; + private Properties webAppProperties; + + public List getContextHandlers() + { + return contextHandlers; + } + + public void setContextHandlers(List contextHandlers) + { + if (contextHandlers == null) + this.contextHandlers = null; + else + this.contextHandlers = new ArrayList<>(contextHandlers); + } + + public List getLoginServices() + { + return loginServices; + } + + public void setLoginServices(List loginServices) + { + if (loginServices == null) + this.loginServices = null; + else + this.loginServices = new ArrayList<>(loginServices); + } + + public RequestLog getRequestLog() + { + return requestLog; + } + + public void setRequestLog(RequestLog requestLog) + { + this.requestLog = requestLog; + } + + public MavenServerConnector getHttpConnector() + { + return httpConnector; + } + + public void setHttpConnector(MavenServerConnector httpConnector) + { + this.httpConnector = httpConnector; + } + + public Server getServer() + { + return server; + } + + public void setServer(Server server) + { + this.server = server; + } + + public MavenWebAppContext getWebApp() + { + return webApp; + } + + public boolean isExitVm() + { + return exitVm; + } + + public void setExitVm(boolean exitVm) + { + this.exitVm = exitVm; + } + + public boolean isStopAtShutdown() + { + return stopAtShutdown; + } + + public void setStopAtShutdown(boolean stopAtShutdown) + { + this.stopAtShutdown = stopAtShutdown; + } + + public List getJettyXmlFiles() + { + return jettyXmlFiles; + } + + public void setJettyXmlFiles(List jettyXmlFiles) + { + this.jettyXmlFiles = jettyXmlFiles; + } + + public Map getJettyProperties() + { + return jettyProperties; + } + + public void setJettyProperties(Map jettyProperties) + { + this.jettyProperties = jettyProperties; + } + + public ShutdownMonitor getShutdownMonitor() + { + return shutdownMonitor; + } + + public void setShutdownMonitor(ShutdownMonitor shutdownMonitor) + { + this.shutdownMonitor = shutdownMonitor; + } + + public int getStopPort() + { + return stopPort; + } + + public void setStopPort(int stopPort) + { + this.stopPort = stopPort; + } + + public String getStopKey() + { + return stopKey; + } + + public void setStopKey(String stopKey) + { + this.stopKey = stopKey; + } + + public void setWebApp(MavenWebAppContext app) throws Exception + { + webApp = app; + } + + public void setWebAppProperties(Properties props) + { + if (webAppProperties != null) + webAppProperties.clear(); + + if (props != null) + { + if (webAppProperties == null) + webAppProperties = new Properties(); + + webAppProperties.putAll(props); + } + } + + public String getContextXml() + { + return contextXml; + } + + public void setContextXml(String contextXml) + { + this.contextXml = contextXml; + } + + public void doStart() throws Exception + { + super.doStart(); + + Resource.setDefaultUseCaches(false); + + configure(); + configureShutdownMonitor(); + server.start(); + } + + protected void redeployWebApp() throws Exception + { + if (!webApp.isStopped()) + webApp.stop(); + + //regenerate config properties + applyWebAppProperties(); + + webApp.start(); + } + + protected void join() throws InterruptedException + { + server.join(); + } + + /** + * Configure the server and the webapp + * @throws Exception + */ + private void configure() throws Exception + { + /* Configure the server */ + //apply any configs from jetty.xml files first + Server tmp = ServerSupport.applyXmlConfigurations(server, jettyXmlFiles, jettyProperties); + if (server == null) + server = tmp; + + if (server == null) + server = new Server(); + + server.setStopAtShutdown(stopAtShutdown); + + //ensure there's a connector + if (httpConnector != null) + httpConnector.setServer(server); + + ServerSupport.configureConnectors(server, httpConnector, jettyProperties); + + //set up handler structure + ServerSupport.configureHandlers(server, contextHandlers, requestLog); + + //Set up list of default Configurations to apply to a webapp + ServerSupport.configureDefaultConfigurationClasses(server); + + // set up security realms + ServerSupport.configureLoginServices(server, loginServices); + + /* Configure the webapp */ + if (webApp == null) + webApp = new MavenWebAppContext(); + + applyWebAppProperties(); + + //If there is a quickstart file, then quickstart the webapp. + if (webApp.getTempDirectory() != null) + { + File qs = new File(webApp.getTempDirectory(), "quickstart-web.xml"); + if (qs.exists() && qs.isFile()) + { + webApp.setAttribute(QuickStartConfiguration.QUICKSTART_WEB_XML, Resource.newResource(qs)); + webApp.addConfiguration(new MavenQuickStartConfiguration()); + webApp.setAttribute(QuickStartConfiguration.MODE, Mode.QUICKSTART); + } + + } + + //add the webapp to the server + ServerSupport.addWebApplication(server, webApp); + } + + private void applyWebAppProperties() throws Exception + { + //apply properties to the webapp if there are any + if (contextXml != null) + { + if (webAppProperties == null) + webAppProperties = new Properties(); + + webAppProperties.put("context.xml", contextXml); + } + WebAppPropertyConverter.fromProperties(webApp, webAppProperties, server, jettyProperties); + } + + private void configureShutdownMonitor() + { + if (stopPort > 0 && stopKey != null) + { + ShutdownMonitor monitor = ShutdownMonitor.getInstance(); + monitor.setPort(stopPort); + monitor.setKey(stopKey); + monitor.setExitVm(exitVm); + } + } +} diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/JettyForkedChild.java b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/JettyForkedChild.java new file mode 100644 index 00000000000..85d1dcf3746 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/JettyForkedChild.java @@ -0,0 +1,218 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.maven.plugin; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Properties; + +import org.eclipse.jetty.util.PathWatcher; +import org.eclipse.jetty.util.PathWatcher.PathWatchEvent; +import org.eclipse.jetty.util.StringUtil; +import org.eclipse.jetty.util.component.AbstractLifeCycle; +import org.eclipse.jetty.util.resource.Resource; +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 AbstractLifeCycle +{ + private static final Logger LOG = LoggerFactory.getLogger(JettyForkedChild.class); + + protected JettyEmbedder jetty; + protected File tokenFile; + protected PathWatcher scanner; + protected File webAppPropsFile; + + /** + * @param args arguments that were passed to main + * @throws Exception + */ + public JettyForkedChild(String[] args) + throws Exception + { + jetty = new JettyEmbedder(); + configure(args); + } + + /** + * Based on the args passed to the program, configure jetty. + * + * @param args args that were passed to the program. + * @throws Exception + */ + public void configure(String[] args) + throws Exception + { + Map jettyProperties = new HashMap<>(); + + for (int i = 0; i < args.length; i++) + { + //--stop-port + if ("--stop-port".equals(args[i])) + { + jetty.setStopPort(Integer.parseInt(args[++i])); + continue; + } + + //--stop-key + if ("--stop-key".equals(args[i])) + { + jetty.setStopKey(args[++i]); + continue; + } + + //--jettyXml + if ("--jetty-xml".equals(args[i])) + { + List jettyXmls = new ArrayList<>(); + String[] names = StringUtil.csvSplit(args[++i]); + for (int j = 0; names != null && j < names.length; j++) + { + jettyXmls.add(new File(names[j].trim())); + } + jetty.setJettyXmlFiles(jettyXmls); + continue; + } + //--webprops + if ("--webprops".equals(args[i])) + { + webAppPropsFile = new File(args[++i].trim()); + jetty.setWebAppProperties(loadWebAppProps()); + continue; + } + + //--token + if ("--token".equals(args[i])) + { + tokenFile = new File(args[++i].trim()); + continue; + } + + if ("--scan".equals(args[i])) + { + scanner = new PathWatcher(); + scanner.setNotifyExistingOnStart(false); + scanner.addListener(new PathWatcher.EventListListener() + { + @Override + public void onPathWatchEvents(List events) + { + if (!Objects.isNull(scanner)) + { + try + { + scanner.stop(); + if (!Objects.isNull(jetty.getWebApp())) + { + //stop the webapp + jetty.getWebApp().stop(); + //reload the props + jetty.setWebAppProperties(loadWebAppProps()); + jetty.setWebApp(jetty.getWebApp()); + //restart the webapp + jetty.redeployWebApp(); + + //restart the scanner + scanner.start(); + } + } + catch (Exception e) + { + LOG.warn("Error restarting webapp", e); + } + } + } + }); + + if (!Objects.isNull(webAppPropsFile)) + scanner.watch(webAppPropsFile.toPath()); + } + + //assume everything else is a jetty property to be passed in + String[] tmp = args[i].trim().split("="); + if (tmp.length == 2) + { + jettyProperties.put(tmp[0], tmp[1]); + } + } + + jetty.setJettyProperties(jettyProperties); + jetty.setExitVm(true); + } + + /** + * Load properties from a file describing the webapp if one is + * present. + * + * @return file contents as properties + * @throws FileNotFoundException + * @throws IOException + */ + private Properties loadWebAppProps() throws FileNotFoundException, IOException + { + Properties props = new Properties(); + if (Objects.nonNull(webAppPropsFile)) + props.load(new FileInputStream(webAppPropsFile)); + return props; + } + + /** + * Start a jetty instance and webapp. This thread will + * wait until jetty exits. + */ + public void doStart() + throws Exception + { + super.doStart(); + + //Start the embedded jetty instance + jetty.start(); + + //touch file to signify start of jetty + Resource r = Resource.newResource(tokenFile); + r.getFile().createNewFile(); + + //Start a watcher on a file that will change if the + //webapp is regenerated; stop the webapp, apply the + //properties and restart it. + if (scanner != null) + scanner.start(); + + //wait for jetty to finish + jetty.join(); + } + + 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-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/JettyForker.java b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/JettyForker.java new file mode 100644 index 00000000000..e2630621453 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/JettyForker.java @@ -0,0 +1,279 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.maven.plugin; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.eclipse.jetty.server.Server; + +/** + * JettyForker + * + * Uses quickstart to generate a webapp and forks a process to run it. + */ +public class JettyForker extends AbstractForker +{ + protected File forkWebXml; + protected Server server; + protected MavenWebAppContext webApp; + protected String containerClassPath; + protected File webAppPropsFile; + protected String contextXml; + protected boolean scan; + QuickStartGenerator generator; + + /** + * @return the scan + */ + public boolean isScan() + { + return scan; + } + + /** + * @param scan if true, the forked child will scan for changes + */ + public void setScan(boolean scan) + { + this.scan = scan; + } + + public File getWebAppPropsFile() + { + return webAppPropsFile; + } + + public void setWebAppPropsFile(File webAppPropsFile) + { + this.webAppPropsFile = webAppPropsFile; + } + + public File getForkWebXml() + { + return forkWebXml; + } + + public void setForkWebXml(File forkWebXml) + { + this.forkWebXml = forkWebXml; + } + + public String getContextXml() + { + return contextXml; + } + + public void setContextXml(String contextXml) + { + this.contextXml = contextXml; + } + + public String getContainerClassPath() + { + return containerClassPath; + } + + public void setContainerClassPath(String containerClassPath) + { + this.containerClassPath = containerClassPath; + } + + public void setWebApp(MavenWebAppContext app) + { + webApp = app; + } + + public Server getServer() + { + return server; + } + + public void setServer(Server server) + { + this.server = server; + } + + @Override + public void doStart() + throws Exception + { + //Run the webapp to create the quickstart file and properties file + generator = new QuickStartGenerator(forkWebXml, webApp); + generator.setContextXml(contextXml); + generator.setWebAppPropsFile(webAppPropsFile); + generator.setServer(server); + generator.generate(); + + super.doStart(); + } + + protected void redeployWebApp() + throws Exception + { + //regenerating the quickstart will be noticed by the JettyForkedChild process + //which will redeploy the webapp + generator.generate(); + } + + public ProcessBuilder createCommand() + { + List cmd = new ArrayList(); + cmd.add(getJavaBin()); + + if (jvmArgs != null) + { + String[] args = jvmArgs.split(" "); + for (int i = 0; args != null && i < args.length; i++) + { + if (args[i] != null && !"".equals(args[i])) + cmd.add(args[i].trim()); + } + } + + if (systemProperties != null) + { + for (Map.Entry e:systemProperties.entrySet()) + { + cmd.add("-D" + e.getKey() + "=" + e.getValue()); + } + } + + if (containerClassPath != null && containerClassPath.length() > 0) + { + cmd.add("-cp"); + cmd.add(containerClassPath); + } + + cmd.add(JettyForkedChild.class.getCanonicalName()); + + if (stopPort > 0 && stopKey != null) + { + cmd.add("--stop-port"); + cmd.add(Integer.toString(stopPort)); + cmd.add("--stop-key"); + cmd.add(stopKey); + } + if (jettyXmlFiles != null) + { + cmd.add("--jetty-xml"); + StringBuilder tmp = new StringBuilder(); + for (File jettyXml:jettyXmlFiles) + { + if (tmp.length() != 0) + tmp.append(","); + tmp.append(jettyXml.getAbsolutePath()); + } + cmd.add(tmp.toString()); + } + + cmd.add("--webprops"); + cmd.add(webAppPropsFile.getAbsolutePath()); + + cmd.add("--token"); + cmd.add(tokenFile.getAbsolutePath()); + + if (scan) + { + cmd.add("--scan"); + } + + if (jettyProperties != null) + { + for (Map.Entry e:jettyProperties.entrySet()) + { + cmd.add(e.getKey() + "=" + e.getValue()); + } + } + + ProcessBuilder command = new ProcessBuilder(cmd); + command.directory(workDir); + + if (PluginLog.getLog().isDebugEnabled()) + PluginLog.getLog().debug("Forked cli:" + command.command()); + + PluginLog.getLog().info("Forked process starting"); + + //set up extra environment vars if there are any + if (env != null && !env.isEmpty()) + command.environment().putAll(env); + + if (waitForChild) + { + command.inheritIO(); + } + else + { + command.redirectOutput(jettyOutputFile); + command.redirectErrorStream(true); + } + return command; + } + + /** + * @return the location of the java binary + */ + private String getJavaBin() + { + String[] javaexes = new String[]{"java", "java.exe"}; + + File javaHomeDir = new File(System.getProperty("java.home")); + for (String javaexe : javaexes) + { + File javabin = new File(javaHomeDir, fileSeparators("bin/" + javaexe)); + if (javabin.exists() && javabin.isFile()) + { + return javabin.getAbsolutePath(); + } + } + + return "java"; + } + + public static String fileSeparators(String path) + { + StringBuilder ret = new StringBuilder(); + for (char c : path.toCharArray()) + { + if ((c == '/') || (c == '\\')) + { + ret.append(File.separatorChar); + } + else + { + ret.append(c); + } + } + return ret.toString(); + } + + public static String pathSeparators(String path) + { + StringBuilder ret = new StringBuilder(); + for (char c : path.toCharArray()) + { + if ((c == ',') || (c == ':')) + { + ret.append(File.pathSeparatorChar); + } + else + { + ret.append(c); + } + } + return ret.toString(); + } +} diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/JettyHomeForker.java b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/JettyHomeForker.java new file mode 100644 index 00000000000..b1c48772ed6 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/JettyHomeForker.java @@ -0,0 +1,420 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.maven.plugin; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.FileSystems; +import java.nio.file.FileVisitOption; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.EnumSet; +import java.util.List; +import java.util.Map; + +import org.eclipse.jetty.util.IO; +import org.eclipse.jetty.util.StringUtil; +import org.eclipse.jetty.util.TypeUtil; +import org.eclipse.jetty.util.resource.JarResource; +import org.eclipse.jetty.util.resource.Resource; + +/** + * JettyHomeBaseForker + * + * Unpacks a jetty-home and configures it with a base that allows it + * to run an unassembled webapp. + */ +public class JettyHomeForker extends AbstractForker +{ + protected MavenWebAppContext webApp; + + protected String contextXml; + + /** + * Location of existing jetty home directory + */ + protected File jettyHome; + + /** + * Zip of jetty-home + */ + protected File jettyHomeZip; + + /** + * Location of existing jetty base directory + */ + protected File jettyBase; + + protected File baseDir; + + /** + * Optional list of other modules to + * activate. + */ + protected String[] modules; + + /* + * Optional jetty commands + */ + protected String jettyOptions; + + protected List libExtJarFiles; + protected Path modulesPath; + protected Path etcPath; + protected Path libPath; + protected Path webappPath; + protected Path mavenLibPath; + protected String version; + + public void setJettyOptions(String jettyOptions) + { + this.jettyOptions = jettyOptions; + } + + public String getJettyOptions() + { + return jettyOptions; + } + + public List getLibExtJarFiles() + { + return libExtJarFiles; + } + + public void setLibExtJarFiles(List libExtJarFiles) + { + this.libExtJarFiles = libExtJarFiles; + } + + public File getJettyHome() + { + return jettyHome; + } + + public void setJettyHome(File jettyHome) + { + this.jettyHome = jettyHome; + } + + public File getJettyBase() + { + return jettyBase; + } + + public void setJettyBase(File jettyBase) + { + this.jettyBase = jettyBase; + } + + public String[] getModules() + { + return modules; + } + + public void setModules(String[] modules) + { + this.modules = modules; + } + + public String getContextXmlFile() + { + return contextXml; + } + + public void setContextXml(String contextXml) + { + this.contextXml = contextXml; + } + + public File getJettyHomeZip() + { + return jettyHomeZip; + } + + public void setJettyHomeZip(File jettyHomeZip) + { + this.jettyHomeZip = jettyHomeZip; + } + + public MavenWebAppContext getWebApp() + { + return webApp; + } + + public void setWebApp(MavenWebAppContext webApp) + { + this.webApp = webApp; + } + + public File getBaseDir() + { + return baseDir; + } + + public void setBaseDir(File baseDir) + { + this.baseDir = baseDir; + } + + @Override + protected ProcessBuilder createCommand() + { + List cmd = new ArrayList<>(); + cmd.add("java"); + + //add any args to the jvm + if (StringUtil.isNotBlank(jvmArgs)) + { + Arrays.stream(jvmArgs.split(" ")).filter(a -> StringUtil.isNotBlank(a)).forEach((a) -> cmd.add(a.trim())); + } + + cmd.add("-jar"); + cmd.add(new File(jettyHome, "start.jar").getAbsolutePath()); + + if (systemProperties != null) + { + for (Map.Entry e : systemProperties.entrySet()) + { + cmd.add("-D" + e.getKey() + "=" + e.getValue()); + } + } + + cmd.add("-DSTOP.PORT=" + stopPort); + if (stopKey != null) + cmd.add("-DSTOP.KEY=" + stopKey); + + //set up enabled jetty modules + StringBuilder tmp = new StringBuilder(); + tmp.append("--module="); + tmp.append("server,http,webapp,deploy"); + if (modules != null) + { + for (String m : modules) + { + if (tmp.indexOf(m) < 0) + tmp.append("," + m); + } + } + + if (libExtJarFiles != null && !libExtJarFiles.isEmpty() && tmp.indexOf("ext") < 0) + tmp.append(",ext"); + tmp.append(",maven"); + cmd.add(tmp.toString()); + + //put any other jetty options onto the command line + if (StringUtil.isNotBlank(jettyOptions)) + { + Arrays.stream(jettyOptions.split(" ")).filter(a -> StringUtil.isNotBlank(a)).forEach((a) -> cmd.add(a.trim())); + } + + //put any jetty properties onto the command line + if (jettyProperties != null) + { + for (Map.Entry e : jettyProperties.entrySet()) + { + cmd.add(e.getKey() + "=" + e.getValue()); + } + } + + //existence of this file signals process started + cmd.add("jetty.token.file=" + tokenFile.getAbsolutePath().toString()); + + ProcessBuilder builder = new ProcessBuilder(cmd); + builder.directory(workDir); + + PluginLog.getLog().info("Home process starting"); + + //set up extra environment vars if there are any + if (!env.isEmpty()) + builder.environment().putAll(env); + + if (waitForChild) + builder.inheritIO(); + else + { + builder.redirectOutput(jettyOutputFile); + builder.redirectErrorStream(true); + } + return builder; + } + + @Override + public void doStart() throws Exception + { + //set up a jetty-home + configureJettyHome(); + + if (jettyHome == null || !jettyHome.exists()) + throw new IllegalStateException("No jetty home"); + + //set up a jetty-base + configureJettyBase(); + + //convert the webapp to properties + generateWebAppPropertiesFile(); + + super.doStart(); + } + + protected void redeployWebApp() + throws Exception + { + generateWebAppPropertiesFile(); + webappPath.resolve("maven.xml").toFile().setLastModified(System.currentTimeMillis()); + } + + private void generateWebAppPropertiesFile() + throws Exception + { + WebAppPropertyConverter.toProperties(webApp, etcPath.resolve("maven.props").toFile(), contextXml); + } + + /** + * Create or configure a jetty base. + */ + private void configureJettyBase() throws Exception + { + if (jettyBase != null && !jettyBase.exists()) + throw new IllegalStateException(jettyBase.getAbsolutePath() + " does not exist"); + + File targetJettyBase = new File(baseDir, "jetty-base"); + Path targetBasePath = targetJettyBase.toPath(); + if (Files.exists(targetBasePath)) + IO.delete(targetJettyBase); + + targetJettyBase.mkdirs(); + + //jetty-base will be the working directory for the forked command + workDir = targetJettyBase; + + //if there is an existing jetty base, copy parts of it + if (jettyBase != null) + { + Path jettyBasePath = jettyBase.toPath(); + + final File contextXmlFile = (contextXml == null ? null : FileSystems.getDefault().getPath(contextXml).toFile()); + + //copy the existing jetty base + Files.walkFileTree(jettyBasePath, EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE, + new SimpleFileVisitor() + { + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException + { + Path targetDir = targetBasePath.resolve(jettyBasePath.relativize(dir)); + try + { + Files.copy(dir, targetDir); + } + catch (FileAlreadyExistsException e) + { + if (!Files.isDirectory(targetDir)) //ignore attempt to recreate dir + throw e; + } + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException + { + if (contextXmlFile != null && Files.isSameFile(contextXmlFile.toPath(), file)) + return FileVisitResult.CONTINUE; //skip copying the context xml file + Files.copy(file, targetBasePath.resolve(jettyBasePath.relativize(file))); + return FileVisitResult.CONTINUE; + } + }); + } + + //make the jetty base structure + modulesPath = Files.createDirectories(targetBasePath.resolve("modules")); + etcPath = Files.createDirectories(targetBasePath.resolve("etc")); + libPath = Files.createDirectories(targetBasePath.resolve("lib")); + webappPath = Files.createDirectories(targetBasePath.resolve("webapps")); + mavenLibPath = Files.createDirectories(libPath.resolve("maven")); + + //copy in the jetty-maven-plugin jar + URI thisJar = TypeUtil.getLocationOfClass(this.getClass()); + if (thisJar == null) + throw new IllegalStateException("Can't find jar for jetty-maven-plugin"); + + try (InputStream jarStream = thisJar.toURL().openStream(); + FileOutputStream fileStream = new FileOutputStream(mavenLibPath.resolve("plugin.jar").toFile())) + { + IO.copy(jarStream, fileStream); + } + + //copy in the maven.xml webapp file + try (InputStream mavenXmlStream = getClass().getClassLoader().getResourceAsStream("maven.xml"); + FileOutputStream fileStream = new FileOutputStream(webappPath.resolve("maven.xml").toFile())) + { + IO.copy(mavenXmlStream, fileStream); + } + + //copy in the maven.mod file + try (InputStream mavenModStream = getClass().getClassLoader().getResourceAsStream("maven.mod"); + FileOutputStream fileStream = new FileOutputStream(modulesPath.resolve("maven.mod").toFile())) + { + IO.copy(mavenModStream, fileStream); + } + + //copy in the jetty-maven.xml file + try (InputStream jettyMavenStream = getClass().getClassLoader().getResourceAsStream("jetty-maven.xml"); + FileOutputStream fileStream = new FileOutputStream(etcPath.resolve("jetty-maven.xml").toFile())) + { + IO.copy(jettyMavenStream, fileStream); + } + + //if there were plugin dependencies, copy them into lib/ext + if (libExtJarFiles != null && !libExtJarFiles.isEmpty()) + { + Path libExtPath = Files.createDirectories(libPath.resolve("ext")); + for (File f : libExtJarFiles) + { + try (InputStream jarStream = new FileInputStream(f); + FileOutputStream fileStream = new FileOutputStream(libExtPath.resolve(f.getName()).toFile())) + { + IO.copy(jarStream, fileStream); + } + } + } + } + + private void configureJettyHome() + throws Exception + { + if (jettyHome == null && jettyHomeZip == null) + throw new IllegalStateException("No jettyHome"); + + if (baseDir == null) + throw new IllegalStateException("No baseDir"); + + if (jettyHome == null) + { + JarResource res = (JarResource)JarResource.newJarResource(Resource.newResource(jettyHomeZip)); + res.copyTo(baseDir); + //zip will unpack to target/jetty-home- + jettyHome = new File(baseDir, "jetty-home-" + version); + } + } +} diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/JettyRunMojo.java b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/JettyRunMojo.java new file mode 100644 index 00000000000..350ff19d4a2 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/JettyRunMojo.java @@ -0,0 +1,417 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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.ee10.webapp.WebAppContext; +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 + forker.setScan(true); //have the forked child notice changes to the webapp + //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 = Resource.newResource(webApp.getDescriptor()); + scanner.addFile(r.getFile().toPath()); + } + + 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 + */ + 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.getWebApp().stop(); + 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-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/JettyRunWarMojo.java b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/JettyRunWarMojo.java new file mode 100644 index 00000000000..54533af55d2 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/JettyRunWarMojo.java @@ -0,0 +1,294 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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.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.getWebApp().stop(); + 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-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/JettyStartMojo.java b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/JettyStartMojo.java new file mode 100644 index 00000000000..628e347a4c7 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/JettyStartMojo.java @@ -0,0 +1,105 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.maven.plugin; + +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.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) +@Execute(phase = LifecyclePhase.VALIDATE) +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-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/JettyStartWarMojo.java b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/JettyStartWarMojo.java new file mode 100644 index 00000000000..b6f6288f429 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/JettyStartWarMojo.java @@ -0,0 +1,144 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/JettyStopMojo.java b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/JettyStopMojo.java new file mode 100644 index 00000000000..bc743e0e1de --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/JettyStopMojo.java @@ -0,0 +1,231 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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) + { + 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 + */ + 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-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/MavenMetaInfConfiguration.java b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/MavenMetaInfConfiguration.java new file mode 100644 index 00000000000..b67885a3062 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/MavenMetaInfConfiguration.java @@ -0,0 +1,119 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.maven.plugin; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import org.eclipse.jetty.ee10.webapp.Configuration; +import org.eclipse.jetty.ee10.webapp.MetaInfConfiguration; +import org.eclipse.jetty.ee10.webapp.WebAppContext; +import org.eclipse.jetty.util.resource.Resource; +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 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 (file.getName().toLowerCase(Locale.ENGLISH).endsWith(".jar") || file.isDirectory()) + { + try + { + LOG.debug(" add resource to resources to examine {}", file); + list.add(Resource.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 + { + list.add(Resource.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-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/MavenQuickStartConfiguration.java b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/MavenQuickStartConfiguration.java new file mode 100644 index 00000000000..7d7328560eb --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/MavenQuickStartConfiguration.java @@ -0,0 +1,62 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.maven.plugin; + +import org.eclipse.jetty.ee10.quickstart.QuickStartConfiguration; +import org.eclipse.jetty.ee10.webapp.Configuration; +import org.eclipse.jetty.ee10.webapp.WebAppContext; +import org.eclipse.jetty.util.IO; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.util.resource.ResourceCollection; +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.isPersistTempDirectory()) + { + 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 ResourceCollection) + { + for (Resource r : ((ResourceCollection)res).getResources()) + { + if (originalBaseStr.contains(r.toString())) + continue; + IO.delete(r.getFile()); + } + } + } + super.deconfigure(context); + } +} diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/MavenServerConnector.java b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/MavenServerConnector.java new file mode 100644 index 00000000000..ac35c0577f2 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/MavenServerConnector.java @@ -0,0 +1,221 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.maven.plugin; + +import java.util.Collection; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; + +import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.server.ConnectionFactory; +import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.util.annotation.ManagedAttribute; +import org.eclipse.jetty.util.component.ContainerLifeCycle; +import org.eclipse.jetty.util.thread.Scheduler; + +/** + * MavenServerConnector + * + * As the ServerConnector class does not have a no-arg constructor, and moreover requires + * the server instance passed in to all its constructors, it cannot + * be referenced in the pom.xml. This class wraps a ServerConnector, delaying setting the + * server instance. Only a few of the setters from the ServerConnector class are supported. + */ +public class MavenServerConnector extends ContainerLifeCycle implements Connector +{ + public static String PORT_SYSPROPERTY = "jetty.http.port"; + + public static final int DEFAULT_PORT = 8080; + public static final String DEFAULT_PORT_STR = String.valueOf(DEFAULT_PORT); + public static final int DEFAULT_MAX_IDLE_TIME = 30000; + + private Server server; + private volatile ServerConnector delegate; + private String host; + private String name; + private int port; + private long idleTimeout; + + public MavenServerConnector() + { + } + + public void setServer(Server server) + { + this.server = server; + } + + public void setHost(String host) + { + this.host = host; + } + + public String getHost() + { + return this.host; + } + + public void setPort(int port) + { + this.port = port; + } + + public int getPort() + { + return this.port; + } + + public void setName(String name) + { + this.name = name; + } + + public void setIdleTimeout(long idleTimeout) + { + this.idleTimeout = idleTimeout; + } + + @Override + protected void doStart() throws Exception + { + + if (this.server == null) + throw new IllegalStateException("Server not set for MavenServerConnector"); + + this.delegate = new ServerConnector(this.server); + this.delegate.setName(this.name); + this.delegate.setPort(this.port); + this.delegate.setHost(this.host); + this.delegate.setIdleTimeout(idleTimeout); + this.delegate.start(); + + super.doStart(); + } + + @Override + protected void doStop() throws Exception + { + this.delegate.stop(); + super.doStop(); + this.delegate = null; + } + + @Override + public CompletableFuture shutdown() + { + return checkDelegate().shutdown(); + } + + @Override + public boolean isShutdown() + { + return checkDelegate().isShutdown(); + } + + @Override + public Server getServer() + { + return this.server; + } + + @Override + public Executor getExecutor() + { + return checkDelegate().getExecutor(); + } + + @Override + public Scheduler getScheduler() + { + return checkDelegate().getScheduler(); + } + + @Override + public ByteBufferPool getByteBufferPool() + { + return checkDelegate().getByteBufferPool(); + } + + @Override + public ConnectionFactory getConnectionFactory(String nextProtocol) + { + return checkDelegate().getConnectionFactory(nextProtocol); + } + + @Override + public T getConnectionFactory(Class factoryType) + { + return checkDelegate().getConnectionFactory(factoryType); + } + + @Override + public ConnectionFactory getDefaultConnectionFactory() + { + return checkDelegate().getDefaultConnectionFactory(); + } + + @Override + public Collection getConnectionFactories() + { + return checkDelegate().getConnectionFactories(); + } + + @Override + public List getProtocols() + { + return checkDelegate().getProtocols(); + } + + @Override + @ManagedAttribute("maximum time a connection can be idle before being closed (in ms)") + public long getIdleTimeout() + { + return checkDelegate().getIdleTimeout(); + } + + @Override + public Object getTransport() + { + return checkDelegate().getTransport(); + } + + @Override + public Collection getConnectedEndPoints() + { + return checkDelegate().getConnectedEndPoints(); + } + + @Override + public String getName() + { + return this.name; + } + + public int getLocalPort() + { + return this.delegate.getLocalPort(); + } + + private ServerConnector checkDelegate() throws IllegalStateException + { + ServerConnector d = this.delegate; + if (d == null) + throw new IllegalStateException("MavenServerConnector delegate not ready"); + return d; + } +} diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/MavenWebAppContext.java b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/MavenWebAppContext.java new file mode 100644 index 00000000000..6482f763c7a --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/MavenWebAppContext.java @@ -0,0 +1,507 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.maven.plugin; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Method; +import java.net.MalformedURLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +import org.eclipse.jetty.ee10.plus.webapp.EnvConfiguration; +import org.eclipse.jetty.ee10.quickstart.QuickStartConfiguration; +import org.eclipse.jetty.ee10.servlet.FilterHolder; +import org.eclipse.jetty.ee10.servlet.FilterMapping; +import org.eclipse.jetty.ee10.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletMapping; +import org.eclipse.jetty.ee10.webapp.Configuration; +import org.eclipse.jetty.ee10.webapp.Configurations; +import org.eclipse.jetty.ee10.webapp.MetaInfConfiguration; +import org.eclipse.jetty.ee10.webapp.WebAppContext; +import org.eclipse.jetty.util.StringUtil; +import org.eclipse.jetty.util.URIUtil; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.util.resource.ResourceCollection; +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 _classpathFiles; // 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() throws Exception + { + 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 getClassPathFiles() + { + return this._classpathFiles; + } + + 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); + } + + /** + * @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 ResourceCollection}. Each resource string may be a + * comma separated list of resources + */ + public void setResourceBases(String[] resourceBases) + { + List resources = new ArrayList(); + for (String rl : resourceBases) + { + String[] rs = StringUtil.csvSplit(rl); + for (String r : rs) + { + resources.add(r); + } + } + //TODO: needs WebAppContext.setResourceBase sorted out + //setBaseResource(new ResourceCollection(resources.toArray(new String[resources.size()]))); + } + + 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) + _webInfClasses.add(_testClasses); + if (_classes != null) + _webInfClasses.add(_classes); + + // Set up the classpath + _classpathFiles = new ArrayList<>(); + _classpathFiles.addAll(_webInfClasses); + _classpathFiles.addAll(_webInfJars); + + // 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 (fileName.endsWith(".jar")) + _webInfJarMap.put(fileName, file); + } + + // check for CDI + initCDI(); + + // CHECK setShutdown(false); + super.doStart(); + } + + @Override + protected Configurations newConfigurations() + { + Configurations configurations = super.newConfigurations(); + if (getJettyEnvXml() != null) + { + try + { + // inject configurations with config from maven plugin + for (Configuration c : configurations) + { + if (c instanceof EnvConfiguration) + ((EnvConfiguration)c).setJettyEnvResource(Resource.newResource(getJettyEnvXml())); + } + } + catch (IOException e) + { + throw new RuntimeException(e); + } + } + + return configurations; + } + + @Override + public void doStop() throws Exception + { + if (_classpathFiles != null) + _classpathFiles.clear(); + _classpathFiles = 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 + Thread.currentThread().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 = null; + // 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) + { + // Canonicalize again to look for the resource inside /WEB-INF subdirectories. + String uri = URIUtil.canonicalPath(pathInContext); + if (uri == null) + return null; + + try + { + // 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 Resource.newResource(_classes); + else if (_testClasses != null) + return Resource.newResource(_testClasses); + } + else + { + // try matching + Resource res = null; + int i = 0; + while (res == null && (i < _webInfClasses.size())) + { + String newPath = StringUtil.replace(uri, WEB_INF_CLASSES_PREFIX, _webInfClasses.get(i).getPath()); + res = Resource.newResource(newPath); + if (!res.exists()) + { + 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 Resource.newResource(jarFile.getPath()); + + return null; + } + } + catch (MalformedURLException e) + { + throw e; + } + catch (IOException e) + { + LOG.trace("IGNORED", e); + } + } + 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<>(); + allPaths.addAll(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 = null; + try + { + cdiInitializer = Thread.currentThread().getContextClassLoader().loadClass("org.eclipse.jetty.ee10.cdi.servlet.JettyWeldInitializer"); + Method initWebAppMethod = cdiInitializer.getMethod("initWebApp", new Class[]{WebAppContext.class}); + initWebAppMethod.invoke(null, new Object[]{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-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/MavenWebInfConfiguration.java b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/MavenWebInfConfiguration.java new file mode 100644 index 00000000000..b5665169ca0 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/MavenWebInfConfiguration.java @@ -0,0 +1,68 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.maven.plugin; + +import java.io.File; + +import org.eclipse.jetty.ee10.webapp.Configuration; +import org.eclipse.jetty.ee10.webapp.WebAppClassLoader; +import org.eclipse.jetty.ee10.webapp.WebAppContext; +import org.eclipse.jetty.ee10.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() + { + 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.getClassPathFiles() != null && context.getClassLoader() instanceof WebAppClassLoader) + { + if (LOG.isDebugEnabled()) + LOG.debug("Setting up classpath ..."); + WebAppClassLoader loader = (WebAppClassLoader)context.getClassLoader(); + for (File classpath : jwac.getClassPathFiles()) + { + loader.addClassPath(classpath.getCanonicalPath()); + } + } + + super.configure(context); + } +} diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/Overlay.java b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/Overlay.java new file mode 100644 index 00000000000..987311233e7 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/Overlay.java @@ -0,0 +1,89 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.maven.plugin; + +import java.io.File; +import java.io.IOException; + +import org.eclipse.jetty.util.resource.Resource; + +/** + * Overlay + * + * An Overlay represents overlay information derived from the + * maven-war-plugin. + */ +public class Overlay +{ + private OverlayConfig _config; + private Resource _resource; + + public Overlay(OverlayConfig config, Resource resource) + { + _config = config; + _resource = resource; + } + + public Overlay(OverlayConfig config) + { + _config = config; + } + + public void setResource(Resource r) + { + _resource = r; + } + + public Resource getResource() + { + return _resource; + } + + public OverlayConfig getConfig() + { + return _config; + } + + @Override + public String toString() + { + StringBuilder strbuff = new StringBuilder(); + if (_resource != null) + strbuff.append(_resource); + if (_config != null) + { + strbuff.append(" ["); + strbuff.append(_config); + strbuff.append("]"); + } + return strbuff.toString(); + } + + /** + * Unpack the overlay into the given directory. Only + * unpack if the directory does not exist, or the overlay + * has been modified since the dir was created. + * + * @param dir the directory into which to unpack the overlay + * @throws IOException + */ + public void unpackTo(File dir) throws IOException + { + if (dir == null) + throw new IllegalStateException("No overly unpack directory"); + //only unpack if the overlay is newer + if (!dir.exists() || (getResource().lastModified() > dir.lastModified())) + getResource().copyTo(dir); + } +} diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/OverlayConfig.java b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/OverlayConfig.java new file mode 100644 index 00000000000..4b5f7715eea --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/OverlayConfig.java @@ -0,0 +1,337 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.maven.plugin; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +import org.codehaus.plexus.util.xml.Xpp3Dom; +import org.eclipse.jetty.util.StringUtil; + +/** + * OverlayConfig + * + * The configuration of a war overlay in a pom. Used to help determine which resources + * from a project's dependent war should be included. + */ +public class OverlayConfig +{ + private String targetPath; + private String groupId; + private String artifactId; + private String classifier; + private List includes; + private List excludes; + private boolean skip; + private boolean filtered; + + public OverlayConfig() + { + } + + public OverlayConfig(String fmt, List defaultIncludes, List defaultExcludes) + { + if (fmt == null) + return; + String[] atoms = StringUtil.csvSplit(fmt); + for (int i = 0; i < atoms.length; i++) + { + String s = atoms[i].trim(); + switch (i) + { + case 0: + { + if (!"".equals(s)) + groupId = s; + break; + } + case 1: + { + if (!"".equals(s)) + artifactId = s; + break; + } + case 2: + { + if (!"".equals(s)) + classifier = s; + break; + } + case 3: + { + if (!"".equals(s)) + targetPath = s; + break; + } + case 4: + { + if ("".equals(s)) + skip = false; + else + skip = Boolean.valueOf(s); + break; + } + case 5: + { + if ("".equals(s)) + filtered = false; + else + filtered = Boolean.valueOf(s); + break; + } + case 6: + { + if ("".equals(s)) + break; + String[] incs = s.split(";"); + if (incs.length > 0) + includes = Arrays.asList(incs); + break; + } + case 7: + { + if ("".equals(s)) + break; + String[] exs = s.split(";"); + if (exs.length > 0) + excludes = Arrays.asList(exs); + break; + } + default: + break; + } + } + } + + public OverlayConfig(Xpp3Dom root, List defaultIncludes, List defaultExcludes) + { + Xpp3Dom node = root.getChild("groupId"); + setGroupId(node == null ? null : node.getValue()); + + node = root.getChild("artifactId"); + setArtifactId(node == null ? null : node.getValue()); + + node = root.getChild("classifier"); + setClassifier(node == null ? null : node.getValue()); + + node = root.getChild("targetPath"); + setTargetPath(node == null ? null : node.getValue()); + + node = root.getChild("skip"); + setSkip(node == null ? false : Boolean.valueOf(node.getValue())); + + node = root.getChild("filtered"); + setFiltered(node == null ? false : Boolean.valueOf(node.getValue())); + + node = root.getChild("includes"); + List includes = null; + if (node != null && node.getChildCount() > 0) + { + Xpp3Dom[] list = node.getChildren("include"); + for (int j = 0; list != null && j < list.length; j++) + { + if (includes == null) + includes = new ArrayList<>(); + includes.add(list[j].getValue()); + } + } + if (includes == null && defaultIncludes != null) + { + includes = new ArrayList<>(); + includes.addAll(defaultIncludes); + } + setIncludes(includes); + + node = root.getChild("excludes"); + List excludes = null; + if (node != null && node.getChildCount() > 0) + { + Xpp3Dom[] list = node.getChildren("exclude"); + for (int j = 0; list != null && j < list.length; j++) + { + if (excludes == null) + excludes = new ArrayList<>(); + excludes.add(list[j].getValue()); + } + } + if (excludes == null && defaultExcludes != null) + { + excludes = new ArrayList<>(); + excludes.addAll(defaultExcludes); + } + setExcludes(excludes); + } + + public String getTargetPath() + { + return targetPath; + } + + public void setTargetPath(String targetPath) + { + this.targetPath = targetPath; + } + + public String getGroupId() + { + return groupId; + } + + public void setGroupId(String groupId) + { + this.groupId = groupId; + } + + public String getArtifactId() + { + return artifactId; + } + + public void setArtifactId(String artifactId) + { + this.artifactId = artifactId; + } + + public String getClassifier() + { + return classifier; + } + + public void setClassifier(String classifier) + { + this.classifier = classifier; + } + + public List getIncludes() + { + return includes; + } + + public void setIncludes(List includes) + { + this.includes = includes; + } + + public List getExcludes() + { + return excludes; + } + + public void setExcludes(List excludes) + { + this.excludes = excludes; + } + + public boolean isSkip() + { + return skip; + } + + public void setSkip(boolean skip) + { + this.skip = skip; + } + + public boolean isFiltered() + { + return filtered; + } + + public void setFiltered(boolean filtered) + { + this.filtered = filtered; + } + + public boolean isCurrentProject() + { + if (this.groupId == null && this.artifactId == null) + return true; + return false; + } + + /** + * Check if this overlay configuration matches an Artifact's info + * + * @param gid Artifact groupId + * @param aid Artifact artifactId + * @param cls Artifact classifier + * @return true if matched + */ + public boolean matchesArtifact(String gid, String aid, String cls) + { + if (((getGroupId() == null && gid == null) || (getGroupId() != null && getGroupId().equals(gid))) && + ((getArtifactId() == null && aid == null) || (getArtifactId() != null && getArtifactId().equals(aid))) && + ((getClassifier() == null) || (getClassifier().equals(cls)))) + return true; + + return false; + } + + /** + * Check if this overlay configuration matches an Artifact's info + * + * @param gid the group id + * @param aid the artifact id + * @return true if matched + */ + public boolean matchesArtifact(String gid, String aid) + { + if (((getGroupId() == null && gid == null) || (getGroupId() != null && getGroupId().equals(gid))) && + ((getArtifactId() == null && aid == null) || (getArtifactId() != null && getArtifactId().equals(aid)))) + return true; + + return false; + } + + @Override + public String toString() + { + StringBuilder strbuff = new StringBuilder(); + strbuff.append((groupId != null ? groupId : "") + ","); + strbuff.append((artifactId != null ? artifactId : "") + ","); + strbuff.append((classifier != null ? classifier : "") + ","); + strbuff.append((targetPath != null ? targetPath : "") + ","); + strbuff.append("" + skip + ","); + strbuff.append("" + filtered + ","); + + if (includes != null) + { + Iterator itor = includes.iterator(); + while (itor.hasNext()) + { + strbuff.append(itor.next()); + if (itor.hasNext()) + strbuff.append(";"); + } + } + + strbuff.append(", "); + + if (excludes != null) + { + Iterator itor = excludes.iterator(); + while (itor.hasNext()) + { + strbuff.append(itor.next()); + if (itor.hasNext()) + strbuff.append(";"); + } + } + + return strbuff.toString(); + } +} + diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/OverlayManager.java b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/OverlayManager.java new file mode 100644 index 00000000000..4d2cce9e709 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/OverlayManager.java @@ -0,0 +1,156 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.maven.plugin; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.apache.maven.artifact.Artifact; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.util.resource.ResourceCollection; + +/** + * OverlayManager + * + * Mediates information about overlays configured in a war plugin. + * + */ +public class OverlayManager +{ + private WarPluginInfo warPlugin; + + public OverlayManager(WarPluginInfo warPlugin) + { + this.warPlugin = warPlugin; + } + + public void applyOverlays(MavenWebAppContext webApp) + throws Exception + { + List resourceBases = new ArrayList(); + + for (Overlay o : getOverlays()) + { + //can refer to the current project in list of overlays for ordering purposes + if (o.getConfig() != null && o.getConfig().isCurrentProject() && webApp.getBaseResource().exists()) + { + resourceBases.add(webApp.getBaseResource()); + continue; + } + //add in the selectively unpacked overlay in the correct order to the webapp's resource base + resourceBases.add(unpackOverlay(o)); + } + + if (!resourceBases.contains(webApp.getBaseResource()) && webApp.getBaseResource().exists()) + { + if (webApp.getBaseAppFirst()) + resourceBases.add(0, webApp.getBaseResource()); + else + resourceBases.add(webApp.getBaseResource()); + } + //TODO needs WebAppContext.setResourceBase sorted out + // webApp.setBaseResource(new ResourceCollection(resourceBases.toArray(new Resource[resourceBases.size()]))); + } + + /** + * Generate an ordered list of overlays + */ + protected List getOverlays() + throws Exception + { + Set matchedWarArtifacts = new HashSet(); + List overlays = new ArrayList(); + + //Check all of the overlay configurations + for (OverlayConfig config:warPlugin.getMavenWarOverlayConfigs()) + { + //overlays can be individually skipped + if (config.isSkip()) + continue; + + //an empty overlay refers to the current project - important for ordering + if (config.isCurrentProject()) + { + Overlay overlay = new Overlay(config, null); + overlays.add(overlay); + continue; + } + + //if a war matches an overlay config + Artifact a = warPlugin.getWarArtifact(config.getGroupId(), config.getArtifactId(), config.getClassifier()); + if (a != null) + { + matchedWarArtifacts.add(a); + SelectiveJarResource r = new SelectiveJarResource(new URL("jar:" + Resource.toURL(a.getFile()).toString() + "!/")); + r.setIncludes(config.getIncludes()); + r.setExcludes(config.getExcludes()); + Overlay overlay = new Overlay(config, r); + overlays.add(overlay); + } + } + + //iterate over the left over war artifacts add them + for (Artifact a: warPlugin.getWarArtifacts()) + { + if (!matchedWarArtifacts.contains(a)) + { + Overlay overlay = new Overlay(null, Resource.newResource(new URL("jar:" + Resource.toURL(a.getFile()).toString() + "!/"))); + overlays.add(overlay); + } + } + return overlays; + } + + /** + * Unpack a war overlay. + * + * @param overlay the war overlay to unpack + * @return the location to which it was unpacked + * @throws IOException + */ + protected Resource unpackOverlay(Overlay overlay) + throws IOException + { + if (overlay.getResource() == null) + return null; //nothing to unpack + + //Get the name of the overlayed war and unpack it to a dir of the + //same name in the temporary directory + String name = overlay.getResource().getName(); + if (name.endsWith("!/")) + name = name.substring(0, name.length() - 2); + int i = name.lastIndexOf('/'); + if (i > 0) + name = name.substring(i + 1, name.length()); + name = name.replace('.', '_'); + + File overlaysDir = new File(warPlugin.getProject().getBuild().getDirectory(), "jetty_overlays"); + File dir = new File(overlaysDir, name); + + //if specified targetPath, unpack to that subdir instead + File unpackDir = dir; + if (overlay.getConfig() != null && overlay.getConfig().getTargetPath() != null) + unpackDir = new File(dir, overlay.getConfig().getTargetPath()); + + overlay.unpackTo(unpackDir); + + //use top level of unpacked content + return Resource.newResource(unpackDir.getCanonicalPath()); + } +} diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/PluginLog.java b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/PluginLog.java new file mode 100644 index 00000000000..e27f44f4436 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/PluginLog.java @@ -0,0 +1,37 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.maven.plugin; + +import org.apache.maven.plugin.logging.Log; + +/** + * PluginLog + * + * Convenience class to provide access to the plugin + * Log for non-mojo classes. + */ +public class PluginLog +{ + private static Log log = null; + + public static void setLog(Log l) + { + log = l; + } + + public static Log getLog() + { + return log; + } +} diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/QuickStartGenerator.java b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/QuickStartGenerator.java new file mode 100644 index 00000000000..b190a7093dc --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/QuickStartGenerator.java @@ -0,0 +1,187 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.maven.plugin; + +import java.io.File; + +import org.eclipse.jetty.ee10.annotations.AnnotationConfiguration; +import org.eclipse.jetty.ee10.quickstart.QuickStartConfiguration; +import org.eclipse.jetty.ee10.quickstart.QuickStartConfiguration.Mode; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.resource.Resource; +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 File quickstartXml; + private MavenWebAppContext webApp; + private File webAppPropsFile; + 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(File quickstartXml, MavenWebAppContext webApp) + { + this.quickstartXml = quickstartXml; + this.webApp = webApp; + } + + /** + * @return the webApp + */ + public MavenWebAppContext getWebApp() + { + return webApp; + } + + /** + * @return the quickstartXml + */ + public File getQuickstartXml() + { + return quickstartXml; + } + + /** + * @return the server + */ + public Server getServer() + { + return server; + } + + /** + * @param server the server to use + */ + public void setServer(Server server) + { + this.server = server; + } + + public File getWebAppPropsFile() + { + return webAppPropsFile; + } + + /** + * @param webAppPropsFile properties file describing the webapp + */ + public void setWebAppPropsFile(File webAppPropsFile) + { + this.webAppPropsFile = webAppPropsFile; + } + + public String getContextXml() + { + return contextXml; + } + + /** + * @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. + * + * @throws Exception + */ + private void prepareWebApp() + throws Exception + { + if (webApp == null) + webApp = new MavenWebAppContext(); + + //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, Resource.newResource(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 + */ + 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); + + ServerSupport.configureDefaultConfigurationClasses(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.setPersistTempDirectory(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 (webAppPropsFile != null) + WebAppPropertyConverter.toProperties(webApp, webAppPropsFile, contextXml); + } + finally + { + webApp.stop(); + if (tpool != null) + tpool.stop(); + } + } +} diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/ScanPattern.java b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/ScanPattern.java new file mode 100644 index 00000000000..d937eeecfab --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/ScanPattern.java @@ -0,0 +1,48 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.maven.plugin; + +import java.util.Collections; +import java.util.List; + +/** + * ScanPattern + * + * Ant-style pattern of includes and excludes. + */ +public class ScanPattern +{ + private List _includes = Collections.emptyList(); + private List _excludes = Collections.emptyList(); + + public void setIncludes(List includes) + { + _includes = includes; + } + + public void setExcludes(List excludes) + { + _excludes = excludes; + } + + public List getIncludes() + { + return _includes; + } + + public List getExcludes() + { + return _excludes; + } +} diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/ScanTargetPattern.java b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/ScanTargetPattern.java new file mode 100644 index 00000000000..f98049a7664 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/ScanTargetPattern.java @@ -0,0 +1,106 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.maven.plugin; + +import java.io.File; +import java.nio.file.Path; +import java.nio.file.PathMatcher; +import java.util.Collections; +import java.util.List; + +import org.eclipse.jetty.util.IncludeExcludeSet; + +/** + * ScanTargetPattern + * + * Utility class to provide the ability for the mvn jetty:run + * mojo to be able to specify filesets of extra files to + * regularly scan for changes in order to redeploy the webapp. + * + * For example: + * + * <scanTargetPattern> + * <directory>/some/place</directory> + * <includes> + * <include>some ant pattern here </include> + * <include>some ant pattern here </include> + * </includes> + * <excludes> + * <exclude>some ant pattern here </exclude> + * <exclude>some ant pattern here </exclude> + * </excludes> + * </scanTargetPattern> + */ +public class ScanTargetPattern +{ + private File _directory; + private ScanPattern _pattern; + + /** + * @return the _directory + */ + public File getDirectory() + { + return _directory; + } + + /** + * @param directory the directory to set + */ + public void setDirectory(File directory) + { + this._directory = directory; + } + + public void setIncludes(List includes) + { + if (_pattern == null) + _pattern = new ScanPattern(); + _pattern.setIncludes(includes); + } + + public void setExcludes(List excludes) + { + if (_pattern == null) + _pattern = new ScanPattern(); + _pattern.setExcludes(excludes); + } + + public List getIncludes() + { + return (_pattern == null ? Collections.emptyList() : _pattern.getIncludes()); + } + + public List getExcludes() + { + return (_pattern == null ? Collections.emptyList() : _pattern.getExcludes()); + } + + public void configureIncludesExcludeSet(IncludeExcludeSet includesExcludes) + { + for (String include:getIncludes()) + { + if (!include.startsWith("glob:")) + include = "glob:" + include; + includesExcludes.include(_directory.toPath().getFileSystem().getPathMatcher(include)); + } + + for (String exclude:getExcludes()) + { + if (!exclude.startsWith("glob:")) + exclude = "glob:" + exclude; + includesExcludes.exclude(_directory.toPath().getFileSystem().getPathMatcher(exclude)); + } + } +} diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/SelectiveJarResource.java b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/SelectiveJarResource.java new file mode 100644 index 00000000000..7ee6879adc1 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/SelectiveJarResource.java @@ -0,0 +1,214 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.maven.plugin; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URL; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarInputStream; +import java.util.jar.Manifest; + +import org.codehaus.plexus.util.SelectorUtils; +import org.eclipse.jetty.util.IO; +import org.eclipse.jetty.util.URIUtil; +import org.eclipse.jetty.util.resource.JarResource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * SelectiveJarResource + * + * Selectively copies resources from a jar file based on includes/excludes. + */ +public class SelectiveJarResource extends JarResource +{ + private static final Logger LOG = LoggerFactory.getLogger(SelectiveJarResource.class); + + /** + * Default matches every resource. + */ + public static final List DEFAULT_INCLUDES = + Arrays.asList(new String[]{"**"}); + + /** + * Default is to exclude nothing. + */ + public static final List DEFAULT_EXCLUDES = Collections.emptyList(); + + List _includes = null; + List _excludes = null; + boolean _caseSensitive = false; + + public SelectiveJarResource(URL url) + { + super(url); + } + + public SelectiveJarResource(URL url, boolean useCaches) + { + super(url, useCaches); + } + + public void setCaseSensitive(boolean caseSensitive) + { + _caseSensitive = caseSensitive; + } + + public void setIncludes(List patterns) + { + _includes = patterns; + } + + public void setExcludes(List patterns) + { + _excludes = patterns; + } + + protected boolean isIncluded(String name) + { + for (String include : _includes) + { + if (SelectorUtils.matchPath(include, name, "/", _caseSensitive)) + { + return true; + } + } + return false; + } + + protected boolean isExcluded(String name) + { + for (String exclude : _excludes) + { + if (SelectorUtils.matchPath(exclude, name, "/", _caseSensitive)) + { + return true; + } + } + return false; + } + + @Override + public void copyTo(File directory) throws IOException + { + if (_includes == null) + _includes = DEFAULT_INCLUDES; + if (_excludes == null) + _excludes = DEFAULT_EXCLUDES; + + //Copy contents of the jar file to the given directory, + //using the includes and excludes patterns to control which + //parts of the jar file are copied + if (!exists()) + return; + + String urlString = this.getURI().toASCIIString().trim(); + int endOfJarUrl = urlString.indexOf("!/"); + int startOfJarUrl = (endOfJarUrl >= 0 ? 4 : 0); + + if (endOfJarUrl < 0) + throw new IOException("Not a valid jar url: " + urlString); + + URL jarFileURL = new URL(urlString.substring(startOfJarUrl, endOfJarUrl)); + + try (InputStream is = jarFileURL.openConnection().getInputStream(); + JarInputStream jin = new JarInputStream(is)) + { + JarEntry entry; + + while ((entry = jin.getNextJarEntry()) != null) + { + String entryName = entry.getName(); + + LOG.debug("Looking at {}", entryName); + // make sure no access out of the root entry is present + String dotCheck = URIUtil.canonicalPath(entryName); + if (dotCheck == null) + { + LOG.info("Invalid entry: {}", entryName); + continue; + } + + File file = new File(directory, entryName); + + if (entry.isDirectory()) + { + if (isIncluded(entryName)) + { + if (!isExcluded(entryName)) + { + // Make directory + if (!file.exists()) + file.mkdirs(); + } + else + LOG.debug("{} dir is excluded", entryName); + } + else + LOG.debug("{} dir is NOT included", entryName); + } + else + { + //entry is a file, is it included? + if (isIncluded(entryName)) + { + if (!isExcluded(entryName)) + { + // make directory (some jars don't list dirs) + File dir = new File(file.getParent()); + if (!dir.exists()) + dir.mkdirs(); + + // Make file + try (OutputStream fout = new FileOutputStream(file)) + { + IO.copy(jin, fout); + } + + // touch the file. + if (entry.getTime() >= 0) + file.setLastModified(entry.getTime()); + } + else + LOG.debug("{} file is excluded", entryName); + } + else + LOG.debug("{} file is NOT included", entryName); + } + } + + Manifest manifest = jin.getManifest(); + if (manifest != null) + { + if (isIncluded("META-INF") && !isExcluded("META-INF")) + { + File metaInf = new File(directory, "META-INF"); + metaInf.mkdir(); + File f = new File(metaInf, "MANIFEST.MF"); + try (OutputStream fout = new FileOutputStream(f)) + { + manifest.write(fout); + } + } + } + } + } +} diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/ServerConnectorListener.java b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/ServerConnectorListener.java new file mode 100644 index 00000000000..9d20ed519c6 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/ServerConnectorListener.java @@ -0,0 +1,107 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.maven.plugin; + +import java.io.Writer; +import java.nio.file.AtomicMoveNotSupportedException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; + +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.util.component.AbstractLifeCycle.AbstractLifeCycleListener; +import org.eclipse.jetty.util.component.LifeCycle; + +/** + * ServerConnectorListener + * + * This is for test support, where we need jetty to run on a random port, and we need + * a client to be able to find out which port was picked. + */ +public class ServerConnectorListener extends AbstractLifeCycleListener +{ + private String _fileName; + private String _sysPropertyName; + + @Override + public void lifeCycleStarted(LifeCycle event) + { + if (getFileName() != null) + { + try + { + Path tmp = Files.createTempFile("jettyport", ".tmp"); + try (Writer writer = Files.newBufferedWriter(tmp)) + { + writer.write(String.valueOf(((ServerConnector)event).getLocalPort())); + } + + Path path = Paths.get(getFileName()); + Files.deleteIfExists(path); + try + { + Files.move(tmp, path, StandardCopyOption.ATOMIC_MOVE); + } + catch (AtomicMoveNotSupportedException e) // can append on some os (windows).. so try again without the option + { + Files.move(tmp, path); + } + } + catch (Exception e) + { + throw new RuntimeException(e); + } + } + + if (getSysPropertyName() != null) + { + System.setProperty(_sysPropertyName, String.valueOf(((ServerConnector)event).getLocalPort())); + } + super.lifeCycleStarted(event); + } + + /** + * @return the file name + */ + public String getFileName() + { + return _fileName; + } + + /** + * @param name the file name to set + */ + public void setFileName(String name) + { + + _fileName = name; + } + + /** + * @return the sysPropertyName + */ + public String getSysPropertyName() + { + return _sysPropertyName; + } + + /** + * @param sysPropertyName the sysPropertyName to set + */ + public void setSysPropertyName(String sysPropertyName) + { + _sysPropertyName = sysPropertyName; + } +} diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/ServerListener.java b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/ServerListener.java new file mode 100644 index 00000000000..8b5655a8966 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/ServerListener.java @@ -0,0 +1,56 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.maven.plugin; + +import org.eclipse.jetty.util.component.LifeCycle; +import org.eclipse.jetty.util.resource.Resource; + +/** + * ServerListener + * + * Listener to create a file that signals that the startup is completed. + * Used by the JettyRunHome maven goal to determine that the child + * process is started, and that jetty is ready. + */ +public class ServerListener implements LifeCycle.Listener +{ + private String _tokenFile; + + public void setTokenFile(String file) + { + _tokenFile = file; + } + + public String getTokenFile() + { + return _tokenFile; + } + + @Override + public void lifeCycleStarted(LifeCycle event) + { + if (_tokenFile != null) + { + try + { + Resource r = Resource.newResource(_tokenFile); + r.getFile().createNewFile(); + } + catch (Exception e) + { + throw new IllegalStateException(e); + } + } + } +} diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/ServerSupport.java b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/ServerSupport.java new file mode 100644 index 00000000000..6b772ee87f0 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/ServerSupport.java @@ -0,0 +1,244 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.maven.plugin; + +import java.io.File; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.jetty.ee10.servlet.security.LoginService; +import org.eclipse.jetty.ee10.webapp.Configurations; +import org.eclipse.jetty.ee10.webapp.WebAppContext; +import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.RequestLog; +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.util.resource.PathResource; +import org.eclipse.jetty.xml.XmlConfiguration; + +/** + * ServerSupport + * + * Helps configure the Server instance. + */ +public class ServerSupport +{ + + public static void configureDefaultConfigurationClasses(Server server) + { + Configurations.setServerDefault(server); + } + + /** + * Set up the handler structure to receive a webapp. + * Also put in a DefaultHandler so we get a nicer page + * than a 404 if we hit the root and the webapp's + * context isn't at root. + * + * @param server the server to use + * @param contextHandlers the context handlers to include + * @param requestLog a request log to use + * @throws Exception + */ + public static void configureHandlers(Server server, List contextHandlers, RequestLog requestLog) throws Exception + { + if (server == null) + throw new IllegalArgumentException("Server is null"); + + if (requestLog != null) + server.setRequestLog(requestLog); + + ContextHandlerCollection contexts = findContextHandlerCollection(server); + if (contexts == null) + { + contexts = new ContextHandlerCollection(); + Handler.Collection handlers = server.getDescendant(Handler.Collection.class); + if (handlers == null) + server.setHandler(new Handler.Collection(contexts, new DefaultHandler())); + else + handlers.addHandler(contexts); + } + + if (contextHandlers != null) + { + for (ContextHandler context:contextHandlers) + { + contexts.addHandler(context); + } + } + } + + /** + * Configure at least one connector for the server + * + * @param server the server + * @param connector the connector + * @param properties jetty properties + */ + public static void configureConnectors(Server server, Connector connector, Map properties) + { + if (server == null) + throw new IllegalArgumentException("Server is null"); + + //if a connector is provided, use it + if (connector != null) + { + server.addConnector(connector); + return; + } + + // if the user hasn't configured the connectors in a jetty.xml file so use a default one + Connector[] connectors = server.getConnectors(); + if (connectors == null || connectors.length == 0) + { + //Make a new default connector + MavenServerConnector tmp = new MavenServerConnector(); + //use any jetty.http.port settings provided, trying system properties before jetty properties + String port = System.getProperty(MavenServerConnector.PORT_SYSPROPERTY); + if (port == null) + port = System.getProperty("jetty.port"); + if (port == null) + port = (properties != null ? properties.get(MavenServerConnector.PORT_SYSPROPERTY) : null); + if (port == null) + port = MavenServerConnector.DEFAULT_PORT_STR; + tmp.setPort(Integer.parseInt(port.trim())); + tmp.setServer(server); + server.setConnectors(new Connector[]{tmp}); + } + } + + /** + * Set up any security LoginServices provided. + * + * @param server the server + * @param loginServices the login services + */ + public static void configureLoginServices(Server server, List loginServices) + { + if (server == null) + throw new IllegalArgumentException("Server is null"); + + if (loginServices != null) + { + for (LoginService loginService : loginServices) + { + PluginLog.getLog().debug(loginService.getClass().getName() + ": " + loginService.toString()); + server.addBean(loginService); + } + } + } + + /** + * Add a WebAppContext to a Server + * @param server the server to use + * @param webapp the webapp to add + * @throws Exception + */ + public static void addWebApplication(Server server, WebAppContext webapp) throws Exception + { + if (server == null) + throw new IllegalArgumentException("Server is null"); + ContextHandlerCollection contexts = findContextHandlerCollection(server); + if (contexts == null) + throw new IllegalStateException("ContextHandlerCollection is null"); + contexts.addHandler(webapp); + } + + /** + * Locate a ContextHandlerCollection for a Server. + * + * @param server the Server to check. + * @return The ContextHandlerCollection or null if not found. + */ + public static ContextHandlerCollection findContextHandlerCollection(Server server) + { + if (server == null) + return null; + + return server.getDescendant(ContextHandlerCollection.class); + } + + /** + * Apply xml files to server instance. + * + * @param server the server to apply the xml to + * @param files the list of xml files + * @param properties list of jetty properties + * @return the Server implementation, after the xml is applied + * @throws Exception if unable to apply the xml configuration + */ + public static Server applyXmlConfigurations(Server server, List files, Map properties) + throws Exception + { + if (files == null || files.isEmpty()) + return server; + + Map lastMap = new HashMap<>(); + + if (server != null) + lastMap.put("Server", server); + + for (File xmlFile : files) + { + if (PluginLog.getLog() != null) + PluginLog.getLog().info("Configuring Jetty from xml configuration file = " + xmlFile.getCanonicalPath()); + + XmlConfiguration xmlConfiguration = new XmlConfiguration(new PathResource(xmlFile)); + + //add in any properties + if (properties != null) + { + for (Map.Entry e : properties.entrySet()) + { + xmlConfiguration.getProperties().put(e.getKey(), e.getValue()); + } + } + + //chain ids from one config file to another + if (lastMap != null) + xmlConfiguration.getIdMap().putAll(lastMap); + + //Set the system properties each time in case the config file set a new one + Enumeration ensysprop = System.getProperties().propertyNames(); + while (ensysprop.hasMoreElements()) + { + String name = (String)ensysprop.nextElement(); + xmlConfiguration.getProperties().put(name, System.getProperty(name)); + } + xmlConfiguration.configure(); + lastMap = xmlConfiguration.getIdMap(); + } + + return (Server)lastMap.get("Server"); + } + + /** + * Apply xml files to server instance. + * + * @param server the Server instance to configure + * @param files the xml configs to apply + * @return the Server after application of configs + * @throws Exception + */ + public static Server applyXmlConfigurations(Server server, List files) + throws Exception + { + return applyXmlConfigurations(server, files, null); + } +} diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/WarPluginInfo.java b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/WarPluginInfo.java new file mode 100644 index 00000000000..138d8e7dfbe --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/WarPluginInfo.java @@ -0,0 +1,238 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.maven.plugin; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.model.Plugin; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.util.xml.Xpp3Dom; +import org.eclipse.jetty.util.StringUtil; + +/** + * WarPluginInfo + * + * Information about the maven-war-plugin contained in the pom + */ +public class WarPluginInfo +{ + private MavenProject _project; + private Plugin _plugin; + private List _dependentMavenWarIncludes; + private List _dependentMavenWarExcludes; + private List _overlayConfigs; + private Set _warArtifacts; + + public WarPluginInfo(MavenProject project) + { + _project = project; + if (_project.getArtifacts() != null) + { + _warArtifacts = _project.getArtifacts() + .stream() + .filter(a -> "war".equals(a.getType()) || "zip".equals(a.getType())).collect(Collectors.toSet()); + } + else + _warArtifacts = Collections.emptySet(); + } + + /** + * @return the project + */ + public MavenProject getProject() + { + return _project; + } + + /** + * Get all dependent artifacts that are wars. + * @return all artifacts of type "war" or "zip" + */ + public Set getWarArtifacts() + { + return _warArtifacts; + } + + /** + * Get an artifact of type war that matches the given coordinates. + * @param groupId the groupId to match + * @param artifactId the artifactId to match + * @param classifier the classified to match + * @return the matching Artifact or null if no match + */ + public Artifact getWarArtifact(String groupId, String artifactId, String classifier) + { + Optional o = _warArtifacts.stream() + .filter(a -> match(a, groupId, artifactId, classifier)).findFirst(); + return o.orElse(null); + } + + /** + * Find the maven-war-plugin, if one is configured + * + * @return the plugin + */ + public Plugin getWarPlugin() + { + if (_plugin == null) + { + List plugins = _project.getBuildPlugins(); + if (plugins == null) + return null; + + for (Plugin p : plugins) + { + if ("maven-war-plugin".equals(p.getArtifactId())) + { + _plugin = p; + break; + } + } + } + return _plugin; + } + + /** + * Get value of dependentWarIncludes for maven-war-plugin + * + * @return the list of dependent war includes + */ + public List getDependentMavenWarIncludes() + { + if (_dependentMavenWarIncludes == null) + { + getWarPlugin(); + + if (_plugin == null) + return null; + + Xpp3Dom node = (Xpp3Dom)_plugin.getConfiguration(); + if (node == null) + return null; + + node = node.getChild("dependentWarIncludes"); + if (node == null) + return null; + String val = node.getValue(); + _dependentMavenWarIncludes = StringUtil.csvSplit(null, val, 0, val.length()); + } + return _dependentMavenWarIncludes; + } + + /** + * Get value of dependentWarExcludes for maven-war-plugin + * + * @return the list of dependent war excludes + */ + public List getDependentMavenWarExcludes() + { + if (_dependentMavenWarExcludes == null) + { + getWarPlugin(); + + if (_plugin == null) + return null; + + Xpp3Dom node = (Xpp3Dom)_plugin.getConfiguration(); + if (node == null) + return null; + + node = node.getChild("dependentWarExcludes"); + if (node == null) + return null; + String val = node.getValue(); + _dependentMavenWarExcludes = StringUtil.csvSplit(null, val, 0, val.length()); + } + return _dependentMavenWarExcludes; + } + + /** + * Get config for any overlays that have been declared for the maven-war-plugin. + * + * @return the list of overlay configs + */ + public List getMavenWarOverlayConfigs() + { + if (_overlayConfigs == null) + { + getWarPlugin(); + + if (_plugin == null) + return Collections.emptyList(); + + getDependentMavenWarIncludes(); + getDependentMavenWarExcludes(); + + Xpp3Dom node = (Xpp3Dom)_plugin.getConfiguration(); + if (node == null) + return Collections.emptyList(); + + node = node.getChild("overlays"); + if (node == null) + return Collections.emptyList(); + + Xpp3Dom[] nodes = node.getChildren("overlay"); + if (nodes == null) + return Collections.emptyList(); + + _overlayConfigs = new ArrayList(); + for (int i = 0; i < nodes.length; i++) + { + OverlayConfig overlayConfig = new OverlayConfig(nodes[i], _dependentMavenWarIncludes, _dependentMavenWarExcludes); + _overlayConfigs.add(overlayConfig); + } + } + + return _overlayConfigs; + } + + public boolean match(Artifact a, String gid, String aid, String cls) + { + if (a == null) + return (gid == null && aid == null && cls == null); + + if (((a.getGroupId() == null && gid == null) || (a.getGroupId() != null && a.getGroupId().equals(gid))) && + ((a.getArtifactId() == null && aid == null) || (a.getArtifactId() != null && a.getArtifactId().equals(aid))) && + ((a.getClassifier() == null) || (a.getClassifier().equals(cls)))) + return true; + + return false; + } + + /** + * Check if the given artifact matches the group and artifact coordinates. + * + * @param a the artifact to check + * @param gid the group id + * @param aid the artifact id + * @return true if matched false otherwise + */ + public boolean match(Artifact a, String gid, String aid) + { + if (a == null) + return (gid == null && aid == null); + + if (((a.getGroupId() == null && gid == null) || (a.getGroupId() != null && a.getGroupId().equals(gid))) && + ((a.getArtifactId() == null && aid == null) || (a.getArtifactId() != null && a.getArtifactId().equals(aid)))) + return true; + + return false; + } +} diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/WebAppPropertyConverter.java b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/WebAppPropertyConverter.java new file mode 100644 index 00000000000..b35b8183749 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/WebAppPropertyConverter.java @@ -0,0 +1,341 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.maven.plugin; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.nio.file.Files; +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.ee10.quickstart.QuickStartConfiguration; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.StringUtil; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.util.resource.ResourceCollection; +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 Exception if any I/O exception occurs + */ + public static void toProperties(MavenWebAppContext webApp, File propsFile, String contextXml) + throws Exception + { + 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.isPersistTempDirectory())); + + //send over the calculated resource bases that includes unpacked overlays + Resource baseResource = webApp.getBaseResource(); + if (baseResource instanceof ResourceCollection) + props.put(BASE_DIRS, toCSV(((ResourceCollection)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 + */ + public static void fromProperties(MavenWebAppContext webApp, String resource, Server server, Map jettyProperties) + throws Exception + { + if (resource == null) + throw new IllegalStateException("No resource"); + + fromProperties(webApp, Resource.newResource(resource).getFile(), 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 + */ + 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, Resource.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.setPersistTempDirectory(Boolean.valueOf(str)); + + //Get the calculated base dirs which includes the overlays + str = webAppProperties.getProperty(BASE_DIRS); + if (!StringUtil.isBlank(str)) + { + ResourceCollection bases = new ResourceCollection(StringUtil.csvSplit(str)); + webApp.setWar(null); + //TODO: needs WebAppContext.setResourceBase sorted out + //webApp.setBaseResource(bases); + } + + 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(Resource.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 + */ + public static void fromProperties(MavenWebAppContext webApp, File propsFile, Server server, Map jettyProperties) + throws Exception + { + + if (propsFile == null) + throw new IllegalArgumentException("No properties file"); + + if (!propsFile.exists()) + throw new IllegalArgumentException(propsFile.getCanonicalPath() + " does not exist"); + + Properties props = new Properties(); + try (InputStream in = new FileInputStream(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-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/package-info.java b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/package-info.java new file mode 100644 index 00000000000..c481aaa15f7 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/package-info.java @@ -0,0 +1,18 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 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.ee10.maven.plugin; + diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/utils/MavenProjectHelper.java b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/utils/MavenProjectHelper.java new file mode 100644 index 00000000000..45e32cbdd49 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/main/java/org/eclipse/jetty/ee10/maven/plugin/utils/MavenProjectHelper.java @@ -0,0 +1,192 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.maven.plugin.utils; + +import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +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.project.DefaultProjectBuildingRequest; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.ProjectBuildingRequest; +import org.apache.maven.shared.transfer.artifact.DefaultArtifactCoordinate; +import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResolver; +import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResolverException; +import org.eclipse.jetty.ee10.maven.plugin.OverlayManager; +import org.eclipse.jetty.ee10.maven.plugin.WarPluginInfo; + +/** + * MavenProjectHelper + * + * A class to facilitate interacting with the build time maven environment. + * + */ +public class MavenProjectHelper +{ + private MavenProject project; + private ArtifactResolver artifactResolver; + private List remoteRepositories; + private MavenSession session; + private final Map artifactToReactorProjectMap; + /** + * maven-war-plugin reference + */ + private WarPluginInfo warPluginInfo; + + /** + * Helper for wrangling war overlays + */ + private OverlayManager overlayManager; + + /** + * @param project the project being built + * @param artifactResolver a resolve for artifacts + * @param remoteRepositories repositories from which to resolve artifacts + * @param session the current maven build session + */ + public MavenProjectHelper(MavenProject project, ArtifactResolver artifactResolver, List remoteRepositories, MavenSession session) + { + this.project = project; + this.artifactResolver = artifactResolver; + this.remoteRepositories = remoteRepositories; + this.session = session; + //work out which dependent projects are in the reactor + Set mavenProjects = findDependenciesInReactor(project, new HashSet<>()); + artifactToReactorProjectMap = mavenProjects.stream() + .collect(Collectors.toMap(MavenProject::getId, Function.identity())); + artifactToReactorProjectMap.put(project.getArtifact().getId(), project); + warPluginInfo = new WarPluginInfo(project); + overlayManager = new OverlayManager(warPluginInfo); + } + + public MavenProject getProject() + { + return this.project; + } + + public WarPluginInfo getWarPluginInfo() + { + return warPluginInfo; + } + + public OverlayManager getOverlayManager() + { + return overlayManager; + } + + /** + * Gets the maven project represented by the artifact iff it is in + * the reactor. + * + * @param artifact the artifact of the project to get + * @return {@link MavenProject} if artifact is referenced in reactor, otherwise null + */ + public MavenProject getMavenProjectFor(Artifact artifact) + { + return artifactToReactorProjectMap.get(artifact.getId()); + } + + /** + * Gets path to artifact. + * If the artifact is referenced in the reactor, returns path to ${project.build.outputDirectory}. + * Otherwise, returns path to location in local m2 repo. + * + * Cannot return null - maven will complain about unsatisfied dependency during project build. + * + * @param artifact maven artifact to check + * @return path to artifact + */ + public Path getPathFor(Artifact artifact) + { + Path path = artifact.getFile().toPath(); + MavenProject mavenProject = getMavenProjectFor(artifact); + if (mavenProject != null) + { + if ("test-jar".equals(artifact.getType())) + { + path = Paths.get(mavenProject.getBuild().getTestOutputDirectory()); + } + else + { + path = Paths.get(mavenProject.getBuild().getOutputDirectory()); + } + } + return path; + } + + /** + * Given the coordinates for an artifact, resolve the artifact from the + * remote repositories. + * + * @param groupId the groupId of the artifact to resolve + * @param artifactId the artifactId of the artifact to resolve + * @param version the version of the artifact to resolve + * @param type the type of the artifact to resolve + * @return a File representing the location of the artifact or null if not resolved + * @throws ArtifactResolverException + */ + public File resolveArtifact(String groupId, String artifactId, String version, String type) + throws ArtifactResolverException + { + DefaultArtifactCoordinate coordinate = new DefaultArtifactCoordinate(); + coordinate.setGroupId(groupId); + coordinate.setArtifactId(artifactId); + coordinate.setVersion(version); + coordinate.setExtension(type); + + ProjectBuildingRequest buildingRequest = + new DefaultProjectBuildingRequest(session.getProjectBuildingRequest()); + + buildingRequest.setRemoteRepositories(remoteRepositories); + + Artifact a = artifactResolver.resolveArtifact(buildingRequest, coordinate).getArtifact(); + + if (a != null) + return a.getFile(); + return null; + } + + /** + * Recursively find projects in the reactor for all dependencies of the given project. + * + * @param project the project for which to find dependencies that are in the reactor + * @param visitedProjects the set of projects already seen + * @return unified set of all related projects in the reactor + */ + private static Set findDependenciesInReactor(MavenProject project, Set visitedProjects) + { + if (visitedProjects.contains(project)) + return Collections.emptySet(); + + visitedProjects.add(project); + Collection refs = project.getProjectReferences().values(); + Set availableProjects = new HashSet<>(refs); + for (MavenProject ref : refs) + { + availableProjects.addAll(findDependenciesInReactor(ref, visitedProjects)); + } + return availableProjects; + } +} diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/main/resources/META-INF/services/org.eclipse.jetty.ee10.webapp.Configuration b/jetty-ee10/jetty-ee10-maven-plugin/src/main/resources/META-INF/services/org.eclipse.jetty.ee10.webapp.Configuration new file mode 100644 index 00000000000..5a7a85aea35 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/main/resources/META-INF/services/org.eclipse.jetty.ee10.webapp.Configuration @@ -0,0 +1,4 @@ +org.eclipse.jetty.ee10.maven.plugin.MavenWebInfConfiguration +org.eclipse.jetty.ee10.maven.plugin.MavenMetaInfConfiguration +#Do not list this because Quickstart is not present for distro +#org.eclipse.jetty.maven.plugin.MavenQuickStartConfiguration diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/main/resources/jetty-maven.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/main/resources/jetty-maven.xml new file mode 100644 index 00000000000..d1ba792557f --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/main/resources/jetty-maven.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/main/resources/maven.mod b/jetty-ee10/jetty-ee10-maven-plugin/src/main/resources/maven.mod new file mode 100644 index 00000000000..8cefce820c2 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/main/resources/maven.mod @@ -0,0 +1,15 @@ +# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html + +[description] +Enables an un-assembled Maven webapp to run in a Jetty distribution. + +[depends] +server +webapp +annotations + +[lib] +lib/maven/**.jar + +[xml] +etc/jetty-maven.xml diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/main/resources/maven.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/main/resources/maven.xml new file mode 100644 index 00000000000..eb4063b82b9 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/main/resources/maven.xml @@ -0,0 +1,17 @@ + + + + + + + + + + /etc/maven.props + + + + + + + diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/site/markdown/index.md b/jetty-ee10/jetty-ee10-maven-plugin/src/site/markdown/index.md new file mode 100644 index 00000000000..9cddd436d39 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/site/markdown/index.md @@ -0,0 +1,10 @@ +Eclipse Jetty Maven Plugin +======================== + +The Jetty Maven plugin is useful for rapid development and testing. + +You can add it to any webapp project that is structured according to the Maven defaults. + +The plugin can then periodically scan your project for changes and automatically redeploy the webapp if any are found. + +This makes the development cycle more productive by eliminating the build and deploy steps: you use your IDE to make changes to the project, and the running web container automatically picks them up, allowing you to test them straight away. diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/site/site.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/site/site.xml new file mode 100644 index 00000000000..d37c6e5f9dc --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/site/site.xml @@ -0,0 +1,44 @@ + + + + + + ${project.name} + https://www.eclipse.org/jetty/images/jetty-logo-80x22.png + https://eclipse.org/jetty/ + + + https://www.eclipse.org/eclipse.org-common/themes/solstice/public/images/logo/eclipse-426x100.png + + + + org.apache.maven.skins + maven-fluido-skin + 1.7 + + + + + true + true + + ${project.url} + + + eclipse/jetty.project + right + gray + + + + + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/test/java/org/eclipse/jetty/ee10/maven/plugin/MockShutdownMonitor.java b/jetty-ee10/jetty-ee10-maven-plugin/src/test/java/org/eclipse/jetty/ee10/maven/plugin/MockShutdownMonitor.java new file mode 100644 index 00000000000..1cb01221982 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/test/java/org/eclipse/jetty/ee10/maven/plugin/MockShutdownMonitor.java @@ -0,0 +1,74 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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-ee10/jetty-ee10-maven-plugin/src/test/java/org/eclipse/jetty/ee10/maven/plugin/MockShutdownMonitorRunnable.java b/jetty-ee10/jetty-ee10-maven-plugin/src/test/java/org/eclipse/jetty/ee10/maven/plugin/MockShutdownMonitorRunnable.java new file mode 100644 index 00000000000..707f5366e09 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/test/java/org/eclipse/jetty/ee10/maven/plugin/MockShutdownMonitorRunnable.java @@ -0,0 +1,111 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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-ee10/jetty-ee10-maven-plugin/src/test/java/org/eclipse/jetty/ee10/maven/plugin/TestForkedChild.java b/jetty-ee10/jetty-ee10-maven-plugin/src/test/java/org/eclipse/jetty/ee10/maven/plugin/TestForkedChild.java new file mode 100644 index 00000000000..05bbb1c9513 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/test/java/org/eclipse/jetty/ee10/maven/plugin/TestForkedChild.java @@ -0,0 +1,178 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Random; + +import org.eclipse.jetty.toolchain.test.FS; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +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.Disabled; +import org.junit.jupiter.api.Test; + +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. + */ + +@Disabled //TODO +public class TestForkedChild +{ + File testDir; + File baseDir; + File 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); + webapp.setResourceBase(baseDir.toPath()); + WebAppPropertyConverter.toProperties(webapp, webappPropsFile, null); + child = new JettyForkedChild(cmd.toArray(new String[cmd.size()])); + child.jetty.setExitVm(false); //ensure jetty doesn't stop vm for testing + child.start(); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + } + } + + @BeforeEach + public void setUp() + { + baseDir = MavenTestingUtils.getTestResourceDir("root"); + testDir = MavenTestingUtils.getTargetTestingDir("forkedChild"); + FS.ensureEmpty(testDir); + tmpDir = new File(testDir, "tmp"); + webappPropsFile = new File(testDir, "webapp.props"); + + String stopPortString = System.getProperty("stop.port"); + assertNotNull(stopPortString, "stop.port System property"); + stopPort = Integer.valueOf(stopPortString); + jettyPortString = System.getProperty("jetty.port"); + assertNotNull(jettyPortString, "jetty.port System property"); + jettyPort = Integer.valueOf(jettyPortString); + + Random random = new Random(); + token = Long.toString(random.nextLong() ^ System.currentTimeMillis(), 36).toUpperCase(Locale.ENGLISH); + tokenFile = testDir.toPath().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 + int attempts = 20; + while (!tokenFile.exists() && attempts > 0) + { + Thread.currentThread().sleep(500); + --attempts; + } + assertThat(attempts, Matchers.greaterThan(0)); + + 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-ee10/jetty-ee10-maven-plugin/src/test/java/org/eclipse/jetty/ee10/maven/plugin/TestJettyEmbedder.java b/jetty-ee10/jetty-ee10-maven-plugin/src/test/java/org/eclipse/jetty/ee10/maven/plugin/TestJettyEmbedder.java new file mode 100644 index 00000000000..cc5d8c79e26 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/test/java/org/eclipse/jetty/ee10/maven/plugin/TestJettyEmbedder.java @@ -0,0 +1,126 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.maven.plugin; + +import java.nio.file.Path; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +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.Disabled; +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; + +@Disabled //TODO +@ExtendWith(WorkDirExtension.class) +public class TestJettyEmbedder +{ + public WorkDir workDir; + + @Test + public void testJettyEmbedderFromDefaults() throws Exception + { + Path baseResource = workDir.getEmptyPathDir(); + MavenWebAppContext webApp = new MavenWebAppContext(); + webApp.setResourceBase(baseResource); + 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() + throws Exception + { + MavenWebAppContext webApp = new MavenWebAppContext(); + Path baseResource = workDir.getEmptyPathDir(); + webApp.setResourceBase(baseResource); + Server server = new Server(); + Map jettyProperties = new HashMap<>(); + jettyProperties.put("jetty.server.dumpAfterStart", "false"); + + ContextHandler otherHandler = new ContextHandler(); + otherHandler.setContextPath("/other"); + otherHandler.setResourceBase(MavenTestingUtils.getTestResourceDir("root").toPath()); + + MavenServerConnector connector = new MavenServerConnector(); + connector.setPort(0); + + JettyEmbedder jetty = new JettyEmbedder(); + jetty.setHttpConnector(connector); + jetty.setExitVm(false); + jetty.setServer(server); + jetty.setContextHandlers(Arrays.asList(otherHandler)); + jetty.setRequestLog(null); + jetty.setJettyXmlFiles(Arrays.asList(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)); + } + finally + { + jetty.stop(); + } + } +} diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/test/java/org/eclipse/jetty/ee10/maven/plugin/TestJettyStopMojo.java b/jetty-ee10/jetty-ee10-maven-plugin/src/test/java/org/eclipse/jetty/ee10/maven/plugin/TestJettyStopMojo.java new file mode 100644 index 00000000000..d161753356e --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/test/java/org/eclipse/jetty/ee10/maven/plugin/TestJettyStopMojo.java @@ -0,0 +1,306 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.maven.plugin; + +import java.io.File; +import java.io.FileReader; +import java.io.LineNumberReader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jetty.server.ShutdownMonitor; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@Disabled //TODO +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 + { + //TODO: needs visibility of ShutdownMonitor + /*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 " + Long.toString(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(); + + Thread.sleep(500); + while (!file.exists() && file.length() == 0) + { + Thread.sleep(300); + } + + String tmp = ""; + String port = null; + try (LineNumberReader reader = new LineNumberReader(new FileReader(file))) + { + while (port == null && tmp != null) + { + tmp = reader.readLine(); + if (tmp != null) + { + if (tmp.startsWith("STOP.PORT=")) + port = tmp.substring(10); + } + } + } + + assertNotNull(port); + + TestLog log = new TestLog(); + JettyStopMojo mojo = new JettyStopMojo(); + mojo.stopWait = 5; + mojo.stopKey = stopKey; + mojo.stopPort = Integer.parseInt(port); + 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-ee10/jetty-ee10-maven-plugin/src/test/java/org/eclipse/jetty/ee10/maven/plugin/TestQuickStartGenerator.java b/jetty-ee10/jetty-ee10-maven-plugin/src/test/java/org/eclipse/jetty/ee10/maven/plugin/TestQuickStartGenerator.java new file mode 100644 index 00000000000..f02df3f1cbf --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/test/java/org/eclipse/jetty/ee10/maven/plugin/TestQuickStartGenerator.java @@ -0,0 +1,50 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.maven.plugin; + +import java.io.File; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * + * + */ +public class TestQuickStartGenerator +{ + @Test + public void testGenerator() throws Exception + { + MavenWebAppContext webApp = new MavenWebAppContext(); + webApp.setContextPath("/shouldbeoverridden"); + webApp.setResourceBase(MavenTestingUtils.getTestResourceDir("root").toPath()); + File quickstartFile = new File(MavenTestingUtils.getTargetTestingDir(), "quickstart-web.xml"); + QuickStartGenerator generator = new QuickStartGenerator(quickstartFile, webApp); + generator.setContextXml(MavenTestingUtils.getTestResourceFile("embedder-context.xml").getAbsolutePath()); + generator.setServer(new Server()); + MavenTestingUtils.getTargetTestingDir().mkdirs(); + File propsFile = new File(MavenTestingUtils.getTargetTestingDir(), "webapp.props"); + propsFile.createNewFile(); + generator.setWebAppPropsFile(propsFile); + generator.generate(); + assertTrue(propsFile.exists()); + assertTrue(propsFile.length() > 0); + assertTrue(quickstartFile.exists()); + assertTrue(quickstartFile.length() > 0); + } +} diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/test/java/org/eclipse/jetty/ee10/maven/plugin/TestSelectiveJarResource.java b/jetty-ee10/jetty-ee10-maven-plugin/src/test/java/org/eclipse/jetty/ee10/maven/plugin/TestSelectiveJarResource.java new file mode 100644 index 00000000000..04463d49eb3 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/test/java/org/eclipse/jetty/ee10/maven/plugin/TestSelectiveJarResource.java @@ -0,0 +1,110 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.maven.plugin; + +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +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.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@ExtendWith(WorkDirExtension.class) +public class TestSelectiveJarResource +{ + public WorkDir workDir; + + @Test + public void testIncludesNoExcludes() throws Exception + { + Path unpackDir = workDir.getEmptyPathDir(); + + Path testJar = MavenTestingUtils.getTestResourcePathFile("selective-jar-test.jar"); + try (SelectiveJarResource sjr = new SelectiveJarResource(new URL("jar:" + testJar.toUri().toASCIIString() + "!/"))) + { + sjr.setCaseSensitive(false); + List includes = new ArrayList<>(); + includes.add("**/*.html"); + sjr.setIncludes(includes); + sjr.copyTo(unpackDir.toFile()); + 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() throws Exception + { + Path unpackDir = workDir.getEmptyPathDir(); + + Path testJar = MavenTestingUtils.getTestResourcePathFile("selective-jar-test.jar"); + try (SelectiveJarResource sjr = new SelectiveJarResource(new URL("jar:" + testJar.toUri().toASCIIString() + "!/"))) + { + sjr.setCaseSensitive(false); + List excludes = new ArrayList<>(); + excludes.add("**/*"); + sjr.setExcludes(excludes); + sjr.copyTo(unpackDir.toFile()); + 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() throws Exception + { + Path unpackDir = workDir.getEmptyPathDir(); + + Path testJar = MavenTestingUtils.getTestResourcePathFile("selective-jar-test.jar"); + try (SelectiveJarResource sjr = new SelectiveJarResource(new URL("jar:" + testJar.toUri().toASCIIString() + "!/"))) + { + 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.toFile()); + 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-ee10/jetty-ee10-maven-plugin/src/test/java/org/eclipse/jetty/ee10/maven/plugin/TestWebAppPropertyConverter.java b/jetty-ee10/jetty-ee10-maven-plugin/src/test/java/org/eclipse/jetty/ee10/maven/plugin/TestWebAppPropertyConverter.java new file mode 100644 index 00000000000..575c8452f58 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/test/java/org/eclipse/jetty/ee10/maven/plugin/TestWebAppPropertyConverter.java @@ -0,0 +1,157 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.maven.plugin; + +import java.io.File; +import java.io.FileInputStream; +import java.util.Arrays; +import java.util.Properties; + +import org.eclipse.jetty.ee10.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.Resource; +import org.eclipse.jetty.util.resource.ResourceCollection; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.instanceOf; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@Disabled //TODO +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.setResourceBase(MavenTestingUtils.getTestResourceDir("root").toPath()); + webApp.setTempDirectory(tmpDir); + webApp.setPersistTempDirectory(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.isPersistTempDirectory()); + assertEquals(war.getAbsolutePath(), webApp.getWar()); + assertEquals(webXml.getAbsolutePath(), webApp.getDescriptor()); + assertThat(webApp.getBaseResource(), instanceOf(ResourceCollection.class)); + assertThat(webApp.getBaseResource().toString(), Matchers.containsString(Resource.newResource(base1).toString())); + assertThat(webApp.getBaseResource().toString(), Matchers.containsString(Resource.newResource(base2).toString())); + } +} diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/test/java/org/eclipse/jetty/ee10/maven/plugin/it/IntegrationTestGetContent.java b/jetty-ee10/jetty-ee10-maven-plugin/src/test/java/org/eclipse/jetty/ee10/maven/plugin/it/IntegrationTestGetContent.java new file mode 100644 index 00000000000..ce5dbc442cd --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/test/java/org/eclipse/jetty/ee10/maven/plugin/it/IntegrationTestGetContent.java @@ -0,0 +1,133 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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 org.apache.commons.lang3.StringUtils; +import org.eclipse.jetty.client.HttpClient; +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 (StringUtils.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 + { + int attempts = 70; + int port = -1; + 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); + while (true) + { + if (Files.exists(p)) + { + try (Reader r = Files.newBufferedReader(p); + LineNumberReader lnr = new LineNumberReader(r);) + { + s = lnr.readLine(); + assertNotNull(s); + port = Integer.parseInt(s.trim()); + } + break; + } + else + { + if (--attempts < 0) + { + break; + } + else + { + Thread.sleep(1000); + } + System.err.printf(" attempts left: #%d%n", attempts); + } + } + return port; + } +} diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/test/resources/embedder-context.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/test/resources/embedder-context.xml new file mode 100644 index 00000000000..3f1522da5a9 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/test/resources/embedder-context.xml @@ -0,0 +1,6 @@ + + + + + /embedder + diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/test/resources/embedder-jetty.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/test/resources/embedder-jetty.xml new file mode 100644 index 00000000000..653542f48b5 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/test/resources/embedder-jetty.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/test/resources/jetty-logging.properties b/jetty-ee10/jetty-ee10-maven-plugin/src/test/resources/jetty-logging.properties new file mode 100644 index 00000000000..c1e6a540a53 --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/test/resources/jetty-logging.properties @@ -0,0 +1,5 @@ +# 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 diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/test/resources/root/index.html b/jetty-ee10/jetty-ee10-maven-plugin/src/test/resources/root/index.html new file mode 100644 index 00000000000..e8d4c65dd0d --- /dev/null +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/test/resources/root/index.html @@ -0,0 +1 @@ +

    ROOT

    \ No newline at end of file diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/test/resources/selective-jar-test.jar b/jetty-ee10/jetty-ee10-maven-plugin/src/test/resources/selective-jar-test.jar new file mode 100644 index 0000000000000000000000000000000000000000..dc7dc4b596c72c26dd61e2587afabf5893b2634f GIT binary patch literal 1577 zcmZ{jPe@cz6vnSJqm2(zkO^`TM6?ifW^6`x35_xX)AQ-`&(_78Q4mR#QZNE>{_{%5vquxxUY%8}(@KZE{&e7R zb+sxT-2O55;vUo=fhk=bs8{QE0?9U#saJk zg0iwk7jfMv zxN$Q~lqVE7fzBX21}NJLtY8T>ibhGM3ZLLn=9=7MvUzNACUk1>NIBB#fxV7_^H!&WyG?D>~TGcl8k`JuA|oD4d-M1bcw+~`OFI{4fm zrkqDO%-w`Q;KKZCuu;It;52YD?({__5N@=lfFS + + org.eclipse.jetty.ee10 + jetty-ee10 + 12.0.0-SNAPSHOT + + + 4.0.0 + jetty-ee10-openid + EE10 :: Jetty :: OpenID + Jetty OpenID Connect infrastructure + + + ${project.groupId}.openid + org.eclipse.jetty.security.openid.* + + + + + + org.apache.felix + maven-bundle-plugin + true + + + + manifest + + + + osgi.extender; filter:="(osgi.extender=osgi.serviceloader.registrar)" + osgi.serviceloader;osgi.serviceloader=org.eclipse.jetty.security.Authenticator$Factory + + + + + + + + + + + org.eclipse.jetty + jetty-server + + + org.eclipse.jetty + jetty-client + + + org.eclipse.jetty.ee10 + jetty-ee10-servlet + + + org.eclipse.jetty + jetty-util-ajax + + + org.slf4j + slf4j-api + + + org.eclipse.jetty + jetty-slf4j-impl + test + + + org.eclipse.jetty.toolchain + jetty-test-helper + test + + + diff --git a/jetty-ee10/jetty-ee10-openid/src/main/config/etc/jetty-openid.xml b/jetty-ee10/jetty-ee10-openid/src/main/config/etc/jetty-openid.xml new file mode 100644 index 00000000000..5c68b5cfdd9 --- /dev/null +++ b/jetty-ee10/jetty-ee10-openid/src/main/config/etc/jetty-openid.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-openid/src/main/config/modules/openid.mod b/jetty-ee10/jetty-ee10-openid/src/main/config/modules/openid.mod new file mode 100644 index 00000000000..c1070b29910 --- /dev/null +++ b/jetty-ee10/jetty-ee10-openid/src/main/config/modules/openid.mod @@ -0,0 +1,47 @@ +# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html + +[description] +Adds OpenId Connect authentication to the server. + +[depend] +security +client + +[lib] +lib/jetty-openid-${jetty.version}.jar +lib/jetty-util-ajax-${jetty.version}.jar + +[files] +basehome:modules/openid/openid-baseloginservice.xml|etc/openid-baseloginservice.xml + +[xml] +etc/openid-baseloginservice.xml +etc/jetty-openid.xml + +[ini-template] +## The OpenID Identity Provider's issuer ID (the entire URL *before* ".well-known/openid-configuration") +# jetty.openid.provider=https://id.example.com/ + +## The OpenID Identity Provider's authorization endpoint (optional if the metadata of the OP is accessible) +# jetty.openid.provider.authorizationEndpoint=https://id.example.com/authorization + +## The OpenID Identity Provider's token endpoint (optional if the metadata of the OP is accessible) +# jetty.openid.provider.tokenEndpoint=https://id.example.com/token + +## The Client Identifier +# jetty.openid.clientId=test1234 + +## The Client Secret +# jetty.openid.clientSecret=XT_Mafv_aUCGheuCaKY8P + +## Additional Scopes to Request +# jetty.openid.scopes=email,profile + +## Whether to Authenticate users not found by base LoginService +# jetty.openid.authenticateNewUsers=false + +## True if all certificates should be trusted by the default SslContextFactory +# jetty.openid.sslContextFactory.trustAll=false + +## What authentication method to use with the Token Endpoint (client_secret_post, client_secret_basic). +# jetty.openid.authMethod=client_secret_post diff --git a/jetty-ee10/jetty-ee10-openid/src/main/config/modules/openid/openid-baseloginservice.xml b/jetty-ee10/jetty-ee10-openid/src/main/config/modules/openid/openid-baseloginservice.xml new file mode 100644 index 00000000000..1773ebd46db --- /dev/null +++ b/jetty-ee10/jetty-ee10-openid/src/main/config/modules/openid/openid-baseloginservice.xml @@ -0,0 +1,10 @@ + + + + + \ No newline at end of file diff --git a/jetty-ee10/jetty-ee10-openid/src/main/java/module-info.java b/jetty-ee10/jetty-ee10-openid/src/main/java/module-info.java new file mode 100644 index 00000000000..0e7c7925876 --- /dev/null +++ b/jetty-ee10/jetty-ee10-openid/src/main/java/module-info.java @@ -0,0 +1,28 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 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.ee10.security.openid.OpenIdAuthenticatorFactory; +import org.eclipse.jetty.ee10.servlet.security.Authenticator; + +module org.eclipse.jetty.security.openid +{ + requires org.eclipse.jetty.util.ajax; + + requires transitive org.eclipse.jetty.client; + requires org.eclipse.jetty.ee10.servlet; + requires org.slf4j; + + exports org.eclipse.jetty.ee10.security.openid; + + provides Authenticator.Factory with OpenIdAuthenticatorFactory; +} diff --git a/jetty-ee10/jetty-ee10-openid/src/main/java/org/eclipse/jetty/ee10/security/openid/JwtDecoder.java b/jetty-ee10/jetty-ee10-openid/src/main/java/org/eclipse/jetty/ee10/security/openid/JwtDecoder.java new file mode 100644 index 00000000000..c6cba06ff96 --- /dev/null +++ b/jetty-ee10/jetty-ee10-openid/src/main/java/org/eclipse/jetty/ee10/security/openid/JwtDecoder.java @@ -0,0 +1,101 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.security.openid; + +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Base64; +import java.util.Map; + +import org.eclipse.jetty.util.ajax.JSON; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Used to decode the ID Token from the base64 encrypted JSON Web Token (JWT). + */ +public class JwtDecoder +{ + private static final Logger LOG = LoggerFactory.getLogger(JwtDecoder.class); + + /** + * Decodes a JSON Web Token (JWT) into a Map of claims. + * + * @param jwt the JWT to decode. + * @return the map of claims encoded in the JWT. + */ + @SuppressWarnings("unchecked") + public static Map decode(String jwt) + { + if (LOG.isDebugEnabled()) + LOG.debug("decode {}", jwt); + + String[] sections = jwt.split("\\."); + if (sections.length != 3) + throw new IllegalArgumentException("JWT does not contain 3 sections"); + + Base64.Decoder decoder = Base64.getUrlDecoder(); + String jwtHeaderString = new String(decoder.decode(padJWTSection(sections[0])), StandardCharsets.UTF_8); + String jwtClaimString = new String(decoder.decode(padJWTSection(sections[1])), StandardCharsets.UTF_8); + String jwtSignature = sections[2]; + + JSON json = new JSON(); + + Object parsedJwtHeader = json.fromJSON(jwtHeaderString); + if (!(parsedJwtHeader instanceof Map)) + throw new IllegalStateException("Invalid JWT header"); + Map jwtHeader = (Map)parsedJwtHeader; + if (LOG.isDebugEnabled()) + LOG.debug("JWT Header: {}", jwtHeader); + + /* If the ID Token is received via direct communication between the Client + and the Token Endpoint (which it is in this flow), the TLS server validation + MAY be used to validate the issuer in place of checking the token signature. */ + if (LOG.isDebugEnabled()) + LOG.debug("JWT signature not validated {}", jwtSignature); + + Object parsedClaims = json.fromJSON(jwtClaimString); + if (!(parsedClaims instanceof Map)) + throw new IllegalStateException("Could not decode JSON for JWT claims."); + return (Map)parsedClaims; + } + + static byte[] padJWTSection(String unpaddedEncodedJwtSection) + { + // If already padded just use what we are given. + if (unpaddedEncodedJwtSection.endsWith("=")) + return unpaddedEncodedJwtSection.getBytes(); + + int length = unpaddedEncodedJwtSection.length(); + int remainder = length % 4; + + // A valid base-64-encoded string will have a remainder of 0, 2 or 3. Never 1! + if (remainder == 1) + throw new IllegalArgumentException("Not a valid Base64-encoded string"); + + byte[] paddedEncodedJwtSection; + if (remainder > 0) + { + int paddingNeeded = (4 - remainder) % 4; + paddedEncodedJwtSection = Arrays.copyOf(unpaddedEncodedJwtSection.getBytes(), length + paddingNeeded); + Arrays.fill(paddedEncodedJwtSection, length, paddedEncodedJwtSection.length, (byte)'='); + } + else + { + paddedEncodedJwtSection = unpaddedEncodedJwtSection.getBytes(); + } + + return paddedEncodedJwtSection; + } +} diff --git a/jetty-ee10/jetty-ee10-openid/src/main/java/org/eclipse/jetty/ee10/security/openid/OpenIdAuthConfiguration.java b/jetty-ee10/jetty-ee10-openid/src/main/java/org/eclipse/jetty/ee10/security/openid/OpenIdAuthConfiguration.java new file mode 100644 index 00000000000..adc0068b4f4 --- /dev/null +++ b/jetty-ee10/jetty-ee10-openid/src/main/java/org/eclipse/jetty/ee10/security/openid/OpenIdAuthConfiguration.java @@ -0,0 +1,52 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.security.openid; + +import org.eclipse.jetty.ee10.servlet.security.Authenticator; +import org.eclipse.jetty.ee10.servlet.security.LoginService; +import org.eclipse.jetty.ee10.servlet.security.WrappedAuthConfiguration; + +/** + *

    This class is used to wrap the {@link Authenticator.AuthConfiguration} given to the {@link OpenIdAuthenticator}.

    + *

    When {@link #getLoginService()} method is called, this implementation will always return an instance of + * {@link OpenIdLoginService}. This allows you to configure an {@link OpenIdAuthenticator} using a {@code null} + * LoginService or any alternative LoginService implementation which will be wrapped by the OpenIdLoginService

    + */ +public class OpenIdAuthConfiguration extends WrappedAuthConfiguration +{ + private final OpenIdLoginService _openIdLoginService; + + public OpenIdAuthConfiguration(OpenIdConfiguration openIdConfiguration, Authenticator.AuthConfiguration authConfiguration) + { + super(authConfiguration); + + LoginService loginService = authConfiguration.getLoginService(); + if (loginService instanceof OpenIdLoginService) + { + _openIdLoginService = (OpenIdLoginService)loginService; + } + else + { + _openIdLoginService = new OpenIdLoginService(openIdConfiguration, loginService); + if (loginService == null) + _openIdLoginService.setIdentityService(authConfiguration.getIdentityService()); + } + } + + @Override + public LoginService getLoginService() + { + return _openIdLoginService; + } +} diff --git a/jetty-ee10/jetty-ee10-openid/src/main/java/org/eclipse/jetty/ee10/security/openid/OpenIdAuthenticator.java b/jetty-ee10/jetty-ee10-openid/src/main/java/org/eclipse/jetty/ee10/security/openid/OpenIdAuthenticator.java new file mode 100644 index 00000000000..a12ee1b2120 --- /dev/null +++ b/jetty-ee10/jetty-ee10-openid/src/main/java/org/eclipse/jetty/ee10/security/openid/OpenIdAuthenticator.java @@ -0,0 +1,671 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.security.openid; + +import java.io.IOException; +import java.io.Serializable; +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.security.SecureRandom; +import java.util.LinkedHashMap; +import java.util.Map; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; +import org.eclipse.jetty.ee10.servlet.ServletContextRequest; +import org.eclipse.jetty.ee10.servlet.security.Authentication; +import org.eclipse.jetty.ee10.servlet.security.Authenticator; +import org.eclipse.jetty.ee10.servlet.security.LoginService; +import org.eclipse.jetty.ee10.servlet.security.ServerAuthException; +import org.eclipse.jetty.ee10.servlet.security.UserAuthentication; +import org.eclipse.jetty.ee10.servlet.security.UserIdentity; +import org.eclipse.jetty.ee10.servlet.security.authentication.DeferredAuthentication; +import org.eclipse.jetty.ee10.servlet.security.authentication.LoginAuthenticator; +import org.eclipse.jetty.ee10.servlet.security.authentication.SessionAuthentication; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpVersion; +import org.eclipse.jetty.http.MimeTypes; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Response; +import org.eclipse.jetty.util.Callback; +import org.eclipse.jetty.util.MultiMap; +import org.eclipse.jetty.util.URIUtil; +import org.eclipse.jetty.util.UrlEncoded; +import org.eclipse.jetty.util.security.Constraint; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + *

    Implements authentication using OpenId Connect on top of OAuth 2.0. + * + *

    The OpenIdAuthenticator redirects unauthenticated requests to the OpenID Connect Provider. The End-User is + * eventually redirected back with an Authorization Code to the path set by {@link #setRedirectPath(String)} within the context. + * The Authorization Code is then used to authenticate the user through the {@link OpenIdCredentials} and {@link OpenIdLoginService}. + *

    + *

    + * Once a user is authenticated the OpenID Claims can be retrieved through an attribute on the session with the key {@link #CLAIMS}. + * The full response containing the OAuth 2.0 Access Token can be obtained with the session attribute {@link #RESPONSE}. + *

    + *

    {@link SessionAuthentication} is then used to wrap Authentication results so that they are associated with the session.

    + */ +public class OpenIdAuthenticator extends LoginAuthenticator +{ + private static final Logger LOG = LoggerFactory.getLogger(OpenIdAuthenticator.class); + + public static final String CLAIMS = "org.eclipse.jetty.security.openid.claims"; + public static final String RESPONSE = "org.eclipse.jetty.security.openid.response"; + public static final String ISSUER = "org.eclipse.jetty.security.openid.issuer"; + public static final String REDIRECT_PATH = "org.eclipse.jetty.security.openid.redirect_path"; + public static final String ERROR_PAGE = "org.eclipse.jetty.security.openid.error_page"; + public static final String J_URI = "org.eclipse.jetty.security.openid.URI"; + public static final String J_POST = "org.eclipse.jetty.security.openid.POST"; + public static final String J_METHOD = "org.eclipse.jetty.security.openid.METHOD"; + public static final String J_SECURITY_CHECK = "/j_security_check"; + public static final String ERROR_PARAMETER = "error_description_jetty"; + private static final String CSRF_MAP = "org.eclipse.jetty.security.openid.csrf_map"; + + @Deprecated + public static final String CSRF_TOKEN = "org.eclipse.jetty.security.openid.csrf_token"; + + private final SecureRandom _secureRandom = new SecureRandom(); + private OpenIdConfiguration _openIdConfiguration; + private String _redirectPath; + private String _errorPage; + private String _errorPath; + private String _errorQuery; + private boolean _alwaysSaveUri; + + public OpenIdAuthenticator() + { + this(null, J_SECURITY_CHECK, null); + } + + public OpenIdAuthenticator(OpenIdConfiguration configuration) + { + this(configuration, J_SECURITY_CHECK, null); + } + + public OpenIdAuthenticator(OpenIdConfiguration configuration, String errorPage) + { + this(configuration, J_SECURITY_CHECK, errorPage); + } + + public OpenIdAuthenticator(OpenIdConfiguration configuration, String redirectPath, String errorPage) + { + _openIdConfiguration = configuration; + setRedirectPath(redirectPath); + if (errorPage != null) + setErrorPage(errorPage); + } + + @Override + public void setConfiguration(Authenticator.AuthConfiguration authConfig) + { + if (_openIdConfiguration == null) + { + LoginService loginService = authConfig.getLoginService(); + if (!(loginService instanceof OpenIdLoginService)) + throw new IllegalArgumentException("invalid LoginService " + loginService); + this._openIdConfiguration = ((OpenIdLoginService)loginService).getConfiguration(); + } + + String redirectPath = authConfig.getInitParameter(REDIRECT_PATH); + if (redirectPath != null) + _redirectPath = redirectPath; + + String error = authConfig.getInitParameter(ERROR_PAGE); + if (error != null) + setErrorPage(error); + + super.setConfiguration(new OpenIdAuthConfiguration(_openIdConfiguration, authConfig)); + } + + @Override + public String getAuthMethod() + { + return Constraint.__OPENID_AUTH; + } + + @Deprecated + public void setAlwaysSaveUri(boolean alwaysSave) + { + _alwaysSaveUri = alwaysSave; + } + + @Deprecated + public boolean isAlwaysSaveUri() + { + return _alwaysSaveUri; + } + + public void setRedirectPath(String redirectPath) + { + if (redirectPath == null) + { + LOG.warn("redirect path must not be null, defaulting to " + J_SECURITY_CHECK); + redirectPath = J_SECURITY_CHECK; + } + else if (!redirectPath.startsWith("/")) + { + LOG.warn("redirect path must start with /"); + redirectPath = "/" + redirectPath; + } + + _redirectPath = redirectPath; + } + + public void setErrorPage(String path) + { + if (path == null || path.trim().length() == 0) + { + _errorPath = null; + _errorPage = null; + } + else + { + if (!path.startsWith("/")) + { + LOG.warn("error-page must start with /"); + path = "/" + path; + } + _errorPage = path; + _errorPath = path; + _errorQuery = ""; + + int queryIndex = _errorPath.indexOf('?'); + if (queryIndex > 0) + { + _errorPath = _errorPage.substring(0, queryIndex); + _errorQuery = _errorPage.substring(queryIndex + 1); + } + } + } + + @Override + public UserIdentity login(String username, Object credentials, Request request) + { + if (LOG.isDebugEnabled()) + LOG.debug("login {} {} {}", username, credentials, request); + + UserIdentity user = super.login(username, credentials, request); + if (user != null) + { + ServletContextRequest servletContextRequest = Request.as(request, ServletContextRequest.class); + HttpSession session = servletContextRequest.getHttpServletRequest().getSession(); + Authentication cached = new SessionAuthentication(getAuthMethod(), user, credentials); + synchronized (session) + { + session.setAttribute(SessionAuthentication.__J_AUTHENTICATED, cached); + session.setAttribute(CLAIMS, ((OpenIdCredentials)credentials).getClaims()); + session.setAttribute(RESPONSE, ((OpenIdCredentials)credentials).getResponse()); + session.setAttribute(ISSUER, _openIdConfiguration.getIssuer()); + } + } + return user; + } + + @Override + public void logout(Request request) + { + super.logout(request); + ServletContextRequest servletContextRequest = Request.as(request, ServletContextRequest.class); + HttpServletRequest httpRequest = servletContextRequest.getHttpServletRequest(); + HttpSession session = httpRequest.getSession(false); + + if (session == null) + return; + + synchronized (session) + { + session.removeAttribute(SessionAuthentication.__J_AUTHENTICATED); + session.removeAttribute(CLAIMS); + session.removeAttribute(RESPONSE); + } + } + + @Override + public void prepareRequest(Request request) + { + //if this is a request resulting from a redirect after auth is complete + //(ie its from a redirect to the original request uri) then due to + //browser handling of 302 redirects, the method may not be the same as + //that of the original request. Replace the method and original post + //params (if it was a post). + // + //See Servlet Spec 3.1 sec 13.6.3 + ServletContextRequest servletContextRequest = Request.as(request, ServletContextRequest.class); + HttpServletRequest httpRequest = servletContextRequest.getHttpServletRequest(); + HttpSession session = httpRequest.getSession(false); + if (session == null) + return; //not authenticated yet + + String juri; + String method; + synchronized (session) + { + if (session.getAttribute(SessionAuthentication.__J_AUTHENTICATED) == null) + return; //not authenticated yet + + juri = (String)session.getAttribute(J_URI); + if (juri == null || juri.length() == 0) + return; //no original uri saved + + method = (String)session.getAttribute(J_METHOD); + if (method == null || method.length() == 0) + return; //didn't save original request method + } + + StringBuffer buf = httpRequest.getRequestURL(); + if (httpRequest.getQueryString() != null) + buf.append("?").append(httpRequest.getQueryString()); + + if (!juri.equals(buf.toString())) + return; //this request is not for the same url as the original + + // Restore the original request's method on this request. + if (LOG.isDebugEnabled()) + LOG.debug("Restoring original method {} for {} with method {}", method, juri, httpRequest.getMethod()); + + /* + TODO: Need to wrap the request for this. + Request baseRequest = Objects.requireNonNull(Request.getBaseRequest(request)); + baseRequest.setMethod(method); + */ + } + + @Override + public Authentication validateRequest(Request req, Response res, Callback cb, boolean mandatory) throws ServerAuthException + { + ServletContextRequest servletContextRequest = Request.as(req, ServletContextRequest.class); + final HttpServletRequest request = servletContextRequest.getHttpServletRequest(); + final HttpServletResponse response = servletContextRequest.getHttpServletResponse(); + final Request baseRequest = req; + final Response baseResponse = res; + + if (LOG.isDebugEnabled()) + LOG.debug("validateRequest({},{},{})", req, res, mandatory); + + String uri = request.getRequestURI(); + if (uri == null) + uri = URIUtil.SLASH; + + mandatory |= isJSecurityCheck(uri); + if (!mandatory) + return new DeferredAuthentication(this); + + if (isErrorPage(baseRequest.getPathInContext()) && !DeferredAuthentication.isDeferred(res)) + return new DeferredAuthentication(this); + + try + { + // Get the Session. + HttpSession session = request.getSession(); + if (request.isRequestedSessionIdFromURL()) + { + sendError(req, res, cb, "Session ID must be a cookie to support OpenID authentication"); + return Authentication.SEND_FAILURE; + } + + // Handle a request for authentication. + if (isJSecurityCheck(uri)) + { + String authCode = request.getParameter("code"); + if (authCode == null) + { + sendError(req, res, cb, "auth failed: no code parameter"); + return Authentication.SEND_FAILURE; + } + + String state = request.getParameter("state"); + if (state == null) + { + sendError(req, res, cb, "auth failed: no state parameter"); + return Authentication.SEND_FAILURE; + } + + // Verify anti-forgery state token. + UriRedirectInfo uriRedirectInfo; + synchronized (session) + { + uriRedirectInfo = removeAndClearCsrfMap(session, state); + } + if (uriRedirectInfo == null) + { + sendError(req, res, cb, "auth failed: invalid state parameter"); + return Authentication.SEND_FAILURE; + } + + // Attempt to login with the provided authCode. + OpenIdCredentials credentials = new OpenIdCredentials(authCode, getRedirectUri(request)); + UserIdentity user = login(null, credentials, req); + if (user == null) + { + sendError(req, res, cb, null); + return Authentication.SEND_FAILURE; + } + + OpenIdAuthentication openIdAuth = new OpenIdAuthentication(getAuthMethod(), user); + if (LOG.isDebugEnabled()) + LOG.debug("authenticated {}->{}", openIdAuth, uriRedirectInfo.getUri()); + + // Save redirect info in session so original request can be restored after redirect. + synchronized (session) + { + session.setAttribute(J_URI, uriRedirectInfo.getUri()); + session.setAttribute(J_METHOD, uriRedirectInfo.getMethod()); + session.setAttribute(J_POST, uriRedirectInfo.getFormParameters()); + } + + // Redirect to the original URI. + response.setContentLength(0); + int redirectCode = req.getConnectionMetaData().getHttpVersion().getVersion() < HttpVersion.HTTP_1_1.getVersion() + ? HttpServletResponse.SC_MOVED_TEMPORARILY : HttpServletResponse.SC_SEE_OTHER; + Response.sendRedirect(req, res, cb, redirectCode, uriRedirectInfo.getUri(), true); + return openIdAuth; + } + + // Look for cached authentication in the Session. + Authentication authentication = (Authentication)session.getAttribute(SessionAuthentication.__J_AUTHENTICATED); + if (authentication != null) + { + // Has authentication been revoked? + if (authentication instanceof Authentication.User && _loginService != null && + !_loginService.validate(((Authentication.User)authentication).getUserIdentity())) + { + if (LOG.isDebugEnabled()) + LOG.debug("auth revoked {}", authentication); + synchronized (session) + { + session.removeAttribute(SessionAuthentication.__J_AUTHENTICATED); + } + } + else + { + synchronized (session) + { + String jUri = (String)session.getAttribute(J_URI); + if (jUri != null) + { + // Check if the request is for the same url as the original and restore params if it was a post. + if (LOG.isDebugEnabled()) + LOG.debug("auth retry {}->{}", authentication, jUri); + StringBuffer buf = request.getRequestURL(); + if (request.getQueryString() != null) + buf.append("?").append(request.getQueryString()); + + if (jUri.equals(buf.toString())) + { + @SuppressWarnings("unchecked") + MultiMap jPost = (MultiMap)session.getAttribute(J_POST); + if (jPost != null) + { + if (LOG.isDebugEnabled()) + LOG.debug("auth rePOST {}->{}", authentication, jUri); + // TODO: + // baseRequest.setContentParameters(jPost); + } + session.removeAttribute(J_URI); + session.removeAttribute(J_METHOD); + session.removeAttribute(J_POST); + } + } + } + } + if (LOG.isDebugEnabled()) + LOG.debug("auth {}", authentication); + return authentication; + } + + // If we can't send challenge. + if (DeferredAuthentication.isDeferred(res)) + { + if (LOG.isDebugEnabled()) + LOG.debug("auth deferred {}", session.getId()); + return Authentication.UNAUTHENTICATED; + } + + // Send the the challenge. + String challengeUri = getChallengeUri(baseRequest); + if (LOG.isDebugEnabled()) + LOG.debug("challenge {}->{}", session.getId(), challengeUri); + int redirectCode = req.getConnectionMetaData().getHttpVersion().getVersion() < HttpVersion.HTTP_1_1.getVersion() + ? HttpServletResponse.SC_MOVED_TEMPORARILY : HttpServletResponse.SC_SEE_OTHER; + Response.sendRedirect(req, res, cb, redirectCode, challengeUri, true); + + return Authentication.SEND_CONTINUE; + } + catch (IOException e) + { + throw new ServerAuthException(e); + } + } + + /** + * Report an error case either by redirecting to the error page if it is defined, otherwise sending a 403 response. + * If the message parameter is not null, a query parameter with a key of {@link #ERROR_PARAMETER} and value of the error + * message will be logged and added to the error redirect URI if the error page is defined. + * @param request the request. + * @param response the response. + * @param message the reason for the error or null. + * @throws IOException if sending the error fails for any reason. + */ + private void sendError(Request request, Response response, Callback callback, String message) throws IOException + { + ServletContextRequest servletContextRequest = Request.as(request, ServletContextRequest.class); + final HttpServletRequest httpServletRequest = servletContextRequest.getHttpServletRequest(); + final HttpServletResponse httpServletResponse = servletContextRequest.getHttpServletResponse(); + + if (LOG.isDebugEnabled()) + LOG.debug("OpenId authentication FAILED: {}", message); + + if (_errorPage == null) + { + if (LOG.isDebugEnabled()) + LOG.debug("auth failed 403"); + if (response != null) + httpServletResponse.sendError(HttpServletResponse.SC_FORBIDDEN); + } + else + { + if (LOG.isDebugEnabled()) + LOG.debug("auth failed {}", _errorPage); + + String redirectUri = URIUtil.addPaths(httpServletRequest.getContextPath(), _errorPage); + if (message != null) + { + String query = URIUtil.addQueries(ERROR_PARAMETER + "=" + UrlEncoded.encodeString(message), _errorQuery); + redirectUri = URIUtil.addPathQuery(URIUtil.addPaths(httpServletRequest.getContextPath(), _errorPath), query); + } + + int redirectCode = request.getConnectionMetaData().getHttpVersion().getVersion() < HttpVersion.HTTP_1_1.getVersion() + ? HttpServletResponse.SC_MOVED_TEMPORARILY : HttpServletResponse.SC_SEE_OTHER; + Response.sendRedirect(request, response, callback, redirectCode, redirectUri, true); + } + } + + public boolean isJSecurityCheck(String uri) + { + int jsc = uri.indexOf(_redirectPath); + + if (jsc < 0) + return false; + int e = jsc + _redirectPath.length(); + if (e == uri.length()) + return true; + char c = uri.charAt(e); + return c == ';' || c == '#' || c == '/' || c == '?'; + } + + public boolean isErrorPage(String pathInContext) + { + return pathInContext != null && (pathInContext.equals(_errorPath)); + } + + private String getRedirectUri(Request request) + { + final StringBuffer redirectUri = new StringBuffer(128); + URIUtil.appendSchemeHostPort(redirectUri, request.getHttpURI().getScheme(), + Request.getServerName(request), Request.getServerPort(request)); + redirectUri.append(URIUtil.addPaths(request.getContext().getContextPath(), _redirectPath)); + return redirectUri.toString(); + } + + private String getRedirectUri(HttpServletRequest request) + { + final StringBuffer redirectUri = new StringBuffer(128); + URIUtil.appendSchemeHostPort(redirectUri, request.getScheme(), + request.getServerName(), request.getServerPort()); + redirectUri.append(request.getContextPath()); + redirectUri.append(_redirectPath); + return redirectUri.toString(); + } + + protected String getChallengeUri(Request request) + { + ServletContextRequest servletContextRequest = Request.as(request, ServletContextRequest.class); + HttpSession session = servletContextRequest.getServletApiRequest().getSession(); + String antiForgeryToken; + synchronized (session) + { + Map csrfMap = ensureCsrfMap(session); + antiForgeryToken = new BigInteger(130, _secureRandom).toString(32); + csrfMap.put(antiForgeryToken, new UriRedirectInfo(request)); + } + + // any custom scopes requested from configuration + StringBuilder scopes = new StringBuilder(); + for (String s : _openIdConfiguration.getScopes()) + { + scopes.append(" ").append(s); + } + + return _openIdConfiguration.getAuthEndpoint() + + "?client_id=" + UrlEncoded.encodeString(_openIdConfiguration.getClientId(), StandardCharsets.UTF_8) + + "&redirect_uri=" + UrlEncoded.encodeString(getRedirectUri(request), StandardCharsets.UTF_8) + + "&scope=openid" + UrlEncoded.encodeString(scopes.toString(), StandardCharsets.UTF_8) + + "&state=" + antiForgeryToken + + "&response_type=code"; + } + + @Override + public boolean secureResponse(Request req, Response res, Callback callback, boolean mandatory, Authentication.User validatedUser) throws ServerAuthException + { + return req.isSecure(); + } + + private UriRedirectInfo removeAndClearCsrfMap(HttpSession session, String csrf) + { + @SuppressWarnings("unchecked") + Map csrfMap = (Map)session.getAttribute(CSRF_MAP); + if (csrfMap == null) + return null; + + UriRedirectInfo uriRedirectInfo = csrfMap.get(csrf); + csrfMap.clear(); + return uriRedirectInfo; + } + + private Map ensureCsrfMap(HttpSession session) + { + @SuppressWarnings("unchecked") + Map csrfMap = (Map)session.getAttribute(CSRF_MAP); + if (csrfMap == null) + { + csrfMap = new MRUMap(64); + session.setAttribute(CSRF_MAP, csrfMap); + } + return csrfMap; + } + + private static class MRUMap extends LinkedHashMap + { + private static final long serialVersionUID = 5375723072014233L; + + private final int _size; + + private MRUMap(int size) + { + _size = size; + } + + @Override + protected boolean removeEldestEntry(Map.Entry eldest) + { + return size() > _size; + } + } + + private static class UriRedirectInfo implements Serializable + { + private static final long serialVersionUID = 139567755844461433L; + + private final String _uri; + private final String _method; + private final MultiMap _formParameters; + + public UriRedirectInfo(Request request) + { + _uri = request.getHttpURI().asString(); + _method = request.getMethod(); + + // TODO: + if (MimeTypes.Type.FORM_ENCODED.is(request.getHeaders().get(HttpHeader.CONTENT_TYPE)) && HttpMethod.POST.is(request.getMethod())) + { + MultiMap formParameters = new MultiMap<>(); + // request.extractFormParameters(formParameters); + _formParameters = formParameters; + } + else + { + _formParameters = null; + } + } + + public String getUri() + { + return _uri; + } + + public String getMethod() + { + return _method; + } + + public MultiMap getFormParameters() + { + return _formParameters; + } + } + + /** + * This Authentication represents a just completed OpenId Connect authentication. + * Subsequent requests from the same user are authenticated by the presents + * of a {@link SessionAuthentication} instance in their session. + */ + public static class OpenIdAuthentication extends UserAuthentication implements Authentication.ResponseSent + { + public OpenIdAuthentication(String method, UserIdentity userIdentity) + { + super(method, userIdentity); + } + + @Override + public String toString() + { + return "OpenId" + super.toString(); + } + } +} diff --git a/jetty-ee10/jetty-ee10-openid/src/main/java/org/eclipse/jetty/ee10/security/openid/OpenIdAuthenticatorFactory.java b/jetty-ee10/jetty-ee10-openid/src/main/java/org/eclipse/jetty/ee10/security/openid/OpenIdAuthenticatorFactory.java new file mode 100644 index 00000000000..88bdc153254 --- /dev/null +++ b/jetty-ee10/jetty-ee10-openid/src/main/java/org/eclipse/jetty/ee10/security/openid/OpenIdAuthenticatorFactory.java @@ -0,0 +1,57 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.security.openid; + +import java.util.Collection; + +import jakarta.servlet.ServletContext; +import org.eclipse.jetty.ee10.servlet.security.Authenticator; +import org.eclipse.jetty.ee10.servlet.security.IdentityService; +import org.eclipse.jetty.ee10.servlet.security.LoginService; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.security.Constraint; + +public class OpenIdAuthenticatorFactory implements Authenticator.Factory +{ + @Override + public Authenticator getAuthenticator(Server server, ServletContext context, Authenticator.AuthConfiguration configuration, IdentityService identityService, LoginService loginService) + { + String auth = configuration.getAuthMethod(); + if (Constraint.__OPENID_AUTH.equalsIgnoreCase(auth)) + { + // If we have an OpenIdLoginService we can extract the configuration. + if (loginService instanceof OpenIdLoginService) + return new OpenIdAuthenticator(((OpenIdLoginService)loginService).getConfiguration()); + + // Otherwise we should find an OpenIdConfiguration for this realm on the Server. + Collection configurations = server.getBeans(OpenIdConfiguration.class); + if (configurations == null || configurations.isEmpty()) + throw new IllegalStateException("No OpenIdConfiguration found"); + + // If only 1 configuration use that regardless of its realm name. + if (configurations.size() == 1) + return new OpenIdAuthenticator(configurations.iterator().next()); + + // If there are multiple configurations then select one matching the realm name. + String realmName = configuration.getRealmName(); + OpenIdConfiguration openIdConfiguration = configurations.stream() + .filter(c -> c.getIssuer().equals(realmName)) + .findAny() + .orElseThrow(() -> new IllegalStateException("No OpenIdConfiguration found for realm \"" + realmName + "\"")); + return new OpenIdAuthenticator(openIdConfiguration); + } + + return null; + } +} diff --git a/jetty-ee10/jetty-ee10-openid/src/main/java/org/eclipse/jetty/ee10/security/openid/OpenIdConfiguration.java b/jetty-ee10/jetty-ee10-openid/src/main/java/org/eclipse/jetty/ee10/security/openid/OpenIdConfiguration.java new file mode 100644 index 00000000000..db46c9c80f3 --- /dev/null +++ b/jetty-ee10/jetty-ee10-openid/src/main/java/org/eclipse/jetty/ee10/security/openid/OpenIdConfiguration.java @@ -0,0 +1,236 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.security.openid; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP; +import org.eclipse.jetty.io.ClientConnector; +import org.eclipse.jetty.util.ajax.JSON; +import org.eclipse.jetty.util.annotation.Name; +import org.eclipse.jetty.util.component.ContainerLifeCycle; +import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Holds the configuration for an OpenID Connect service. + * + * This uses the OpenID Provider URL with the path {@link #CONFIG_PATH} to discover + * the required information about the OIDC service. + */ +public class OpenIdConfiguration extends ContainerLifeCycle +{ + private static final Logger LOG = LoggerFactory.getLogger(OpenIdConfiguration.class); + private static final String CONFIG_PATH = "/.well-known/openid-configuration"; + + private final HttpClient httpClient; + private final String issuer; + private final String clientId; + private final String clientSecret; + private final List scopes = new ArrayList<>(); + private final String authMethod; + private String authEndpoint; + private String tokenEndpoint; + private boolean authenticateNewUsers = false; + + /** + * Create an OpenID configuration for a specific OIDC provider. + * @param provider The URL of the OpenID provider. + * @param clientId OAuth 2.0 Client Identifier valid at the Authorization Server. + * @param clientSecret The client secret known only by the Client and the Authorization Server. + */ + public OpenIdConfiguration(String provider, String clientId, String clientSecret) + { + this(provider, null, null, clientId, clientSecret, null); + } + + /** + * Create an OpenID configuration for a specific OIDC provider. + * @param issuer The URL of the OpenID provider. + * @param authorizationEndpoint the URL of the OpenID provider's authorization endpoint if configured. + * @param tokenEndpoint the URL of the OpenID provider's token endpoint if configured. + * @param clientId OAuth 2.0 Client Identifier valid at the Authorization Server. + * @param clientSecret The client secret known only by the Client and the Authorization Server. + * @param httpClient The {@link HttpClient} instance to use. + */ + public OpenIdConfiguration(String issuer, String authorizationEndpoint, String tokenEndpoint, + String clientId, String clientSecret, HttpClient httpClient) + { + this(issuer, authorizationEndpoint, tokenEndpoint, clientId, clientSecret, "client_secret_post", httpClient); + } + + /** + * Create an OpenID configuration for a specific OIDC provider. + * @param issuer The URL of the OpenID provider. + * @param authorizationEndpoint the URL of the OpenID provider's authorization endpoint if configured. + * @param tokenEndpoint the URL of the OpenID provider's token endpoint if configured. + * @param clientId OAuth 2.0 Client Identifier valid at the Authorization Server. + * @param clientSecret The client secret known only by the Client and the Authorization Server. + * @param authMethod Authentication method to use with the Token Endpoint. + * @param httpClient The {@link HttpClient} instance to use. + */ + public OpenIdConfiguration(@Name("issuer") String issuer, + @Name("authorizationEndpoint") String authorizationEndpoint, + @Name("tokenEndpoint") String tokenEndpoint, + @Name("clientId") String clientId, + @Name("clientSecret") String clientSecret, + @Name("authMethod") String authMethod, + @Name("httpClient") HttpClient httpClient) + { + this.issuer = issuer; + this.clientId = clientId; + this.clientSecret = clientSecret; + this.authEndpoint = authorizationEndpoint; + this.tokenEndpoint = tokenEndpoint; + this.httpClient = httpClient != null ? httpClient : newHttpClient(); + this.authMethod = authMethod; + + if (this.issuer == null) + throw new IllegalArgumentException("Issuer was not configured"); + + addBean(this.httpClient); + } + + @Override + protected void doStart() throws Exception + { + super.doStart(); + + if (authEndpoint == null || tokenEndpoint == null) + { + Map discoveryDocument = fetchOpenIdConnectMetadata(issuer, httpClient); + + authEndpoint = (String)discoveryDocument.get("authorization_endpoint"); + if (authEndpoint == null) + throw new IllegalArgumentException("authorization_endpoint"); + + tokenEndpoint = (String)discoveryDocument.get("token_endpoint"); + if (tokenEndpoint == null) + throw new IllegalArgumentException("token_endpoint"); + + if (!Objects.equals(discoveryDocument.get("issuer"), issuer)) + LOG.warn("The issuer in the metadata is not correct."); + } + } + + private static HttpClient newHttpClient() + { + ClientConnector connector = new ClientConnector(); + connector.setSslContextFactory(new SslContextFactory.Client(false)); + return new HttpClient(new HttpClientTransportOverHTTP(connector)); + } + + private static Map fetchOpenIdConnectMetadata(String provider, HttpClient httpClient) + { + try + { + if (provider.endsWith("/")) + provider = provider.substring(0, provider.length() - 1); + + Map result; + String responseBody = httpClient.GET(provider + CONFIG_PATH).getContentAsString(); + Object parsedResult = new JSON().fromJSON(responseBody); + + if (parsedResult instanceof Map) + { + Map rawResult = (Map)parsedResult; + result = rawResult.entrySet().stream() + .filter(entry -> entry.getValue() != null) + .collect(Collectors.toMap(it -> it.getKey().toString(), Map.Entry::getValue)); + if (LOG.isDebugEnabled()) + LOG.debug("discovery document {}", result); + return result; + } + else + { + LOG.warn("OpenID provider did not return a proper JSON object response. Result was '{}'", responseBody); + throw new IllegalStateException("Could not parse OpenID provider's malformed response"); + } + } + catch (Exception e) + { + throw new IllegalArgumentException("invalid identity provider " + provider, e); + } + } + + public HttpClient getHttpClient() + { + return httpClient; + } + + public String getAuthEndpoint() + { + return authEndpoint; + } + + public String getClientId() + { + return clientId; + } + + public String getClientSecret() + { + return clientSecret; + } + + public String getIssuer() + { + return issuer; + } + + public String getTokenEndpoint() + { + return tokenEndpoint; + } + + public String getAuthMethod() + { + return authMethod; + } + + public void addScopes(String... scopes) + { + if (scopes != null) + Collections.addAll(this.scopes, scopes); + } + + public List getScopes() + { + return scopes; + } + + public boolean isAuthenticateNewUsers() + { + return authenticateNewUsers; + } + + public void setAuthenticateNewUsers(boolean authenticateNewUsers) + { + this.authenticateNewUsers = authenticateNewUsers; + } + + @Override + public String toString() + { + return String.format("%s@%x{iss=%s, clientId=%s, authEndpoint=%s, authMethod=%s, tokenEndpoint=%s, scopes=%s, authNewUsers=%s}", + getClass().getSimpleName(), hashCode(), issuer, clientId, authEndpoint, authMethod, tokenEndpoint, scopes, authenticateNewUsers); + } +} diff --git a/jetty-ee10/jetty-ee10-openid/src/main/java/org/eclipse/jetty/ee10/security/openid/OpenIdCredentials.java b/jetty-ee10/jetty-ee10-openid/src/main/java/org/eclipse/jetty/ee10/security/openid/OpenIdCredentials.java new file mode 100644 index 00000000000..8f4bbd8d3d4 --- /dev/null +++ b/jetty-ee10/jetty-ee10-openid/src/main/java/org/eclipse/jetty/ee10/security/openid/OpenIdCredentials.java @@ -0,0 +1,213 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.security.openid; + +import java.io.Serializable; +import java.net.URI; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jetty.client.api.Authentication; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.client.api.Request; +import org.eclipse.jetty.client.util.BasicAuthentication; +import org.eclipse.jetty.client.util.FormRequestContent; +import org.eclipse.jetty.util.Fields; +import org.eclipse.jetty.util.ajax.JSON; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + *

    The credentials of an user to be authenticated with OpenID Connect. This will contain + * the OpenID ID Token and the OAuth 2.0 Access Token.

    + * + *

    + * This is constructed with an authorization code from the authentication request. This authorization code + * is then exchanged using {@link #redeemAuthCode(OpenIdConfiguration)} for a response containing the ID Token and Access Token. + * The response is then validated against the {@link OpenIdConfiguration}. + *

    + */ +public class OpenIdCredentials implements Serializable +{ + private static final Logger LOG = LoggerFactory.getLogger(OpenIdCredentials.class); + private static final long serialVersionUID = 4766053233370044796L; + + private final String redirectUri; + private String authCode; + private Map response; + private Map claims; + private boolean verified = false; + + public OpenIdCredentials(Map claims) + { + this.redirectUri = null; + this.authCode = null; + this.claims = claims; + } + + public OpenIdCredentials(String authCode, String redirectUri) + { + this.authCode = authCode; + this.redirectUri = redirectUri; + } + + public String getUserId() + { + return (String)claims.get("sub"); + } + + public Map getClaims() + { + return claims; + } + + public Map getResponse() + { + return response; + } + + public void redeemAuthCode(OpenIdConfiguration configuration) throws Exception + { + if (LOG.isDebugEnabled()) + LOG.debug("redeemAuthCode() {}", this); + + if (authCode != null) + { + try + { + response = claimAuthCode(configuration); + if (LOG.isDebugEnabled()) + LOG.debug("response: {}", response); + + String idToken = (String)response.get("id_token"); + if (idToken == null) + throw new AuthenticationException("no id_token"); + + String accessToken = (String)response.get("access_token"); + if (accessToken == null) + throw new AuthenticationException("no access_token"); + + String tokenType = (String)response.get("token_type"); + if (!"Bearer".equalsIgnoreCase(tokenType)) + throw new AuthenticationException("invalid token_type"); + + claims = JwtDecoder.decode(idToken); + if (LOG.isDebugEnabled()) + LOG.debug("claims {}", claims); + } + finally + { + // reset authCode as it can only be used once + authCode = null; + } + } + + if (!verified) + { + validateClaims(configuration); + verified = true; + } + } + + private void validateClaims(OpenIdConfiguration configuration) throws Exception + { + // Issuer Identifier for the OpenID Provider MUST exactly match the value of the iss (issuer) Claim. + if (!configuration.getIssuer().equals(claims.get("iss"))) + throw new AuthenticationException("Issuer Identifier MUST exactly match the iss Claim"); + + // The aud (audience) Claim MUST contain the client_id value. + validateAudience(configuration); + + // If an azp (authorized party) Claim is present, verify that its client_id is the Claim Value. + Object azp = claims.get("azp"); + if (azp != null && !configuration.getClientId().equals(azp)) + throw new AuthenticationException("Authorized party claim value should be the client_id"); + + // Check that the ID token has not expired by checking the exp claim. + long expiry = (Long)claims.get("exp"); + long currentTimeSeconds = (long)(System.currentTimeMillis() / 1000F); + if (currentTimeSeconds > expiry) + throw new AuthenticationException("ID Token has expired"); + } + + private void validateAudience(OpenIdConfiguration configuration) throws AuthenticationException + { + Object aud = claims.get("aud"); + String clientId = configuration.getClientId(); + boolean isString = aud instanceof String; + boolean isList = aud instanceof Object[]; + boolean isValidType = isString || isList; + + if (isString && !clientId.equals(aud)) + throw new AuthenticationException("Audience Claim MUST contain the client_id value"); + else if (isList) + { + List list = Arrays.asList((Object[])aud); + if (!list.contains(clientId)) + throw new AuthenticationException("Audience Claim MUST contain the client_id value"); + + if (list.size() > 1 && claims.get("azp") == null) + throw new AuthenticationException("A multi-audience ID token needs to contain an azp claim"); + } + else if (!isValidType) + throw new AuthenticationException("Audience claim was not valid"); + } + + @SuppressWarnings("unchecked") + private Map claimAuthCode(OpenIdConfiguration configuration) throws Exception + { + Fields fields = new Fields(); + fields.add("code", authCode); + fields.add("redirect_uri", redirectUri); + fields.add("grant_type", "authorization_code"); + + Request request = configuration.getHttpClient().POST(configuration.getTokenEndpoint()); + switch (configuration.getAuthMethod()) + { + case "client_secret_basic": + URI uri = URI.create(configuration.getTokenEndpoint()); + Authentication.Result authentication = new BasicAuthentication.BasicResult(uri, configuration.getClientId(), configuration.getClientSecret()); + authentication.apply(request); + break; + case "client_secret_post": + fields.add("client_id", configuration.getClientId()); + fields.add("client_secret", configuration.getClientSecret()); + break; + default: + throw new IllegalStateException(configuration.getAuthMethod()); + } + + FormRequestContent formContent = new FormRequestContent(fields); + request = request.body(formContent).timeout(10, TimeUnit.SECONDS); + ContentResponse response = request.send(); + String responseBody = response.getContentAsString(); + if (LOG.isDebugEnabled()) + LOG.debug("Authentication response: {}", responseBody); + + Object parsedResponse = new JSON().fromJSON(responseBody); + if (!(parsedResponse instanceof Map)) + throw new AuthenticationException("Malformed response from OpenID Provider"); + return (Map)parsedResponse; + } + + public static class AuthenticationException extends Exception + { + public AuthenticationException(String message) + { + super(message); + } + } +} diff --git a/jetty-ee10/jetty-ee10-openid/src/main/java/org/eclipse/jetty/ee10/security/openid/OpenIdLoginService.java b/jetty-ee10/jetty-ee10-openid/src/main/java/org/eclipse/jetty/ee10/security/openid/OpenIdLoginService.java new file mode 100644 index 00000000000..24a740bc4eb --- /dev/null +++ b/jetty-ee10/jetty-ee10-openid/src/main/java/org/eclipse/jetty/ee10/security/openid/OpenIdLoginService.java @@ -0,0 +1,167 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.security.openid; + +import java.util.Objects; +import javax.security.auth.Subject; + +import jakarta.servlet.ServletRequest; +import org.eclipse.jetty.ee10.servlet.security.IdentityService; +import org.eclipse.jetty.ee10.servlet.security.LoginService; +import org.eclipse.jetty.ee10.servlet.security.UserIdentity; +import org.eclipse.jetty.util.component.ContainerLifeCycle; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The implementation of {@link LoginService} required to use OpenID Connect. + * + *

    + * Can contain an optional wrapped {@link LoginService} which is used to store role information about users. + *

    + */ +public class OpenIdLoginService extends ContainerLifeCycle implements LoginService +{ + private static final Logger LOG = LoggerFactory.getLogger(OpenIdLoginService.class); + + private final OpenIdConfiguration configuration; + private final LoginService loginService; + private IdentityService identityService; + private boolean authenticateNewUsers; + + public OpenIdLoginService(OpenIdConfiguration configuration) + { + this(configuration, null); + } + + /** + * Use a wrapped {@link LoginService} to store information about user roles. + * Users in the wrapped loginService must be stored with their username as + * the value of the sub (subject) Claim, and a credentials value of the empty string. + * @param configuration the OpenID configuration to use. + * @param loginService the wrapped LoginService to defer to for user roles. + */ + public OpenIdLoginService(OpenIdConfiguration configuration, LoginService loginService) + { + this.configuration = Objects.requireNonNull(configuration); + this.loginService = loginService; + addBean(this.configuration); + addBean(this.loginService); + + setAuthenticateNewUsers(configuration.isAuthenticateNewUsers()); + } + + @Override + public String getName() + { + return configuration.getIssuer(); + } + + public OpenIdConfiguration getConfiguration() + { + return configuration; + } + + @Override + public UserIdentity login(String identifier, Object credentials, ServletRequest req) + { + if (LOG.isDebugEnabled()) + LOG.debug("login({}, {}, {})", identifier, credentials, req); + + OpenIdCredentials openIdCredentials = (OpenIdCredentials)credentials; + try + { + openIdCredentials.redeemAuthCode(configuration); + } + catch (Throwable e) + { + LOG.warn("Unable to redeem auth code", e); + return null; + } + + OpenIdUserPrincipal userPrincipal = new OpenIdUserPrincipal(openIdCredentials); + Subject subject = new Subject(); + subject.getPrincipals().add(userPrincipal); + subject.getPrivateCredentials().add(credentials); + subject.setReadOnly(); + + IdentityService identityService = getIdentityService(); + if (loginService != null) + { + UserIdentity userIdentity = loginService.login(openIdCredentials.getUserId(), "", req); + if (userIdentity == null) + { + if (isAuthenticateNewUsers()) + return identityService.newUserIdentity(subject, userPrincipal, new String[0]); + return null; + } + return new OpenIdUserIdentity(subject, userPrincipal, userIdentity); + } + + return identityService.newUserIdentity(subject, userPrincipal, new String[0]); + } + + public boolean isAuthenticateNewUsers() + { + return authenticateNewUsers; + } + + /** + * This setting is only meaningful if a wrapped {@link LoginService} has been set. + *

    + * If set to true, any users not found by the wrapped {@link LoginService} will still + * be authenticated but with no roles, if set to false users will not be + * authenticated unless they are discovered by the wrapped {@link LoginService}. + *

    + * @param authenticateNewUsers whether to authenticate users not found by a wrapping LoginService + */ + public void setAuthenticateNewUsers(boolean authenticateNewUsers) + { + this.authenticateNewUsers = authenticateNewUsers; + } + + @Override + public boolean validate(UserIdentity user) + { + if (!(user.getUserPrincipal() instanceof OpenIdUserPrincipal)) + return false; + + return loginService == null || loginService.validate(user); + } + + @Override + public IdentityService getIdentityService() + { + return loginService == null ? identityService : loginService.getIdentityService(); + } + + @Override + public void setIdentityService(IdentityService service) + { + if (isRunning()) + throw new IllegalStateException("Running"); + + if (loginService != null) + loginService.setIdentityService(service); + else + identityService = service; + } + + @Override + public void logout(UserIdentity user) + { + if (loginService != null) + loginService.logout(user); + } +} diff --git a/jetty-ee10/jetty-ee10-openid/src/main/java/org/eclipse/jetty/ee10/security/openid/OpenIdUserIdentity.java b/jetty-ee10/jetty-ee10-openid/src/main/java/org/eclipse/jetty/ee10/security/openid/OpenIdUserIdentity.java new file mode 100644 index 00000000000..28f1f0fcd2e --- /dev/null +++ b/jetty-ee10/jetty-ee10-openid/src/main/java/org/eclipse/jetty/ee10/security/openid/OpenIdUserIdentity.java @@ -0,0 +1,51 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.security.openid; + +import java.security.Principal; +import javax.security.auth.Subject; + +import org.eclipse.jetty.ee10.servlet.security.UserIdentity; + +public class OpenIdUserIdentity implements UserIdentity +{ + private final Subject subject; + private final Principal userPrincipal; + private final UserIdentity userIdentity; + + public OpenIdUserIdentity(Subject subject, Principal userPrincipal, UserIdentity userIdentity) + { + this.subject = subject; + this.userPrincipal = userPrincipal; + this.userIdentity = userIdentity; + } + + @Override + public Subject getSubject() + { + return subject; + } + + @Override + public Principal getUserPrincipal() + { + return userPrincipal; + } + + @Override + public boolean isUserInRole(String role) + { + return userIdentity != null && userIdentity.isUserInRole(role); + } +} diff --git a/jetty-ee10/jetty-ee10-openid/src/main/java/org/eclipse/jetty/ee10/security/openid/OpenIdUserPrincipal.java b/jetty-ee10/jetty-ee10-openid/src/main/java/org/eclipse/jetty/ee10/security/openid/OpenIdUserPrincipal.java new file mode 100644 index 00000000000..2b9d1595897 --- /dev/null +++ b/jetty-ee10/jetty-ee10-openid/src/main/java/org/eclipse/jetty/ee10/security/openid/OpenIdUserPrincipal.java @@ -0,0 +1,45 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.security.openid; + +import java.io.Serializable; +import java.security.Principal; + +public class OpenIdUserPrincipal implements Principal, Serializable +{ + private static final long serialVersionUID = 1521094652756670469L; + private final OpenIdCredentials _credentials; + + public OpenIdUserPrincipal(OpenIdCredentials credentials) + { + _credentials = credentials; + } + + public OpenIdCredentials getCredentials() + { + return _credentials; + } + + @Override + public String getName() + { + return _credentials.getUserId(); + } + + @Override + public String toString() + { + return _credentials.getUserId(); + } +} diff --git a/jetty-ee10/jetty-ee10-openid/src/main/resources/META-INF/services/org.eclipse.jetty.ee9.security.Authenticator$Factory b/jetty-ee10/jetty-ee10-openid/src/main/resources/META-INF/services/org.eclipse.jetty.ee9.security.Authenticator$Factory new file mode 100644 index 00000000000..cc53212a57b --- /dev/null +++ b/jetty-ee10/jetty-ee10-openid/src/main/resources/META-INF/services/org.eclipse.jetty.ee9.security.Authenticator$Factory @@ -0,0 +1 @@ +org.eclipse.jetty.security.openid.OpenIdAuthenticatorFactory \ No newline at end of file diff --git a/jetty-ee10/jetty-ee10-openid/src/main/resources/META-INF/services/org.eclipse.jetty.security.Authenticator$Factory b/jetty-ee10/jetty-ee10-openid/src/main/resources/META-INF/services/org.eclipse.jetty.security.Authenticator$Factory new file mode 100644 index 00000000000..0d34960f753 --- /dev/null +++ b/jetty-ee10/jetty-ee10-openid/src/main/resources/META-INF/services/org.eclipse.jetty.security.Authenticator$Factory @@ -0,0 +1 @@ +org.eclipse.jetty.ee9.security.openid.OpenIdAuthenticatorFactory \ No newline at end of file diff --git a/jetty-ee10/jetty-ee10-openid/src/test/java/org/eclipse/jetty/ee10/security/openid/JwtDecoderTest.java b/jetty-ee10/jetty-ee10-openid/src/test/java/org/eclipse/jetty/ee10/security/openid/JwtDecoderTest.java new file mode 100644 index 00000000000..6e57ee12b97 --- /dev/null +++ b/jetty-ee10/jetty-ee10-openid/src/test/java/org/eclipse/jetty/ee10/security/openid/JwtDecoderTest.java @@ -0,0 +1,117 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.security.openid; + +import java.util.Map; +import java.util.stream.Stream; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class JwtDecoderTest +{ + public static Stream paddingExamples() + { + return Stream.of( + Arguments.of("XXXX", "XXXX"), + Arguments.of("XXX", "XXX="), + Arguments.of("XX", "XX=="), + Arguments.of("XXX=", "XXX="), + Arguments.of("X-X", "X-X="), + Arguments.of("@#", "@#=="), + Arguments.of("X=", "X="), + Arguments.of("XX=", "XX="), + Arguments.of("XX==", "XX=="), + Arguments.of("XXX=", "XXX="), + Arguments.of("", "") + ); + } + + public static Stream badPaddingExamples() + { + return Stream.of( + Arguments.of("X"), + Arguments.of("XXXXX") + ); + } + + @ParameterizedTest + @MethodSource("paddingExamples") + public void testPaddingBase64(String input, String expected) + { + byte[] actual = JwtDecoder.padJWTSection(input); + assertThat(actual, is(expected.getBytes())); + } + + @ParameterizedTest + @MethodSource("badPaddingExamples") + public void testPaddingInvalidBase64(String input) + { + IllegalArgumentException error = assertThrows(IllegalArgumentException.class, + () -> JwtDecoder.padJWTSection(input)); + + assertThat(error.getMessage(), is("Not a valid Base64-encoded string")); + } + + @Test + public void testEncodeDecode() + { + String issuer = "example.com"; + String subject = "1234"; + String clientId = "1234.client.id"; + String name = "Bob"; + long expiry = 123; + + // Create a fake ID Token. + String claims = JwtEncoder.createIdToken(issuer, clientId, subject, name, expiry); + String idToken = JwtEncoder.encode(claims); + + // Decode the ID Token and verify the claims are the same. + Map decodedClaims = JwtDecoder.decode(idToken); + assertThat(decodedClaims.get("iss"), is(issuer)); + assertThat(decodedClaims.get("sub"), is(subject)); + assertThat(decodedClaims.get("aud"), is(clientId)); + assertThat(decodedClaims.get("name"), is(name)); + assertThat(decodedClaims.get("exp"), is(expiry)); + } + + @Test + public void testDecodeMissingPadding() + { + // Example given in Issue #4128 which requires the re-adding the B64 padding to decode. + String jwt = "eyJraWQiOiIxNTU1OTM0ODQ3IiwieDV0IjoiOWdCOW9zRldSRHRSMkhtNGNmVnJnWTBGcmZRIiwiYWxnIjoiUlMyNTYifQ" + + ".eyJhdF9oYXNoIjoiQTA0NUoxcE5YRk1nYzlXN2wxSk1fUSIsImRlbGVnYXRpb25faWQiOiJjZTBhNjRlNS0xYWY3LTQ2MzEtOGUz" + + "NC1mNDE5N2JkYzVjZTAiLCJhY3IiOiJ1cm46c2U6Y3VyaXR5OmF1dGhlbnRpY2F0aW9uOmh0bWwtZm9ybTpodG1sLXByaW1hcnkiL" + + "CJzX2hhc2giOiIwc1FtRG9YY3FwcnM4NWUzdy0wbHdBIiwiYXpwIjoiNzZiZTc5Y2ItM2E1Ni00ZTE3LTg3NzYtNDI1Nzc5MjRjYz" + + "c2IiwiYXV0aF90aW1lIjoxNTY5NjU4MDk1LCJleHAiOjE1Njk2NjE5OTUsIm5iZiI6MTU2OTY1ODM5NSwianRpIjoiZjJkNWI2YzE" + + "tNTIxYi00Y2Y5LThlNWEtOTg5NGJhNmE0MzkyIiwiaXNzIjoiaHR0cHM6Ly9ub3JkaWNhcGlzLmN1cml0eS5pby9-IiwiYXVkIjoi" + + "NzZiZTc5Y2ItM2E1Ni00ZTE3LTg3NzYtNDI1Nzc5MjRjYzc2Iiwic3ViIjoibmlrb3MiLCJpYXQiOjE1Njk2NTgzOTUsInB1cnBvc" + + "2UiOiJpZCJ9.Wd458zNmXggpkDN6vbS3-aiajh4-VbkmcStLYUqahYJUp9p-AUI_RZttWvwh3UDMG9rWww_ya8KFK_SkPfKooEaSN" + + "OjOhw0ox4d-9lgti3J49eRyO20RViXvRHyLVtcjv5IaqvMXgwW60Thubv19OION7DstyArffcxNNSpiqDq6wjd0T2DJ3gSXXlJHLT" + + "Wrry3svqu1j_GCbHc04XYGicxsusKgc3n22dh4I6p4trdo0Gu5Un0bZ8Yov7IzWItqTgm9X5r9gZlAOLcAuK1WTwkzAwZJ24HgvxK" + + "muYfV_4ZCg_VPN2Op8YPuRAQOgUERpeTv1RDFTOG9GKZIMBVR0A"; + + // Decode the ID Token and verify the claims are the correct. + Map decodedClaims = JwtDecoder.decode(jwt); + assertThat(decodedClaims.get("sub"), is("nikos")); + assertThat(decodedClaims.get("aud"), is("76be79cb-3a56-4e17-8776-42577924cc76")); + assertThat(decodedClaims.get("exp"), is(1569661995L)); + } +} diff --git a/jetty-ee10/jetty-ee10-openid/src/test/java/org/eclipse/jetty/ee10/security/openid/JwtEncoder.java b/jetty-ee10/jetty-ee10-openid/src/test/java/org/eclipse/jetty/ee10/security/openid/JwtEncoder.java new file mode 100644 index 00000000000..4dfd84ffeb3 --- /dev/null +++ b/jetty-ee10/jetty-ee10-openid/src/test/java/org/eclipse/jetty/ee10/security/openid/JwtEncoder.java @@ -0,0 +1,53 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.security.openid; + +import java.util.Base64; + +/** + * A basic JWT encoder for testing purposes. + */ +public class JwtEncoder +{ + private static final Base64.Encoder ENCODER = Base64.getUrlEncoder(); + private static final String DEFAULT_HEADER = "{\"INFO\": \"this is not used or checked in our implementation\"}"; + private static final String DEFAULT_SIGNATURE = "we do not validate signature as we use the authorization code flow"; + + public static String encode(String idToken) + { + return stripPadding(ENCODER.encodeToString(DEFAULT_HEADER.getBytes())) + "." + + stripPadding(ENCODER.encodeToString(idToken.getBytes())) + "." + + stripPadding(ENCODER.encodeToString(DEFAULT_SIGNATURE.getBytes())); + } + + private static String stripPadding(String paddedBase64) + { + return paddedBase64.split("=")[0]; + } + + /** + * Create a basic JWT for testing using argument supplied attributes. + */ + public static String createIdToken(String provider, String clientId, String subject, String name, long expiry) + { + return "{" + + "\"iss\": \"" + provider + "\"," + + "\"sub\": \"" + subject + "\"," + + "\"aud\": \"" + clientId + "\"," + + "\"exp\": " + expiry + "," + + "\"name\": \"" + name + "\"," + + "\"email\": \"" + name + "@example.com" + "\"" + + "}"; + } +} diff --git a/jetty-ee10/jetty-ee10-openid/src/test/java/org/eclipse/jetty/ee10/security/openid/OpenIdAuthenticationTest.java b/jetty-ee10/jetty-ee10-openid/src/test/java/org/eclipse/jetty/ee10/security/openid/OpenIdAuthenticationTest.java new file mode 100644 index 00000000000..d1d023fcd68 --- /dev/null +++ b/jetty-ee10/jetty-ee10-openid/src/test/java/org/eclipse/jetty/ee10/security/openid/OpenIdAuthenticationTest.java @@ -0,0 +1,224 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.security.openid; + +import java.io.IOException; +import java.security.Principal; +import java.util.Map; + +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.security.ConstraintMapping; +import org.eclipse.jetty.ee10.servlet.security.ConstraintSecurityHandler; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.util.security.Constraint; +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.greaterThanOrEqualTo; +import static org.hamcrest.Matchers.is; + +@SuppressWarnings("unchecked") +public class OpenIdAuthenticationTest +{ + public static final String CLIENT_ID = "testClient101"; + public static final String CLIENT_SECRET = "secret37989798"; + + private OpenIdProvider openIdProvider; + private Server server; + private ServerConnector connector; + private HttpClient client; + + @BeforeEach + public void setup() throws Exception + { + openIdProvider = new OpenIdProvider(CLIENT_ID, CLIENT_SECRET); + openIdProvider.start(); + + server = new Server(); + connector = new ServerConnector(server); + server.addConnector(connector); + ServletContextHandler context = new ServletContextHandler(server, "/", ServletContextHandler.SESSIONS); + + // Add servlets + context.addServlet(LoginPage.class, "/login"); + context.addServlet(LogoutPage.class, "/logout"); + context.addServlet(HomePage.class, "/*"); + context.addServlet(ErrorPage.class, "/error"); + + // configure security constraints + Constraint constraint = new Constraint(); + constraint.setName(Constraint.__OPENID_AUTH); + constraint.setRoles(new String[]{"**"}); + constraint.setAuthenticate(true); + + Constraint adminConstraint = new Constraint(); + adminConstraint.setName(Constraint.__OPENID_AUTH); + adminConstraint.setRoles(new String[]{"admin"}); + adminConstraint.setAuthenticate(true); + + // constraint mappings + ConstraintMapping profileMapping = new ConstraintMapping(); + profileMapping.setConstraint(constraint); + profileMapping.setPathSpec("/profile"); + ConstraintMapping loginMapping = new ConstraintMapping(); + loginMapping.setConstraint(constraint); + loginMapping.setPathSpec("/login"); + ConstraintMapping adminMapping = new ConstraintMapping(); + adminMapping.setConstraint(adminConstraint); + adminMapping.setPathSpec("/admin"); + + // security handler + ConstraintSecurityHandler securityHandler = new ConstraintSecurityHandler(); + assertThat(securityHandler.getKnownAuthenticatorFactories().size(), greaterThanOrEqualTo(2)); + + securityHandler.setAuthMethod(Constraint.__OPENID_AUTH); + securityHandler.setRealmName(openIdProvider.getProvider()); + securityHandler.addConstraintMapping(profileMapping); + securityHandler.addConstraintMapping(loginMapping); + securityHandler.addConstraintMapping(adminMapping); + + // Authentication using local OIDC Provider + server.addBean(new OpenIdConfiguration(openIdProvider.getProvider(), CLIENT_ID, CLIENT_SECRET)); + securityHandler.setInitParameter(OpenIdAuthenticator.REDIRECT_PATH, "/redirect_path"); + securityHandler.setInitParameter(OpenIdAuthenticator.ERROR_PAGE, "/error"); + context.setSecurityHandler(securityHandler); + + server.start(); + String redirectUri = "http://localhost:" + connector.getLocalPort() + "/redirect_path"; + openIdProvider.addRedirectUri(redirectUri); + + client = new HttpClient(); + client.start(); + } + + @AfterEach + public void stop() throws Exception + { + openIdProvider.stop(); + server.stop(); + } + + @Test + public void testLoginLogout() throws Exception + { + openIdProvider.setUser(new OpenIdProvider.User("123456789", "Alice")); + + String appUriString = "http://localhost:" + connector.getLocalPort(); + + // Initially not authenticated + ContentResponse response = client.GET(appUriString + "/"); + assertThat(response.getStatus(), is(HttpStatus.OK_200)); + String content = response.getContentAsString(); + assertThat(content, containsString("not authenticated")); + + // Request to login is success + response = client.GET(appUriString + "/login"); + assertThat(response.getStatus(), is(HttpStatus.OK_200)); + content = response.getContentAsString(); + assertThat(content, containsString("success")); + + // Now authenticated we can get info + response = client.GET(appUriString + "/"); + assertThat(response.getStatus(), is(HttpStatus.OK_200)); + content = response.getContentAsString(); + assertThat(content, containsString("userId: 123456789")); + assertThat(content, containsString("name: Alice")); + assertThat(content, containsString("email: Alice@example.com")); + + // Request to admin page gives 403 as we do not have admin role + response = client.GET(appUriString + "/admin"); + assertThat(response.getStatus(), is(HttpStatus.FORBIDDEN_403)); + + // We are no longer authenticated after logging out + response = client.GET(appUriString + "/logout"); + assertThat(response.getStatus(), is(HttpStatus.OK_200)); + content = response.getContentAsString(); + assertThat(content, containsString("not authenticated")); + } + + public static class LoginPage extends HttpServlet + { + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException + { + response.setContentType("text/html"); + response.getWriter().println("success"); + response.getWriter().println("
    Home"); + } + } + + public static class LogoutPage extends HttpServlet + { + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException + { + request.getSession().invalidate(); + response.sendRedirect("/"); + } + } + + public static class AdminPage extends HttpServlet + { + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException + { + Map userInfo = (Map)request.getSession().getAttribute(OpenIdAuthenticator.CLAIMS); + response.getWriter().println(userInfo.get("sub") + ": success"); + } + } + + public static class HomePage extends HttpServlet + { + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException + { + response.setContentType("text/html"); + Principal userPrincipal = request.getUserPrincipal(); + if (userPrincipal != null) + { + Map userInfo = (Map)request.getSession().getAttribute(OpenIdAuthenticator.CLAIMS); + response.getWriter().println("userId: " + userInfo.get("sub") + "
    "); + response.getWriter().println("name: " + userInfo.get("name") + "
    "); + response.getWriter().println("email: " + userInfo.get("email") + "
    "); + response.getWriter().println("
    Logout"); + } + else + { + response.getWriter().println("not authenticated"); + response.getWriter().println("
    Login"); + } + } + } + + public static class ErrorPage extends HttpServlet + { + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException + { + response.setContentType("text/html"); + response.getWriter().println("not authorized"); + response.getWriter().println("
    Home"); + } + } +} \ No newline at end of file diff --git a/jetty-ee10/jetty-ee10-openid/src/test/java/org/eclipse/jetty/ee10/security/openid/OpenIdCredentialsTest.java b/jetty-ee10/jetty-ee10-openid/src/test/java/org/eclipse/jetty/ee10/security/openid/OpenIdCredentialsTest.java new file mode 100644 index 00000000000..e62da0597e7 --- /dev/null +++ b/jetty-ee10/jetty-ee10-openid/src/test/java/org/eclipse/jetty/ee10/security/openid/OpenIdCredentialsTest.java @@ -0,0 +1,40 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.security.openid; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jetty.client.HttpClient; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +public class OpenIdCredentialsTest +{ + @Test + public void testSingleAudienceValueInArray() throws Exception + { + String issuer = "myIssuer123"; + String clientId = "myClientId456"; + OpenIdConfiguration configuration = new OpenIdConfiguration(issuer, "", "", clientId, "", new HttpClient()); + + Map claims = new HashMap<>(); + claims.put("iss", issuer); + claims.put("aud", new String[]{clientId}); + claims.put("exp", System.currentTimeMillis() + 5000); + + assertDoesNotThrow(() -> new OpenIdCredentials(claims).redeemAuthCode(configuration)); + } +} diff --git a/jetty-ee10/jetty-ee10-openid/src/test/java/org/eclipse/jetty/ee10/security/openid/OpenIdProvider.java b/jetty-ee10/jetty-ee10-openid/src/test/java/org/eclipse/jetty/ee10/security/openid/OpenIdProvider.java new file mode 100644 index 00000000000..e38c2ecabdb --- /dev/null +++ b/jetty-ee10/jetty-ee10-openid/src/test/java/org/eclipse/jetty/ee10/security/openid/OpenIdProvider.java @@ -0,0 +1,332 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.security.openid; + +import java.io.IOException; +import java.io.PrintWriter; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.util.StringUtil; +import org.eclipse.jetty.util.component.ContainerLifeCycle; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class OpenIdProvider extends ContainerLifeCycle +{ + private static final Logger LOG = LoggerFactory.getLogger(OpenIdProvider.class); + + private static final String CONFIG_PATH = "/.well-known/openid-configuration"; + private static final String AUTH_PATH = "/auth"; + private static final String TOKEN_PATH = "/token"; + private final Map issuedAuthCodes = new HashMap<>(); + + protected final String clientId; + protected final String clientSecret; + protected final List redirectUris = new ArrayList<>(); + private final ServerConnector connector; + private final Server server; + private int port = 0; + private String provider; + private User preAuthedUser; + + public static void main(String[] args) throws Exception + { + String clientId = "CLIENT_ID123"; + String clientSecret = "PASSWORD123"; + int port = 5771; + String redirectUri = "http://localhost:8080/openid/auth"; + + OpenIdProvider openIdProvider = new OpenIdProvider(clientId, clientSecret); + openIdProvider.addRedirectUri(redirectUri); + openIdProvider.setPort(port); + openIdProvider.start(); + try + { + openIdProvider.join(); + } + finally + { + openIdProvider.stop(); + } + } + + public OpenIdProvider(String clientId, String clientSecret) + { + this.clientId = clientId; + this.clientSecret = clientSecret; + + server = new Server(); + connector = new ServerConnector(server); + server.addConnector(connector); + + ServletContextHandler contextHandler = new ServletContextHandler(); + contextHandler.setContextPath("/"); + contextHandler.addServlet(new ServletHolder(new OpenIdConfigServlet()), CONFIG_PATH); + contextHandler.addServlet(new ServletHolder(new OpenIdAuthEndpoint()), AUTH_PATH); + contextHandler.addServlet(new ServletHolder(new OpenIdTokenEndpoint()), TOKEN_PATH); + server.setHandler(contextHandler); + + addBean(server); + } + + public void join() throws InterruptedException + { + server.join(); + } + + public OpenIdConfiguration getOpenIdConfiguration() + { + String provider = getProvider(); + String authEndpoint = provider + AUTH_PATH; + String tokenEndpoint = provider + TOKEN_PATH; + return new OpenIdConfiguration(provider, authEndpoint, tokenEndpoint, clientId, clientSecret, null); + } + + @Override + protected void doStart() throws Exception + { + connector.setPort(port); + super.doStart(); + provider = "http://localhost:" + connector.getLocalPort(); + } + + public void setPort(int port) + { + if (isStarted()) + throw new IllegalStateException(); + this.port = port; + } + + public void setUser(User user) + { + this.preAuthedUser = user; + } + + public String getProvider() + { + if (!isStarted() && port == 0) + throw new IllegalStateException("Port of OpenIdProvider not configured"); + return provider; + } + + public void addRedirectUri(String uri) + { + redirectUris.add(uri); + } + + public class OpenIdAuthEndpoint extends HttpServlet + { + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException + { + if (!clientId.equals(req.getParameter("client_id"))) + { + resp.sendError(HttpServletResponse.SC_FORBIDDEN, "invalid client_id"); + return; + } + + String redirectUri = req.getParameter("redirect_uri"); + if (!redirectUris.contains(redirectUri)) + { + LOG.warn("invalid redirectUri {}", redirectUri); + resp.sendError(HttpServletResponse.SC_FORBIDDEN, "invalid redirect_uri"); + return; + } + + String scopeString = req.getParameter("scope"); + List scopes = (scopeString == null) ? Collections.emptyList() : Arrays.asList(StringUtil.csvSplit(scopeString)); + if (!scopes.contains("openid")) + { + resp.sendError(HttpServletResponse.SC_FORBIDDEN, "no openid scope"); + return; + } + + if (!"code".equals(req.getParameter("response_type"))) + { + resp.sendError(HttpServletResponse.SC_FORBIDDEN, "response_type must be code"); + return; + } + + String state = req.getParameter("state"); + if (state == null) + { + resp.sendError(HttpServletResponse.SC_FORBIDDEN, "no state param"); + return; + } + + if (preAuthedUser == null) + { + PrintWriter writer = resp.getWriter(); + resp.setContentType("text/html"); + writer.println("

    Login to OpenID Connect Provider

    "); + writer.println("
    "); + writer.println(""); + writer.println(""); + writer.println(""); + writer.println(""); + writer.println("
    "); + } + else + { + redirectUser(resp, preAuthedUser, redirectUri, state); + } + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException + { + String redirectUri = req.getParameter("redirectUri"); + if (!redirectUris.contains(redirectUri)) + { + resp.sendError(HttpServletResponse.SC_FORBIDDEN, "invalid redirect_uri"); + return; + } + + String state = req.getParameter("state"); + if (state == null) + { + resp.sendError(HttpServletResponse.SC_FORBIDDEN, "no state param"); + return; + } + + String username = req.getParameter("username"); + if (username == null) + { + resp.sendError(HttpServletResponse.SC_FORBIDDEN, "no username"); + return; + } + + User user = new User(username); + redirectUser(resp, user, redirectUri, state); + } + + public void redirectUser(HttpServletResponse response, User user, String redirectUri, String state) throws IOException + { + String authCode = UUID.randomUUID().toString().replace("-", ""); + issuedAuthCodes.put(authCode, user); + + try + { + redirectUri += "?code=" + authCode + "&state=" + state; + response.sendRedirect(response.encodeRedirectURL(redirectUri)); + } + catch (Throwable t) + { + issuedAuthCodes.remove(authCode); + throw t; + } + } + } + + public class OpenIdTokenEndpoint extends HttpServlet + { + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException + { + String code = req.getParameter("code"); + + if (!clientId.equals(req.getParameter("client_id")) || + !clientSecret.equals(req.getParameter("client_secret")) || + !redirectUris.contains(req.getParameter("redirect_uri")) || + !"authorization_code".equals(req.getParameter("grant_type")) || + code == null) + { + resp.sendError(HttpServletResponse.SC_FORBIDDEN, "bad auth request"); + return; + } + + User user = issuedAuthCodes.remove(code); + if (user == null) + { + resp.sendError(HttpServletResponse.SC_FORBIDDEN, "invalid auth code"); + return; + } + + String accessToken = "ABCDEFG"; + long expiry = System.currentTimeMillis() + Duration.ofMinutes(10).toMillis(); + String response = "{" + + "\"access_token\": \"" + accessToken + "\"," + + "\"id_token\": \"" + JwtEncoder.encode(user.getIdToken(provider, clientId)) + "\"," + + "\"expires_in\": " + expiry + "," + + "\"token_type\": \"Bearer\"" + + "}"; + + resp.setContentType("text/plain"); + resp.getWriter().print(response); + } + } + + public class OpenIdConfigServlet extends HttpServlet + { + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException + { + String discoveryDocument = "{" + + "\"issuer\": \"" + provider + "\"," + + "\"authorization_endpoint\": \"" + provider + AUTH_PATH + "\"," + + "\"token_endpoint\": \"" + provider + TOKEN_PATH + "\"," + + "}"; + + resp.getWriter().write(discoveryDocument); + } + } + + public static class User + { + private final String subject; + private final String name; + + public User(String name) + { + this(UUID.nameUUIDFromBytes(name.getBytes()).toString(), name); + } + + public User(String subject, String name) + { + this.subject = subject; + this.name = name; + } + + public String getName() + { + return name; + } + + public String getSubject() + { + return subject; + } + + public String getIdToken(String provider, String clientId) + { + long expiry = System.currentTimeMillis() + Duration.ofMinutes(1).toMillis(); + return JwtEncoder.createIdToken(provider, clientId, subject, name, expiry); + } + } +} diff --git a/jetty-ee10/jetty-ee10-openid/src/test/java/org/eclipse/jetty/ee10/security/openid/OpenIdReamNameTest.java b/jetty-ee10/jetty-ee10-openid/src/test/java/org/eclipse/jetty/ee10/security/openid/OpenIdReamNameTest.java new file mode 100644 index 00000000000..239eac4efb4 --- /dev/null +++ b/jetty-ee10/jetty-ee10-openid/src/test/java/org/eclipse/jetty/ee10/security/openid/OpenIdReamNameTest.java @@ -0,0 +1,184 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.security.openid; + +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.security.Authenticator; +import org.eclipse.jetty.ee10.servlet.security.ConstraintSecurityHandler; +import org.eclipse.jetty.ee10.servlet.security.LoginService; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.ContextHandlerCollection; +import org.eclipse.jetty.util.security.Constraint; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.greaterThanOrEqualTo; +import static org.hamcrest.Matchers.instanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class OpenIdReamNameTest +{ + private final Server server = new Server(); + + public static ServletContextHandler configureOpenIdContext(String realmName) + { + ConstraintSecurityHandler securityHandler = new ConstraintSecurityHandler(); + assertThat(securityHandler.getKnownAuthenticatorFactories().size(), greaterThanOrEqualTo(2)); + securityHandler.setAuthMethod(Constraint.__OPENID_AUTH); + securityHandler.setRealmName(realmName); + ServletContextHandler context = new ServletContextHandler(); + context.setContextPath("/" + realmName); + context.setSecurityHandler(securityHandler); + return context; + } + + @Test + public void testSingleConfiguration() throws Exception + { + // Add some OpenID configurations. + OpenIdConfiguration config1 = new OpenIdConfiguration("provider1", + "", "", "", "", null); + server.addBean(config1); + + // Configure two webapps to select configs based on realm name. + ServletContextHandler context1 = configureOpenIdContext("This doesn't matter if only 1 OpenIdConfiguration"); + ContextHandlerCollection contextHandlerCollection = new ContextHandlerCollection(); + contextHandlerCollection.addHandler(context1); + server.setHandler(contextHandlerCollection); + + try + { + server.start(); + + // The OpenIdConfiguration from context1 matches to config1. + Authenticator authenticator = context1.getSecurityHandler().getAuthenticator(); + assertThat(authenticator, instanceOf(OpenIdAuthenticator.class)); + LoginService loginService = ((OpenIdAuthenticator)authenticator).getLoginService(); + assertThat(loginService, instanceOf(OpenIdLoginService.class)); + assertThat(((OpenIdLoginService)loginService).getConfiguration(), Matchers.is(config1)); + } + finally + { + server.stop(); + } + } + + @Test + public void testSingleConfigurationNoRealmName() throws Exception + { + // Add some OpenID configurations. + OpenIdConfiguration config1 = new OpenIdConfiguration("provider1", + "", "", "", "", null); + server.addBean(config1); + + // Configure two webapps to select configs based on realm name. + ServletContextHandler context1 = configureOpenIdContext(null); + ContextHandlerCollection contextHandlerCollection = new ContextHandlerCollection(); + contextHandlerCollection.addHandler(context1); + server.setHandler(contextHandlerCollection); + + try + { + server.start(); + + // The OpenIdConfiguration from context1 matches to config1. + Authenticator authenticator = context1.getSecurityHandler().getAuthenticator(); + assertThat(authenticator, instanceOf(OpenIdAuthenticator.class)); + LoginService loginService = ((OpenIdAuthenticator)authenticator).getLoginService(); + assertThat(loginService, instanceOf(OpenIdLoginService.class)); + assertThat(((OpenIdLoginService)loginService).getConfiguration(), Matchers.is(config1)); + } + finally + { + server.stop(); + } + } + + @Test + public void testMultipleConfiguration() throws Exception + { + // Add some OpenID configurations. + OpenIdConfiguration config1 = new OpenIdConfiguration("provider1", + "", "", "", "", null); + OpenIdConfiguration config2 = new OpenIdConfiguration("provider2", + "", "", "", "", null); + server.addBean(config1); + server.addBean(config2); + + // Configure two webapps to select configs based on realm name. + ServletContextHandler context1 = configureOpenIdContext(config1.getIssuer()); + ServletContextHandler context2 = configureOpenIdContext(config2.getIssuer()); + ContextHandlerCollection contextHandlerCollection = new ContextHandlerCollection(); + contextHandlerCollection.addHandler(context1); + contextHandlerCollection.addHandler(context2); + server.setHandler(contextHandlerCollection); + + try + { + server.start(); + + // The OpenIdConfiguration from context1 matches to config1. + Authenticator authenticator = context1.getSecurityHandler().getAuthenticator(); + assertThat(authenticator, instanceOf(OpenIdAuthenticator.class)); + LoginService loginService = ((OpenIdAuthenticator)authenticator).getLoginService(); + assertThat(loginService, instanceOf(OpenIdLoginService.class)); + assertThat(((OpenIdLoginService)loginService).getConfiguration(), Matchers.is(config1)); + + // The OpenIdConfiguration from context2 matches to config2. + authenticator = context2.getSecurityHandler().getAuthenticator(); + assertThat(authenticator, instanceOf(OpenIdAuthenticator.class)); + loginService = ((OpenIdAuthenticator)authenticator).getLoginService(); + assertThat(loginService, instanceOf(OpenIdLoginService.class)); + assertThat(((OpenIdLoginService)loginService).getConfiguration(), Matchers.is(config2)); + } + finally + { + server.stop(); + } + } + + @Test + public void testMultipleConfigurationNoMatch() throws Exception + { + // Add some OpenID configurations. + OpenIdConfiguration config1 = new OpenIdConfiguration("provider1", + "", "", "", "", null); + OpenIdConfiguration config2 = new OpenIdConfiguration("provider2", + "", "", "", "", null); + server.addBean(config1); + server.addBean(config2); + + // Configure two webapps to select configs based on realm name. + ServletContextHandler context1 = configureOpenIdContext("provider3"); + ContextHandlerCollection contextHandlerCollection = new ContextHandlerCollection(); + contextHandlerCollection.addHandler(context1); + server.setHandler(contextHandlerCollection); + + // Multiple OpenIdConfigurations were available and didn't match one based on realm name. + assertThrows(IllegalStateException.class, server::start); + } + + @Test + public void testNoConfiguration() throws Exception + { + ServletContextHandler context1 = configureOpenIdContext(null); + ContextHandlerCollection contextHandlerCollection = new ContextHandlerCollection(); + contextHandlerCollection.addHandler(context1); + server.setHandler(contextHandlerCollection); + + // If no OpenIdConfigurations are present it is bad configuration. + assertThrows(IllegalStateException.class, server::start); + } +} diff --git a/jetty-ee10/jetty-ee10-openid/src/test/resources/jetty-logging.properties b/jetty-ee10/jetty-ee10-openid/src/test/resources/jetty-logging.properties new file mode 100755 index 00000000000..6f21b764f54 --- /dev/null +++ b/jetty-ee10/jetty-ee10-openid/src/test/resources/jetty-logging.properties @@ -0,0 +1,3 @@ +# Jetty Logging using jetty-slf4j-impl +# org.eclipse.jetty.LEVEL=DEBUG +# org.eclipse.jetty.security.openid.LEVEL=DEBUG \ No newline at end of file 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 new file mode 100644 index 00000000000..8f05e124e5f --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-alpn/pom.xml @@ -0,0 +1,33 @@ + + + org.eclipse.jetty.ee10.osgi + jetty-ee10-osgi-project + 12.0.0-SNAPSHOT + + + + + 4.0.0 + jetty-ee10-osgi-alpn + EE10 :: Jetty :: OSGi ALPN Fragment + jar + + ${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-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot-jsp/pom.xml b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot-jsp/pom.xml new file mode 100644 index 00000000000..b0da5f3526c --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot-jsp/pom.xml @@ -0,0 +1,152 @@ + + + org.eclipse.jetty.ee10.osgi + jetty-ee10-osgi-project + 12.0.0-SNAPSHOT + + 4.0.0 + jetty-ee10-osgi-boot-jsp + EE10 :: Jetty :: OSGi :: Boot JSP + Jetty OSGi Boot JSP bundle + + ${project.groupId}.boot.jsp + org.eclipse.jetty.ee10.osgi.boot.jasper.*,org.eclipse.jetty.ee10.osgi.boot.jsp.* + + + + org.eclipse.jetty + jetty-deploy + + + org.eclipse.jetty.ee10.osgi + jetty-ee10-osgi-boot + ${project.version} + provided + + + org.eclipse.platform + org.eclipse.osgi + + + org.eclipse.platform + org.eclipse.osgi.services + + + + org.eclipse.jetty.toolchain + jetty-jakarta-servlet-api + + + + org.eclipse.jetty.ee10 + jetty-ee10-apache-jsp + + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + set-jsp-api-version + validate + + parse-version + + + ${jsp.impl.version} + jspImpl + + + + + + + org.apache.felix + maven-bundle-plugin + true + + + Jetty-OSGi-Jasper Integration + + org.eclipse.jetty.ee10.osgi.boot + !org.eclipse.jetty.ee10.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="[4.0,5.0)", + jakarta.servlet;version="[$(version;==;${servletImpl.osgiVersion}),$(version;+;${servletImpl.osgiVersion}))", + jakarta.servlet.resources;version="[$(version;==;${servletImpl.osgiVersion}),$(version;+;${servletImpl.osgiVersion}))", + 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.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.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.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}))" + + + + + + + diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot-jsp/src/main/java/org/eclipse/jetty/ee10/osgi/boot/jasper/ContainerTldBundleDiscoverer.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot-jsp/src/main/java/org/eclipse/jetty/ee10/osgi/boot/jasper/ContainerTldBundleDiscoverer.java new file mode 100644 index 00000000000..0ba87c1ea07 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot-jsp/src/main/java/org/eclipse/jetty/ee10/osgi/boot/jasper/ContainerTldBundleDiscoverer.java @@ -0,0 +1,249 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.osgi.boot.jasper; + +import java.io.File; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.StringTokenizer; +import java.util.regex.Pattern; + +import jakarta.servlet.jsp.JspFactory; +import org.eclipse.jetty.deploy.DeploymentManager; +import org.eclipse.jetty.ee10.osgi.boot.JettyBootstrapActivator; +import org.eclipse.jetty.ee10.osgi.boot.OSGiMetaInfConfiguration; +import org.eclipse.jetty.ee10.osgi.boot.utils.BundleFileLocatorHelper; +import org.eclipse.jetty.ee10.osgi.boot.utils.TldBundleDiscoverer; +import org.osgi.framework.Bundle; +import org.osgi.framework.FrameworkUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * ContainerTldBundleDiscoverer + * + * Finds bundles that are considered as on the container classpath that + * contain tlds. + * + * The System property org.eclipse.jetty.ee10.osgi.tldbundles is a comma + * separated list of exact symbolic names of bundles that have container classpath + * tlds. + * + * The DeploymentManager context attribute "org.eclipse.jetty.server.webapp.containerIncludeBundlePattern" + * can be used to define a pattern of symbolic names of bundles that contain container + * classpath tlds. + * + * The matching bundles are converted to URLs that are put onto a special classloader that acts as the + * parent classloader for contexts deployed by the jetty Server instance (see ServerInstanceWrapper). + * + * It also discovers the bundle that contains the jstl taglib and adds it into the + * "org.eclipse.jetty.server.webapp.containerIncludeBundlePattern" (if it is not already there) so + * that the WebInfOSGiConfiguration class will add the jstl taglib bundle into the list of container + * resources. + * + * Eg: + * -Dorg.eclipse.jetty.ee10.osgi.tldbundles=org.springframework.web.servlet,com.opensymphony.module.sitemesh + */ +public class ContainerTldBundleDiscoverer implements TldBundleDiscoverer +{ + + private static final Logger LOG = LoggerFactory.getLogger(ContainerTldBundleDiscoverer.class); + + private static String DEFAULT_JSP_FACTORY_IMPL_CLASS = "org.apache.jasper.runtime.JspFactoryImpl"; + /** + * Default name of a class that belongs to the jstl bundle. From that class + * we locate the corresponding bundle and register it as a bundle that + * contains tld files. + */ + private static String DEFAULT_JSTL_BUNDLE_CLASS = "org.apache.taglibs.standard.tag.rt.core.WhenTag"; + + private Bundle jstlBundle = null; + + /** + * Check the System property "org.eclipse.jetty.ee10.osgi.tldbundles" for names of + * bundles that contain tlds and convert to URLs. + * + * @return The location of the jars that contain tld files as URLs. + */ + @Override + public URL[] getUrlsForBundlesWithTlds(DeploymentManager deploymentManager, BundleFileLocatorHelper locatorHelper) throws Exception + { + if (!isJspAvailable()) + { + return new URL[0]; + } + + if (jstlBundle == null) + jstlBundle = findJstlBundle(); + + final Bundle[] bundles = FrameworkUtil.getBundle(ContainerTldBundleDiscoverer.class).getBundleContext().getBundles(); + HashSet urls = new HashSet(); + String tmp = System.getProperty(OSGiMetaInfConfiguration.SYS_PROP_TLD_BUNDLES); //comma separated exact names + List sysNames = new ArrayList(); + if (tmp != null) + { + StringTokenizer tokenizer = new StringTokenizer(tmp, ", \n\r\t", false); + while (tokenizer.hasMoreTokens()) + { + sysNames.add(tokenizer.nextToken()); + } + } + tmp = (String)deploymentManager.getContextAttribute(OSGiMetaInfConfiguration.CONTAINER_BUNDLE_PATTERN); //bundle name patterns + + Pattern pattern = (tmp == null ? null : Pattern.compile(tmp)); + + //check that the jstl bundle is not already included in the pattern, and include it if it is not because + //subsequent classes such as OSGiWebInfConfiguration use this pattern to determine which jars are + //considered to be on the container classpath + if (jstlBundle != null) + { + if (pattern == null) + { + pattern = Pattern.compile(jstlBundle.getSymbolicName()); + deploymentManager.setContextAttribute(OSGiMetaInfConfiguration.CONTAINER_BUNDLE_PATTERN, jstlBundle.getSymbolicName()); + } + else if (!(pattern.matcher(jstlBundle.getSymbolicName()).matches())) + { + String s = tmp + "|" + jstlBundle.getSymbolicName(); + pattern = Pattern.compile(s); + deploymentManager.setContextAttribute(OSGiMetaInfConfiguration.CONTAINER_BUNDLE_PATTERN, s); + } + } + + for (Bundle bundle : bundles) + { + if (sysNames.contains(bundle.getSymbolicName())) + convertBundleLocationToURL(locatorHelper, bundle, urls); + else if (pattern != null && pattern.matcher(bundle.getSymbolicName()).matches()) + convertBundleLocationToURL(locatorHelper, bundle, urls); + } + + return urls.toArray(new URL[urls.size()]); + } + + /** + * 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"); + } + catch (Exception e) + { + LOG.warn("Unable to locate the JspServlet: jsp support unavailable.", e); + return false; + } + return true; + } + + /** + * Some versions of JspFactory do Class.forName, which probably won't work in an + * OSGi environment. + */ + public void fixJspFactory() + { + try + { + Class servletContextClass = jakarta.servlet.ServletContext.class; + // bug #299733 + JspFactory fact = JspFactory.getDefaultFactory(); + if (fact == null) + { // bug #299733 + // JspFactory does a simple + // Class.getForName("org.apache.jasper.runtime.JspFactoryImpl") + // however its bundles does not import the jasper package + // so it fails. let's help things out: + fact = (JspFactory)JettyBootstrapActivator.class.getClassLoader() + .loadClass(DEFAULT_JSP_FACTORY_IMPL_CLASS).getDeclaredConstructor().newInstance(); + JspFactory.setDefaultFactory(fact); + } + } + catch (Exception e) + { + LOG.warn("Unable to set the JspFactory: jsp support incomplete.", e); + } + } + + /** + * 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 = JSTLBundleDiscoverer.class.getClassLoader().loadClass(DEFAULT_JSTL_BUNDLE_CLASS); + } + catch (ClassNotFoundException e) + { + LOG.info("jstl not on classpath", e); + } + + if (jstlClass != null) + //get the bundle containing jstl + return FrameworkUtil.getBundle(jstlClass); + + return null; + } + + /** + * Resolves a bundle that contains tld files as a URL. The URLs are + * used by jasper to discover the tld files. + * + * Support only 2 types of packaging for the bundle: - the bundle is a jar + * (recommended for runtime.) - the bundle is a folder and contain jars in + * the root and/or in the lib folder (nice for PDE development situations) + * Unsupported: the bundle is a jar that embeds more jars. + */ + private void convertBundleLocationToURL(BundleFileLocatorHelper locatorHelper, Bundle bundle, Set urls) throws Exception + { + File jasperLocation = locatorHelper.getBundleInstallLocation(bundle); + if (jasperLocation.isDirectory()) + { + for (File f : jasperLocation.listFiles()) + { + if (f.getName().endsWith(".jar") && f.isFile()) + { + urls.add(f.toURI().toURL()); + } + else if (f.isDirectory() && f.getName().equals("lib")) + { + for (File f2 : jasperLocation.listFiles()) + { + if (f2.getName().endsWith(".jar") && f2.isFile()) + { + urls.add(f2.toURI().toURL()); + } + } + } + } + urls.add(jasperLocation.toURI().toURL()); + } + else + { + urls.add(jasperLocation.toURI().toURL()); + } + } +} diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot-jsp/src/main/java/org/eclipse/jetty/ee10/osgi/boot/jasper/JSTLBundleDiscoverer.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot-jsp/src/main/java/org/eclipse/jetty/ee10/osgi/boot/jasper/JSTLBundleDiscoverer.java new file mode 100644 index 00000000000..c6ab888f47f --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot-jsp/src/main/java/org/eclipse/jetty/ee10/osgi/boot/jasper/JSTLBundleDiscoverer.java @@ -0,0 +1,178 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.osgi.boot.jasper; + +import java.io.File; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; + +import jakarta.servlet.jsp.JspFactory; +import org.eclipse.jetty.deploy.DeploymentManager; +import org.eclipse.jetty.ee10.osgi.boot.JettyBootstrapActivator; +import org.eclipse.jetty.ee10.osgi.boot.utils.BundleFileLocatorHelper; +import org.eclipse.jetty.ee10.osgi.boot.utils.TldBundleDiscoverer; +import org.osgi.framework.Bundle; +import org.osgi.framework.FrameworkUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * JSTLBundleDiscoverer + * + * Fix various shortcomings with the way jasper parses the tld files. Plugs the + * JSTL tlds assuming that they are packaged with the bundle that contains the + * JSTL classes. + *

    + * Pluggable tlds at the server level are handled by + * {@link ContainerTldBundleDiscoverer}. + *

    + */ +public class JSTLBundleDiscoverer implements TldBundleDiscoverer +{ + private static final Logger LOG = LoggerFactory.getLogger(JSTLBundleDiscoverer.class); + + /** + * Default name of a class that belongs to the jstl bundle. From that class + * we locate the corresponding bundle and register it as a bundle that + * contains tld files. + */ + private static String DEFAULT_JSTL_BUNDLE_CLASS = "org.apache.taglibs.standard.tag.el.core.WhenTag"; + + /** + * Default jsp factory implementation. Idally jasper is osgified and we can + * use services. In the mean time we statically set the jsp factory + * implementation. bug #299733 + */ + private static String DEFAULT_JSP_FACTORY_IMPL_CLASS = "org.apache.jasper.runtime.JspFactoryImpl"; + + private static final Set __tldBundleCache = new HashSet(); + + public JSTLBundleDiscoverer() + { + try + { + // sanity check: + Class cl = getClass().getClassLoader().loadClass("org.apache.jasper.servlet.JspServlet"); + } + catch (Exception e) + { + LOG.warn("Unable to locate the JspServlet: jsp support unavailable.", e); + return; + } + try + { + Class servletContextClass = jakarta.servlet.ServletContext.class; + // bug #299733 + JspFactory fact = JspFactory.getDefaultFactory(); + if (fact == null) + { // bug #299733 + // JspFactory does a simple + // Class.getForName("org.apache.jasper.runtime.JspFactoryImpl") + // however its bundles does not import the jasper package + // so it fails. let's help things out: + fact = (JspFactory)JettyBootstrapActivator.class.getClassLoader() + .loadClass(DEFAULT_JSP_FACTORY_IMPL_CLASS).getDeclaredConstructor().newInstance(); + JspFactory.setDefaultFactory(fact); + } + } + catch (Exception e) + { + LOG.warn("Unable to set the JspFactory: jsp support incomplete.", e); + } + } + + /** + * The jasper TldScanner expects a URLClassloader to parse a jar for the + * /META-INF/*.tld it may contain. We place the bundles that we know contain + * such tag-libraries. Please note that it will work if and only if the + * bundle is a jar (!) Currently we just hardcode the bundle that contains + * the jstl implemenation. + * + * A workaround when the tld cannot be parsed with this method is to copy + * and paste it inside the WEB-INF of the webapplication where it is used. + * + * Support only 2 types of packaging for the bundle: - the bundle is a jar + * (recommended for runtime.) - the bundle is a folder and contain jars in + * the root and/or in the lib folder (nice for PDE development situations) + * Unsupported: the bundle is a jar that embeds more jars. + * + * @return array of URLs + * @throws Exception In case of errors during resolving TLDs files + */ + @Override + public URL[] getUrlsForBundlesWithTlds(DeploymentManager deployer, BundleFileLocatorHelper locatorHelper) throws Exception + { + + ArrayList urls = new ArrayList(); + Class jstlClass = null; + + // Look for the jstl bundle + // We assume the jstl's tlds are defined there. + // We assume that the jstl bundle is imported by this bundle + // So we can look for this class using this bundle's classloader: + try + { + jstlClass = JSTLBundleDiscoverer.class.getClassLoader().loadClass(DEFAULT_JSTL_BUNDLE_CLASS); + } + catch (ClassNotFoundException e) + { + LOG.info("jstl not on classpath", e); + } + + if (jstlClass != null) + { + //get the bundle containing jstl + Bundle tldBundle = FrameworkUtil.getBundle(jstlClass); + File tldBundleLocation = locatorHelper.getBundleInstallLocation(tldBundle); + + if (tldBundleLocation != null && tldBundleLocation.isDirectory()) + { + // try to find the jar files inside this folder + for (File f : tldBundleLocation.listFiles()) + { + if (f.getName().endsWith(".jar") && f.isFile()) + { + urls.add(f.toURI().toURL()); + } + else if (f.isDirectory() && f.getName().equals("lib")) + { + for (File f2 : tldBundleLocation.listFiles()) + { + if (f2.getName().endsWith(".jar") && f2.isFile()) + { + urls.add(f2.toURI().toURL()); + } + } + } + } + } + else if (tldBundleLocation != null) + { + urls.add(tldBundleLocation.toURI().toURL()); + + String pattern = (String)deployer.getContextAttribute("org.eclipse.jetty.server.webapp.containerIncludeBundlePattern"); + pattern = (pattern == null ? "" : pattern); + if (!pattern.contains(tldBundle.getSymbolicName())) + { + pattern += "|" + tldBundle.getSymbolicName(); + deployer.setContextAttribute("org.eclipse.jetty.server.webapp.containerIncludeBundlePattern", pattern); + } + } + } + + return urls.toArray(new URL[urls.size()]); + } +} diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot-jsp/src/main/java/org/eclipse/jetty/ee10/osgi/boot/jsp/FragmentActivator.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot-jsp/src/main/java/org/eclipse/jetty/ee10/osgi/boot/jsp/FragmentActivator.java new file mode 100644 index 00000000000..b795fdc7e76 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot-jsp/src/main/java/org/eclipse/jetty/ee10/osgi/boot/jsp/FragmentActivator.java @@ -0,0 +1,59 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.osgi.boot.jsp; + +import org.eclipse.jetty.ee10.osgi.boot.internal.serverfactory.ServerInstanceWrapper; +import org.eclipse.jetty.ee10.osgi.boot.jasper.ContainerTldBundleDiscoverer; +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.ee10.osgi.boot.utils.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 +{ + /** + * + */ + @Override + public void start(BundleContext context) throws Exception + { + //set up some classes that will look for bundles with tlds that must be converted + //to urls and treated as if they are on the Jetty container's classpath so that + //jasper can deal with them + ServerInstanceWrapper.addContainerTldBundleDiscoverer(new ContainerTldBundleDiscoverer()); + } + + /** + * + */ + @Override + public void stop(BundleContext context) throws Exception + { + + } +} diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot-warurl/pom.xml b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot-warurl/pom.xml new file mode 100644 index 00000000000..8f3d886723a --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot-warurl/pom.xml @@ -0,0 +1,42 @@ + + + org.eclipse.jetty.ee10.osgi + jetty-ee10-osgi-project + 12.0.0-SNAPSHOT + ../pom.xml + + 4.0.0 + jetty-ee10-osgi-boot-warurl + EE10 :: Jetty :: OSGi :: Boot :: Warurl + Jetty OSGi Boot-Warurl bundle + + ${project.groupId}.boot.warurl + org.eclipse.jetty.ee10.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.ee10.osgi.boot.warurl.WarUrlActivator + + + + + + diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot-warurl/src/main/java/org/eclipse/jetty/ee10/osgi/boot/warurl/WarUrlActivator.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot-warurl/src/main/java/org/eclipse/jetty/ee10/osgi/boot/warurl/WarUrlActivator.java new file mode 100644 index 00000000000..aee08673443 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot-warurl/src/main/java/org/eclipse/jetty/ee10/osgi/boot/warurl/WarUrlActivator.java @@ -0,0 +1,69 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot-warurl/src/main/java/org/eclipse/jetty/ee10/osgi/boot/warurl/WarUrlStreamHandler.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot-warurl/src/main/java/org/eclipse/jetty/ee10/osgi/boot/warurl/WarUrlStreamHandler.java new file mode 100644 index 00000000000..a3a58520056 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot-warurl/src/main/java/org/eclipse/jetty/ee10/osgi/boot/warurl/WarUrlStreamHandler.java @@ -0,0 +1,98 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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.ee10.osgi.boot.warurl.internal.WarBundleManifestGenerator; +import org.eclipse.jetty.ee10.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-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot-warurl/src/main/java/org/eclipse/jetty/ee10/osgi/boot/warurl/internal/WarBundleManifestGenerator.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot-warurl/src/main/java/org/eclipse/jetty/ee10/osgi/boot/warurl/internal/WarBundleManifestGenerator.java new file mode 100644 index 00000000000..4bf92fa3f36 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot-warurl/src/main/java/org/eclipse/jetty/ee10/osgi/boot/warurl/internal/WarBundleManifestGenerator.java @@ -0,0 +1,276 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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/") && e.getName().endsWith(".jar")) + { + res.add(e.getName()); + } + } + return res; + } +} diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot-warurl/src/main/java/org/eclipse/jetty/ee10/osgi/boot/warurl/internal/WarURLConnection.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot-warurl/src/main/java/org/eclipse/jetty/ee10/osgi/boot/warurl/internal/WarURLConnection.java new file mode 100644 index 00000000000..0dfe8af6034 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot-warurl/src/main/java/org/eclipse/jetty/ee10/osgi/boot/warurl/internal/WarURLConnection.java @@ -0,0 +1,360 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/jettyhome/contexts/README b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/jettyhome/contexts/README new file mode 100644 index 00000000000..91f4a49ead3 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-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-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/jettyhome/etc/README b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/jettyhome/etc/README new file mode 100644 index 00000000000..dbc9fa71564 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-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-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/jettyhome/etc/jetty-deploy.xml b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/jettyhome/etc/jetty-deploy.xml new file mode 100644 index 00000000000..ba8c4da7feb --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/jettyhome/etc/jetty-deploy.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/jettyhome/etc/jetty-http.xml b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/jettyhome/etc/jetty-http.xml new file mode 100644 index 00000000000..303ada127b6 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/jettyhome/etc/jetty-http.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/jettyhome/etc/jetty.xml b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/jettyhome/etc/jetty.xml new file mode 100644 index 00000000000..77a771591f7 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/jettyhome/etc/jetty.xml @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + 10 + 200 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + org.eclipse.jetty.ee10.webapp.FragmentConfiguration + org.eclipse.jetty.ee10.webapp.JettyWebXmlConfiguration + org.eclipse.jetty.ee10.webapp.WebXmlConfiguration + org.eclipse.jetty.ee10.webapp.WebAppConfiguration + org.eclipse.jetty.ee10.webapp.ServletsConfiguration + org.eclipse.jetty.ee10.webapp.JspConfiguration + org.eclipse.jetty.ee10.webapp.JaasConfiguration + org.eclipse.jetty.ee10.webapp.JndiConfiguration + org.eclipse.jetty.plus.webapp.PlusConfiguration + org.eclipse.jetty.plus.webapp.EnvConfiguration + org.eclipse.jetty.ee10.webapp.JmxConfiguration + org.eclipse.jetty.ee10.osgi.annotations.AnnotationConfiguration + org.eclipse.jetty.websocket.server.config.JettyWebSocketConfiguration + org.eclipse.jetty.websocket.jakarta.server.config.JakartaWebSocketConfiguration + org.eclipse.jetty.ee10.osgi.boot.OSGiWebInfConfiguration + org.eclipse.jetty.ee10.osgi.boot.OSGiMetaInfConfiguration + + + + + + + + java.naming.factory.initial + + + + java.naming.factory.url.pkgs + + + + diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/jettyhome/lib/ext/README b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/jettyhome/lib/ext/README new file mode 100644 index 00000000000..921d5f41bd4 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-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-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/jettyhome/logs/README b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/jettyhome/logs/README new file mode 100644 index 00000000000..ef503747e9d --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-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-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/jettyhome/resources/README b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/jettyhome/resources/README new file mode 100644 index 00000000000..f2baef21eff --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-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-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/jettyhome/webapps/README b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/jettyhome/webapps/README new file mode 100644 index 00000000000..08a57edf6ef --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-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-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/pom.xml b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/pom.xml new file mode 100644 index 00000000000..9a107f3a46c --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/pom.xml @@ -0,0 +1,115 @@ + + + org.eclipse.jetty.ee10.osgi + jetty-ee10-osgi-project + 12.0.0-SNAPSHOT + + 4.0.0 + jetty-ee10-osgi-boot + EE10 :: Jetty :: OSGi :: Boot + Jetty OSGi Boot bundle + + ${project.groupId}.boot + org.eclipse.jetty.ee10.osgi.boot.* + + + + org.eclipse.jetty.ee10 + jetty-ee10-annotations + ${project.version} + + + org.eclipse.jetty.ee10 + jetty-ee10-webapp + ${project.version} + + + org.eclipse.jetty + jetty-deploy + ${project.version} + + + org.eclipse.jetty + jetty-jmx + ${project.version} + + + org.eclipse.platform + org.eclipse.osgi + + + org.eclipse.platform + org.eclipse.osgi.services + + + + + + + maven-antrun-plugin + + + process-resources + + + + + + + + + + + + run + + + + + + org.apache.felix + maven-bundle-plugin + true + + + org.eclipse.jetty.ee10.osgi.boot;singleton:=true + org.eclipse.jetty.ee10.osgi.boot.JettyBootstrapActivator + org.eclipse.jetty.*;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;==;${servletImpl.osgiVersion}),$(version;+;${servletImpl.osgiVersion}))", + jakarta.servlet.http;version="[$(version;==;${servletImpl.osgiVersion}),$(version;+;${servletImpl.osgiVersion}))", + 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="1.4.0", + org.osgi.service.event;version="1.4.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.annotations;resolution:=optional, + * + + + osgi.extender; filter:="(osgi.extender=osgi.serviceloader.registrar)" + + + osgi.serviceloader; osgi.serviceloader=org.eclipse.jetty.ee10.webapp.Configuration + + <_nouses>true + + + + + + + diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/annotations/AnnotationConfiguration.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/annotations/AnnotationConfiguration.java new file mode 100644 index 00000000000..0fe43aa3a28 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/annotations/AnnotationConfiguration.java @@ -0,0 +1,244 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.osgi.annotations; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.util.HashSet; +import java.util.Set; + +import jakarta.servlet.ServletContainerInitializer; +import org.eclipse.jetty.ee10.annotations.AnnotationParser.Handler; +import org.eclipse.jetty.ee10.osgi.boot.OSGiMetaInfConfiguration; +import org.eclipse.jetty.ee10.osgi.boot.OSGiWebappConstants; +import org.eclipse.jetty.ee10.webapp.Configuration; +import org.eclipse.jetty.ee10.webapp.WebAppContext; +import org.eclipse.jetty.util.StringUtil; +import org.eclipse.jetty.util.resource.Resource; +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.ee10.annotations.AnnotationConfiguration +{ + private static final Logger LOG = LoggerFactory.getLogger(org.eclipse.jetty.ee10.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.ee10.osgi.annotations.AnnotationParser osgiAnnotationParser = (org.eclipse.jetty.ee10.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.ee10.annotations.AnnotationConfiguration.class; + } + + /** + * This parser scans the bundles using the OSGi APIs instead of assuming a jar. + */ + @Override + protected org.eclipse.jetty.ee10.annotations.AnnotationParser createAnnotationParser(int javaTargetVersion) + { + return new AnnotationParser(javaTargetVersion); + } + + @Override + public Resource getJarFor(ServletContainerInitializer service) throws MalformedURLException, IOException + { + Resource resource = super.getJarFor(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 && !resource.toString().endsWith(".jar")) + 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(WebAppContext context, org.eclipse.jetty.ee10.annotations.AnnotationParser parser) + throws Exception + { + AnnotationParser oparser = (AnnotationParser)parser; + + if (_webInfLibStats == null) + _webInfLibStats = new CounterStatistic(); + + 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(bundle); + if (!context.getMetaData().getWebInfResources(false).contains(bundleRes)) + { + context.getMetaData().addWebInfResource(bundleRes); + } + + if (bundle.getHeaders().get(Constants.FRAGMENT_HOST) != null) + { + //a fragment indeed: + parseFragmentBundle(context, oparser, webbundle, bundle); + _webInfLibStats.increment(); + } + } + } + //scan ourselves + oparser.indexBundle(webbundle); + parseWebBundle(context, oparser, webbundle); + _webInfLibStats.increment(); + + //scan the WEB-INF/lib + super.parseWebInfLib(context, 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(context, oparser, webbundle, requiredBundle); + _webInfLibStats.increment(); + } + } + } + } + + /** + * Scan a fragment bundle for servlet annotations + * + * @param context The webapp context + * @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(WebAppContext context, AnnotationParser parser, + Bundle webbundle, Bundle fragmentBundle) throws Exception + { + parseBundle(context, parser, webbundle, fragmentBundle); + } + + /** + * Scan a bundle required by the webbundle for servlet annotations + * + * @param context The webapp context + * @param parser The parser + * @param webbundle The current webbundle + * @throws Exception if unable to parse the web bundle + */ + protected void parseWebBundle(WebAppContext context, AnnotationParser parser, Bundle webbundle) + throws Exception + { + parseBundle(context, parser, webbundle, webbundle); + } + + @Override + public void parseWebInfClasses(WebAppContext context, org.eclipse.jetty.ee10.annotations.AnnotationParser parser) + throws Exception + { + Bundle webbundle = (Bundle)context.getAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE); + String bundleClasspath = (String)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(context, parser); + } + + /** + * Scan a bundle required by the webbundle for servlet annotations + * + * @param context The webapp context + * @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(WebAppContext context, AnnotationParser parser, + Bundle webbundle, Bundle requiredBundle) throws Exception + { + parseBundle(context, parser, webbundle, requiredBundle); + } + + protected void parseBundle(WebAppContext context, AnnotationParser parser, + Bundle webbundle, Bundle bundle) throws Exception + { + + Resource bundleRes = parser.getResource(bundle); + Set handlers = new HashSet<>(); + handlers.addAll(_discoverableAnnotationHandlers); + if (_classInheritanceHandler != null) + handlers.add(_classInheritanceHandler); + handlers.addAll(_containerInitializerAnnotationHandlers); + + if (_parserTasks != null) + { + BundleParserTask task = new BundleParserTask(parser, handlers, bundleRes); + _parserTasks.add(task); + if (LOG.isDebugEnabled()) + task.setStatistic(new TimeStatistic()); + } + } +} diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/annotations/AnnotationParser.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/annotations/AnnotationParser.java new file mode 100644 index 00000000000..5c576700955 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/annotations/AnnotationParser.java @@ -0,0 +1,225 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.osgi.annotations; + +import java.io.File; +import java.io.InputStream; +import java.net.URI; +import java.net.URL; +import java.util.Comparator; +import java.util.Enumeration; +import java.util.Set; +import java.util.StringTokenizer; +import java.util.TreeSet; +import java.util.concurrent.ConcurrentHashMap; + +import org.eclipse.jetty.ee10.osgi.boot.utils.BundleFileLocatorHelperFactory; +import org.eclipse.jetty.util.StringUtil; +import org.eclipse.jetty.util.resource.Resource; +import org.osgi.framework.Bundle; +import org.osgi.framework.Constants; + +/** + * + */ +public class AnnotationParser extends org.eclipse.jetty.ee10.annotations.AnnotationParser +{ + private Set _alreadyParsed = ConcurrentHashMap.newKeySet(); + + private ConcurrentHashMap _uriToBundle = new ConcurrentHashMap<>(); + private ConcurrentHashMap _bundleToResource = new ConcurrentHashMap<>(); + private ConcurrentHashMap _resourceToBundle = new ConcurrentHashMap<>(); + private ConcurrentHashMap _bundleToUri = new ConcurrentHashMap<>(); + + public AnnotationParser(int javaPlatform) + { + super(javaPlatform); + } + + /** + * Keep track of a jetty URI Resource and its associated OSGi bundle. + * + * @param bundle the bundle to index + * @return the resource for the bundle + * @throws Exception if unable to create the resource reference + */ + public Resource indexBundle(Bundle bundle) throws Exception + { + File bundleFile = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(bundle); + Resource resource = Resource.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); + } + + /** + * + */ + @Override + public void parse(Set handlers, URI[] uris) + throws Exception + { + for (URI uri : uris) + { + Bundle associatedBundle = _uriToBundle.get(uri); + if (associatedBundle == null) + { + if (!_alreadyParsed.add(uri)) + { + continue; + } + //a jar in WEB-INF/lib or the WEB-INF/classes + //use the behavior of the super class for a standard jar. + super.parse(handlers, new URI[]{uri}); + } + else + { + parse(handlers, associatedBundle); + } + } + } + + public void parse(Set handlers, Bundle bundle) + throws Exception + { + URI uri = _bundleToUri.get(bundle); + if (!_alreadyParsed.add(uri)) + { + return; + } + + String bundleClasspath = (String)bundle.getHeaders().get(Constants.BUNDLE_CLASSPATH); + if (bundleClasspath == null) + { + bundleClasspath = "."; + } + //order the paths first by the number of tokens in the path second alphabetically. + TreeSet paths = new TreeSet<>( + new Comparator() + { + @Override + public int compare(String o1, String o2) + { + int paths1 = new StringTokenizer(o1, "/", false).countTokens(); + int paths2 = new StringTokenizer(o2, "/", false).countTokens(); + if (paths1 == paths2) + { + return o1.compareTo(o2); + } + return paths2 - paths1; + } + }); + boolean hasDotPath = false; + StringTokenizer tokenizer = new StringTokenizer(bundleClasspath, StringUtil.DEFAULT_DELIMS, false); + while (tokenizer.hasMoreTokens()) + { + String token = tokenizer.nextToken().trim(); + if (!token.startsWith("/")) + { + token = "/" + token; + } + if (token.equals("/.")) + { + hasDotPath = true; + } + else if (!token.endsWith(".jar") && !token.endsWith("/")) + { + paths.add(token + "/"); + } + else + { + paths.add(token); + } + } + //support the development environment: maybe the classes are inside bin or target/classes + //this is certainly not useful in production. + //however it makes our life so much easier during development. + if (bundle.getEntry("/.classpath") != null) + { + if (bundle.getEntry("/bin/") != null) + { + paths.add("/bin/"); + } + else if (bundle.getEntry("/target/classes/") != null) + { + paths.add("/target/classes/"); + } + } + @SuppressWarnings("rawtypes") + Enumeration classes = bundle.findEntries("/", "*.class", true); + if (classes == null) + { + return; + } + while (classes.hasMoreElements()) + { + URL classUrl = (URL)classes.nextElement(); + String path = classUrl.getPath(); + //remove the longest path possible: + String name = null; + for (String prefixPath : paths) + { + if (path.startsWith(prefixPath)) + { + name = path.substring(prefixPath.length()); + break; + } + } + if (name == null && hasDotPath) + { + //remove the starting '/' + name = path.substring(1); + } + if (name == null) + { + //found some .class file in the archive that was not under one of the prefix paths + //or the bundle classpath wasn't simply ".", so skip it + continue; + } + + if (!isValidClassFileName(name)) + { + continue; //eg skip module-info.class + } + + //transform into a classname to pass to the resolver + String shortName = StringUtil.replace(name, '/', '.').substring(0, name.length() - 6); + + addParsedClass(shortName, getResource(bundle)); + + try (InputStream classInputStream = classUrl.openStream()) + { + scanClass(handlers, getResource(bundle), classInputStream); + } + } + } +} diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/AbstractContextProvider.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/AbstractContextProvider.java new file mode 100644 index 00000000000..9b8e98058c6 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/AbstractContextProvider.java @@ -0,0 +1,235 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.osgi.boot; + +import java.io.File; +import java.util.Dictionary; +import java.util.HashMap; + +import org.eclipse.jetty.deploy.App; +import org.eclipse.jetty.deploy.AppProvider; +import org.eclipse.jetty.deploy.DeploymentManager; +import org.eclipse.jetty.ee10.osgi.boot.internal.serverfactory.ServerInstanceWrapper; +import org.eclipse.jetty.ee10.osgi.boot.utils.BundleFileLocatorHelperFactory; +import org.eclipse.jetty.ee10.osgi.boot.utils.OSGiClassLoader; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.util.component.AbstractLifeCycle; +import org.eclipse.jetty.util.resource.JarResource; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.xml.XmlConfiguration; +import org.osgi.framework.Bundle; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * AbstractContextProvider + * + * Base class for DeploymentManager Providers that can deploy ContextHandlers into + * Jetty that have been discovered via OSGI either as bundles or services. + */ +public abstract class AbstractContextProvider extends AbstractLifeCycle implements AppProvider +{ + private static final Logger LOG = LoggerFactory.getLogger(AbstractContextProvider.class); + + private DeploymentManager _deploymentManager; + + private ServerInstanceWrapper _serverWrapper; + + /** + * OSGiApp + */ + public class OSGiApp extends AbstractOSGiApp + { + private String _contextFile; + private ContextHandler _contextHandler; + private boolean _configured = false; + + public OSGiApp(DeploymentManager manager, AppProvider provider, String originId, Bundle bundle, String contextFile) + { + super(manager, provider, bundle, originId); + _contextFile = contextFile; + } + + public OSGiApp(DeploymentManager manager, AppProvider provider, Bundle bundle, Dictionary properties, String contextFile, String originId) + { + super(manager, provider, bundle, properties, originId); + _contextFile = contextFile; + } + + public String getContextFile() + { + return _contextFile; + } + + public void setHandler(ContextHandler h) + { + _contextHandler = h; + } + + public ContextHandler createContextHandler() + throws Exception + { + configureContextHandler(); + return _contextHandler; + } + + public void configureContextHandler() + throws Exception + { + if (_configured) + return; + + _configured = true; + + //Override for bundle root may have been set + String bundleOverrideLocation = (String)_properties.get(OSGiWebappConstants.JETTY_BUNDLE_INSTALL_LOCATION_OVERRIDE); + + //Location on filesystem of bundle or the bundle override location + File bundleLocation = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(_bundle); + File root = (bundleOverrideLocation == null ? bundleLocation : new File(bundleOverrideLocation)); + Resource rootResource = Resource.newResource(BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(root.toURI().toURL())); + + //try and make sure the rootResource is useable - if its a jar then make it a jar file url + if (rootResource.exists() && !rootResource.isDirectory() && !rootResource.toString().startsWith("jar:")) + { + Resource jarResource = JarResource.newJarResource(rootResource); + if (jarResource.exists() && jarResource.isDirectory()) + rootResource = jarResource; + } + + //Set the base resource of the ContextHandler, if not already set, can also be overridden by the context xml file + if (_contextHandler != null && _contextHandler.getResourceBase() == null) + { + _contextHandler.setResourceBase(rootResource.getPath()); + } + + //Use a classloader that knows about the common jetty parent loader, and also the bundle + OSGiClassLoader classLoader = new OSGiClassLoader(getServerInstanceWrapper().getParentClassLoaderForWebapps(), _bundle); + + //if there is a context file, find it and apply it + if (_contextFile == null && _contextHandler == null) + throw new IllegalStateException("No context file or ContextHandler"); + + if (_contextFile != null) + { + //apply the contextFile, creating the ContextHandler, the DeploymentManager will register it in the ContextHandlerCollection + Resource res = null; + + String jettyHome = (String)getServerInstanceWrapper().getServer().getAttribute(OSGiServerConstants.JETTY_HOME); + if (jettyHome == null) + jettyHome = System.getProperty(OSGiServerConstants.JETTY_HOME); + + res = findFile(_contextFile, jettyHome, bundleOverrideLocation, _bundle); + + //apply the context xml file, either to an existing ContextHandler, or letting the + //it create the ContextHandler as necessary + if (res != null) + { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + + if (LOG.isDebugEnabled()) + LOG.debug("Context classloader = {}", cl); + try + { + Thread.currentThread().setContextClassLoader(classLoader); + + XmlConfiguration xmlConfiguration = new XmlConfiguration(res); + HashMap properties = new HashMap(); + //put the server instance in + properties.put("Server", getServerInstanceWrapper().getServer()); + //put in the location of the bundle root + properties.put(OSGiWebappConstants.JETTY_BUNDLE_ROOT, rootResource.toString()); + + // insert the bundle's location as a property. + xmlConfiguration.getProperties().putAll(properties); + + if (_contextHandler == null) + _contextHandler = (ContextHandler)xmlConfiguration.configure(); + else + xmlConfiguration.configure(_contextHandler); + } + finally + { + Thread.currentThread().setContextClassLoader(cl); + } + } + } + + //Set up the class loader we created + _contextHandler.setClassLoader(classLoader); + + //If a bundle/service property specifies context path, let it override the context xml + String contextPath = (String)_properties.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH); + if (contextPath != null) + _contextHandler.setContextPath(contextPath); + + //osgi Enterprise Spec r4 p.427 + _contextHandler.setAttribute(OSGiWebappConstants.OSGI_BUNDLECONTEXT, _bundle.getBundleContext()); + + //make sure we protect also the osgi dirs specified by OSGi Enterprise spec + + if (_contextHandler instanceof ServletContextHandler servletContextHandler) + { + String[] targets = servletContextHandler.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); + servletContextHandler.setProtectedTargets(updatedTargets); + } + } + } + + public AbstractContextProvider(ServerInstanceWrapper wrapper) + { + _serverWrapper = wrapper; + } + + public ServerInstanceWrapper getServerInstanceWrapper() + { + return _serverWrapper; + } + + @Override + public ContextHandler createContextHandler(App app) throws Exception + { + if (app == null) + return null; + if (!(app instanceof OSGiApp)) + throw new IllegalStateException(app + " is not a BundleApp"); + + //Create a ContextHandler suitable to deploy in OSGi + ContextHandler h = ((OSGiApp)app).createContextHandler(); + return h; + } + + @Override + public void setDeploymentManager(DeploymentManager deploymentManager) + { + _deploymentManager = deploymentManager; + } + + public DeploymentManager getDeploymentManager() + { + return _deploymentManager; + } +} diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/AbstractOSGiApp.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/AbstractOSGiApp.java new file mode 100644 index 00000000000..9d95c013ecd --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/AbstractOSGiApp.java @@ -0,0 +1,195 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.osgi.boot; + +import java.io.File; +import java.net.URL; +import java.util.Dictionary; +import java.util.Hashtable; + +import org.eclipse.jetty.deploy.App; +import org.eclipse.jetty.deploy.AppProvider; +import org.eclipse.jetty.deploy.DeploymentManager; +import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.util.resource.Resource; +import org.osgi.framework.Bundle; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.ServiceRegistration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * AbstractOSGiApp + * + * Base class representing info about a webapp/ContextHandler that is deployed into Jetty. + */ +public abstract class AbstractOSGiApp extends App +{ + private static final Logger LOG = LoggerFactory.getLogger(AbstractOSGiApp.class); + + protected Bundle _bundle; + protected Dictionary _properties; + protected ServiceRegistration _registration; + + public AbstractOSGiApp(DeploymentManager manager, AppProvider provider, Bundle bundle, String originId) + { + this(manager, provider, bundle, bundle.getHeaders(), originId); + } + + public AbstractOSGiApp(DeploymentManager manager, AppProvider provider, Bundle bundle, Dictionary properties, String originId) + { + super(manager, provider, null, originId); // TODO environment + _properties = properties; + _bundle = bundle; + } + + public String getBundleSymbolicName() + { + return _bundle.getSymbolicName(); + } + + public String getBundleVersionAsString() + { + if (_bundle.getVersion() == null) + return null; + return _bundle.getVersion().toString(); + } + + public Bundle getBundle() + { + return _bundle; + } + + public void setRegistration(ServiceRegistration registration) + { + _registration = registration; + } + + public ServiceRegistration getRegistration() + { + return _registration; + } + + public void registerAsOSGiService() throws Exception + { + if (_registration == null) + { + Dictionary properties = new Hashtable(); + properties.put(OSGiWebappConstants.WATERMARK, OSGiWebappConstants.WATERMARK); + if (getBundleSymbolicName() != null) + properties.put(OSGiWebappConstants.OSGI_WEB_SYMBOLICNAME, getBundleSymbolicName()); + if (getBundleVersionAsString() != null) + properties.put(OSGiWebappConstants.OSGI_WEB_VERSION, getBundleVersionAsString()); + properties.put(OSGiWebappConstants.OSGI_WEB_CONTEXTPATH, getContextPath()); + ServiceRegistration rego = FrameworkUtil.getBundle(this.getClass()).getBundleContext().registerService(ContextHandler.class.getName(), getContextHandler(), properties); + setRegistration(rego); + } + } + + protected void deregisterAsOSGiService() throws Exception + { + if (_registration == null) + return; + + _registration.unregister(); + _registration = null; + } + + protected Resource getFileAsResource(String dir, String file) + { + Resource r = null; + try + { + File asFile = new File(dir, file); + if (asFile.exists()) + r = Resource.newResource(asFile); + } + catch (Exception e) + { + r = null; + } + return r; + } + + protected Resource getFileAsResource(String file) + { + Resource r = null; + try + { + File asFile = new File(file); + if (asFile.exists()) + r = Resource.newResource(asFile); + } + catch (Exception e) + { + r = null; + } + return r; + } + + protected Resource findFile(String fileName, String jettyHome, String bundleOverrideLocation, Bundle containingBundle) + { + Resource res = null; + + //try to find the context file in the filesystem + if (fileName.startsWith("/")) + res = getFileAsResource(fileName); + if (res != null) + return res; + + //try to find it relative to jetty home + if (jettyHome != null) + { + if (jettyHome.startsWith("\"") || jettyHome.startsWith("'")) + jettyHome = jettyHome.substring(1); + if (jettyHome.endsWith("\"") || (jettyHome.endsWith("'"))) + jettyHome = jettyHome.substring(0, jettyHome.length() - 1); + + res = getFileAsResource(jettyHome, fileName); + } + if (res != null) + return res; + + //try to find it relative to an override location that has been specified + if (bundleOverrideLocation != null) + { + try (Resource location = Resource.newResource(bundleOverrideLocation)) + { + res = location.addPath(fileName); + } + catch (Exception e) + { + LOG.warn("Unable to find relative override location: {}", bundleOverrideLocation, e); + } + } + if (res != null) + return res; + + //try to find it relative to the bundle in which it is being deployed + if (containingBundle != null) + { + if (fileName.startsWith("./")) + fileName = fileName.substring(1); + + if (!fileName.startsWith("/")) + fileName = "/" + fileName; + + URL entry = _bundle.getEntry(fileName); + if (entry != null) + res = Resource.newResource(entry); + } + + return res; + } +} diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/AbstractWebAppProvider.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/AbstractWebAppProvider.java new file mode 100644 index 00000000000..6ec43b60ab0 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/AbstractWebAppProvider.java @@ -0,0 +1,553 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.osgi.boot; + +import java.io.File; +import java.net.URI; +import java.net.URL; +import java.util.Dictionary; +import java.util.Enumeration; +import java.util.HashMap; + +import org.eclipse.jetty.deploy.App; +import org.eclipse.jetty.deploy.AppProvider; +import org.eclipse.jetty.deploy.DeploymentManager; +import org.eclipse.jetty.ee10.osgi.boot.internal.serverfactory.ServerInstanceWrapper; +import org.eclipse.jetty.ee10.osgi.boot.internal.webapp.OSGiWebappClassLoader; +import org.eclipse.jetty.ee10.osgi.boot.utils.BundleFileLocatorHelperFactory; +import org.eclipse.jetty.ee10.webapp.WebAppClassLoader; +import org.eclipse.jetty.ee10.webapp.WebAppContext; +import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.util.component.AbstractLifeCycle; +import org.eclipse.jetty.util.resource.JarResource; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.xml.XmlConfiguration; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.osgi.service.packageadmin.PackageAdmin; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * AbstractWebAppProvider + *

    + * Base class for Jetty DeploymentManager Providers that are capable of deploying a webapp, + * either from a bundle or an OSGi service. + */ +public abstract class AbstractWebAppProvider extends AbstractLifeCycle implements AppProvider +{ + private static final Logger LOG = LoggerFactory.getLogger(AbstractWebAppProvider.class); + + private boolean _parentLoaderPriority; + + private String _defaultsDescriptor; + + private boolean _extractWars = true; //See WebAppContext.extractWars + + private String _tldBundles; + + private DeploymentManager _deploymentManager; + + private ServerInstanceWrapper _serverWrapper; + + /** + * OSGiApp + * + * Represents a deployable webapp. + */ + public class OSGiApp extends AbstractOSGiApp + { + private String _contextPath; + private String _webAppPath; + private WebAppContext _webApp; + + public OSGiApp(DeploymentManager manager, AppProvider provider, Bundle bundle, String originId) + { + super(manager, provider, bundle, originId); + } + + public OSGiApp(DeploymentManager manager, AppProvider provider, Bundle bundle, Dictionary properties, String originId) + { + super(manager, provider, bundle, properties, originId); + } + + public void setWebAppContext(WebAppContext webApp) + { + _webApp = webApp; + } + + @Override + public String getContextPath() + { + return _contextPath; + } + + public void setContextPath(String contextPath) + { + this._contextPath = contextPath; + } + + public String getBundlePath() + { + return _webAppPath; + } + + public void setWebAppPath(String path) + { + this._webAppPath = path; + } + + public ContextHandler createContextHandler() + throws Exception + { + if (_webApp != null) + { + configureWebApp(); + return _webApp; + } + + createWebApp(); + return _webApp; + } + + protected void createWebApp() + throws Exception + { + _webApp = newWebApp(); + configureWebApp(); + } + + protected WebAppContext newWebApp() + { + WebAppContext webApp = new WebAppContext(); + 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); + + return webApp; + } + + public void configureWebApp() + throws Exception + { + //TODO turn this around and let any context.xml file get applied first, and have the properties override + _webApp.setContextPath(_contextPath); + + //osgi Enterprise Spec r4 p.427 + _webApp.setAttribute(OSGiWebappConstants.OSGI_BUNDLECONTEXT, _bundle.getBundleContext()); + + String overrideBundleInstallLocation = (String)_properties.get(OSGiWebappConstants.JETTY_BUNDLE_INSTALL_LOCATION_OVERRIDE); + File bundleInstallLocation = + (overrideBundleInstallLocation == null + ? BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(_bundle) + : new File(overrideBundleInstallLocation)); + + if (LOG.isDebugEnabled()) + { + LOG.debug("Bundle location is {}, install location: {}", _bundle.getLocation(), bundleInstallLocation); + } + + URL url = null; + Resource rootResource = Resource.newResource(BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(bundleInstallLocation.toURI().toURL())); + //try and make sure the rootResource is useable - if its a jar then make it a jar file url + if (rootResource.exists() && !rootResource.isDirectory() && !rootResource.toString().startsWith("jar:")) + { + Resource jarResource = JarResource.newJarResource(rootResource); + if (jarResource.exists() && jarResource.isDirectory()) + rootResource = jarResource; + } + + //if the path wasn't set or it was ., then it is the root of the bundle's installed location + if (_webAppPath == null || _webAppPath.length() == 0 || ".".equals(_webAppPath)) + { + url = bundleInstallLocation.toURI().toURL(); + if (LOG.isDebugEnabled()) + LOG.debug("Webapp base using bundle install location: {}", url); + } + else + { + //Get the location of the root of the webapp inside the installed bundle + if (_webAppPath.startsWith("/") || _webAppPath.startsWith("file:")) + { + url = new File(_webAppPath).toURI().toURL(); + if (LOG.isDebugEnabled()) + LOG.debug("Webapp base using absolute location: {}", url); + } + else if (bundleInstallLocation != null && bundleInstallLocation.isDirectory()) + { + url = new File(bundleInstallLocation, _webAppPath).toURI().toURL(); + if (LOG.isDebugEnabled()) + LOG.debug("Webapp base using path relative to bundle unpacked install location: {}", url); + } + else if (bundleInstallLocation != null) + { + Enumeration urls = BundleFileLocatorHelperFactory.getFactory().getHelper().findEntries(_bundle, _webAppPath); + if (urls != null && urls.hasMoreElements()) + { + url = urls.nextElement(); + if (LOG.isDebugEnabled()) + LOG.debug("Webapp base using path relative to packed bundle location: {}", url); + } + } + } + + if (url == null) + { + throw new IllegalArgumentException("Unable to locate " + _webAppPath + " in " + + (bundleInstallLocation != null ? bundleInstallLocation.getAbsolutePath() : "unlocated bundle '" + _bundle.getSymbolicName() + "'")); + } + + //Sets the location of the war file + // converts bundleentry: protocol if necessary + _webApp.setWar(BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(url).toString()); + + // Set up what has been configured on the provider + _webApp.setParentLoaderPriority(isParentLoaderPriority()); + _webApp.setExtractWAR(isExtract()); + + //Set up configuration from manifest headers + //extra classpath + String tmp = (String)_properties.get(OSGiWebappConstants.JETTY_EXTRA_CLASSPATH); + if (tmp != null) + _webApp.setExtraClasspath(tmp); + + //web.xml + tmp = (String)_properties.get(OSGiWebappConstants.JETTY_WEB_XML_PATH); + if (tmp != null && tmp.trim().length() != 0) + { + File webXml = getFile(tmp, bundleInstallLocation); + if (webXml != null && webXml.exists()) + _webApp.setDescriptor(webXml.getAbsolutePath()); + } + + //webdefault.xml + tmp = (String)_properties.get(OSGiWebappConstants.JETTY_DEFAULT_WEB_XML_PATH); + if (tmp != null) + { + File defaultWebXml = getFile(tmp, bundleInstallLocation); + if (defaultWebXml != null) + { + if (defaultWebXml.exists()) + _webApp.setDefaultsDescriptor(defaultWebXml.getAbsolutePath()); + else + LOG.warn("{} does not exist", defaultWebXml.getAbsolutePath()); + } + } + + //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)_properties.get(OSGiWebappConstants.REQUIRE_TLD_BUNDLE); + String pathsToTldBundles = getPathsToRequiredBundles(requireTldBundles); + + // make sure we provide access to all the jetty bundles by going + // through this bundle. + OSGiWebappClassLoader webAppLoader = new OSGiWebappClassLoader(_serverWrapper.getParentClassLoaderForWebapps(), _webApp, _bundle); + + if (pathsToTldBundles != null) + webAppLoader.addClassPath(pathsToTldBundles); + _webApp.setClassLoader(webAppLoader); + + // apply any META-INF/context.xml file that is found to configure + // the webapp first + try + { + final Resource finalRootResource = rootResource; + WebAppClassLoader.runWithServerClassAccess(() -> + { + applyMetaInfContextXml(finalRootResource, overrideBundleInstallLocation); + return null; + }); + } + catch (Exception e) + { + LOG.warn("Error applying context xml"); + throw e; + } + + _webApp.setAttribute(OSGiWebappConstants.REQUIRE_TLD_BUNDLE, requireTldBundles); + + //Set up some attributes + // rfc66 + _webApp.setAttribute(OSGiWebappConstants.RFC66_OSGI_BUNDLE_CONTEXT, _bundle.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(), _bundle.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, _bundle); + } + + protected String getPathsToRequiredBundles(String requireTldBundles) + throws Exception + { + if (requireTldBundles == null) + return null; + + ServiceReference ref = _bundle.getBundleContext().getServiceReference(org.osgi.service.packageadmin.PackageAdmin.class.getName()); + PackageAdmin packageAdmin = (ref == null) ? null : (PackageAdmin)_bundle.getBundleContext().getService(ref); + if (packageAdmin == null) + throw new IllegalStateException("Unable to get PackageAdmin reference to locate required Tld bundles"); + + StringBuilder paths = new StringBuilder(); + String[] symbNames = requireTldBundles.split("[, ]"); + + for (String symbName : symbNames) + { + Bundle[] bs = packageAdmin.getBundles(symbName, null); + if (bs == null || bs.length == 0) + { + throw new IllegalArgumentException("Unable to locate the bundle '" + symbName + "' specified by " + + OSGiWebappConstants.REQUIRE_TLD_BUNDLE + " in manifest of " + + (_bundle == null ? "unknown" : _bundle.getSymbolicName())); + } + + File f = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(bs[0]); + if (paths.length() > 0) + paths.append(", "); + paths.append(f.toURI().toURL().toString()); + if (LOG.isDebugEnabled()) + LOG.debug("getPathsToRequiredBundles: bundle path={} uri={}", bs[0].getLocation(), f.toURI()); + } + + return paths.toString(); + } + + protected void applyMetaInfContextXml(Resource rootResource, String overrideBundleInstallLocation) + throws Exception + { + if (_bundle == null) + return; + if (_webApp == null) + return; + + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + if (LOG.isDebugEnabled()) + LOG.debug("Context classloader = {}", cl); + try + { + + Thread.currentThread().setContextClassLoader(_webApp.getClassLoader()); + + URI contextXmlUri = null; + + //TODO replace this with getting the InputStream so we don't cache in URL + //Try looking for a context xml file in META-INF with a specific name + URL url = _bundle.getEntry("/META-INF/jetty-webapp-context.xml"); + if (url != null) + { + contextXmlUri = url.toURI(); + } + + if (contextXmlUri == null) + { + //Didn't find specially named file, try looking for a property that names a context xml file to use + if (_properties != null) + { + String tmp = (String)_properties.get(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH); + if (tmp != null) + { + String[] filenames = tmp.split("[,;]"); + if (filenames != null && filenames.length > 0) + { + String filename = filenames[0]; //should only be 1 filename in this usage + String jettyHome = (String)getServerInstanceWrapper().getServer().getAttribute(OSGiServerConstants.JETTY_HOME); + if (jettyHome == null) + jettyHome = System.getProperty(OSGiServerConstants.JETTY_HOME); + Resource res = findFile(filename, jettyHome, overrideBundleInstallLocation, _bundle); + if (res != null) + { + contextXmlUri = res.getURI(); + } + } + } + } + } + if (contextXmlUri == null) + return; + + // Apply it just as the standard jetty ContextProvider would do + LOG.info("Applying {} to {}", contextXmlUri, _webApp); + + XmlConfiguration xmlConfiguration = new XmlConfiguration(Resource.newResource(contextXmlUri)); + WebAppClassLoader.runWithServerClassAccess(() -> + { + HashMap properties = new HashMap<>(); + xmlConfiguration.getIdMap().put("Server", getDeploymentManager().getServer()); + properties.put(OSGiWebappConstants.JETTY_BUNDLE_ROOT, rootResource.toString()); + properties.put(OSGiServerConstants.JETTY_HOME, (String)getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME)); + xmlConfiguration.getProperties().putAll(properties); + xmlConfiguration.configure(_webApp); + return null; + }); + } + finally + { + Thread.currentThread().setContextClassLoader(cl); + } + } + + private File getFile(String file, File bundleInstall) + { + if (file == null) + return null; + + if (file.startsWith("/") || file.startsWith("file:/")) //absolute location + return new File(file); + else + { + //relative location + //try inside the bundle first + File f = new File(bundleInstall, file); + if (f.exists()) + return f; + String jettyHome = (String)getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME); + if (jettyHome != null) + return new File(jettyHome, file); + } + + return null; + } + } + + public AbstractWebAppProvider(ServerInstanceWrapper wrapper) + { + _serverWrapper = wrapper; + } + + /** + * Get the parentLoaderPriority. + * + * @return the parentLoaderPriority + */ + public boolean isParentLoaderPriority() + { + return _parentLoaderPriority; + } + + /** + * Set the parentLoaderPriority. + * + * @param parentLoaderPriority the parentLoaderPriority to set + */ + public void setParentLoaderPriority(boolean parentLoaderPriority) + { + _parentLoaderPriority = parentLoaderPriority; + } + + /** + * Get the defaultsDescriptor. + * + * @return the defaultsDescriptor + */ + public String getDefaultsDescriptor() + { + return _defaultsDescriptor; + } + + /** + * Set the defaultsDescriptor. + * + * @param defaultsDescriptor the defaultsDescriptor to set + */ + public void setDefaultsDescriptor(String defaultsDescriptor) + { + _defaultsDescriptor = defaultsDescriptor; + } + + public boolean isExtract() + { + return _extractWars; + } + + public void setExtract(boolean extract) + { + _extractWars = extract; + } + + /** + * @param tldBundles Comma separated list of bundles that contain tld jars + * that should be setup on the jetty instances created here. + */ + public void setTldBundles(String tldBundles) + { + _tldBundles = tldBundles; + } + + /** + * @return The list of bundles that contain tld jars that should be setup on + * the jetty instances created here. + */ + public String getTldBundles() + { + return _tldBundles; + } + + public void setServerInstanceWrapper(ServerInstanceWrapper wrapper) + { + _serverWrapper = wrapper; + } + + public ServerInstanceWrapper getServerInstanceWrapper() + { + return _serverWrapper; + } + + public DeploymentManager getDeploymentManager() + { + return _deploymentManager; + } + + @Override + public void setDeploymentManager(DeploymentManager deploymentManager) + { + _deploymentManager = deploymentManager; + } + + @Override + public ContextHandler createContextHandler(App app) throws Exception + { + if (app == null) + return null; + if (!(app instanceof OSGiApp)) + throw new IllegalStateException(app + " is not a BundleApp"); + + //Create a WebAppContext suitable to deploy in OSGi + ContextHandler ch = ((OSGiApp)app).createContextHandler(); + return ch; + } + + public static String getOriginId(Bundle contributor, String path) + { + return contributor.getSymbolicName() + "-" + contributor.getVersion().toString() + (path.startsWith("/") ? path : "/" + path); + } +} diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/BundleContextProvider.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/BundleContextProvider.java new file mode 100644 index 00000000000..b1102ad2528 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/BundleContextProvider.java @@ -0,0 +1,199 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.osgi.boot; + +import java.util.ArrayList; +import java.util.Dictionary; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; + +import org.eclipse.jetty.deploy.App; +import org.eclipse.jetty.ee10.osgi.boot.internal.serverfactory.ServerInstanceWrapper; +import org.eclipse.jetty.util.StringUtil; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.BundleEvent; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.ServiceRegistration; +import org.osgi.util.tracker.BundleTracker; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * BundleContextProvider + *

    + * Handles deploying OSGi bundles that define a context xml file for configuring them. + */ +public class BundleContextProvider extends AbstractContextProvider implements BundleProvider +{ + private static final Logger LOG = LoggerFactory.getLogger(AbstractContextProvider.class); + + private Map _appMap = new HashMap(); + + private Map> _bundleMap = new HashMap>(); + + private ServiceRegistration _serviceRegForBundles; + + private BundleTracker _tracker; + + public class ContextBundleTracker extends BundleTracker + { + protected String _managedServerName; + + public ContextBundleTracker(BundleContext bundleContext, String managedServerName) + { + super(bundleContext, Bundle.ACTIVE | Bundle.STOPPING, null); + _managedServerName = managedServerName; + } + + @Override + public Object addingBundle(Bundle bundle, BundleEvent event) + { + try + { + String serverName = (String)bundle.getHeaders().get(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME); + if ((StringUtil.isBlank(serverName) && _managedServerName.equals(OSGiServerConstants.MANAGED_JETTY_SERVER_DEFAULT_NAME)) || + (!StringUtil.isBlank(serverName) && (serverName.equals(_managedServerName)))) + { + if (bundleAdded(bundle)) + return bundle; + } + } + catch (Exception e) + { + LOG.warn("Unable to add bundle {}", bundle, e); + } + return null; + } + + @Override + public void removedBundle(Bundle bundle, BundleEvent event, Object object) + { + try + { + bundleRemoved(bundle); + } + catch (Exception e) + { + LOG.warn("Unable to remove bundle {}", bundle, e); + } + } + } + + public BundleContextProvider(ServerInstanceWrapper wrapper) + { + super(wrapper); + } + + @Override + protected void doStart() throws Exception + { + //Track bundles that are ContextHandlers that should be deployed + _tracker = new ContextBundleTracker(FrameworkUtil.getBundle(this.getClass()).getBundleContext(), getServerInstanceWrapper().getManagedServerName()); + _tracker.open(); + + //register as an osgi service for deploying contexts defined in a bundle, advertising the name of the jetty Server instance we are related to + Dictionary properties = new Hashtable(); + properties.put(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME, getServerInstanceWrapper().getManagedServerName()); + _serviceRegForBundles = FrameworkUtil.getBundle(this.getClass()).getBundleContext().registerService(BundleProvider.class.getName(), this, properties); + super.doStart(); + } + + @Override + protected void doStop() throws Exception + { + _tracker.close(); + + //unregister ourselves + if (_serviceRegForBundles != null) + { + try + { + _serviceRegForBundles.unregister(); + } + catch (Exception e) + { + LOG.warn("Unable to unregister {}", _serviceRegForBundles, e); + } + } + } + + @Override + public boolean bundleAdded(Bundle bundle) throws Exception + { + if (bundle == null) + return false; + + //If the bundle defines a Web-ContextPath then its probably a webapp and the BundleWebAppProvider should deploy it + if ((String)bundle.getHeaders().get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH) != null) + { + if (LOG.isDebugEnabled()) + LOG.debug("BundleContextProvider ignoring bundle {} with {} set", bundle.getSymbolicName(), OSGiWebappConstants.RFC66_WEB_CONTEXTPATH); + return false; + } + + String contextFiles = (String)bundle.getHeaders().get(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH); + + if (contextFiles == null) + return false; + + boolean added = false; + //bundle defines JETTY_CONTEXT_FILE_PATH header, + //a comma separated list of context xml files that each define a ContextHandler + //TODO: (could be WebAppContexts) + String[] tmp = contextFiles.split("[,;]"); + for (String contextFile : tmp) + { + String originId = bundle.getSymbolicName() + "-" + bundle.getVersion().toString() + "-" + contextFile; + OSGiApp app = new OSGiApp(getDeploymentManager(), this, originId, bundle, contextFile); + _appMap.put(originId, app); + List apps = _bundleMap.get(bundle); + if (apps == null) + { + apps = new ArrayList(); + _bundleMap.put(bundle, apps); + } + apps.add(app); + getDeploymentManager().addApp(app); + added = true; + } + + return added; //true if even 1 context from this bundle was added + } + + /** + * Bundle has been removed. If it was a context we deployed, undeploy it. + * + * @param bundle the bundle + * @return true if this was a context we had deployed, false otherwise + */ + @Override + public boolean bundleRemoved(Bundle bundle) throws Exception + { + List apps = _bundleMap.remove(bundle); + boolean removed = false; + if (apps != null) + { + for (App app : apps) + { + _appMap.remove(app.getOriginId()); + getDeploymentManager().removeApp(app); + removed = true; + } + } + return removed; //true if even 1 context was removed associated with this bundle + } +} diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/BundleProvider.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/BundleProvider.java new file mode 100644 index 00000000000..e86fc0d88eb --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/BundleProvider.java @@ -0,0 +1,28 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.osgi.boot; + +import org.osgi.framework.Bundle; + +/** + * BundleProvider + * + * Jetty DeploymentManager Provider api for webapps or ContextHandlers that are discovered as osgi bundles. + */ +public interface BundleProvider +{ + public boolean bundleAdded(Bundle bundle) throws Exception; + + public boolean bundleRemoved(Bundle bundle) throws Exception; +} diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/BundleWebAppProvider.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/BundleWebAppProvider.java new file mode 100644 index 00000000000..2f223c22879 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/BundleWebAppProvider.java @@ -0,0 +1,257 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.osgi.boot; + +import java.util.Dictionary; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Map; + +import org.eclipse.jetty.deploy.App; +import org.eclipse.jetty.ee10.osgi.boot.internal.serverfactory.ServerInstanceWrapper; +import org.eclipse.jetty.ee10.osgi.boot.utils.Util; +import org.eclipse.jetty.util.StringUtil; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.BundleEvent; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.ServiceRegistration; +import org.osgi.util.tracker.BundleTracker; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * BundleWebAppProvider + *

    + * A Jetty Provider that knows how to deploy a WebApp contained inside a Bundle. + */ +public class BundleWebAppProvider extends AbstractWebAppProvider implements BundleProvider +{ + private static final Logger LOG = LoggerFactory.getLogger(AbstractWebAppProvider.class); + + /** + * Map of Bundle to App. Used when a Bundle contains a webapp. + */ + private Map _bundleMap = new HashMap<>(); + + private ServiceRegistration _serviceRegForBundles; + + private WebAppTracker _webappTracker; + + public class WebAppTracker extends BundleTracker + { + protected String _managedServerName; + + public WebAppTracker(BundleContext bundleContext, String managedServerName) + { + super(bundleContext, Bundle.ACTIVE | Bundle.STOPPING, null); + _managedServerName = managedServerName; + } + + @Override + public Object addingBundle(Bundle bundle, BundleEvent event) + { + try + { + String serverName = (String)bundle.getHeaders().get(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME); + if ((StringUtil.isBlank(serverName) && _managedServerName.equals(OSGiServerConstants.MANAGED_JETTY_SERVER_DEFAULT_NAME)) || + (!StringUtil.isBlank(serverName) && (serverName.equals(_managedServerName)))) + { + if (bundleAdded(bundle)) + return bundle; + } + } + catch (Exception e) + { + LOG.warn("Unable to add bundle {}", bundle, e); + } + return null; + } + + @Override + public void removedBundle(Bundle bundle, BundleEvent event, Object object) + { + try + { + bundleRemoved(bundle); + } + catch (Exception e) + { + LOG.warn("Unable to remove bundle {}", bundle, e); + } + } + } + + public BundleWebAppProvider(ServerInstanceWrapper wrapper) + { + super(wrapper); + } + + @Override + protected void doStart() throws Exception + { + _webappTracker = new WebAppTracker(FrameworkUtil.getBundle(this.getClass()).getBundleContext(), getServerInstanceWrapper().getManagedServerName()); + _webappTracker.open(); + //register as an osgi service for deploying bundles, advertising the name of the jetty Server instance we are related to + Dictionary properties = new Hashtable<>(); + properties.put(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME, getServerInstanceWrapper().getManagedServerName()); + _serviceRegForBundles = FrameworkUtil.getBundle(this.getClass()).getBundleContext().registerService(BundleProvider.class.getName(), this, properties); + super.doStart(); + } + + @Override + protected void doStop() throws Exception + { + _webappTracker.close(); + + //unregister ourselves + if (_serviceRegForBundles != null) + { + try + { + _serviceRegForBundles.unregister(); + } + catch (Exception e) + { + LOG.warn("Unable to unregister {}", _serviceRegForBundles, e); + } + } + + super.doStop(); + } + + /** + * A bundle has been added that could be a webapp + * + * @param bundle the bundle + */ + @Override + public boolean bundleAdded(Bundle bundle) throws Exception + { + if (bundle == null) + return false; + + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader(getServerInstanceWrapper().getParentClassLoaderForWebapps()); + String contextPath = null; + try + { + @SuppressWarnings("unchecked") + Dictionary headers = bundle.getHeaders(); + + //does the bundle have a OSGiWebappConstants.JETTY_WAR_FOLDER_PATH + String resourcePath = Util.getManifestHeaderValue(OSGiWebappConstants.JETTY_WAR_RESOURCE_PATH, headers); + if (resourcePath != null) + { + String base = resourcePath; + contextPath = getContextPath(bundle); + String originId = getOriginId(bundle, base); + + //TODO : we don't know whether an app is actually deployed, as deploymentManager swallows all + //exceptions inside the impl of addApp. Need to send the Event and also register as a service + //only if the deployment succeeded + OSGiApp app = new OSGiApp(getDeploymentManager(), this, bundle, originId); + app.setWebAppPath(base); + app.setContextPath(contextPath); + _bundleMap.put(bundle, app); + getDeploymentManager().addApp(app); + return true; + } + + //does the bundle have a WEB-INF/web.xml + if (bundle.getEntry("/WEB-INF/web.xml") != null) + { + String base = "."; + contextPath = getContextPath(bundle); + String originId = getOriginId(bundle, base); + + OSGiApp app = new OSGiApp(getDeploymentManager(), this, bundle, originId); + app.setContextPath(contextPath); + app.setWebAppPath(base); + _bundleMap.put(bundle, app); + getDeploymentManager().addApp(app); + return true; + } + + //does the bundle define a OSGiWebappConstants.RFC66_WEB_CONTEXTPATH + if (headers.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH) != null) + { + //Could be a static webapp with no web.xml + String base = "."; + contextPath = headers.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH); + String originId = getOriginId(bundle, base); + + OSGiApp app = new OSGiApp(getDeploymentManager(), this, bundle, originId); + app.setContextPath(contextPath); + app.setWebAppPath(base); + _bundleMap.put(bundle, app); + getDeploymentManager().addApp(app); + return true; + } + + return false; + } + catch (Exception e) + { + + throw e; + } + finally + { + Thread.currentThread().setContextClassLoader(cl); + } + } + + /** + * Bundle has been removed. If it was a webapp we deployed, undeploy it. + * + * @param bundle the bundle + * @return true if this was a webapp we had deployed, false otherwise + */ + @Override + public boolean bundleRemoved(Bundle bundle) throws Exception + { + App app = _bundleMap.remove(bundle); + if (app != null) + { + getDeploymentManager().removeApp(app); + return true; + } + return false; + } + + private static String getContextPath(Bundle bundle) + { + Dictionary headers = bundle.getHeaders(); + String contextPath = (String)headers.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH); + if (contextPath == null) + { + // extract from the last token of the bundle's location: + // (really ?could consider processing the symbolic name as an alternative + // the location will often reflect the version. + // maybe this is relevant when the file is a war) + String location = bundle.getLocation(); + String[] toks = StringUtil.replace(location, '\\', '/').split("/"); + contextPath = toks[toks.length - 1]; + // remove .jar, .war etc: + int lastDot = contextPath.lastIndexOf('.'); + if (lastDot != -1) + contextPath = contextPath.substring(0, lastDot); + } + if (!contextPath.startsWith("/")) + contextPath = "/" + contextPath; + + return contextPath; + } +} diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/JettyBootstrapActivator.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/JettyBootstrapActivator.java new file mode 100644 index 00000000000..faf198a537d --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/JettyBootstrapActivator.java @@ -0,0 +1,126 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.osgi.boot; + +import org.eclipse.jetty.ee10.osgi.boot.internal.serverfactory.DefaultJettyAtJettyHomeHelper; +import org.eclipse.jetty.ee10.osgi.boot.internal.serverfactory.JettyServerServiceTracker; +import org.eclipse.jetty.ee10.osgi.boot.utils.internal.PackageAdminServiceTracker; +import org.eclipse.jetty.server.Server; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.osgi.framework.ServiceRegistration; +import org.osgi.util.tracker.ServiceTracker; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * JettyBootstrapActivator + *

    + * Bootstrap jetty and publish a default Server instance as an OSGi service. + *

    + * Listen for other Server instances to be published as services and support them as deployment targets. + *

    + * Listen for Bundles to be activated, and deploy those that represent webapps/ContextHandlers to one of the known Server instances. + */ +public class JettyBootstrapActivator implements BundleActivator +{ + private static final Logger LOG = LoggerFactory.getLogger(JettyBootstrapActivator.class); + + private static JettyBootstrapActivator INSTANCE = null; + + public static JettyBootstrapActivator getInstance() + { + return INSTANCE; + } + + private ServiceRegistration _registeredServer; + + private PackageAdminServiceTracker _packageAdminServiceTracker; + + private ServiceTracker _jettyServerServiceTracker; + + /** + * Setup a new jetty Server, registers it as a service. Setup the Service + * tracker for the jetty ContextHandlers that are in charge of deploying the + * webapps. Setup the BundleListener that supports the extender pattern for + * the jetty ContextHandler. + * + * @param context the bundle context + */ + @Override + public void start(final BundleContext context) throws Exception + { + ServiceReference[] references = context.getAllServiceReferences("org.eclipse.jetty.http.HttpFieldPreEncoder", null); + + if (references == null || references.length == 0) + LOG.warn("OSGi support for java.util.ServiceLoader may not be present. You may experience runtime errors."); + + INSTANCE = this; + + // track other bundles and fragments attached to this bundle that we + // should activate. + _packageAdminServiceTracker = new PackageAdminServiceTracker(context); + + // track jetty Server instances that we should support as deployment targets + _jettyServerServiceTracker = new ServiceTracker(context, context.createFilter("(objectclass=" + Server.class.getName() + ")"), new JettyServerServiceTracker()); + _jettyServerServiceTracker.open(); + + // Create a default jetty instance right now. + DefaultJettyAtJettyHomeHelper.startJettyAtJettyHome(context); + } + + /** + * Stop the activator. + * + * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext) + */ + @Override + public void stop(BundleContext context) throws Exception + { + try + { + if (_jettyServerServiceTracker != null) + { + _jettyServerServiceTracker.close(); + _jettyServerServiceTracker = null; + } + if (_packageAdminServiceTracker != null) + { + _packageAdminServiceTracker.stop(); + context.removeServiceListener(_packageAdminServiceTracker); + _packageAdminServiceTracker = null; + } + if (_registeredServer != null) + { + try + { + _registeredServer.unregister(); + } + catch (IllegalArgumentException ill) + { + // already unregistered. + } + finally + { + _registeredServer = null; + } + } + } + finally + { + INSTANCE = null; + } + } +} diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/OSGiDeployer.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/OSGiDeployer.java new file mode 100644 index 00000000000..7e95c81b10f --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/OSGiDeployer.java @@ -0,0 +1,77 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.osgi.boot; + +import org.eclipse.jetty.deploy.App; +import org.eclipse.jetty.deploy.bindings.StandardDeployer; +import org.eclipse.jetty.deploy.graph.Node; +import org.eclipse.jetty.ee10.osgi.boot.internal.serverfactory.ServerInstanceWrapper; +import org.eclipse.jetty.ee10.osgi.boot.utils.EventSender; + +/** + * OSGiDeployer + * + * Extension of standard Jetty deployer that emits OSGi EventAdmin + * events whenever a webapp is deployed into OSGi via Jetty. + */ +public class OSGiDeployer extends StandardDeployer +{ + + private ServerInstanceWrapper _server; + + public OSGiDeployer(ServerInstanceWrapper server) + { + _server = server; + } + + @Override + public void processBinding(Node node, App app) throws Exception + { + //TODO how to NOT send this event if its not a webapp: + //OSGi Enterprise Spec only wants an event sent if its a webapp bundle (ie not a ContextHandler) + if (!(app instanceof AbstractOSGiApp)) + { + doProcessBinding(node, app); + } + else + { + EventSender.getInstance().send(EventSender.DEPLOYING_EVENT, ((AbstractOSGiApp)app).getBundle(), app.getContextPath()); + try + { + doProcessBinding(node, app); + ((AbstractOSGiApp)app).registerAsOSGiService(); + EventSender.getInstance().send(EventSender.DEPLOYED_EVENT, ((AbstractOSGiApp)app).getBundle(), app.getContextPath()); + } + catch (Exception e) + { + EventSender.getInstance().send(EventSender.FAILED_EVENT, ((AbstractOSGiApp)app).getBundle(), app.getContextPath()); + throw e; + } + } + } + + protected void doProcessBinding(Node node, App app) throws Exception + { + ClassLoader old = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader(_server.getParentClassLoaderForWebapps()); + try + { + super.processBinding(node, app); + } + finally + { + Thread.currentThread().setContextClassLoader(old); + } + } +} diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/OSGiMetaInfConfiguration.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/OSGiMetaInfConfiguration.java new file mode 100644 index 00000000000..c3eeba7450c --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/OSGiMetaInfConfiguration.java @@ -0,0 +1,338 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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.ee10.osgi.boot.utils.BundleFileLocatorHelperFactory; +import org.eclipse.jetty.ee10.osgi.boot.utils.Util; +import org.eclipse.jetty.ee10.osgi.boot.utils.internal.PackageAdminServiceTracker; +import org.eclipse.jetty.ee10.webapp.Configuration; +import org.eclipse.jetty.ee10.webapp.MetaInfConfiguration; +import org.eclipse.jetty.ee10.webapp.WebAppContext; +import org.eclipse.jetty.ee10.webapp.WebInfConfiguration; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.util.resource.ResourceCollection; +import org.osgi.framework.Bundle; +import org.osgi.framework.FrameworkUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * OSGiWebInfConfiguration + * + * Handle adding resources found in bundle fragments, and add them into the + */ +public class OSGiMetaInfConfiguration extends MetaInfConfiguration +{ + private static final Logger LOG = LoggerFactory.getLogger(WebInfConfiguration.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.ee10.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.ee10.osgi.fragmentAndRequiredBundles"; + public static final String FRAGMENT_AND_REQUIRED_RESOURCES = "org.eclipse.jetty.ee10.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.ee10.webapp.WebInfConfiguration#preConfigure(org.eclipse.jetty.ee10.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) + { + 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(bundle)); + } + } + if (names != null) + { + //if there is an explicit bundle name, then check if it matches + if (names.contains(bundle.getSymbolicName())) + matchingResources.addAll(getBundleAsResource(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.ee10.webapp.MetaInfConfiguration#findJars(org.eclipse.jetty.ee10.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) + { + Set fragsAndReqsBundles = (Set)context.getAttribute(FRAGMENT_AND_REQUIRED_BUNDLES); + if (fragsAndReqsBundles == null) + { + fragsAndReqsBundles = new HashSet(); + context.setAttribute(FRAGMENT_AND_REQUIRED_BUNDLES, fragsAndReqsBundles); + } + + 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 = Resource.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.ee10.webapp.WebInfConfiguration#configure(org.eclipse.jetty.ee10.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) + { + 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(path, frag, appendedResourcesPath); + path = Util.getManifestHeaderValue(OSGiWebappConstants.JETTY_WAR_PREPEND_FRAGMENT_RESOURCE_PATH, frag.getHeaders()); + convertFragmentPathToResource(path, frag, prependedResourcesPath); + } + if (!appendedResourcesPath.isEmpty()) + { + LinkedHashSet resources = new LinkedHashSet(); + //Add in any existing setting of extra resource dirs + 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(); + //TODO needs WebAppContext ResourceCollection fixed + //context.setBaseResource(new ResourceCollection(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(Bundle bundle) + throws Exception + { + List resources = new ArrayList(); + + File file = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(bundle); + if (file.isDirectory()) + { + for (File f : file.listFiles()) + { + if (f.getName().endsWith(".jar") && f.isFile()) + { + resources.add(Resource.newResource(f)); + } + else if (f.isDirectory() && f.getName().equals("lib")) + { + for (File f2 : file.listFiles()) + { + if (f2.getName().endsWith(".jar") && f2.isFile()) + { + resources.add(Resource.newResource(f)); + } + } + } + } + resources.add(Resource.newResource(file)); //TODO really??? + } + else + { + resources.add(Resource.newResource(file)); + } + + return resources; + } + + /** + * Convert a path inside a fragment into a Resource + */ + private void convertFragmentPathToResource(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(), Resource.newResource(uri)); + } +} diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/OSGiServerConstants.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/OSGiServerConstants.java new file mode 100644 index 00000000000..3fbc39e973a --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/OSGiServerConstants.java @@ -0,0 +1,83 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.osgi.boot; + +/** + * OSGiServerConstants + * + * Name of the properties that configure a jetty Server OSGi service. + */ +public class OSGiServerConstants +{ + /** + * Usual system property used as the hostname for a typical jetty + * configuration. + */ + public static final String JETTY_HOME = "jetty.home"; + public static final String JETTY_BASE = "jetty.base"; + + /** + * System property to point to a bundle that embeds a jetty configuration + * and that jetty configuration should be the default jetty server. First we + * look for jetty.home. If we don't find it then we look for this property. + */ + public static final String JETTY_HOME_BUNDLE = "jetty.home.bundle"; + + /** + * Usual system property used as the hostname for a typical jetty + * configuration. + */ + public static final String JETTY_HOST = "jetty.http.host"; + + /** + * Usual system property used as the port for http for a typical jetty + * configuration. + */ + public static final String JETTY_PORT = "jetty.http.port"; + + /** + * Usual system property used as the port for https for a typical jetty + * configuration. + */ + public static final String JETTY_PORT_SSL = "jetty.ssl.port"; + + //for managed jetty instances, name of the configuration parameters + /** + * PID of the jetty servers's ManagedFactory + */ + public static final String MANAGED_JETTY_SERVER_FACTORY_PID = "org.eclipse.jetty.ee10.osgi.boot.managedserverfactory"; + + /** + * The associated value of that configuration parameter is the name under which this + * instance of the jetty server is tracked. + * When a ContextHandler is deployed and it specifies the managedServerName property, it is deployed + * on the corresponding jetty managed server or it throws an exception: jetty server not available. + */ + public static final String MANAGED_JETTY_SERVER_NAME = "managedServerName"; + /** + * Name of the 'default' jetty server instance. + * Usually the first one to be created. + */ + public static final String MANAGED_JETTY_SERVER_DEFAULT_NAME = "defaultJettyServer"; + + /** + * List of URLs to the jetty.xml files that configure th server. + */ + public static final String MANAGED_JETTY_XML_CONFIG_URLS = "jetty.etc.config.urls"; + + /** + * List of URLs to the folders where the legacy J2EE shared libraries are stored aka lib/ext, lib/jsp etc. + */ + public static final String MANAGED_JETTY_SHARED_LIB_FOLDER_URLS = "managedJettySharedLibFolderUrls"; +} diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/OSGiUndeployer.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/OSGiUndeployer.java new file mode 100644 index 00000000000..9185ab7fe55 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/OSGiUndeployer.java @@ -0,0 +1,54 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.osgi.boot; + +import org.eclipse.jetty.deploy.App; +import org.eclipse.jetty.deploy.bindings.StandardUndeployer; +import org.eclipse.jetty.deploy.graph.Node; +import org.eclipse.jetty.ee10.osgi.boot.internal.serverfactory.ServerInstanceWrapper; +import org.eclipse.jetty.ee10.osgi.boot.utils.EventSender; + +/** + * OSGiUndeployer + * + * Extension of the Jetty Undeployer which emits OSGi EventAdmin events + * whenever a webapp is undeployed from Jetty. + */ +public class OSGiUndeployer extends StandardUndeployer +{ + private ServerInstanceWrapper _server; + + public OSGiUndeployer(ServerInstanceWrapper server) + { + _server = server; + } + + @Override + public void processBinding(Node node, App app) throws Exception + { + EventSender.getInstance().send(EventSender.UNDEPLOYING_EVENT, ((AbstractOSGiApp)app).getBundle(), app.getContextPath()); + ClassLoader old = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader(_server.getParentClassLoaderForWebapps()); + try + { + super.processBinding(node, app); + } + finally + { + Thread.currentThread().setContextClassLoader(old); + } + EventSender.getInstance().send(EventSender.UNDEPLOYED_EVENT, ((AbstractOSGiApp)app).getBundle(), app.getContextPath()); + ((AbstractOSGiApp)app).deregisterAsOSGiService(); + } +} diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/OSGiWebInfConfiguration.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/OSGiWebInfConfiguration.java new file mode 100644 index 00000000000..e17bc9f1de2 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/OSGiWebInfConfiguration.java @@ -0,0 +1,31 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.osgi.boot; + +import org.eclipse.jetty.ee10.webapp.Configuration; +import org.eclipse.jetty.ee10.webapp.WebInfConfiguration; + +/** + * OSGiWebInfConfiguration + * + * Handle adding resources found in bundle fragments, and add them into the + */ +public class OSGiWebInfConfiguration extends WebInfConfiguration +{ + @Override + public Class replaces() + { + return WebInfConfiguration.class; + } +} diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/OSGiWebappConstants.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/OSGiWebappConstants.java new file mode 100644 index 00000000000..dfc5a9fa647 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/OSGiWebappConstants.java @@ -0,0 +1,138 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.osgi.boot; + +/** + * OSGiWebappConstants + * + * + * Constants (MANIFEST headers, service properties etc) associated with deploying + * webapps into OSGi via Jetty. + */ +public class OSGiWebappConstants +{ + /** + * service property osgi.web.symbolicname. See OSGi r4 + */ + public static final String OSGI_WEB_SYMBOLICNAME = "osgi.web.symbolicname"; + + /** + * service property osgi.web.symbolicname. See OSGi r4 + */ + public static final String OSGI_WEB_VERSION = "osgi.web.version"; + + /** + * service property osgi.web.contextpath. See OSGi r4 + */ + public static final String OSGI_WEB_CONTEXTPATH = "osgi.web.contextpath"; + + /** + * See OSGi r4 p.427 + */ + public static final String OSGI_BUNDLECONTEXT = "osgi-bundlecontext"; + + /** + * url scheme to deploy war file as bundled webapp + */ + public static final String RFC66_WAR_URL_SCHEME = "war"; + + /** + * Name of the header that defines the context path for the embedded webapp. + */ + public static final String RFC66_WEB_CONTEXTPATH = "Web-ContextPath"; + + /** + * Name of the header that defines the path to the folder where the jsp + * files are extracted. + */ + public static final String RFC66_JSP_EXTRACT_LOCATION = "Jsp-ExtractLocation"; + + /** + * Name of the servlet context attribute that points to the bundle context. + */ + public static final String RFC66_OSGI_BUNDLE_CONTEXT = "osgi-bundlecontext"; + + /** + * Name of the servlet context attribute that points to the bundle object. + * We can't always rely on the bundle-context as there might be no such thing. + */ + public static final String JETTY_OSGI_BUNDLE = "osgi-bundle"; + + /** + * List of relative pathes within the bundle to the jetty context files. + */ + public static final String JETTY_CONTEXT_FILE_PATH = "Jetty-ContextFilePath"; + + /** + * path within the bundle to the folder that contains the basic resources. + */ + public static final String JETTY_WAR_RESOURCE_PATH = "Jetty-WarResourcePath"; + + /** + * path within a fragment hosted by a web-bundle to a folder that contains basic resources. + * the path is appended to the lookup path where jetty locates static resources + */ + public static final String JETTY_WAR_FRAGMENT_RESOURCE_PATH = "Jetty-WarFragmentResourcePath"; + + /** + * path within a fragment hosted by a web-bundle to a folder that contains basic resources. + * The path is prefixed to the lookup path where jetty locates static resources: + * this will override static resources with the same name in the web-bundle. + */ + public static final String JETTY_WAR_PREPEND_FRAGMENT_RESOURCE_PATH = "Jetty-WarPrependFragmentResourcePath"; + + /** + * installation path of webapp bundle + */ + public static final String JETTY_BUNDLE_ROOT = "bundle.root"; + + /** + * Extra classpath + */ + public static final String JETTY_EXTRA_CLASSPATH = "Jetty-extraClasspath"; + + /** + * web.xml file path + */ + public static final String JETTY_WEB_XML_PATH = "Jetty-WebXmlFilePath"; + + /** + * defaultweb.xml file path + */ + public static final String JETTY_DEFAULT_WEB_XML_PATH = "Jetty-defaultWebXmlFilePath"; + + /** + * path to the base folder that overrides the computed bundle installation + * location if not null useful to install webapps or jetty context files + * that are in fact not embedded in a bundle + */ + public static final String JETTY_BUNDLE_INSTALL_LOCATION_OVERRIDE = "Jetty-bundleInstall"; + + /** + * Comma separated list of bundles that contain tld file used by the webapp. + */ + public static final String REQUIRE_TLD_BUNDLE = "Require-TldBundle"; + /** + * Comma separated list of bundles that contain tld file used by the webapp. + * Both the name of the manifest header and the name of the service property. + */ + public static final String SERVICE_PROP_REQUIRE_TLD_BUNDLE = REQUIRE_TLD_BUNDLE; + + public static final String WATERMARK = "o.e.j.o.b.watermark"; + + /** + * Set of extra dirs that must not be served by osgi webapps + */ + public static final String[] DEFAULT_PROTECTED_OSGI_TARGETS = {"/osgi-inf", "/osgi-opts"}; +} diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/ServiceContextProvider.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/ServiceContextProvider.java new file mode 100644 index 00000000000..2fdb662d471 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/ServiceContextProvider.java @@ -0,0 +1,224 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.osgi.boot; + +import java.util.Dictionary; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Map; + +import org.eclipse.jetty.deploy.App; +import org.eclipse.jetty.deploy.AppProvider; +import org.eclipse.jetty.deploy.DeploymentManager; +import org.eclipse.jetty.ee10.osgi.boot.internal.serverfactory.ServerInstanceWrapper; +import org.eclipse.jetty.ee10.osgi.boot.utils.Util; +import org.eclipse.jetty.server.handler.ContextHandler; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.osgi.framework.Filter; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.ServiceReference; +import org.osgi.framework.ServiceRegistration; +import org.osgi.util.tracker.ServiceTracker; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * ServiceContextProvider + * + * Jetty DeploymentManager Provider that is able to deploy ContextHandlers discovered via OSGi as services. + */ +public class ServiceContextProvider extends AbstractContextProvider implements ServiceProvider +{ + private static final Logger LOG = LoggerFactory.getLogger(AbstractContextProvider.class); + + private Map _serviceMap = new HashMap<>(); + + private ServiceRegistration _serviceRegForServices; + + ServiceTracker _tracker; + + /** + * ContextTracker + */ + public class ContextTracker extends ServiceTracker + { + + public ContextTracker(BundleContext bundleContext, Filter filter) + { + super(bundleContext, filter, null); + } + + @Override + public Object addingService(ServiceReference reference) + { + ContextHandler h = (ContextHandler)context.getService(reference); + serviceAdded(reference, h); + return h; + } + + @Override + public void modifiedService(ServiceReference reference, Object service) + { + removedService(reference, service); + addingService(reference); + } + + @Override + public void removedService(ServiceReference reference, Object service) + { + context.ungetService(reference); + serviceRemoved(reference, (ContextHandler)service); + } + } + + /** + * ServiceApp + */ + public class ServiceApp extends OSGiApp + { + public ServiceApp(DeploymentManager manager, AppProvider provider, Bundle bundle, Dictionary properties, String contextFile, String originId) + { + super(manager, provider, bundle, properties, contextFile, originId); + } + + public ServiceApp(DeploymentManager manager, AppProvider provider, String originId, Bundle bundle, String contextFile) + { + super(manager, provider, originId, bundle, contextFile); + } + + @Override + public void registerAsOSGiService() throws Exception + { + //not applicable for apps that are already services + } + + @Override + protected void deregisterAsOSGiService() throws Exception + { + //not applicable for apps that are already services + } + } + + public ServiceContextProvider(ServerInstanceWrapper wrapper) + { + super(wrapper); + } + + @Override + public boolean serviceAdded(ServiceReference serviceRef, ContextHandler context) + { + if (context == null || serviceRef == null) + return false; + + if (context instanceof org.eclipse.jetty.ee10.webapp.WebAppContext) + return false; //the ServiceWebAppProvider will deploy it + + String watermark = (String)serviceRef.getProperty(OSGiWebappConstants.WATERMARK); + if (watermark != null && !"".equals(watermark)) + return false; //this service represents a contexthandler that has already been registered as a service by another of our deployers + + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader(getServerInstanceWrapper().getParentClassLoaderForWebapps()); + try + { + //See if there is a context file to apply to this pre-made context + String contextFile = (String)serviceRef.getProperty(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH); + + String[] keys = serviceRef.getPropertyKeys(); + Dictionary properties = new Hashtable<>(); + if (keys != null) + { + for (String key : keys) + { + properties.put(key, serviceRef.getProperty(key)); + } + } + Bundle bundle = serviceRef.getBundle(); + String originId = bundle.getSymbolicName() + "-" + bundle.getVersion().toString() + "-" + (contextFile != null ? contextFile : serviceRef.getProperty(Constants.SERVICE_ID)); + ServiceApp app = new ServiceApp(getDeploymentManager(), this, bundle, properties, contextFile, originId); + app.setHandler(context); //set the pre=made ContextHandler instance + _serviceMap.put(serviceRef, app); + getDeploymentManager().addApp(app); + return true; + } + finally + { + Thread.currentThread().setContextClassLoader(cl); + } + } + + @Override + public boolean serviceRemoved(ServiceReference serviceRef, ContextHandler context) + { + + if (context == null || serviceRef == null) + return false; + + String watermark = (String)serviceRef.getProperty(OSGiWebappConstants.WATERMARK); + if (watermark != null && !"".equals(watermark)) + return false; //this service represents a contexthandler that will be deregistered as a service by another of our deployers + + App app = _serviceMap.remove(serviceRef); + if (app != null) + { + getDeploymentManager().removeApp(app); + return true; + } + + return false; + } + + @Override + protected void doStart() throws Exception + { + + BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass()).getBundleContext(); + + //Start a tracker to find webapps that are osgi services that are targeted to my server name + _tracker = new ContextTracker(bundleContext, + Util.createFilter(bundleContext, ContextHandler.class.getName(), getServerInstanceWrapper().getManagedServerName())); + _tracker.open(); + + //register as an osgi service for deploying contexts defined in a bundle, advertising the name of the jetty Server instance we are related to + Dictionary properties = new Hashtable<>(); + properties.put(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME, getServerInstanceWrapper().getManagedServerName()); + + //register as an osgi service for deploying contexts, advertising the name of the jetty Server instance we are related to + _serviceRegForServices = FrameworkUtil.getBundle(this.getClass()).getBundleContext().registerService(ServiceProvider.class.getName(), this, properties); + super.doStart(); + } + + @Override + protected void doStop() throws Exception + { + if (_tracker != null) + _tracker.close(); + + //unregister ourselves + if (_serviceRegForServices != null) + { + try + { + _serviceRegForServices.unregister(); + } + catch (Exception e) + { + LOG.warn("Unable to unregister {}", _serviceRegForServices, e); + } + } + super.doStop(); + } +} diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/ServiceProvider.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/ServiceProvider.java new file mode 100644 index 00000000000..68f26c98370 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/ServiceProvider.java @@ -0,0 +1,29 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.osgi.boot; + +import org.eclipse.jetty.server.handler.ContextHandler; +import org.osgi.framework.ServiceReference; + +/** + * ServiceProvider + * + * Jetty DeploymentManager Provider api for webapps or ContextHandlers that are discovered as OSGi services. + */ +public interface ServiceProvider +{ + public boolean serviceAdded(ServiceReference ref, ContextHandler handler) throws Exception; + + public boolean serviceRemoved(ServiceReference ref, ContextHandler handler) throws Exception; +} diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/ServiceWebAppProvider.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/ServiceWebAppProvider.java new file mode 100644 index 00000000000..85dd13ae5c2 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/ServiceWebAppProvider.java @@ -0,0 +1,256 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.osgi.boot; + +import java.util.Dictionary; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Map; + +import org.eclipse.jetty.deploy.App; +import org.eclipse.jetty.deploy.AppProvider; +import org.eclipse.jetty.deploy.DeploymentManager; +import org.eclipse.jetty.ee10.osgi.boot.internal.serverfactory.ServerInstanceWrapper; +import org.eclipse.jetty.ee10.osgi.boot.utils.Util; +import org.eclipse.jetty.ee10.webapp.WebAppContext; +import org.eclipse.jetty.server.handler.ContextHandler; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Filter; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.ServiceReference; +import org.osgi.framework.ServiceRegistration; +import org.osgi.util.tracker.ServiceTracker; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * ServiceWebAppProvider + *

    + * Jetty Provider that knows how to deploy a WebApp that has been registered as an OSGi service. + */ +public class ServiceWebAppProvider extends AbstractWebAppProvider implements ServiceProvider +{ + private static final Logger LOG = LoggerFactory.getLogger(AbstractWebAppProvider.class); + + /** + * Map of ServiceRef to App. Used when it is an osgi service that is a WebAppContext. + */ + private Map _serviceMap = new HashMap<>(); + + private ServiceRegistration _serviceRegForServices; + + private ServiceTracker webappTracker; + + /** + * WebAppTracker + */ + public class WebAppTracker extends ServiceTracker + { + /** + * @param bundleContext the osgi context + * @param filter the osgi filter for the tracker + */ + public WebAppTracker(BundleContext bundleContext, Filter filter) + { + super(bundleContext, filter, null); + } + + @Override + public Object addingService(ServiceReference reference) + { + WebAppContext wac = (WebAppContext)context.getService(reference); + serviceAdded(reference, wac); + return wac; + } + + @Override + public void modifiedService(ServiceReference reference, Object service) + { + removedService(reference, service); + addingService(reference); + } + + @Override + public void removedService(ServiceReference reference, Object service) + { + serviceRemoved(reference, (WebAppContext)service); + context.ungetService(reference); + } + } + + /** + * ServiceApp + */ + public class ServiceApp extends OSGiApp + { + + public ServiceApp(DeploymentManager manager, AppProvider provider, Bundle bundle, Dictionary properties, String originId) + { + super(manager, provider, bundle, properties, originId); + } + + public ServiceApp(DeploymentManager manager, AppProvider provider, Bundle bundle, String originId) + { + super(manager, provider, bundle, originId); + } + + @Override + public void registerAsOSGiService() throws Exception + { + //not applicable for apps that are already services + } + + @Override + protected void deregisterAsOSGiService() throws Exception + { + //not applicable for apps that are already services + } + } + + public ServiceWebAppProvider(ServerInstanceWrapper wrapper) + { + super(wrapper); + } + + /** + * A webapp that was deployed as an osgi service has been added, + * and we want to deploy it. + * + * @param context the webapp + */ + @Override + public boolean serviceAdded(ServiceReference serviceRef, ContextHandler context) + { + if (context == null || !(context instanceof WebAppContext)) + return false; + + String watermark = (String)serviceRef.getProperty(OSGiWebappConstants.WATERMARK); + if (watermark != null && !"".equals(watermark)) + return false; //this service represents a webapp that has already been registered as a service by another of our deployers + + WebAppContext webApp = (WebAppContext)context; + Dictionary properties = new Hashtable<>(); + + String contextPath = (String)serviceRef.getProperty(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH); + if (contextPath == null) + return false; //No context path + + String base = (String)serviceRef.getProperty(OSGiWebappConstants.JETTY_WAR_RESOURCE_PATH); + + if (base == null) + return false; //No webapp base + + String webdefaultXml = (String)serviceRef.getProperty(OSGiWebappConstants.JETTY_DEFAULT_WEB_XML_PATH); + if (webdefaultXml != null) + properties.put(OSGiWebappConstants.JETTY_DEFAULT_WEB_XML_PATH, webdefaultXml); + + String webXml = (String)serviceRef.getProperty(OSGiWebappConstants.JETTY_WEB_XML_PATH); + if (webXml != null) + properties.put(OSGiWebappConstants.JETTY_WEB_XML_PATH, webXml); + + String extraClassPath = (String)serviceRef.getProperty(OSGiWebappConstants.JETTY_EXTRA_CLASSPATH); + if (extraClassPath != null) + properties.put(OSGiWebappConstants.JETTY_EXTRA_CLASSPATH, extraClassPath); + + String bundleInstallOverride = (String)serviceRef.getProperty(OSGiWebappConstants.JETTY_BUNDLE_INSTALL_LOCATION_OVERRIDE); + if (bundleInstallOverride != null) + properties.put(OSGiWebappConstants.JETTY_BUNDLE_INSTALL_LOCATION_OVERRIDE, bundleInstallOverride); + + String requiredTlds = (String)serviceRef.getProperty(OSGiWebappConstants.REQUIRE_TLD_BUNDLE); + if (requiredTlds == null) + requiredTlds = (String)serviceRef.getProperty(OSGiWebappConstants.SERVICE_PROP_REQUIRE_TLD_BUNDLE); + if (requiredTlds != null) + properties.put(OSGiWebappConstants.REQUIRE_TLD_BUNDLE, requiredTlds); + + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader(getServerInstanceWrapper().getParentClassLoaderForWebapps()); + try + { + String originId = getOriginId(serviceRef.getBundle(), base); + ServiceApp app = new ServiceApp(getDeploymentManager(), this, serviceRef.getBundle(), properties, originId); + app.setContextPath(contextPath); + app.setWebAppPath(base); + app.setWebAppContext(webApp); //set the pre=made webapp instance + _serviceMap.put(serviceRef, app); + getDeploymentManager().addApp(app); + return true; + } + finally + { + Thread.currentThread().setContextClassLoader(cl); + } + } + + /** + * @param context the webapp + */ + @Override + public boolean serviceRemoved(ServiceReference serviceRef, ContextHandler context) + { + if (context == null || !(context instanceof WebAppContext)) + return false; + + String watermark = (String)serviceRef.getProperty(OSGiWebappConstants.WATERMARK); + if (watermark != null && !"".equals(watermark)) + return false; //this service represents a contexthandler that will be deregistered as a service by another of our deployers + + App app = _serviceMap.remove(serviceRef); + if (app != null) + { + getDeploymentManager().removeApp(app); + return true; + } + return false; + } + + @Override + protected void doStart() throws Exception + { + BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass()).getBundleContext(); + + //Start a tracker to find webapps that are osgi services that are targeted to my server name + webappTracker = new WebAppTracker(bundleContext, + Util.createFilter(bundleContext, WebAppContext.class.getName(), getServerInstanceWrapper().getManagedServerName())); + webappTracker.open(); + + //register as an osgi service for deploying bundles, advertising the name of the jetty Server instance we are related to + Dictionary properties = new Hashtable<>(); + properties.put(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME, getServerInstanceWrapper().getManagedServerName()); + + //register as an osgi service for deploying contexts (discovered as osgi services), advertising the name of the jetty Server instance we are related to + _serviceRegForServices = FrameworkUtil.getBundle(this.getClass()).getBundleContext().registerService(ServiceProvider.class.getName(), this, properties); + super.doStart(); + } + + @Override + protected void doStop() throws Exception + { + webappTracker.close(); + + //unregister ourselves + if (_serviceRegForServices != null) + { + try + { + _serviceRegForServices.unregister(); + } + catch (Exception e) + { + LOG.warn("Unable to unregister {}", _serviceRegForServices, e); + } + } + super.doStop(); + } +} diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/internal/serverfactory/DefaultJettyAtJettyHomeHelper.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/internal/serverfactory/DefaultJettyAtJettyHomeHelper.java new file mode 100644 index 00000000000..677cce99ae3 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/internal/serverfactory/DefaultJettyAtJettyHomeHelper.java @@ -0,0 +1,334 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.osgi.boot.internal.serverfactory; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Dictionary; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; + +import org.eclipse.jetty.ee10.osgi.boot.JettyBootstrapActivator; +import org.eclipse.jetty.ee10.osgi.boot.OSGiServerConstants; +import org.eclipse.jetty.ee10.osgi.boot.utils.BundleFileLocatorHelperFactory; +import org.eclipse.jetty.ee10.osgi.boot.utils.OSGiClassLoader; +import org.eclipse.jetty.ee10.osgi.boot.utils.Util; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.resource.JarResource; +import org.eclipse.jetty.util.resource.Resource; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * DefaultJettyAtJettyHomeHelper + *

    + * Creates a default instance of Jetty, based on the values of the + * System properties "jetty.home" or "jetty.home.bundle", one of which + * must be specified in order to create the default instance. + *

    + * Called by the {@link JettyBootstrapActivator} during the starting of the + * bundle. + */ +public class DefaultJettyAtJettyHomeHelper +{ + private static final Logger LOG = LoggerFactory.getLogger(DefaultJettyAtJettyHomeHelper.class); + + /** + * contains a comma separated list of paths to the etc/jetty-*.xml files + */ + public static final String JETTY_ETC_FILES = OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS; + + /** + * Set of config files to apply to a jetty Server instance if none are supplied by SYS_PROP_JETTY_ETC_FILES + */ + public static final String DEFAULT_JETTY_ETC_FILES = "etc/jetty.xml,etc/jetty-http.xml,etc/jetty-deploy.xml"; + + /** + * Default location within bundle of a jetty home dir. + */ + public static final String DEFAULT_JETTYHOME = "/jettyhome"; + + /** + * Called by the JettyBootStrapActivator. If the system property jetty.home + * is defined and points to a folder, creates a corresponding jetty + * server. + *

    + * If the system property jetty.home.bundle is defined and points to a + * bundle, look for the configuration of jetty inside that bundle. + *

    + *

    + * In both cases reads the system property 'jetty.etc.config.urls' to locate + * the configuration files for the deployed jetty. It is a comma separated + * list of URLs or relative paths inside the bundle or folder to the config + * files. + *

    + *

    + * In both cases the system properties jetty.http.host, jetty.http.port and + * jetty.ssl.port are passed to the configuration files that might use them + * as part of their properties. + *

    + * + * @param bundleContext the bundle context + * @return the configured server + * @throws Exception if unable to create / configure / or start the server + */ + public static Server startJettyAtJettyHome(BundleContext bundleContext) throws Exception + { + String jettyHomeSysProp = System.getProperty(OSGiServerConstants.JETTY_HOME); + String jettyHomeBundleSysProp = System.getProperty(OSGiServerConstants.JETTY_HOME_BUNDLE); + File jettyHomeDir = null; + Bundle jettyHomeBundle = null; + + Dictionary properties = new Hashtable<>(); + if (jettyHomeSysProp != null) + { + jettyHomeSysProp = Util.resolvePropertyValue(jettyHomeSysProp); + // bug 329621 + if (jettyHomeSysProp.startsWith("\"") && jettyHomeSysProp.endsWith("\"") || (jettyHomeSysProp.startsWith("'") && jettyHomeSysProp.endsWith("'"))) + jettyHomeSysProp = jettyHomeSysProp.substring(1, jettyHomeSysProp.length() - 1); + + if (jettyHomeBundleSysProp != null) + LOG.warn("Both jetty.home and jetty.home.bundle property defined: jetty.home.bundle ignored."); + + jettyHomeDir = new File(jettyHomeSysProp); + if (!jettyHomeDir.exists() || !jettyHomeDir.isDirectory()) + { + LOG.warn("Unable to locate the jetty.home folder {}", jettyHomeSysProp); + return null; + } + + //set jetty.home + Util.setProperty(properties, OSGiServerConstants.JETTY_HOME, jettyHomeDir.getAbsolutePath()); + } + else if (jettyHomeBundleSysProp != null) + { + jettyHomeBundleSysProp = Util.resolvePropertyValue(jettyHomeBundleSysProp); + for (Bundle b : bundleContext.getBundles()) + { + if (b.getState() == Bundle.UNINSTALLED) + continue; + + if (b.getSymbolicName().equals(jettyHomeBundleSysProp)) + { + jettyHomeBundle = b; + break; + } + } + if (jettyHomeBundle == null) + { + LOG.warn("Unable to find the jetty.home.bundle named {}", jettyHomeSysProp); + return null; + } + } + + if (jettyHomeDir == null && jettyHomeBundle == null) + { + LOG.warn("No default jetty created."); + return null; + } + + //resolve the jetty xml config files + List configURLs = jettyHomeDir != null ? getJettyConfigurationURLs(jettyHomeDir) : getJettyConfigurationURLs(jettyHomeBundle, properties); + + LOG.info("Configuring the default jetty server with {}", configURLs); + String home = (String)properties.get(OSGiServerConstants.JETTY_HOME); + String base = (String)properties.get(OSGiServerConstants.JETTY_BASE); + if (base == null) + base = home; + LOG.info("JETTY.HOME={} JETTY.BASE={}", home, base); + ClassLoader contextCl = Thread.currentThread().getContextClassLoader(); + try + { + ClassLoader cl; + if (jettyHomeBundle != null) + { + cl = new OSGiClassLoader(JettyBootstrapActivator.class.getClassLoader(), jettyHomeBundle); + } + else + { + cl = JettyBootstrapActivator.class.getClassLoader(); + } + Thread.currentThread().setContextClassLoader(cl); + + //the default server name + properties.put(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME, OSGiServerConstants.MANAGED_JETTY_SERVER_DEFAULT_NAME); + + //Always set home and base + Util.setProperty(properties, OSGiServerConstants.JETTY_HOME, home); + Util.setProperty(properties, OSGiServerConstants.JETTY_BASE, base); + + // copy all system properties starting with "jetty." to service properties for the jetty server service. + // these will be used as xml configuration properties. + for (Map.Entry prop : System.getProperties().entrySet()) + { + if (prop.getKey() instanceof String) + { + String skey = (String)prop.getKey(); + //never copy the jetty xml config files into the properties as we pass them explicitly into + //the call to configure, also we set home and base explicitly + if (OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS.equals(skey) || + OSGiServerConstants.JETTY_HOME.equals(skey) || + OSGiServerConstants.JETTY_BASE.equals(skey)) + continue; + + if (skey.startsWith("jetty.")) + { + Util.setProperty(properties, skey, prop.getValue()); + } + } + } + + //configure the server here rather than letting the JettyServerServiceTracker do it, because we want to be able to + //configure the ThreadPool, which can only be done via the constructor, ie from within the xml configuration processing + Server server = ServerInstanceWrapper.configure(null, configURLs, properties); + + //Register the default Server instance as an OSGi service. + //The JettyServerServiceTracker will notice it and set it up to deploy bundles as wars etc + bundleContext.registerService(Server.class.getName(), server, properties); + LOG.info("Default jetty server configured"); + return server; + } + catch (Exception e) + { + LOG.warn("Failed to start Jetty at Jetty Home", e); + throw e; + } + finally + { + Thread.currentThread().setContextClassLoader(contextCl); + } + } + + /** + * Minimum setup for the location of the configuration files given a + * jettyhome folder. Reads the system property jetty.etc.config.urls and + * look for the corresponding jetty configuration files that will be used to + * setup the jetty server. + */ + private static List getJettyConfigurationURLs(File jettyhome) + throws MalformedURLException + { + List configURLs = new ArrayList<>(); + String jettyetc = System.getProperty(JETTY_ETC_FILES, DEFAULT_JETTY_ETC_FILES); + StringTokenizer tokenizer = new StringTokenizer(jettyetc, ";,", false); + while (tokenizer.hasMoreTokens()) + { + String next = tokenizer.nextToken().trim(); + //etc files can either be relative to jetty.home or absolute disk locations + if (!next.startsWith("/") && (next.indexOf(':') == -1)) + configURLs.add(new File(jettyhome, next).toURI().toURL()); + else + configURLs.add(new URL(next)); + } + return configURLs; + } + + /** + * Minimum setup for the location of the configuration files given a + * configuration embedded inside a bundle. Reads the system property + * jetty.etc.config.urls and look for the corresponding jetty configuration + * files that will be used to setup the jetty server. + */ + private static List getJettyConfigurationURLs(Bundle configurationBundle, Dictionary properties) + throws Exception + { + List configURLs = new ArrayList<>(); + String files = System.getProperty(JETTY_ETC_FILES, DEFAULT_JETTY_ETC_FILES); + StringTokenizer tokenizer = new StringTokenizer(files, ";,", false); + + while (tokenizer.hasMoreTokens()) + { + String etcFile = tokenizer.nextToken().trim(); + + //file path is absolute + if (etcFile.startsWith("/") || etcFile.indexOf(":") != -1) + configURLs.add(new URL(etcFile)); + else //relative file path + { + Enumeration enUrls = BundleFileLocatorHelperFactory.getFactory().getHelper().findEntries(configurationBundle, etcFile); + + String home = null; + // default for org.eclipse.osgi.boot where we look inside + // jettyhome/ for the default embedded configuration. + if ((enUrls == null || !enUrls.hasMoreElements())) + { + home = DEFAULT_JETTYHOME; + String tmp = DEFAULT_JETTYHOME + (DEFAULT_JETTYHOME.endsWith("/") ? "" : "/") + etcFile; + enUrls = BundleFileLocatorHelperFactory.getFactory().getHelper().findEntries(configurationBundle, tmp); + LOG.info("Configuring jetty from bundle: {} with {}", configurationBundle.getSymbolicName(), tmp); + } + + //lazily ensure jetty.home value is set based on location of etc files + if (properties.get(OSGiServerConstants.JETTY_HOME) == null) + { + Resource res = findDir(configurationBundle, home); + if (res != null) + properties.put(OSGiServerConstants.JETTY_HOME, res.toString()); + } + + if (enUrls == null || !enUrls.hasMoreElements()) + throw new IllegalStateException("Unable to locate a jetty configuration file for " + etcFile); + + URL url = BundleFileLocatorHelperFactory.getFactory().getHelper().getFileURL(enUrls.nextElement()); + configURLs.add(url); + } + } + return configURLs; + } + + /** + * Get a resource representing a directory inside a bundle. If the dir is null, + * return a resource representing the installation location of the bundle. + * + * @param bundle the bundle + * @param dir the directory + * @return the resource found + */ + public static Resource findDir(Bundle bundle, String dir) + { + if (bundle == null) + return null; + + try + { + File f = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(bundle); + URL u = f.toURI().toURL(); + u = BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(u); + Resource res = Resource.newResource(u); + String s = res.toString(); + + //check if it is an unarchived bundle + if (s.endsWith(".jar") && s.startsWith("file:")) + res = JarResource.newJarResource(res); + + //if looking for a directory + if (dir != null) + res = res.addPath(dir); + + return res; + } + catch (Exception e) + { + LOG.warn("Bad bundle location", e); + return null; + } + } +} diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/internal/serverfactory/JettyServerServiceTracker.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/internal/serverfactory/JettyServerServiceTracker.java new file mode 100644 index 00000000000..0d292e44e79 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/internal/serverfactory/JettyServerServiceTracker.java @@ -0,0 +1,92 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.osgi.boot.internal.serverfactory; + +import java.util.Dictionary; +import java.util.Hashtable; + +import org.eclipse.jetty.ee10.osgi.boot.OSGiServerConstants; +import org.eclipse.jetty.server.Server; +import org.osgi.framework.Bundle; +import org.osgi.framework.ServiceReference; +import org.osgi.util.tracker.ServiceTrackerCustomizer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * JettyServerServiceTracker + * + * Tracks instances of Jetty Servers, and configures them so that they can deploy + * webapps or ContextHandlers discovered from the OSGi environment. + */ +public class JettyServerServiceTracker implements ServiceTrackerCustomizer +{ + private static Logger LOG = LoggerFactory.getLogger(JettyServerServiceTracker.class.getName()); + + @Override + public Object addingService(ServiceReference sr) + { + Bundle contributor = sr.getBundle(); + Server server = (Server)contributor.getBundleContext().getService(sr); + String name = (String)sr.getProperty(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME); + if (name == null) + { + throw new IllegalArgumentException("The property " + OSGiServerConstants.MANAGED_JETTY_SERVER_NAME + " is mandatory"); + } + if (LOG.isDebugEnabled()) + LOG.debug("Adding Server {}", name); + ServerInstanceWrapper wrapper = new ServerInstanceWrapper(name); + Dictionary props = new Hashtable<>(); + for (String key : sr.getPropertyKeys()) + { + props.put(key, sr.getProperty(key)); + } + try + { + wrapper.start(server, props); + LOG.info("Started Server {}", name); + return wrapper; + } + catch (Exception e) + { + LOG.warn("Failed to start server {}", name, e); + return sr.getBundle().getBundleContext().getService(sr); + } + } + + @Override + public void modifiedService(ServiceReference reference, Object service) + { + removedService(reference, service); + addingService(reference); + } + + @Override + public void removedService(ServiceReference reference, Object service) + { + if (service instanceof ServerInstanceWrapper) + { + ServerInstanceWrapper wrapper = (ServerInstanceWrapper)service; + try + { + wrapper.stop(); + LOG.info("Stopped Server {}", wrapper.getManagedServerName()); + } + catch (Exception e) + { + LOG.warn("Failed to stop server {}", wrapper.getManagedServerName(), e); + } + } + } +} diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/internal/serverfactory/ServerInstanceWrapper.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/internal/serverfactory/ServerInstanceWrapper.java new file mode 100644 index 00000000000..adf7d363f4e --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/internal/serverfactory/ServerInstanceWrapper.java @@ -0,0 +1,441 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.osgi.boot.internal.serverfactory; + +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Dictionary; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.StringTokenizer; + +import org.eclipse.jetty.deploy.AppLifeCycle; +import org.eclipse.jetty.deploy.AppProvider; +import org.eclipse.jetty.deploy.DeploymentManager; +import org.eclipse.jetty.deploy.bindings.StandardStarter; +import org.eclipse.jetty.deploy.bindings.StandardStopper; +import org.eclipse.jetty.ee10.osgi.boot.BundleContextProvider; +import org.eclipse.jetty.ee10.osgi.boot.BundleWebAppProvider; +import org.eclipse.jetty.ee10.osgi.boot.JettyBootstrapActivator; +import org.eclipse.jetty.ee10.osgi.boot.OSGiDeployer; +import org.eclipse.jetty.ee10.osgi.boot.OSGiServerConstants; +import org.eclipse.jetty.ee10.osgi.boot.OSGiUndeployer; +import org.eclipse.jetty.ee10.osgi.boot.ServiceContextProvider; +import org.eclipse.jetty.ee10.osgi.boot.ServiceWebAppProvider; +import org.eclipse.jetty.ee10.osgi.boot.internal.webapp.LibExtClassLoaderHelper; +import org.eclipse.jetty.ee10.osgi.boot.utils.BundleFileLocatorHelperFactory; +import org.eclipse.jetty.ee10.osgi.boot.utils.FakeURLClassLoader; +import org.eclipse.jetty.ee10.osgi.boot.utils.TldBundleDiscoverer; +import org.eclipse.jetty.ee10.osgi.boot.utils.Util; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.ContextHandlerCollection; +import org.eclipse.jetty.util.StringUtil; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.xml.XmlConfiguration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * ServerInstanceWrapper + * + * Configures and starts a jetty Server instance. + */ +public class ServerInstanceWrapper +{ + + /** + * The value of this property points to the parent director of the jetty.xml + * configuration file currently executed. Everything is passed as a URL to + * support the case where the bundle is zipped. + */ + public static final String PROPERTY_THIS_JETTY_XML_FOLDER_URL = "this.jetty.xml.parent.folder.url"; + + private static Collection __containerTldBundleDiscoverers = new ArrayList<>(); + + private static final Logger LOG = LoggerFactory.getLogger(ServerInstanceWrapper.class.getName()); + + private final String _managedServerName; + + /** + * The managed jetty server + */ + private Server _server; + + private ContextHandlerCollection _ctxtCollection; + + /** + * This is the class loader that should be the parent classloader of any + * webapp classloader. It is in fact the _libExtClassLoader with a trick to + * let the TldScanner find the jars where the tld files are. + */ + private ClassLoader _commonParentClassLoaderForWebapps; + + private DeploymentManager _deploymentManager; + + public static void addContainerTldBundleDiscoverer(TldBundleDiscoverer tldBundleDiscoverer) + { + __containerTldBundleDiscoverers.add(tldBundleDiscoverer); + } + + public static Collection getContainerTldBundleDiscoverers() + { + return __containerTldBundleDiscoverers; + } + + public static Server configure(Server server, List jettyConfigurations, Dictionary props) throws Exception + { + + if (jettyConfigurations == null || jettyConfigurations.isEmpty()) + { + return server; + } + + Map idMap = new HashMap<>(); + if (server != null) + { + //Put in a mapping for the id "Server" and the name of the server as the instance being configured + idMap.put("Server", server); + idMap.put((String)props.get(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME), server); + } + + Map properties = new HashMap<>(); + if (props != null) + { + Enumeration en = props.keys(); + while (en.hasMoreElements()) + { + String key = en.nextElement(); + Object value = props.get(key); + properties.put(key, value.toString()); + if (server != null) + server.setAttribute(key, value); + } + } + + for (URL jettyConfiguration : jettyConfigurations) + { + try + { + // Execute a Jetty configuration file + XmlConfiguration config = new XmlConfiguration(Resource.newResource(jettyConfiguration)); + + config.getIdMap().putAll(idMap); + config.getProperties().putAll(properties); + + // #334062 compute the URL of the folder that contains the + // conf file and set it as a property so we can compute relative paths + // from it. + String urlPath = jettyConfiguration.toString(); + int lastSlash = urlPath.lastIndexOf('/'); + if (lastSlash > 4) + { + urlPath = urlPath.substring(0, lastSlash); + config.getProperties().put(PROPERTY_THIS_JETTY_XML_FOLDER_URL, urlPath); + } + + Object o = config.configure(); + if (server == null) + server = (Server)o; + + idMap = config.getIdMap(); + } + catch (Exception e) + { + LOG.warn("Configuration error in {}", jettyConfiguration); + throw e; + } + } + + return server; + } + + public ServerInstanceWrapper(String managedServerName) + { + _managedServerName = managedServerName; + } + + public String getManagedServerName() + { + return _managedServerName; + } + + /** + * The classloader that should be the parent classloader for each webapp + * deployed on this server. + * + * @return the classloader + */ + public ClassLoader getParentClassLoaderForWebapps() + { + return _commonParentClassLoaderForWebapps; + } + + /** + * @return The deployment manager registered on this server. + */ + public DeploymentManager getDeploymentManager() + { + return _deploymentManager; + } + + /** + * @return The app provider registered on this server. + */ + public Server getServer() + { + return _server; + } + + /** + * @return The collection of context handlers + */ + public ContextHandlerCollection getContextHandlerCollection() + { + return _ctxtCollection; + } + + public void start(Server server, Dictionary props) throws Exception + { + _server = server; + ClassLoader contextCl = Thread.currentThread().getContextClassLoader(); + try + { + List sharedURLs = getManagedJettySharedLibFolderUrls(props); + + // passing this bundle's classloader as the context classloader + // makes sure there is access to all the jetty's bundles + ClassLoader libExtClassLoader = LibExtClassLoaderHelper.createLibExtClassLoader(null, sharedURLs, JettyBootstrapActivator.class.getClassLoader()); + + if (LOG.isDebugEnabled()) + LOG.debug("LibExtClassLoader = {}", libExtClassLoader); + + Thread.currentThread().setContextClassLoader(libExtClassLoader); + + String jettyConfigurationUrls = (String)props.get(OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS); + List jettyConfigurations = jettyConfigurationUrls != null ? Util.fileNamesAsURLs(jettyConfigurationUrls, StringUtil.DEFAULT_DELIMS) : null; + + _server = configure(server, jettyConfigurations, props); + + init(); + + //if support for jsp is enabled, we need to convert locations of bundles that contain tlds into urls. + //these are tlds that we want jasper to treat as if they are on the container's classpath. Web bundles + //can use the Require-TldBundle MANIFEST header to name other tld-containing bundles that should be regarded + //as on the webapp classpath. + if (!__containerTldBundleDiscoverers.isEmpty()) + { + Set urls = new HashSet<>(); + //discover bundles with tlds that need to be on the container's classpath as URLs + for (TldBundleDiscoverer d : __containerTldBundleDiscoverers) + { + URL[] list = d.getUrlsForBundlesWithTlds(_deploymentManager, BundleFileLocatorHelperFactory.getFactory().getHelper()); + if (list != null) + { + for (URL u : list) + { + urls.add(u); + } + } + } + _commonParentClassLoaderForWebapps = new FakeURLClassLoader(libExtClassLoader, urls.toArray(new URL[urls.size()])); + } + else + _commonParentClassLoaderForWebapps = libExtClassLoader; + + if (LOG.isDebugEnabled()) + LOG.debug("common classloader = {}", _commonParentClassLoaderForWebapps); + + server.start(); + } + catch (Exception e) + { + if (server != null) + { + try + { + server.stop(); + } + catch (Exception x) + { + LOG.trace("IGNORED", x); + } + } + throw e; + } + finally + { + Thread.currentThread().setContextClassLoader(contextCl); + } + } + + public void stop() + { + try + { + if (_server.isRunning()) + { + _server.stop(); + } + } + catch (Exception e) + { + LOG.warn("Failed to stop server", e); + } + } + + /** + * Must be called after the server is configured. + * + * It is assumed the server has already been configured with the ContextHandlerCollection structure. + */ + private void init() + { + // Get the context handler + _ctxtCollection = (ContextHandlerCollection)_server.getDescendant(ContextHandlerCollection.class); + + if (_ctxtCollection == null) + throw new IllegalStateException("ERROR: No ContextHandlerCollection configured in Server"); + + List providerClassNames = new ArrayList<>(); + + // get a deployerManager and some providers + Collection deployers = _server.getBeans(DeploymentManager.class); + if (deployers != null && !deployers.isEmpty()) + { + _deploymentManager = deployers.iterator().next(); + + for (AppProvider provider : _deploymentManager.getAppProviders()) + { + providerClassNames.add(provider.getClass().getName()); + } + } + else + { + //add some kind of default + _deploymentManager = new DeploymentManager(); + _deploymentManager.setContexts(_ctxtCollection); + _server.addBean(_deploymentManager); + } + + _deploymentManager.setUseStandardBindings(false); + List deploymentLifeCycleBindings = new ArrayList<>(); + deploymentLifeCycleBindings.add(new OSGiDeployer(this)); + deploymentLifeCycleBindings.add(new StandardStarter()); + deploymentLifeCycleBindings.add(new StandardStopper()); + deploymentLifeCycleBindings.add(new OSGiUndeployer(this)); + _deploymentManager.setLifeCycleBindings(deploymentLifeCycleBindings); + + if (!providerClassNames.contains(BundleWebAppProvider.class.getName())) + { + // create it on the fly with reasonable default values. + try + { + BundleWebAppProvider webAppProvider = new BundleWebAppProvider(this); + _deploymentManager.addAppProvider(webAppProvider); + } + catch (Exception e) + { + LOG.warn("Failed to add BundleAppProvider to DeploymentManager", e); + } + } + + if (!providerClassNames.contains(ServiceWebAppProvider.class.getName())) + { + // create it on the fly with reasonable default values. + try + { + ServiceWebAppProvider webAppProvider = new ServiceWebAppProvider(this); + _deploymentManager.addAppProvider(webAppProvider); + } + catch (Exception e) + { + LOG.warn("Failed to add ServiceWebAppProvider to DeploymentManager", e); + } + } + + if (!providerClassNames.contains(BundleContextProvider.class.getName())) + { + try + { + BundleContextProvider contextProvider = new BundleContextProvider(this); + _deploymentManager.addAppProvider(contextProvider); + } + catch (Exception e) + { + LOG.warn("Failed to add BundleContextProvider to DeploymentManager", e); + } + } + + if (!providerClassNames.contains(ServiceContextProvider.class.getName())) + { + try + { + ServiceContextProvider contextProvider = new ServiceContextProvider(this); + _deploymentManager.addAppProvider(contextProvider); + } + catch (Exception e) + { + LOG.warn("Failed to add ServiceContextProvider to DeploymentManager", e); + } + } + } + + /** + * Get the Jetty Shared Lib Folder URLs in a form that is suitable for + * {@link LibExtClassLoaderHelper} to use. + * + * @param props the properties to look for the configuration in + * @return the list of URLs found, or null if none found + */ + private List getManagedJettySharedLibFolderUrls(Dictionary props) + { + String sharedURLs = (String)props.get(OSGiServerConstants.MANAGED_JETTY_SHARED_LIB_FOLDER_URLS); + if (StringUtil.isBlank(sharedURLs)) + { + return null; + } + + List libURLs = new ArrayList<>(); + + StringTokenizer tokenizer = new StringTokenizer(sharedURLs, StringUtil.DEFAULT_DELIMS, false); + while (tokenizer.hasMoreTokens()) + { + String tok = tokenizer.nextToken(); + try + { + URL url = new URL(tok); + url = BundleFileLocatorHelperFactory.getFactory().getHelper().getFileURL(url); + if (url.getProtocol().equals("file")) + { + libURLs.add(new URL("jar:" + url.toExternalForm() + "!/")); + } + else + { + if (LOG.isDebugEnabled()) + LOG.debug("Unrecognized Jetty Shared Lib URL: {}", url); + } + } + catch (Throwable mfe) + { + LOG.warn("Unable to process legacy lib folder {}", tok, mfe); + } + } + return libURLs; + } +} diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/internal/webapp/LibExtClassLoaderHelper.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/internal/webapp/LibExtClassLoaderHelper.java new file mode 100644 index 00000000000..e662c65cc33 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/internal/webapp/LibExtClassLoaderHelper.java @@ -0,0 +1,196 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.osgi.boot.internal.webapp; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +/** + * LibExtClassLoaderHelper + *

    + * Helper to create a URL class-loader with the jars inside + * ${jetty.home}/lib/ext and ${jetty.home}/resources. In an ideal world, every + * library is an OSGi bundle that does loads nicely. To support standard jars or + * bundles that cannot be loaded in the current OSGi environment, we support + * inserting the jars in the usual jetty/lib/ext folders in the proper classpath + * for the webapps. + *

    + * The drawback is that those jars will not be available in the OSGi + * classloader. + *

    + * Alternatives to placing jars in lib/ext: + *

      + *
    1. Bundle the jars in an osgi bundle. Have the webapp(s) that need these jars + * depend on that bundle.
    2. + *
    3. Bundle those jars in an osgi bundle-fragment that targets the + * jetty-bootstrap bundle
    4. + *
    5. Use equinox Buddy-Policy: register a buddy of the jetty bootstrapper + * bundle. (Note: it will work only on equinox)
    6. + *
    + */ +public class LibExtClassLoaderHelper +{ + + /** + * IFilesInJettyHomeResourcesProcessor + * + * Interface for callback impls + */ + public interface IFilesInJettyHomeResourcesProcessor + { + void processFilesInResourcesFolder(File jettyHome, Map filesInResourcesFolder); + } + + public static final Set registeredFilesInJettyHomeResourcesProcessors = new HashSet<>(); + + /** + * @param jettyHome the jetty home + * @param parentClassLoader the parent classloader + * @return a url classloader with the jars of resources, lib/ext and the + * jars passed in the other argument. The parent classloader usually + * is the JettyBootStrapper (an osgi classloader. + * @throws MalformedURLException if the jetty home reference is invalid + */ + public static ClassLoader createLibEtcClassLoader(File jettyHome, ClassLoader parentClassLoader) throws MalformedURLException + { + if (jettyHome == null) + { + return parentClassLoader; + } + ArrayList urls = new ArrayList<>(); + File jettyResources = new File(jettyHome, "resources"); + if (jettyResources.exists()) + { + // make sure it contains something else than README: + Map jettyResFiles = new HashMap<>(); + for (File f : jettyResources.listFiles()) + { + jettyResFiles.put(f.getName(), f); + if (f.getName().toLowerCase(Locale.ENGLISH).startsWith("readme")) + { + continue; + } + else + { + if (urls.isEmpty()) + { + urls.add(jettyResources.toURI().toURL()); + } + } + } + processFilesInResourcesFolder(jettyHome, jettyResFiles); + } + File libExt = new File(jettyHome, "lib/ext"); + if (libExt.exists()) + { + for (File f : libExt.listFiles()) + { + if (f.getName().endsWith(".jar")) + { + // cheap to tolerate folders so let's do it. + URL url = f.toURI().toURL(); + if (f.isFile()) + { + // is this necessary anyways? + url = new URL("jar:" + url.toString() + "!/"); + } + urls.add(url); + } + } + } + + return new URLClassLoader(urls.toArray(new URL[urls.size()]), parentClassLoader); + } + + /** + * @param jarsContainerOrJars the jars via file references + * @param otherJarsOrFolder more jars via url references + * @param parentClassLoader the parent classloader + * @return a url classloader with the jars of resources, lib/ext and the + * jars passed in the other argument. The parent classloader usually + * is the JettyBootStrapper (an osgi classloader). If there was no + * extra jars to insert, then just return the parentClassLoader. + * @throws MalformedURLException if there is a bad jar file reference + */ + public static ClassLoader createLibExtClassLoader(List jarsContainerOrJars, List otherJarsOrFolder, ClassLoader parentClassLoader) + throws MalformedURLException + { + if (jarsContainerOrJars == null && otherJarsOrFolder == null) + { + return parentClassLoader; + } + List urls = new ArrayList<>(); + if (otherJarsOrFolder != null) + { + urls.addAll(otherJarsOrFolder); + } + if (jarsContainerOrJars != null) + { + for (File libExt : jarsContainerOrJars) + { + if (libExt.isDirectory()) + { + for (File f : libExt.listFiles()) + { + if (f.getName().endsWith(".jar")) + { + // cheap to tolerate folders so let's do it. + URL url = f.toURI().toURL(); + if (f.isFile()) + { + // is this necessary anyways? + url = new URL("jar:" + url.toString() + "!/"); + } + urls.add(url); + } + } + } + } + } + return new URLClassLoader(urls.toArray(new URL[urls.size()]), parentClassLoader); + } + + /** + * When we find files typically used for central logging configuration we do + * what it takes in this method to do what the user expects. Without + * depending too much directly on a particular logging framework. + *

    + * We can afford to do some implementation specific code for a logging + * framework only in a fragment. + *

    + * Trying to configure log4j and logback in here. + *

    + * We recommend that slf4j jars are all placed in the osgi framework. And a + * single implementation if possible packaged as an osgi bundle is there. + * + * @param jettyHome the jetty home reference + * @param childrenFiles the map of child files + */ + protected static void processFilesInResourcesFolder(File jettyHome, Map childrenFiles) + { + for (IFilesInJettyHomeResourcesProcessor processor : registeredFilesInJettyHomeResourcesProcessors) + { + processor.processFilesInResourcesFolder(jettyHome, childrenFiles); + } + } +} diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/internal/webapp/OSGiWebappClassLoader.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/internal/webapp/OSGiWebappClassLoader.java new file mode 100644 index 00000000000..03b46c39650 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/internal/webapp/OSGiWebappClassLoader.java @@ -0,0 +1,261 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.osgi.boot.internal.webapp; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.jar.JarFile; + +import jakarta.servlet.http.HttpServlet; +import org.eclipse.jetty.ee10.osgi.boot.utils.BundleClassLoaderHelperFactory; +import org.eclipse.jetty.ee10.webapp.WebAppClassLoader; +import org.eclipse.jetty.ee10.webapp.WebAppContext; +import org.eclipse.jetty.util.TypeUtil; +import org.eclipse.jetty.util.resource.Resource; +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()); + + /** + * when a logging framework is setup in the osgi classloaders, it can access + * this and register the classes that must not be found in the jar. + */ + public static final Set JAR_WITH_SUCH_CLASS_MUST_BE_EXCLUDED = new HashSet<>(); + + public static void addClassThatIdentifiesAJarThatMustBeRejected(Class zclass) + { + JAR_WITH_SUCH_CLASS_MUST_BE_EXCLUDED.add(TypeUtil.toClassReference(zclass.getName())); + } + + public static void addClassThatIdentifiesAJarThatMustBeRejected(String zclassName) + { + JAR_WITH_SUCH_CLASS_MUST_BE_EXCLUDED.add(TypeUtil.toClassReference(zclassName)); + } + + static + { + addClassThatIdentifiesAJarThatMustBeRejected(HttpServlet.class); + } + + 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.ee10.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; + } + } + } + + private List toList(Enumeration e, Enumeration e2) + { + List list = new ArrayList<>(); + while (e != null && e.hasMoreElements()) + { + list.add(e.nextElement()); + } + while (e2 != null && e2.hasMoreElements()) + { + list.add(e2.nextElement()); + } + return list; + } + + /** + * Parse the classpath ourselves to be able to filter things. This is a + * derivative work of the super class + */ + @Override + public void addClassPath(String classPath) throws IOException + { + for (Resource resource : Resource.fromList(classPath, false, (path) -> getContext().newResource(path))) + { + File file = resource.getFile(); + if (file != null && isAcceptableLibrary(file, JAR_WITH_SUCH_CLASS_MUST_BE_EXCLUDED)) + { + super.addClassPath(resource); + } + else + { + LOG.info("Did not add {} to the classloader of the webapp {}", resource, getContext()); + } + } + } + + /** + * @return true if the lib should be included in the webapp classloader. + */ + private boolean isAcceptableLibrary(File file, Set pathToClassFiles) + { + try + { + if (file.isDirectory()) + { + for (String criteria : pathToClassFiles) + { + if (new File(file, criteria).exists()) + { + return false; + } + } + } + else + { + JarFile jar = null; + try + { + jar = new JarFile(file); + for (String criteria : pathToClassFiles) + { + if (jar.getEntry(criteria) != null) + { + return false; + } + } + } + finally + { + if (jar != null) + try + { + jar.close(); + } + catch (IOException ignored) + { + } + } + } + } + catch (IOException e) + { + // nevermind. just trying our best + LOG.trace("IGNORED", e); + } + return true; + } +} diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/BundleClassLoaderHelper.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/BundleClassLoaderHelper.java new file mode 100644 index 00000000000..25a7a22af0d --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/BundleClassLoaderHelper.java @@ -0,0 +1,53 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.osgi.boot.utils; + +import org.eclipse.jetty.ee10.osgi.boot.utils.internal.DefaultBundleClassLoaderHelper; +import org.osgi.framework.Bundle; + +/** + * BundleClassLoaderHelper + *

    + * Is there a clean OSGi way to go from the Bundle object to the classloader of + * the Bundle ? You can certainly take a class inside the bundle and get the + * bundle's classloader that way. Getting the classloader directly from the + * bundle would be nice. + *

    + * We could use fragments that are specific to each OSGi implementation. Using + * introspection here to keep packaging simple and avoid the multiplication of + * the jars. + *

    + * The default implementation relies on introspection and supports equinox-3.5 + * and felix-2.0.0 + */ +public interface BundleClassLoaderHelper +{ + + /** + * The name of the custom implementation for this interface in a fragment. + */ + public static final String CLASS_NAME = "org.eclipse.jetty.ee10.osgi.boot.utils.BundleClassLoaderHelperImpl"; + + /** + * The default instance supports felix and equinox + */ + public static BundleClassLoaderHelper DEFAULT = new DefaultBundleClassLoaderHelper(); + + /** + * @param bundle the bundle + * @return The classloader of a given bundle. Assuming the bundle is + * started. + */ + public ClassLoader getBundleClassLoader(Bundle bundle); +} diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/BundleClassLoaderHelperFactory.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/BundleClassLoaderHelperFactory.java new file mode 100644 index 00000000000..a1936b6a3a1 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/BundleClassLoaderHelperFactory.java @@ -0,0 +1,56 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.osgi.boot.utils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * BundleClassLoaderHelperFactory + * + * Get a class loader helper adapted for the particular osgi environment. + */ +public class BundleClassLoaderHelperFactory +{ + private static final Logger LOG = LoggerFactory.getLogger(BundleClassLoaderHelperFactory.class); + + private static BundleClassLoaderHelperFactory _instance = new BundleClassLoaderHelperFactory(); + + public static BundleClassLoaderHelperFactory getFactory() + { + return _instance; + } + + private BundleClassLoaderHelperFactory() + { + } + + public BundleClassLoaderHelper getHelper() + { + //use the default + BundleClassLoaderHelper helper = BundleClassLoaderHelper.DEFAULT; + try + { + //if a fragment has not provided their own impl + helper = (BundleClassLoaderHelper)Class.forName(BundleClassLoaderHelper.CLASS_NAME) + .getDeclaredConstructor().newInstance(); + } + catch (Throwable t) + { + LOG.trace("IGNORED", t); + } + + return helper; + } +} diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/BundleFileLocatorHelper.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/BundleFileLocatorHelper.java new file mode 100644 index 00000000000..22ad790d438 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/BundleFileLocatorHelper.java @@ -0,0 +1,118 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.osgi.boot.utils; + +import java.io.File; +import java.net.URL; +import java.util.Enumeration; + +import org.eclipse.jetty.ee10.osgi.boot.utils.internal.DefaultFileLocatorHelper; +import org.osgi.framework.Bundle; + +/** + * BundleFileLocatorHelper + *

    + * From a bundle to its location on the filesystem. Assumes the bundle is not a + * jar. + */ +public interface BundleFileLocatorHelper +{ + + /** + * The name of the custom implementation for this interface in a fragment. + */ + public static final String CLASS_NAME = "org.eclipse.jetty.ee10.osgi.boot.utils.FileLocatorHelperImpl"; + + /** + * The default instance supports felix and equinox + */ + public static BundleFileLocatorHelper DEFAULT = new DefaultFileLocatorHelper(); + + /** + * Works with equinox, felix, nuxeo and probably more. Not exactly in the + * spirit of OSGi but quite necessary to support self-contained webapps and + * other situations. + *

    + * Currently only works with bundles that are not jar. + * + * @param bundle The bundle + * @return Its installation location as a file. + * @throws Exception if unable to get the install location + */ + public File getBundleInstallLocation(Bundle bundle) throws Exception; + + /** + * Locate a file inside a bundle. + * + * @param bundle the bundle + * @param path the path + * @return file the file object + * @throws Exception if unable to get the file + */ + public File getFileInBundle(Bundle bundle, String path) throws Exception; + + /** + * If the bundle is a jar, returns the jar. If the bundle is a folder, look + * inside it and search for jars that it returns. + *

    + * Good enough for our purpose (TldLocationsCache when it scans for tld + * files inside jars alone. In fact we only support the second situation for + * development purpose where the bundle was imported in pde and the classes + * kept in a jar. + * + * @param bundle the bundle + * @return The jar(s) file that is either the bundle itself, either the jars + * embedded inside it. + * @throws Exception if unable to locate the jars + */ + public File[] locateJarsInsideBundle(Bundle bundle) throws Exception; + + /** + * Helper method equivalent to Bundle#getEntry(String entryPath) except that + * it searches for entries in the fragments by using the findEntries method. + * + * @param bundle the bundle + * @param entryPath the entry path + * @return null or all the entries found for that path. + */ + public Enumeration findEntries(Bundle bundle, String entryPath); + + /** + * Only useful for equinox: on felix we get the file:// or jar:// url + * already. Other OSGi implementations have not been tested + *

    + * Get a URL to the bundle entry that uses a common protocol (i.e. file: + * jar: or http: etc.). + * + * @param url the url + * @return a URL to the bundle entry that uses a common protocol + * @throws Exception if unable to get the local url + */ + public URL getLocalURL(URL url) throws Exception; + + /** + * Only useful for equinox: on felix we get the file:// url already. Other + * OSGi implementations have not been tested + *

    + * Get a URL to the content of the bundle entry that uses the file: + * protocol. The content of the bundle entry may be downloaded or extracted + * to the local file system in order to create a file: URL. + * + * @param url the url + * @return a URL to the content of the bundle entry that uses the file: + * protocol + * @throws Exception if unable to get the file url + */ + public URL getFileURL(URL url) throws Exception; +} diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/BundleFileLocatorHelperFactory.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/BundleFileLocatorHelperFactory.java new file mode 100644 index 00000000000..65dd438b066 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/BundleFileLocatorHelperFactory.java @@ -0,0 +1,54 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.osgi.boot.utils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * BundleFileLocatorHelperFactory + * + * Obtain a helper for locating files based on the bundle. + */ +public class BundleFileLocatorHelperFactory +{ + private static final Logger LOG = LoggerFactory.getLogger(BundleFileLocatorHelperFactory.class); + + private static BundleFileLocatorHelperFactory _instance = new BundleFileLocatorHelperFactory(); + + private BundleFileLocatorHelperFactory() + { + } + + public static BundleFileLocatorHelperFactory getFactory() + { + return _instance; + } + + public BundleFileLocatorHelper getHelper() + { + BundleFileLocatorHelper helper = BundleFileLocatorHelper.DEFAULT; + try + { + //see if a fragment has supplied an alternative + helper = (BundleFileLocatorHelper)Class.forName(BundleFileLocatorHelper.CLASS_NAME) + .getDeclaredConstructor().newInstance(); + } + catch (Throwable t) + { + LOG.trace("IGNORED", t); + } + return helper; + } +} diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/EventSender.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/EventSender.java new file mode 100644 index 00000000000..6f4bfc0d5fe --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/EventSender.java @@ -0,0 +1,84 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.osgi.boot.utils; + +import java.util.Dictionary; +import java.util.Hashtable; + +import org.osgi.framework.Bundle; +import org.osgi.framework.FrameworkUtil; +import org.osgi.service.event.Event; +import org.osgi.service.event.EventAdmin; +import org.osgi.util.tracker.ServiceTracker; + +/** + * Utility class for emitting OSGi EventAdmin events + */ +public class EventSender +{ + //OSGi Event Admin events for webapps + public static final String DEPLOYING_EVENT = "org/osgi/service/web/DEPLOYING"; + public static final String DEPLOYED_EVENT = "org/osgi/service/web/DEPLOYED"; + public static final String UNDEPLOYING_EVENT = "org/osgi/service/web/UNDEPLOYING"; + public static final String UNDEPLOYED_EVENT = "org/osgi/service/web/UNDEPLOYED"; + public static final String FAILED_EVENT = "org/osgi/service/web/FAILED"; + + private static final EventSender __instance = new EventSender(); + private Bundle _myBundle; + private ServiceTracker _serviceTracker; + + private EventSender() + { + _myBundle = FrameworkUtil.getBundle(EventSender.class); + _serviceTracker = new ServiceTracker(_myBundle.getBundleContext(), EventAdmin.class.getName(), null); + _serviceTracker.open(); + } + + public static EventSender getInstance() + { + return __instance; + } + + public void send(String topic, Bundle wab, String contextPath) + { + if (topic == null || wab == null || contextPath == null) + return; + + send(topic, wab, contextPath, null); + } + + public void send(String topic, Bundle wab, String contextPath, Exception ex) + { + EventAdmin service = (EventAdmin)_serviceTracker.getService(); + if (service != null) + { + Dictionary props = new Hashtable<>(); + props.put("bundle.symbolicName", wab.getSymbolicName()); + props.put("bundle.id", wab.getBundleId()); + props.put("bundle", wab); + props.put("bundle.version", wab.getVersion()); + props.put("context.path", contextPath); + props.put("timestamp", System.currentTimeMillis()); + props.put("extender.bundle", _myBundle); + props.put("extender.bundle.symbolicName", _myBundle.getSymbolicName()); + props.put("extender.bundle.id", _myBundle.getBundleId()); + props.put("extender.bundle.version", _myBundle.getVersion()); + + if (FAILED_EVENT.equalsIgnoreCase(topic) && ex != null) + props.put("exception", ex); + + service.sendEvent(new Event(topic, props)); + } + } +} diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/FakeURLClassLoader.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/FakeURLClassLoader.java new file mode 100644 index 00000000000..f9800e1a8db --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/FakeURLClassLoader.java @@ -0,0 +1,66 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.osgi.boot.utils; + +import java.net.URL; +import java.net.URLClassLoader; + +/** + * FakeURLClassLoader + *

    + * A URLClassloader that overrides the getURLs() method to return the list + * of urls passed in to the constructor, but otherwise acts as if it has no + * urls, which would cause it to delegate to the parent classloader (in this + * case an OSGi classloader). + *

    + * The main use of this class is with jars containing tlds. Jasper expects a + * URL classloader to inspect for jars with tlds. + */ +public class FakeURLClassLoader extends URLClassLoader +{ + private URL[] _jars; + + public FakeURLClassLoader(ClassLoader osgiClassLoader, URL[] jars) + { + super(new URL[]{}, osgiClassLoader); + _jars = jars; + } + + /** + * @return the jars that contains tlds so that TldLocationsCache or + * TldScanner can find them. + */ + @Override + public URL[] getURLs() + { + return _jars; + } + + @Override + public String toString() + { + StringBuilder builder = new StringBuilder(); + + if (_jars != null) + { + for (URL u : _jars) + { + builder.append(" " + u.toString()); + } + return builder.toString(); + } + else + return super.toString(); + } +} diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/OSGiClassLoader.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/OSGiClassLoader.java new file mode 100644 index 00000000000..546588d287f --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/OSGiClassLoader.java @@ -0,0 +1,160 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.osgi.boot.utils; + +import java.io.IOException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; + +import org.osgi.framework.Bundle; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * OSGiClassLoader + * + * Class loader that is aware of a bundle. Similar to WebAppClassLoader from Jetty + * and the OSGiWebAppClassLoader, but works without webapps. + */ +public class OSGiClassLoader extends URLClassLoader +{ + private static final Logger LOG = LoggerFactory.getLogger(OSGiClassLoader.class); + + private Bundle _bundle; + private ClassLoader _osgiBundleClassLoader; + private ClassLoader _parent; + + public OSGiClassLoader(ClassLoader parent, Bundle bundle) + { + super(new URL[]{}, parent); + _parent = getParent(); + _bundle = bundle; + _osgiBundleClassLoader = BundleClassLoaderHelperFactory.getFactory().getHelper().getBundleClassLoader(_bundle); + } + + /** + * Get a resource from the classloader + * + * Copied from WebAppClassLoader + */ + @Override + public URL getResource(String name) + { + URL url = null; + boolean triedParent = false; + + if (url == null) + { + url = _osgiBundleClassLoader.getResource(name); + + if (url == null && name.startsWith("/")) + { + if (LOG.isDebugEnabled()) + LOG.debug("HACK leading / off {}", name); + + url = _osgiBundleClassLoader.getResource(name.substring(1)); + } + } + + if (url == null && !triedParent) + { + if (_parent != null) + url = _parent.getResource(name); + } + + if (url != null) + if (LOG.isDebugEnabled()) + LOG.debug("getResource({})={}", name, url); + + return url; + } + + @Override + public Class loadClass(String name) throws ClassNotFoundException + { + return loadClass(name, false); + } + + @Override + protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException + { + synchronized (getClassLoadingLock(name)) + { + Class c = findLoadedClass(name); + ClassNotFoundException ex = null; + boolean triedParent = false; + + if (c == null) + { + try + { + c = this.findClass(name); + } + catch (ClassNotFoundException e) + { + ex = e; + } + } + + if (c == null && _parent != null && !triedParent) + c = _parent.loadClass(name); + + if (c == null) + throw ex; + + if (resolve) + resolveClass(c); + + if (LOG.isDebugEnabled()) + LOG.debug("loaded {} from {}", c, c.getClassLoader()); + + return c; + } + } + + @Override + public Enumeration getResources(String name) throws IOException + { + Enumeration osgiUrls = _osgiBundleClassLoader.getResources(name); + Enumeration urls = super.getResources(name); + return Collections.enumeration(toList(osgiUrls, urls)); + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException + { + return _osgiBundleClassLoader.loadClass(name); + } + + /** + * + */ + private List toList(Enumeration e, Enumeration e2) + { + List list = new ArrayList<>(); + while (e != null && e.hasMoreElements()) + { + list.add(e.nextElement()); + } + while (e2 != null && e2.hasMoreElements()) + { + list.add(e2.nextElement()); + } + return list; + } +} diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/ServerConnectorListener.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/ServerConnectorListener.java new file mode 100644 index 00000000000..3d03dcb62e2 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/ServerConnectorListener.java @@ -0,0 +1,91 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.osgi.boot.utils; + +import java.io.FileWriter; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.util.component.AbstractLifeCycle.AbstractLifeCycleListener; +import org.eclipse.jetty.util.component.LifeCycle; + +/** + * ServerConnectorListener + * + * This is for test support, where we need jetty to run on a random port, and we need + * a client to be able to find out which port was picked. + */ +public class ServerConnectorListener extends AbstractLifeCycleListener +{ + + private Path _filePath; + private String _sysPropertyName; + + @Override + public void lifeCycleStarted(LifeCycle event) + { + if (getFilePath() != null) + { + try (FileWriter writer = new FileWriter(getFilePath().toFile())) + { + Files.deleteIfExists(_filePath); + writer.write(((ServerConnector)event).getLocalPort()); + writer.close(); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + } + + if (getSysPropertyName() != null) + { + System.setProperty(_sysPropertyName, String.valueOf(((ServerConnector)event).getLocalPort())); + } + super.lifeCycleStarted(event); + } + + /** + * @return the filePath + */ + public Path getFilePath() + { + return _filePath; + } + + /** + * @param filePath the filePath to set + */ + public void setFilePath(Path filePath) + { + _filePath = filePath; + } + + /** + * @return the sysPropertyName + */ + public String getSysPropertyName() + { + return _sysPropertyName; + } + + /** + * @param sysPropertyName the sysPropertyName to set + */ + public void setSysPropertyName(String sysPropertyName) + { + _sysPropertyName = sysPropertyName; + } +} diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/TldBundleDiscoverer.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/TldBundleDiscoverer.java new file mode 100644 index 00000000000..f0087573432 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/TldBundleDiscoverer.java @@ -0,0 +1,36 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.osgi.boot.utils; + +import java.net.URL; + +import org.eclipse.jetty.deploy.DeploymentManager; + +/** + * TldBundleDiscoverer + * + * Convert bundles that contain tlds into URL locations for consumption by jasper. + */ +public interface TldBundleDiscoverer +{ + /** + * Find bundles that contain tlds and convert into URL references to their location. + * + * @param manager The {@link DeploymentManager} instance to use + * @param fileLocator the {@link BundleFileLocatorHelper} instance to use + * @return array of URLs representing locations of tld containing bundles + * @throws Exception In case of errors during resolving TLDs files + */ + URL[] getUrlsForBundlesWithTlds(DeploymentManager manager, BundleFileLocatorHelper fileLocator) throws Exception; +} diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/Util.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/Util.java new file mode 100644 index 00000000000..2cd6a080137 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/Util.java @@ -0,0 +1,166 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.osgi.boot.utils; + +import java.net.URL; +import java.util.ArrayList; +import java.util.Dictionary; +import java.util.List; +import java.util.StringTokenizer; + +import org.eclipse.jetty.ee10.osgi.boot.OSGiServerConstants; +import org.eclipse.jetty.util.StringUtil; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Filter; +import org.osgi.framework.InvalidSyntaxException; + +/** + * Various useful functions utility methods for OSGi wide use. + */ +public class Util +{ + /** + * Create an osgi filter for the given classname and server name. + * + * @param bundleContext the {@link BundleContext} instance to use + * @param classname the class to match on the filter + * @param managedServerName the name of the jetty server instance + * @return a new filter + * @throws InvalidSyntaxException If the filter contains an invalid string that cannot be parsed. + */ + public static Filter createFilter(BundleContext bundleContext, String classname, String managedServerName) throws InvalidSyntaxException + { + if (StringUtil.isBlank(managedServerName) || managedServerName.equals(OSGiServerConstants.MANAGED_JETTY_SERVER_DEFAULT_NAME)) + { + return bundleContext.createFilter("(&(objectclass=" + classname + ")(|(managedServerName=" + managedServerName + ")(!(managedServerName=*))))"); + } + else + { + return bundleContext.createFilter("(&(objectclass=" + classname + ")(managedServerName=" + managedServerName + "))"); + } + } + + /** + * Get the value of a manifest header. + * + * @param name the name of the header + * @param altName an alternative name for the header (useful for deprecated names) + * @param manifest the dictionary + * @return the value from the manifest + */ + public static String getManifestHeaderValue(String name, String altName, Dictionary manifest) + { + if (manifest == null) + return null; + if (name == null && altName == null) + return null; + if (name != null) + return (String)manifest.get(name); + return (String)manifest.get(altName); + } + + /** + * Get the value of a manifest header. + * + * @param name the name of the header + * @param manifest the dictionary + * @return the value from the manifest + */ + public static String getManifestHeaderValue(String name, Dictionary manifest) + { + return getManifestHeaderValue(name, null, manifest); + } + + /** + * Treating the string as a separated list of filenames, + * convert and return the list of urls. + * + * @param val the separated list of filenames + * @param delims the separators (default is ,;) + * @return the list of URLs found in the input list + * @throws Exception if unable to convert entry to a URL + */ + public static List fileNamesAsURLs(String val, String delims) + throws Exception + { + String separators = StringUtil.DEFAULT_DELIMS; + if (delims == null) + delims = separators; + + StringTokenizer tokenizer = new StringTokenizer(val, delims, false); + List urls = new ArrayList<>(); + while (tokenizer.hasMoreTokens()) + { + urls.add(BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(new URL(tokenizer.nextToken()))); + } + return urls; + } + + public static void setProperty(Dictionary properties, String key, Object value) + { + if (value != null) + { + properties.put(key, value); + } + } + + /** + * recursively substitute the ${sysprop} by their actual system property. + * ${sysprop,defaultvalue} will use 'defaultvalue' as the value if no + * sysprop is defined. Not the most efficient code but we are shooting for + * simplicity and speed of development here. + * + * @param value the input string + * @return the string with replaced properties + */ + public static String resolvePropertyValue(String value) + { + int ind = value.indexOf("${"); + if (ind == -1) + { + return value; + } + int ind2 = value.indexOf('}', ind); + if (ind2 == -1) + { + return value; + } + String sysprop = value.substring(ind + 2, ind2); + String defaultValue = null; + int comma = sysprop.indexOf(','); + if (comma != -1 && comma + 1 != sysprop.length()) + { + defaultValue = sysprop.substring(comma + 1); + defaultValue = resolvePropertyValue(defaultValue); + sysprop = sysprop.substring(0, comma); + } + else + { + defaultValue = "${" + sysprop + "}"; + } + + String v = System.getProperty(sysprop); + + String reminder = value.length() > ind2 + 1 ? value.substring(ind2 + 1) : ""; + reminder = resolvePropertyValue(reminder); + if (v != null) + { + return value.substring(0, ind) + v + reminder; + } + else + { + return value.substring(0, ind) + defaultValue + reminder; + } + } +} diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/internal/DefaultBundleClassLoaderHelper.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/internal/DefaultBundleClassLoaderHelper.java new file mode 100644 index 00000000000..f83fcb32936 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/internal/DefaultBundleClassLoaderHelper.java @@ -0,0 +1,428 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.osgi.boot.utils.internal; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.List; + +import org.eclipse.jetty.ee10.osgi.boot.utils.BundleClassLoaderHelper; +import org.osgi.framework.Bundle; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * DefaultBundleClassLoaderHelper + *

    + * Default implementation of the BundleClassLoaderHelper. Uses introspection to + * support equinox-3.5 and felix-2.0.0 + */ +public class DefaultBundleClassLoaderHelper implements BundleClassLoaderHelper +{ + private static final Logger LOG = LoggerFactory.getLogger(BundleClassLoaderHelper.class); + + private static enum OSGiContainerType + { + EquinoxOld, EquinoxLuna, FelixOld, Felix403, Concierge + } + + ; + private static OSGiContainerType osgiContainer; + private static Class Equinox_BundleHost_Class; + private static Class Equinox_EquinoxBundle_Class; + private static Class Felix_BundleImpl_Class; + private static Class Felix_BundleWiring_Class; + //old equinox + private static Method Equinox_BundleHost_getBundleLoader_method; + private static Method Equinox_BundleLoader_createClassLoader_method; + //new equinox + private static Method Equinox_EquinoxBundle_getModuleClassLoader_Method; + + //new felix + private static Method Felix_BundleImpl_Adapt_Method; + //old felix + private static Field Felix_BundleImpl_m_Modules_Field; + private static Field Felix_ModuleImpl_m_ClassLoader_Field; + private static Method Felix_BundleWiring_getClassLoader_Method; + + // Concierge + private static Class Concierge_BundleImpl_Class; + private static Class Concierge_BundleWiring_Class; + private static Method Concierge_BundleImpl_Adapt_Method; + private static Method Concierge_BundleWiring_getClassLoader_Method; + + private static void checkContainerType(Bundle bundle) + { + if (osgiContainer != null) + return; + + try + { + Equinox_BundleHost_Class = bundle.getClass().getClassLoader().loadClass("org.eclipse.osgi.framework.internal.core.BundleHost"); + osgiContainer = OSGiContainerType.EquinoxOld; + return; + } + catch (ClassNotFoundException e) + { + LOG.trace("IGNORED", e); + } + + try + { + Equinox_EquinoxBundle_Class = bundle.getClass().getClassLoader().loadClass("org.eclipse.osgi.internal.framework.EquinoxBundle"); + osgiContainer = OSGiContainerType.EquinoxLuna; + return; + } + catch (ClassNotFoundException e) + { + LOG.trace("IGNORED", e); + } + + try + { + //old felix or new felix? + Felix_BundleImpl_Class = bundle.getClass().getClassLoader().loadClass("org.apache.felix.framework.BundleImpl"); + try + { + Felix_BundleImpl_Adapt_Method = Felix_BundleImpl_Class.getDeclaredMethod("adapt", new Class[]{Class.class}); + osgiContainer = OSGiContainerType.Felix403; + return; + } + catch (NoSuchMethodException e) + { + osgiContainer = OSGiContainerType.FelixOld; + return; + } + } + catch (ClassNotFoundException e) + { + LOG.trace("IGNORED", e); + } + + try + { + Concierge_BundleImpl_Class = bundle.getClass().getClassLoader().loadClass("org.eclipse.concierge.BundleImpl"); + osgiContainer = OSGiContainerType.Concierge; + return; + } + catch (ClassNotFoundException e) + { + LOG.trace("IGNORED", e); + } + + LOG.warn("Unknown OSGi container type"); + return; + } + + /** + * Assuming the bundle is started. + * + * @param bundle the bundle + * @return classloader object + */ + @Override + public ClassLoader getBundleClassLoader(Bundle bundle) + { + String bundleActivator = (String)bundle.getHeaders().get("Bundle-Activator"); + + if (bundleActivator == null) + { + bundleActivator = (String)bundle.getHeaders().get("Jetty-ClassInBundle"); + } + if (bundleActivator != null) + { + try + { + return bundle.loadClass(bundleActivator).getClassLoader(); + } + catch (ClassNotFoundException e) + { + LOG.warn("Unable to load bundle activator {}", bundleActivator, e); + } + } + + // resort to introspection + return getBundleClassLoaderForContainer(bundle); + } + + /** + * + */ + private ClassLoader getBundleClassLoaderForContainer(Bundle bundle) + { + checkContainerType(bundle); + if (osgiContainer == null) + { + LOG.warn("No classloader for unknown OSGi container type"); + return null; + } + + switch (osgiContainer) + { + case EquinoxOld: + case EquinoxLuna: + { + return internalGetEquinoxBundleClassLoader(bundle); + } + + case FelixOld: + case Felix403: + { + return internalGetFelixBundleClassLoader(bundle); + } + + case Concierge: + { + return internalGetConciergeBundleClassLoader(bundle); + } + + default: + { + LOG.warn("No classloader found for bundle {}", bundle.getSymbolicName()); + return null; + } + } + } + + /** + * + */ + private static ClassLoader internalGetEquinoxBundleClassLoader(Bundle bundle) + { + if (osgiContainer == OSGiContainerType.EquinoxOld) + { + String bundleLoaderName = "org.eclipse.osgi.internal.loader.BundleLoader"; + try + { + if (Equinox_BundleHost_getBundleLoader_method == null) + { + Equinox_BundleHost_getBundleLoader_method = + Equinox_BundleHost_Class.getDeclaredMethod("getBundleLoader", new Class[]{}); + Equinox_BundleHost_getBundleLoader_method.setAccessible(true); + } + Object bundleLoader = Equinox_BundleHost_getBundleLoader_method.invoke(bundle, new Object[]{}); + if (Equinox_BundleLoader_createClassLoader_method == null && bundleLoader != null) + { + Equinox_BundleLoader_createClassLoader_method = + bundleLoader.getClass().getClassLoader().loadClass(bundleLoaderName).getDeclaredMethod("createClassLoader", new Class[]{}); + Equinox_BundleLoader_createClassLoader_method.setAccessible(true); + } + return (ClassLoader)Equinox_BundleLoader_createClassLoader_method.invoke(bundleLoader, new Object[]{}); + } + catch (Throwable t) + { + LOG.warn("Unable to get equinox bundle classloader", t); + return null; + } + } + + if (osgiContainer == OSGiContainerType.EquinoxLuna) + { + try + { + if (Equinox_EquinoxBundle_getModuleClassLoader_Method == null) + Equinox_EquinoxBundle_getModuleClassLoader_Method = Equinox_EquinoxBundle_Class.getDeclaredMethod("getModuleClassLoader", new Class[]{ + Boolean.TYPE + }); + + Equinox_EquinoxBundle_getModuleClassLoader_Method.setAccessible(true); + return (ClassLoader)Equinox_EquinoxBundle_getModuleClassLoader_Method.invoke(bundle, new Object[]{ + Boolean.FALSE + }); + } + catch (Exception e) + { + LOG.warn("Unable to get equinox luna bundle classloader", e); + return null; + } + } + + LOG.warn("No classloader for equinox platform for bundle {}", bundle.getSymbolicName()); + return null; + } + + /** + * + */ + @SuppressWarnings("unchecked") + private static ClassLoader internalGetFelixBundleClassLoader(Bundle bundle) + { + + if (osgiContainer == OSGiContainerType.Felix403) + { + try + { + if (Felix_BundleWiring_Class == null) + Felix_BundleWiring_Class = bundle.getClass().getClassLoader().loadClass("org.osgi.framework.wiring.BundleWiring"); + + Felix_BundleImpl_Adapt_Method.setAccessible(true); + + if (Felix_BundleWiring_getClassLoader_Method == null) + { + Felix_BundleWiring_getClassLoader_Method = Felix_BundleWiring_Class.getDeclaredMethod("getClassLoader"); + Felix_BundleWiring_getClassLoader_Method.setAccessible(true); + } + + Object wiring = Felix_BundleImpl_Adapt_Method.invoke(bundle, new Object[]{Felix_BundleWiring_Class}); + return (ClassLoader)Felix_BundleWiring_getClassLoader_Method.invoke(wiring); + } + catch (Exception e) + { + LOG.warn("Unable to get felix bundle classloader", e); + return null; + } + } + + if (osgiContainer == OSGiContainerType.FelixOld) + { + try + { + if (Felix_BundleImpl_m_Modules_Field == null) + { + Felix_BundleImpl_m_Modules_Field = Felix_BundleImpl_Class.getDeclaredField("m_modules"); + Felix_BundleImpl_m_Modules_Field.setAccessible(true); + } + + // Figure out which version of the modules is exported + Object currentModuleImpl; + + try + { + Object[] moduleArray = (Object[])Felix_BundleImpl_m_Modules_Field.get(bundle); + currentModuleImpl = moduleArray[moduleArray.length - 1]; + } + catch (Throwable ex) + { + try + { + List moduleArray = (List)Felix_BundleImpl_m_Modules_Field.get(bundle); + currentModuleImpl = moduleArray.get(moduleArray.size() - 1); + } + catch (Exception e) + { + LOG.warn("Unable to get field {}", Felix_BundleImpl_m_Modules_Field, e); + return null; + } + } + + if (Felix_ModuleImpl_m_ClassLoader_Field == null && currentModuleImpl != null) + { + String felixFrameworkModuleImplClassName = "org.apache.felix.framework.ModuleImpl"; + String felixFrameworkModuleImplClassLoaderField = "m_classLoader"; + try + { + Felix_ModuleImpl_m_ClassLoader_Field = bundle.getClass().getClassLoader().loadClass(felixFrameworkModuleImplClassName).getDeclaredField(felixFrameworkModuleImplClassLoaderField); + Felix_ModuleImpl_m_ClassLoader_Field.setAccessible(true); + } + catch (Exception e) + { + LOG.warn("Unable to find field {}.{}", felixFrameworkModuleImplClassName, felixFrameworkModuleImplClassLoaderField, e); + return null; + } + } + + // first make sure that the classloader is ready: + // the m_classLoader field must be initialized by the + // ModuleImpl.getClassLoader() private method. + ClassLoader cl = null; + try + { + cl = (ClassLoader)Felix_ModuleImpl_m_ClassLoader_Field.get(currentModuleImpl); + if (cl != null) + return cl; + } + catch (Exception e) + { + LOG.warn("Unable to get field {}", Felix_ModuleImpl_m_ClassLoader_Field, e); + return null; + } + + // looks like it was not ready: + // the m_classLoader field must be initialized by the + // ModuleImpl.getClassLoader() private method. + // this call will do that. + try + { + bundle.loadClass("java.lang.Object"); + cl = (ClassLoader)Felix_ModuleImpl_m_ClassLoader_Field.get(currentModuleImpl); + return cl; + } + catch (Exception e) + { + LOG.warn("Unable to get field {}", Felix_ModuleImpl_m_ClassLoader_Field, e); + return null; + } + } + catch (Exception e) + { + LOG.warn("Unable to load old felix container", e); + return null; + } + } + + LOG.warn("No classloader for felix platform for bundle {}", bundle.getSymbolicName()); + return null; + } + + /** + * + */ + private static ClassLoader internalGetConciergeBundleClassLoader(Bundle bundle) + { + if (osgiContainer == OSGiContainerType.Concierge) + { + try + { + /** + * In Concierge: + * + * Option A: + *
    +                 * Concierge concierge = new Concierge(...);
    +                 * BundleWiring bundleWiring = concierge.getWiring(); // method is public
    +                 * 
    + * Problem: getWiring not yet implementd + * + * Option B: + *
    +                 * Concierge concierge = new Concierge(...);
    +                 * BundleWiring bundleWiring = concierge.adapt(org.osgi.framework.wiring.BundleWiring);
    +                 * 
    + * Same approach as done in Felix. + * + */ + if (Concierge_BundleWiring_Class == null) + { + Concierge_BundleWiring_Class = bundle.getClass().getClassLoader().loadClass("org.osgi.framework.wiring.BundleWiring"); + Concierge_BundleImpl_Adapt_Method = Concierge_BundleImpl_Class.getMethod("adapt", new Class[]{Class.class}); + Concierge_BundleImpl_Adapt_Method.setAccessible(true); + Concierge_BundleWiring_getClassLoader_Method = Concierge_BundleWiring_Class.getMethod("getClassLoader"); + Concierge_BundleWiring_getClassLoader_Method.setAccessible(true); + } + + Object wiring = Concierge_BundleImpl_Adapt_Method.invoke(bundle, new Object[]{Concierge_BundleWiring_Class}); + ClassLoader cl = (ClassLoader)Concierge_BundleWiring_getClassLoader_Method.invoke(wiring); + return cl; + } + catch (Exception e) + { + LOG.warn("Unable to load Concierge platform", e); + return null; + } + } + + LOG.warn("No classloader for Concierge platform for bundle {}", bundle.getSymbolicName()); + return null; + } +} diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/internal/DefaultFileLocatorHelper.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/internal/DefaultFileLocatorHelper.java new file mode 100644 index 00000000000..6697ff166c4 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/internal/DefaultFileLocatorHelper.java @@ -0,0 +1,385 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.osgi.boot.utils.internal; + +import java.io.File; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.net.URI; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLDecoder; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.zip.ZipFile; + +import org.eclipse.jetty.ee10.osgi.boot.utils.BundleFileLocatorHelper; +import org.eclipse.jetty.util.URIUtil; +import org.eclipse.jetty.util.resource.PathResource; +import org.eclipse.jetty.util.resource.Resource; +import org.osgi.framework.Bundle; + +/** + * DefaultFileLocatorHelper + *

    + * From a bundle to its location on the filesystem. Assumes the bundle is not a + * jar. + */ +public class DefaultFileLocatorHelper implements BundleFileLocatorHelper +{ + + // hack to locate the file-system directly from the bundle. + // support equinox, felix and nuxeo's osgi implementations. + // not tested on nuxeo and felix just yet. + // The url nuxeo and felix return is created directly from the File so it + // should work. + private static Field BUNDLE_ENTRY_FIELD = null; + + private static Field FILE_FIELD = null; + + private static Field BUNDLE_FILE_FIELD_FOR_DIR_ZIP_BUNDLE_ENTRY = null; // ZipBundleFile + + // inside + // DirZipBundleEntry + + private static Field ZIP_FILE_FILED_FOR_ZIP_BUNDLE_FILE = null; // ZipFile + + private static final String[] FILE_BUNDLE_ENTRY_CLASSES = { + "org.eclipse.osgi.baseadaptor.bundlefile.FileBundleEntry", "org.eclipse.osgi.storage.bundlefile.FileBundleEntry" + }; + private static final String[] ZIP_BUNDLE_ENTRY_CLASSES = { + "org.eclipse.osgi.baseadaptor.bundlefile.ZipBundleEntry", "org.eclipse.osgi.storage.bundlefile.ZipBundleEntry" + }; + private static final String[] DIR_ZIP_BUNDLE_ENTRY_CLASSES = { + "org.eclipse.osgi.baseadaptor.bundlefile.DirZipBundleEntry", "org.eclipse.osgi.storage.bundlefile.DirZipBundleEntry" + }; + private static final String[] BUNDLE_URL_CONNECTION_CLASSES = { + "org.eclipse.osgi.framework.internal.core.BundleURLConnection", "org.eclipse.osgi.storage.url.BundleURLConnection" + }; + + public static boolean match(String name, String... names) + { + if (name == null || names == null) + return false; + boolean matched = false; + for (int i = 0; i < names.length && !matched; i++) + { + if (name.equals(names[i])) + matched = true; + } + return matched; + } + + /** + * Works with equinox, felix, nuxeo and probably more. Not exactly in the + * spirit of OSGi but quite necessary to support self-contained webapps and + * other situations. + * + * @param bundle The bundle + * @return Its installation location as a file. + * @throws Exception if unable to get the bundle install location + */ + @SuppressWarnings("resource") + public File getBundleInstallLocation(Bundle bundle) throws Exception + { + // String installedBundles = System.getProperty("osgi.bundles"); + // grab the MANIFEST.MF's url + // and then do what it takes. + URL url = bundle.getEntry("/META-INF/MANIFEST.MF"); + + if (url.getProtocol().equals("file")) + { + // some osgi frameworks do use the file protocol directly in some + // situations. Do use the PathResource to transform the URL into a + // File: URL#toURI is broken + return new PathResource(url).getFile().getParentFile().getParentFile().getCanonicalFile(); + } + else if (url.getProtocol().equals("bundleentry")) + { + // say hello to equinox who has its own protocol. + // we use introspection like there is no tomorrow to get access to + // the File + + URLConnection con = url.openConnection(); + con.setUseCaches(Resource.getDefaultUseCaches()); // work around + // problems where + // url connections + // cache + // references to + // jars + + if (BUNDLE_ENTRY_FIELD == null) + { + BUNDLE_ENTRY_FIELD = con.getClass().getDeclaredField("bundleEntry"); + BUNDLE_ENTRY_FIELD.setAccessible(true); + } + Object bundleEntry = BUNDLE_ENTRY_FIELD.get(con); + + if (match(bundleEntry.getClass().getName(), FILE_BUNDLE_ENTRY_CLASSES)) + { + if (FILE_FIELD == null) + { + FILE_FIELD = bundleEntry.getClass().getDeclaredField("file"); + FILE_FIELD.setAccessible(true); + } + File f = (File)FILE_FIELD.get(bundleEntry); + return f.getParentFile().getParentFile().getCanonicalFile(); + } + else if (match(bundleEntry.getClass().getName(), ZIP_BUNDLE_ENTRY_CLASSES)) + { + url = bundle.getEntry("/"); + + con = url.openConnection(); + con.setDefaultUseCaches(Resource.getDefaultUseCaches()); + + if (BUNDLE_ENTRY_FIELD == null) + { + // this one will be a DirZipBundleEntry + BUNDLE_ENTRY_FIELD = con.getClass().getDeclaredField("bundleEntry"); + BUNDLE_ENTRY_FIELD.setAccessible(true); + } + bundleEntry = BUNDLE_ENTRY_FIELD.get(con); + if (BUNDLE_FILE_FIELD_FOR_DIR_ZIP_BUNDLE_ENTRY == null) + { + BUNDLE_FILE_FIELD_FOR_DIR_ZIP_BUNDLE_ENTRY = bundleEntry.getClass().getDeclaredField("bundleFile"); + BUNDLE_FILE_FIELD_FOR_DIR_ZIP_BUNDLE_ENTRY.setAccessible(true); + } + Object zipBundleFile = BUNDLE_FILE_FIELD_FOR_DIR_ZIP_BUNDLE_ENTRY.get(bundleEntry); + if (ZIP_FILE_FILED_FOR_ZIP_BUNDLE_FILE == null) + { + ZIP_FILE_FILED_FOR_ZIP_BUNDLE_FILE = zipBundleFile.getClass().getDeclaredField("zipFile"); + ZIP_FILE_FILED_FOR_ZIP_BUNDLE_FILE.setAccessible(true); + } + ZipFile zipFile = (ZipFile)ZIP_FILE_FILED_FOR_ZIP_BUNDLE_FILE.get(zipBundleFile); + return new File(zipFile.getName()); + } + else if (match(bundleEntry.getClass().getName(), DIR_ZIP_BUNDLE_ENTRY_CLASSES)) + { + // that will not happen as we did ask for the manifest not a + // directory. + } + } + else if ("bundle".equals(url.getProtocol())) + { + // observed this on felix-2.0.0 + String location = bundle.getLocation(); + if (location.startsWith("file:/")) + { + URI uri = new URI(URIUtil.encodePath(location)); + return new File(uri).getCanonicalFile(); + } + else if (location.startsWith("file:")) + { + // location defined in the BundleArchive m_bundleArchive + // it is relative to relative to the BundleArchive's + // m_archiveRootDir + File res = new File(location.substring("file:".length())); + if (!res.exists()) + { + return null; + // Object bundleArchive = getFelixBundleArchive(bundle); + // File archiveRoot = + // getFelixBundleArchiveRootDir(bundleArchive); + // String currentLocation = + // getFelixBundleArchiveCurrentLocation(bundleArchive); + // System.err.println("Got the archive root " + + // archiveRoot.getAbsolutePath() + // + " current location " + currentLocation + + // " is directory ?"); + // res = new File(archiveRoot, currentLocation != null + // ? currentLocation : location.substring("file:".length())); + } + return res; + } + else if (location.startsWith("reference:file:")) + { + location = URLDecoder.decode(location.substring("reference:".length()), "UTF-8"); + File file = new File(location.substring("file:".length())).getCanonicalFile(); + return file; + } + } + return null; + } + + /** + * Locate a file inside a bundle. + * + * @param bundle the bundle + * @param path the path + * @return file object + * @throws Exception if unable to get the file in the bundle + */ + @Override + public File getFileInBundle(Bundle bundle, String path) throws Exception + { + if (path != null && path.length() > 0 && path.charAt(0) == '/') + { + path = path.substring(1); + } + File bundleInstall = getBundleInstallLocation(bundle); + File webapp = path != null && path.length() != 0 ? new File(bundleInstall, path) : bundleInstall; + if (!webapp.exists()) + { + throw new IllegalArgumentException("Unable to locate " + path + " inside " + bundle.getSymbolicName() + + " (" + (bundleInstall != null ? bundleInstall.getAbsolutePath() : " no_bundle_location ") + ")"); + } + return webapp; + } + + /** + * Helper method equivalent to Bundle#getEntry(String entryPath) except that + * it searches for entries in the fragments by using the Bundle#findEntries + * method. + * + * @param bundle the bundle + * @param entryPath the entry path + * @return null or all the entries found for that path. + */ + @Override + public Enumeration findEntries(Bundle bundle, String entryPath) + { + int last = entryPath.lastIndexOf('/'); + String path = last != -1 && last < entryPath.length() - 2 ? entryPath.substring(0, last) : "/"; + if (!path.startsWith("/")) + { + path = "/" + path; + } + String pattern = last != -1 && last < entryPath.length() - 2 ? entryPath.substring(last + 1) : entryPath; + @SuppressWarnings("unchecked") + Enumeration enUrls = bundle.findEntries(path, pattern, false); + return enUrls; + } + + /** + * If the bundle is a jar, returns the jar. If the bundle is a folder, look + * inside it and search for jars that it returns. + *

    + * Good enough for our purpose (TldLocationsCache when it scans for tld + * files inside jars alone. In fact we only support the second situation for + * development purpose where the bundle was imported in pde and the classes + * kept in a jar. + *

    + * + * @param bundle the bundle + * @return The jar(s) file that is either the bundle itself, either the jars + * embedded inside it. + */ + @Override + public File[] locateJarsInsideBundle(Bundle bundle) throws Exception + { + File jasperLocation = getBundleInstallLocation(bundle); + if (jasperLocation.isDirectory()) + { + // try to find the jar files inside this folder + ArrayList urls = new ArrayList<>(); + for (File f : jasperLocation.listFiles()) + { + if (f.getName().endsWith(".jar") && f.isFile()) + { + urls.add(f); + } + else if (f.isDirectory() && f.getName().equals("lib")) + { + for (File f2 : jasperLocation.listFiles()) + { + if (f2.getName().endsWith(".jar") && f2.isFile()) + { + urls.add(f2); + } + } + } + } + return urls.toArray(new File[urls.size()]); + } + else + { + return new File[]{jasperLocation}; + } + } + + // introspection on equinox to invoke the getLocalURL method on + // BundleURLConnection + // equivalent to using the FileLocator without depending on an equinox + // class. + private static Method BUNDLE_URL_CONNECTION_getLocalURL = null; + + private static Method BUNDLE_URL_CONNECTION_getFileURL = null; + + /** + * Only useful for equinox: on felix we get the file:// or jar:// url + * already. Other OSGi implementations have not been tested + *

    + * Get a URL to the bundle entry that uses a common protocol (i.e. file: + * jar: or http: etc.). + *

    + * + * @return a URL to the bundle entry that uses a common protocol + */ + @Override + public URL getLocalURL(URL url) + throws Exception + { + if ("bundleresource".equals(url.getProtocol()) || "bundleentry".equals(url.getProtocol())) + { + + URLConnection conn = url.openConnection(); + conn.setDefaultUseCaches(Resource.getDefaultUseCaches()); + if (BUNDLE_URL_CONNECTION_getLocalURL == null && match(conn.getClass().getName(), BUNDLE_URL_CONNECTION_CLASSES)) + { + BUNDLE_URL_CONNECTION_getLocalURL = conn.getClass().getMethod("getLocalURL"); + BUNDLE_URL_CONNECTION_getLocalURL.setAccessible(true); + } + if (BUNDLE_URL_CONNECTION_getLocalURL != null) + { + return (URL)BUNDLE_URL_CONNECTION_getLocalURL.invoke(conn); + } + } + return url; + } + + /** + * Only useful for equinox: on felix we get the file:// url already. Other + * OSGi implementations have not been tested + *

    + * Get a URL to the content of the bundle entry that uses the file: + * protocol. The content of the bundle entry may be downloaded or extracted + * to the local file system in order to create a file: URL. + *

    + * + * @return a URL to the content of the bundle entry that uses the file: + * protocol + * @throws Exception if unable to get the file url + */ + @Override + public URL getFileURL(URL url) throws Exception + + { + if ("bundleresource".equals(url.getProtocol()) || "bundleentry".equals(url.getProtocol())) + { + + URLConnection conn = url.openConnection(); + conn.setDefaultUseCaches(Resource.getDefaultUseCaches()); + if (BUNDLE_URL_CONNECTION_getFileURL == null && match(conn.getClass().getName(), BUNDLE_URL_CONNECTION_CLASSES)) + { + BUNDLE_URL_CONNECTION_getFileURL = conn.getClass().getMethod("getFileURL"); + BUNDLE_URL_CONNECTION_getFileURL.setAccessible(true); + } + if (BUNDLE_URL_CONNECTION_getFileURL != null) + { + return (URL)BUNDLE_URL_CONNECTION_getFileURL.invoke(conn); + } + } + return url; + } +} diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/internal/PackageAdminServiceTracker.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/internal/PackageAdminServiceTracker.java new file mode 100644 index 00000000000..d7a31cdd394 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/utils/internal/PackageAdminServiceTracker.java @@ -0,0 +1,391 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.osgi.boot.utils.internal; + +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.InvalidSyntaxException; +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; + +/** + * 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-ee10-osgi-boot-jsp fragment bundle that uses this + * facility. + */ +public class PackageAdminServiceTracker implements ServiceListener +{ + 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) + { + INSTANCE = this; + _context = context; + if (!setup()) + { + try + { + _context.addServiceListener(this, "(objectclass=" + PackageAdmin.class.getName() + ")"); + } + catch (InvalidSyntaxException e) + { + e.printStackTrace(); // won't happen + } + } + } + + /** + * @return true if the fragments were activated by this method. + */ + private boolean setup() + { + 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) + { + invokeFragmentActivators(event.getServiceReference()); + } + } + + /** + * 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) + { + 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. + try + { + 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); + } + } + catch (Exception e) + { + e.printStackTrace(); + } + } + } + + 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-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/resources/META-INF/services/org.eclipse.jetty.ee10.webapp.Configuration b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/resources/META-INF/services/org.eclipse.jetty.ee10.webapp.Configuration new file mode 100644 index 00000000000..a9508c57a3d --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/resources/META-INF/services/org.eclipse.jetty.ee10.webapp.Configuration @@ -0,0 +1,2 @@ +org.eclipse.jetty.ee10.osgi.annotations.AnnotationConfiguration +org.eclipse.jetty.ee10.osgi.boot.OSGiWebInfConfiguration diff --git a/jetty-ee10/jetty-ee10-osgi/pom.xml b/jetty-ee10/jetty-ee10-osgi/pom.xml new file mode 100644 index 00000000000..b2a3edd7aad --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/pom.xml @@ -0,0 +1,134 @@ + + + org.eclipse.jetty.ee10 + jetty-ee10 + 12.0.0-SNAPSHOT + + + 4.0.0 + org.eclipse.jetty.ee10.osgi + jetty-ee10-osgi-project + EE10 :: Jetty :: OSGi + pom + + + 3.17.200 + 3.10.200 + 3.6.100 + 1.0.0-v20070606 + + + + jetty-ee10-osgi-boot + jetty-ee10-osgi-boot-jsp + jetty-ee10-osgi-boot-warurl + test-jetty-ee10-osgi-webapp + test-jetty-ee10-osgi-webapp-resources + test-jetty-ee10-osgi-context + test-jetty-ee10-osgi-fragment + test-jetty-ee10-osgi-server + jetty-ee10-osgi-alpn + test-jetty-ee10-osgi + + + + + + META-INF/.. + true + + **/.* + **/*.jar + .settings/**/* + pom.xml + + jettyhome/**/* + src/**/* + target/**/* + build.properties + + + META-INF/**/* + + + + src/main/java + + **/*.java + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + set-servlet-api-version + validate + + parse-version + + + ${jetty.servlet.api.version} + servletImpl + + + + + + org.apache.maven.plugins + maven-eclipse-plugin + + prevent/overwriting/by/pointing/to/nonexisting/MANIFEST.MF + true + true + + **/.svn/** + + + + + org.jacoco + jacoco-maven-plugin + + true + + + + + + + + + org.eclipse.platform + org.eclipse.osgi.services + ${osgi-services-version} + + + + jakarta.servlet + servlet-api + + + + org.apache.felix + org.osgi.foundation + + + + + org.eclipse.platform + org.eclipse.osgi + ${osgi-version} + + + org.eclipse.equinox.http + servlet + ${equinox-http-servlet-version} + + + + + diff --git a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-context/pom.xml b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-context/pom.xml new file mode 100644 index 00000000000..9005d3ff08f --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-context/pom.xml @@ -0,0 +1,97 @@ + + + org.eclipse.jetty.ee10.osgi + jetty-ee10-osgi-project + 12.0.0-SNAPSHOT + + 4.0.0 + test-jetty-ee10-osgi-context + EE10 :: Jetty :: OSGi :: Test Context + Test Jetty OSGi bundle with a ContextHandler + + ${project.groupId}.testcontext + + + + org.eclipse.jetty + jetty-server + + + org.eclipse.jetty.toolchain + jetty-jakarta-servlet-api + + + org.eclipse.platform + org.eclipse.osgi + provided + + + org.eclipse.platform + org.eclipse.osgi.services + provided + + + + + + + src/main/resources + + + src/main/context + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + true + + + + + + + org.apache.maven.plugins + maven-deploy-plugin + + + true + + + + org.apache.felix + maven-bundle-plugin + true + + + org.eclipse.jetty.ee10.osgi.testcontext;singleton:=true + Jetty OSGi Test Context + com.acme.osgi.Activator + J2SE-1.5 + + <_nouses>true + + jakarta.servlet;version="[$(version;==;${servletImpl.osgiVersion}),$(version;+;${servletImpl.osgiVersion}))", + jakarta.servlet.resources;version="[$(version;==;${servletImpl.osgiVersion}),$(version;+;${servletImpl.osgiVersion}))", + 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.eclipse.jetty.*;version="[$(version;==;${parsedVersion.osgiVersion}),$(version;+;${parsedVersion.osgiVersion}))" + + + + + + + diff --git a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-context/src/main/context/acme.xml b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-context/src/main/context/acme.xml new file mode 100644 index 00000000000..f2052300efb --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-context/src/main/context/acme.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + /static/ + + + + /unset + + + + + + + + + + + index.html + + + max-age=3600,public + + + + + diff --git a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-context/src/main/java/com/acme/osgi/Activator.java b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-context/src/main/java/com/acme/osgi/Activator.java new file mode 100644 index 00000000000..6639fd4211d --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-context/src/main/java/com/acme/osgi/Activator.java @@ -0,0 +1,72 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.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 jakarta.servlet.ServletContextEvent; +import jakarta.servlet.ServletContextListener; +import org.eclipse.jetty.server.handler.ContextHandler; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceRegistration; + +/** + * Bootstrap a ContextHandler + */ +public class Activator implements BundleActivator +{ + + private ServiceRegistration _sr; + + /** + * + */ + @Override + public void start(final BundleContext context) throws Exception + { + ContextHandler ch = new ContextHandler(); + ch.addEventListener(new ServletContextListener() + { + + @Override + public void contextInitialized(ServletContextEvent sce) + { + //System.err.println("Context is initialized"); + } + + @Override + public void contextDestroyed(ServletContextEvent sce) + { + //System.err.println("CONTEXT IS DESTROYED!"); + } + }); + Dictionary props = new Hashtable(); + props.put("Web-ContextPath", "/acme"); + props.put("Jetty-ContextFilePath", "acme.xml"); + _sr = context.registerService(ContextHandler.class.getName(), ch, props); + } + + /** + * 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-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-context/src/main/resources/static/index.html b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-context/src/main/resources/static/index.html new file mode 100644 index 00000000000..3189646adce --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-context/src/main/resources/static/index.html @@ -0,0 +1,6 @@ + + +

    Test OSGi Context

    +

    ContextHandler registered as a service successfully deployed.

    + + 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 new file mode 100644 index 00000000000..28abf67c842 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-fragment/pom.xml @@ -0,0 +1,59 @@ + + + org.eclipse.jetty.ee10.osgi + jetty-ee10-osgi-project + 12.0.0-SNAPSHOT + ../pom.xml + + 4.0.0 + test-jetty-ee10-osgi-fragment + EE10 :: Jetty :: OSGi :: WebApp Fragment + Test Jetty OSGi Webapp Fragment bundle + + ${project.groupId}.webapp.fragment + + + + + src/main/resources + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + true + + + + + + + org.apache.maven.plugins + maven-deploy-plugin + + + true + + + + org.apache.felix + maven-bundle-plugin + true + + + ${bundle-symbolic-name} + Jetty OSGi Test WebApp Fragment + J2SE-1.5 + org.eclipse.jetty.demos.spec.webapp + / + + + + + + + diff --git a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-fragment/src/main/resources/frag.html b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-fragment/src/main/resources/frag.html new file mode 100644 index 00000000000..9dd6187f75e --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-fragment/src/main/resources/frag.html @@ -0,0 +1,5 @@ + + +

    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 new file mode 100644 index 00000000000..c6e1ac6a9c8 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-server/pom.xml @@ -0,0 +1,85 @@ + + + org.eclipse.jetty.ee10.osgi + jetty-ee10-osgi-project + 12.0.0-SNAPSHOT + + 4.0.0 + test-jetty-ee10-osgi-server + EE10 :: Jetty :: OSGi :: Server + Test Jetty OSGi bundle with a Server + + ${project.groupId}.testserver + + + + org.eclipse.jetty.ee10 + jetty-ee10-webapp + + + org.eclipse.platform + org.eclipse.osgi + provided + + + org.eclipse.platform + org.eclipse.osgi.services + provided + + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + true + + + + + + + org.apache.maven.plugins + maven-deploy-plugin + + + true + + + + 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}))" + + + + + + + diff --git a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-server/src/main/java/com/acme/osgi/Activator.java b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-server/src/main/java/com/acme/osgi/Activator.java new file mode 100644 index 00000000000..e9e43213a59 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-server/src/main/java/com/acme/osgi/Activator.java @@ -0,0 +1,83 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.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.ee10.osgi.boot.OSGiWebInfConfiguration", + "org.eclipse.jetty.ee10.webapp.WebXmlConfiguration", + "org.eclipse.jetty.ee10.webapp.MetaInfConfiguration", + "org.eclipse.jetty.ee10.webapp.FragmentConfiguration", + "org.eclipse.jetty.ee10.webapp.JettyWebXmlConfiguration" + }; + server.setAttribute("org.eclipse.jetty.ee10.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-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-server/src/main/resources/index.html b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-server/src/main/resources/index.html new file mode 100644 index 00000000000..9e62c04bb91 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-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-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 new file mode 100644 index 00000000000..f75b8cd536c --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-webapp-resources/pom.xml @@ -0,0 +1,90 @@ + + + + org.eclipse.jetty.ee10.osgi + jetty-ee10-osgi-project + 12.0.0-SNAPSHOT + + 4.0.0 + test-jetty-ee10-osgi-webapp-resources + OSGi Test :: Webapp With Resources + war + + ${project.groupId}.webapp.resources + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + true + + + + + + + org.apache.maven.plugins + maven-resources-plugin + + + copy-resources + validate + + copy-resources + + + ${basedir}/target/classes + + + src/main/resources + + + + + + + + org.apache.felix + maven-bundle-plugin + true + + + war + + + !com.acme* + / + + + + + + maven-war-plugin + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + org.apache.maven.plugins + maven-deploy-plugin + + + false + + + + + + + org.eclipse.jetty.toolchain + jetty-jakarta-servlet-api + provided + + + diff --git a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-webapp-resources/src/main/java/com/acme/HelloWorld.java b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-webapp-resources/src/main/java/com/acme/HelloWorld.java new file mode 100644 index 00000000000..8c8bef2f04e --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-webapp-resources/src/main/java/com/acme/HelloWorld.java @@ -0,0 +1,62 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.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-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-webapp-resources/src/main/resources/fake.properties b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-webapp-resources/src/main/resources/fake.properties new file mode 100644 index 00000000000..e69de29bb2d diff --git a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-webapp-resources/src/main/webapp/WEB-INF/web.xml b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-webapp-resources/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000000..90efb1dceac --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-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-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-webapp/pom.xml b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-webapp/pom.xml new file mode 100644 index 00000000000..d2a3d575e1c --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-webapp/pom.xml @@ -0,0 +1,79 @@ + + + org.eclipse.jetty.ee10.osgi + jetty-ee10-osgi-project + 12.0.0-SNAPSHOT + ../pom.xml + + 4.0.0 + test-jetty-ee10-osgi-webapp + EE10 :: Jetty :: OSGi :: Test WebApp + Test Jetty OSGi Webapp bundle + + ${project.groupId}.webapp + + + + org.eclipse.jetty.ee10 + jetty-ee10-webapp + + + org.eclipse.platform + org.eclipse.osgi + provided + + + org.eclipse.platform + org.eclipse.osgi.services + provided + + + + + + + src/main/resources + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + true + + + + + + + org.apache.maven.plugins + maven-deploy-plugin + + + true + + + + org.apache.felix + maven-bundle-plugin + true + + + org.eclipse.jetty.ee10.osgi.testapp;singleton:=true + Jetty OSGi Test WebApp + com.acme.osgi.Activator + J2SE-1.5 + + com.acme.osgi + org.eclipse.jetty.*;version="[$(version;===;${parsedVersion.osgiVersion}),$(version;==+;${parsedVersion.osgiVersion}))" + + + + + + diff --git a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-webapp/src/main/java/com/acme/osgi/Activator.java b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-webapp/src/main/java/com/acme/osgi/Activator.java new file mode 100644 index 00000000000..5f1e532f083 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-webapp/src/main/java/com/acme/osgi/Activator.java @@ -0,0 +1,91 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.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.io.IOException; +import java.io.PrintWriter; +import java.util.Dictionary; +import java.util.Hashtable; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.eclipse.jetty.ee10.servlet.ServletHolder; +import org.eclipse.jetty.ee10.webapp.WebAppContext; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceRegistration; + +/** + * Bootstrap a webapp + */ +public class Activator implements BundleActivator +{ + + private ServiceRegistration _srA; + private ServiceRegistration _srB; + + public static class TestServlet extends HttpServlet + { + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException + { + //report the mimetype of a file + String mimetype = req.getServletContext().getMimeType("file.gz"); + resp.setContentType("text/html"); + PrintWriter writer = resp.getWriter(); + writer.write("

    MIMETYPE=" + mimetype + "

    "); + writer.flush(); + } + } + + /** + * + */ + @Override + public void start(BundleContext context) throws Exception + { + //Create webappA as a Service and target it at the default server + WebAppContext webapp = new WebAppContext(); + webapp.addServlet(new ServletHolder(new TestServlet()), "/mime"); + Dictionary props = new Hashtable(); + props.put("Jetty-WarResourcePath", "webappA"); + props.put("Web-ContextPath", "/acme"); + props.put("managedServerName", "defaultJettyServer"); + _srA = context.registerService(WebAppContext.class.getName(), webapp, props); + + //Create a second webappB as a Service and target it at a custom Server + //deployed by another bundle + final WebAppContext webappB = new WebAppContext(); + Dictionary propsB = new Hashtable(); + propsB.put("Jetty-WarResourcePath", "webappB"); + propsB.put("Web-ContextPath", "/acme"); + propsB.put("managedServerName", "fooServer"); + _srB = context.registerService(WebAppContext.class.getName(), webappB, propsB); + } + + /** + * Stop the activator. + * + * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext) + */ + @Override + public void stop(BundleContext context) throws Exception + { + _srA.unregister(); + _srB.unregister(); + } +} diff --git a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-webapp/src/main/resources/index.html b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-webapp/src/main/resources/index.html new file mode 100644 index 00000000000..9e62c04bb91 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-webapp/src/main/resources/index.html @@ -0,0 +1,6 @@ + + +

    Test OSGi WebApp

    +

    Webapp registered by bundle as service successfully deployed.

    + + diff --git a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-webapp/src/main/resources/webappA/index.html b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-webapp/src/main/resources/webappA/index.html new file mode 100644 index 00000000000..bc4c64faa54 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-webapp/src/main/resources/webappA/index.html @@ -0,0 +1,6 @@ + + +

    Test OSGi WebAppA

    +

    Webapp registered by bundle as service successfully deployed.

    + + diff --git a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-webapp/src/main/resources/webappB/index.html b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-webapp/src/main/resources/webappB/index.html new file mode 100644 index 00000000000..4c6a3ae9b22 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi-webapp/src/main/resources/webappB/index.html @@ -0,0 +1,6 @@ + + +

    Test OSGi WebAppB

    +

    Webapp registered by bundle as service successfully deployed.

    + + diff --git a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/README.txt b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/README.txt new file mode 100644 index 00000000000..c8b7d9a43ca --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-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-ee10-osgi infrastructure (like jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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.ee10.websocket.api file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee10-osgi/test-jetty-ee10-osgi/target/1584628869418-0/pax-exam-downloads/org.eclipse.jetty.ee10.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-ee10-osgi/test-jetty-ee10-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.ee10.websocket.client file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee10-osgi/test-jetty-ee10-osgi/target/1584628869418-0/pax-exam-downloads/org.eclipse.jetty.ee10.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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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.ee10.osgi.boot file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee10-osgi/test-jetty-ee10-osgi/target/1584628869418-0/pax-exam-downloads/org.eclipse.jetty.ee10.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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/test-jetty-ee10-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.ee10.osgi.boot.jsp file:/home/janb/src/jetty-eclipse/jetty-10.0.x/jetty-ee10-osgi/test-jetty-ee10-osgi/target/1584628869418-0/pax-exam-downloads/org.eclipse.jetty.ee10.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-ee10-osgi/test-jetty-ee10-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-ee10-osgi/jetty-ee10-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.ee10.osgi.boot.internal.serverfactory.ServerInstanceWrapper.configure(ServerInstanceWrapper.java:143) +at org.eclipse.jetty.ee10.osgi.boot.internal.serverfactory.DefaultJettyAtJettyHomeHelper.startJettyAtJettyHome(DefaultJettyAtJettyHomeHelper.java:211) +at org.eclipse.jetty.ee10.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-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/pom.xml b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/pom.xml new file mode 100644 index 00000000000..170e89c1b84 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/pom.xml @@ -0,0 +1,616 @@ + + + org.eclipse.jetty.ee10.osgi + jetty-ee10-osgi-project + 12.0.0-SNAPSHOT + ../pom.xml + + 4.0.0 + test-jetty-ee10-osgi + EE10 :: Jetty :: OSGi :: Test + Jetty OSGi Integration test + + ${project.groupId}.boot.test.osgi + https://download.eclipse.org/jetty/orbit/ + target/distribution + 4.13.1 + 2.6.2 + 1.8.3 + 3.0.0 + 1.3.4 + + true + + + + + org.ops4j.pax.exam + pax-exam + ${pax.exam.version} + test + + + org.ops4j.pax.exam + pax-exam-inject + ${pax.exam.version} + test + + + + org.ops4j.pax.exam + pax-exam-container-forked + ${pax.exam.version} + test + + + biz.aQute.bnd + bndlib + + + org.ops4j.pax.tinybundles + tinybundles + + + + + org.ops4j.pax.tinybundles + tinybundles + ${tinybundles.version} + + + org.ops4j.pax.swissbox + pax-swissbox-framework + ${swissbox.version} + test + + + org.ops4j.base + ops4j-base-monitors + + + + + org.ops4j.pax.swissbox + pax-swissbox-tracker + ${swissbox.version} + test + + + org.ops4j.pax.exam + pax-exam-junit4 + ${pax.exam.version} + test + + + org.ops4j.pax.exam + pax-exam-link-mvn + ${pax.exam.version} + test + + + org.ops4j.pax.url + pax-url-aether + ${pax.url.version} + test + + + org.ops4j.pax.url + pax-url-wrap + ${pax.url.version} + test + + + biz.aQute.bnd + bndlib + + + + + biz.aQute.bnd + biz.aQute.bndlib + + + org.osgi + org.osgi.core + + + + + org.eclipse.platform + org.eclipse.osgi + test + + + org.eclipse.platform + org.eclipse.osgi.services + test + + + org.eclipse.platform + org.eclipse.osgi.util + ${osgi-util-version} + test + + + org.apache.geronimo.specs + geronimo-atinject_1.0_spec + 1.2 + test + + + + org.slf4j + slf4j-api + test + + + org.slf4j + slf4j-simple + test + + + org.eclipse.jetty + jetty-slf4j-impl + test + + + org.eclipse.jetty.ee10.osgi + jetty-ee10-osgi-boot + ${project.version} + test + + + org.eclipse.platform + org.eclipse.osgi + + + org.eclipse.platform + org.eclipse.osgi.services + + + + + org.eclipse.jetty.ee10.osgi + jetty-ee10-osgi-boot-jsp + ${project.version} + test + + + org.eclipse.platform + org.eclipse.osgi + + + org.eclipse.platform + org.eclipse.osgi.services + + + + + org.eclipse.jetty.toolchain + jetty-jakarta-servlet-api + + + jakarta.inject + jakarta.inject-api + test + + + jakarta.transaction + jakarta.transaction-api + test + + + jakarta.interceptor + jakarta.interceptor-api + test + + + jakarta.enterprise + jakarta.enterprise.cdi-api + test + + + jakarta.el + jakarta.el-api + test + + + org.apache.aries.spifly + org.apache.aries.spifly.dynamic.bundle + ${spifly.version} + test + + + org.apache.felix + org.apache.felix.framework + + + + + jakarta.activation + jakarta.activation-api + test + + + org.mortbay.jasper + apache-jsp + + + org.mortbay.jasper + apache-el + + + jakarta.servlet.jsp.jstl + jakarta.servlet.jsp.jstl-api + + + jakarta.el + jakarta.el-api + + + + + org.glassfish.web + jakarta.servlet.jsp.jstl + + + + org.eclipse.jetty.ee10 + jetty-ee10-annotations + runtime + + + org.eclipse.jetty.ee10 + jetty-ee10-webapp + runtime + + + org.eclipse.jetty + jetty-deploy + runtime + + + org.eclipse.jetty + jetty-server + runtime + + + org.eclipse.jetty.ee10 + jetty-ee10-servlet + runtime + + + org.eclipse.jetty.ee10 + jetty-ee10-servlets + runtime + + + org.eclipse.jetty + jetty-xml + runtime + + + org.eclipse.jetty + jetty-jmx + runtime + + + org.eclipse.jetty + jetty-util + runtime + + + org.eclipse.jetty + jetty-client + runtime + + + org.eclipse.jetty.ee10 + jetty-ee10-websocket-jetty-api + runtime + + + org.eclipse.jetty.ee10 + jetty-ee10-websocket-jetty-common + runtime + + + org.eclipse.jetty.ee10 + jetty-ee10-websocket-jetty-client + runtime + + + org.eclipse.jetty.ee10 + jetty-ee10-websocket-jakarta-client + runtime + + + org.eclipse.jetty.ee10 + jetty-ee10-websocket-servlet + runtime + + + org.eclipse.jetty.ee10 + jetty-ee10-websocket-jetty-server + runtime + + + org.eclipse.jetty.toolchain + jetty-jakarta-websocket-api + runtime + + + org.eclipse.jetty.ee10 + jetty-ee10-websocket-jakarta-server + runtime + + + org.eclipse.jetty.http2 + http2-server + + + org.eclipse.jetty.http2 + http2-hpack + + + org.eclipse.jetty.ee10.osgi + jetty-ee10-osgi-alpn + ${project.version} + test + + + org.eclipse.jetty + jetty-alpn-server + ${project.version} + test + + + org.eclipse.jetty.ee10 + jetty-ee10-plus + runtime + + + + + org.eclipse.jetty.ee10.demos + demo-jsp-webapp + ${project.version} + webbundle + test + + + org.eclipse.jetty.ee10.demos + demo-jetty-webapp + ${project.version} + webbundle + test + + + org.eclipse.jetty.ee10.demos + demo-spec-webapp + ${project.version} + war + test + + + org.eclipse.jetty.ee10.demos + demo-container-initializer + ${project.version} + test + + + org.eclipse.jetty.ee10.osgi + test-jetty-ee10-osgi-webapp-resources + ${project.version} + war + test + + + org.eclipse.jetty.ee10.osgi + test-jetty-ee10-osgi-fragment + ${project.version} + test + + + org.eclipse.jetty.ee10.osgi + test-jetty-ee10-osgi-server + ${project.version} + test + + + org.eclipse.jetty.ee10.demos + demo-mock-resources + ${project.version} + + + org.eclipse.jetty.ee10.osgi + test-jetty-ee10-osgi-context + ${project.version} + test + + + org.eclipse.jetty.ee10.osgi + test-jetty-ee10-osgi-webapp + ${project.version} + test + + + org.eclipse.jetty.toolchain + jetty-test-helper + test + + + org.ow2.asm + asm + test + ${asm.version} + + + org.ow2.asm + asm-commons + test + ${asm.version} + + + org.ow2.asm + asm-tree + test + ${asm.version} + + + org.ow2.asm + asm-analysis + test + ${asm.version} + + + org.ow2.asm + asm-util + test + ${asm.version} + + + org.eclipse.jetty.http2 + http2-client + ${project.version} + test + + + org.eclipse.jetty.http2 + http2-http-client-transport + ${project.version} + test + + + org.eclipse.jetty + jetty-alpn-conscrypt-server + ${project.version} + test + + + org.eclipse.jetty + jetty-alpn-conscrypt-client + ${project.version} + test + + + org.conscrypt + conscrypt-openjdk-uber + ${conscrypt.version} + test + + + org.eclipse.jetty + jetty-alpn-java-server + ${project.version} + test + + + org.eclipse.jetty + jetty-alpn-java-client + ${project.version} + test + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + true + + + + maven-surefire-plugin + + ${skipTests} + + ${settings.localRepository} + ${env.GLOBAL_MVN_SETTINGS} + + -Dconscrypt-version=${conscrypt.version} + + + + + 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 + + + copy + process-test-resources + + copy-dependencies + + + + + test-jetty-ee10-osgi-webapp-resources + target + true + + + + org.apache.servicemix.tooling + depends-maven-plugin + + + generate-depends-file + + generate-depends-file + + + + + + + diff --git a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-alpn.xml b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-alpn.xml new file mode 100644 index 00000000000..22eacf1cf38 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-alpn.xml @@ -0,0 +1,23 @@ + + + + + + + + + alpn + + + + + + + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-deploy.xml b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-deploy.xml new file mode 100644 index 00000000000..fe917c2430e --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-deploy.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern + .*/jsp-api-[^/]*\.jar$|.*/jsp-[^/]*\.jar$ + + + + + + diff --git a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http-boot-context-as-service.xml b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http-boot-context-as-service.xml new file mode 100644 index 00000000000..374a4a02129 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http-boot-context-as-service.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + boot.context.service.port + + + + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http-boot-webapp-as-service.xml b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http-boot-webapp-as-service.xml new file mode 100644 index 00000000000..2bce7ed4b94 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http-boot-webapp-as-service.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + boot.webapp.service.port + + + + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http-boot-with-annotations.xml b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http-boot-with-annotations.xml new file mode 100644 index 00000000000..217496d7d34 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http-boot-with-annotations.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + boot.annotations.port + + + + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http-boot-with-bundle.xml b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http-boot-with-bundle.xml new file mode 100644 index 00000000000..c3d310790ad --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http-boot-with-bundle.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + boot.bundle.port + + + + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http-boot-with-jakarta-websocket.xml b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http-boot-with-jakarta-websocket.xml new file mode 100644 index 00000000000..5b5e7d54cb0 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http-boot-with-jakarta-websocket.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + boot.jakarta.websocket.port + + + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http-boot-with-jsp.xml b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http-boot-with-jsp.xml new file mode 100644 index 00000000000..87ab46d7181 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http-boot-with-jsp.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + boot.jsp.port + + + + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http-boot-with-resources.xml b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http-boot-with-resources.xml new file mode 100644 index 00000000000..7df0151bfdd --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http-boot-with-resources.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + boot.resources.port + + + + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http-boot-with-websocket.xml b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http-boot-with-websocket.xml new file mode 100644 index 00000000000..f5519072d60 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http-boot-with-websocket.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + boot.websocket.port + + + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http.xml b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http.xml new file mode 100644 index 00000000000..98f6d4e56d2 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + foo.foo + + + + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http2-jdk9.xml b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http2-jdk9.xml new file mode 100644 index 00000000000..ad51713fd9c --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http2-jdk9.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + true + + + diff --git a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http2.xml b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http2.xml new file mode 100644 index 00000000000..2bf9d1051b1 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http2.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + true + + + + TLSv1.3 + + + + + + diff --git a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-https.xml b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-https.xml new file mode 100644 index 00000000000..b140e422084 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-https.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + http/1.1 + + + + + + + + boot.https.port + + + + + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-ssl.xml b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-ssl.xml new file mode 100644 index 00000000000..b25525dec5c --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-ssl.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + / + + / + + + + + + + + + + + + + + + + + + + diff --git a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-testrealm.xml b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-testrealm.xml new file mode 100644 index 00000000000..e9ebabc4699 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-testrealm.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + Test Realm + /etc/realm.properties + + + + diff --git a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-with-custom-class.xml b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-with-custom-class.xml new file mode 100644 index 00000000000..420640353c8 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-with-custom-class.xml @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + 10 + 200 + + + + + + + + + + + + + + + + + + + + + + + + + https + + 32768 + 8192 + 8192 + true + false + 512 + + + + + + + true + 1000 + false + false + + + + + + + + + org.eclipse.jetty.ee10.webapp.FragmentConfiguration + org.eclipse.jetty.ee10.webapp.JettyWebXmlConfiguration + org.eclipse.jetty.ee10.webapp.WebXmlConfiguration + org.eclipse.jetty.ee10.webapp.WebAppConfiguration + org.eclipse.jetty.ee10.webapp.ServletsConfiguration + org.eclipse.jetty.ee10.webapp.JspConfiguration + org.eclipse.jetty.ee10.webapp.JaasConfiguration + org.eclipse.jetty.ee10.webapp.JndiConfiguration + org.eclipse.jetty.ee10.plus.webapp.PlusConfiguration + org.eclipse.jetty.ee10.plus.webapp.EnvConfiguration + org.eclipse.jetty.ee10.webapp.JmxConfiguration + org.eclipse.jetty.websocket.server.config.JettyWebSocketConfiguration + org.eclipse.jetty.websocket.jakarta.server.config.JakartaWebSocketConfiguration + org.eclipse.jetty.ee10.osgi.annotations.AnnotationConfiguration + org.eclipse.jetty.ee10.osgi.boot.OSGiWebInfConfiguration + org.eclipse.jetty.ee10.osgi.boot.OSGiMetaInfConfiguration + + + + + + java.naming.factory.initial + + + + java.naming.factory.url.pkgs + + + + diff --git a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty.xml b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty.xml new file mode 100644 index 00000000000..58bcafde431 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty.xml @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + 10 + 200 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + org.eclipse.jetty.ee10.webapp.FragmentConfiguration + org.eclipse.jetty.ee10.webapp.JettyWebXmlConfiguration + org.eclipse.jetty.ee10.webapp.WebXmlConfiguration + org.eclipse.jetty.ee10.webapp.WebAppConfiguration + org.eclipse.jetty.ee10.webapp.ServletsConfiguration + org.eclipse.jetty.ee10.webapp.JspConfiguration + org.eclipse.jetty.ee10.webapp.JaasConfiguration + org.eclipse.jetty.ee10.webapp.JndiConfiguration + org.eclipse.jetty.ee10.plus.webapp.PlusConfiguration + org.eclipse.jetty.ee10.plus.webapp.EnvConfiguration + org.eclipse.jetty.ee10.webapp.JmxConfiguration + org.eclipse.jetty.ee10.osgi.annotations.AnnotationConfiguration + org.eclipse.jetty.websocket.server.config.JettyWebSocketConfiguration + org.eclipse.jetty.websocket.jakarta.server.config.JakartaWebSocketConfiguration + org.eclipse.jetty.ee10.osgi.boot.OSGiWebInfConfiguration + org.eclipse.jetty.ee10.osgi.boot.OSGiMetaInfConfiguration + + + + + + + java.naming.factory.initial + + + + java.naming.factory.url.pkgs + + + + diff --git a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/keystore.p12 b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/keystore.p12 new file mode 100644 index 0000000000000000000000000000000000000000..7196dcdadc0aad2d431390c4027d5a491c15a20c GIT binary patch literal 2565 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 literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/realm.properties b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/realm.properties new file mode 100644 index 00000000000..cbf905de9fb --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-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-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/webdefault.xml b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/webdefault.xml new file mode 100644 index 00000000000..cc7e262b2ed --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/webdefault.xml @@ -0,0 +1,534 @@ + + + + + + + + + + + + + + + + + + + + + + + Default web.xml file. + This file is applied to a Web application before its own WEB_INF/web.xml file + + + + + + + + org.eclipse.jetty.ee10.servlet.listener.ELContextCleaner + + + + + + + + org.eclipse.jetty.ee10.servlet.listener.IntrospectorCleaner + + + + + + + + + + + + + + + + + default + org.eclipse.jetty.ee10.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-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/java/org/eclipse/jetty/ee10/osgi/test/SimpleEchoSocket.java b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/java/org/eclipse/jetty/ee10/osgi/test/SimpleEchoSocket.java new file mode 100644 index 00000000000..d4ef68feebb --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/java/org/eclipse/jetty/ee10/osgi/test/SimpleEchoSocket.java @@ -0,0 +1,72 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.osgi.test; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jetty.ee10.websocket.api.Session; +import org.eclipse.jetty.ee10.websocket.api.StatusCode; +import org.eclipse.jetty.ee10.websocket.api.annotations.OnWebSocketClose; +import org.eclipse.jetty.ee10.websocket.api.annotations.OnWebSocketConnect; +import org.eclipse.jetty.ee10.websocket.api.annotations.OnWebSocketMessage; +import org.eclipse.jetty.ee10.websocket.api.annotations.WebSocket; + +/** + * Basic Echo Client Socket + */ +@WebSocket(maxTextMessageSize = 64 * 1024) +public class SimpleEchoSocket +{ + private final CountDownLatch closeLatch; + @SuppressWarnings("unused") + private Session session; + + 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.session = null; + this.closeLatch.countDown(); // trigger latch + } + + @OnWebSocketConnect + public void onConnect(Session session) + { + this.session = session; + try + { + session.getRemote().sendString("Foo"); + session.close(StatusCode.NORMAL, "I'm done"); + } + catch (Throwable t) + { + t.printStackTrace(); + } + } + + @OnWebSocketMessage + public void onMessage(String msg) + { + } +} diff --git a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/java/org/eclipse/jetty/ee10/osgi/test/SimpleJakartaWebSocket.java b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/java/org/eclipse/jetty/ee10/osgi/test/SimpleJakartaWebSocket.java new file mode 100644 index 00000000000..20b1a77deb9 --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/java/org/eclipse/jetty/ee10/osgi/test/SimpleJakartaWebSocket.java @@ -0,0 +1,66 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/java/org/eclipse/jetty/ee10/osgi/test/SomeCustomBean.java b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/java/org/eclipse/jetty/ee10/osgi/test/SomeCustomBean.java new file mode 100644 index 00000000000..62d9c82a88f --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/java/org/eclipse/jetty/ee10/osgi/test/SomeCustomBean.java @@ -0,0 +1,24 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.osgi.test; + +/** + * Just a simple bean + * + * @author laeubi + */ +public class SomeCustomBean +{ + +} diff --git a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/java/org/eclipse/jetty/ee10/osgi/test/TestJettyOSGiAnnotationParser.java b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/java/org/eclipse/jetty/ee10/osgi/test/TestJettyOSGiAnnotationParser.java new file mode 100644 index 00000000000..bb09e30dcce --- /dev/null +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/java/org/eclipse/jetty/ee10/osgi/test/TestJettyOSGiAnnotationParser.java @@ -0,0 +1,97 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// 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.ee10.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.ee10.annotations.ClassInheritanceHandler; +import org.eclipse.jetty.ee10.osgi.annotations.AnnotationParser; +import org.junit.Test; +import org.junit.jupiter.api.Disabled; +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 + * + */ +@Disabled //TODO +//@RunWith(PaxExam.class) +public class TestJettyOSGiAnnotationParser +{ + @Inject + BundleContext bundleContext = null; + + @Configuration + public static Option[] configure() throws IOException + { + ArrayList

    0eRmlH3ab`XZ2r)_LB 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 literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile43.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile43.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5ea4f5582d43b4a885ee7fccf13c777115fa792a GIT binary patch literal 15958 zcmeHOc|26#`@b`TvF{3rDKd65W1F$>O!hrXg)x{g7-MavQe-cRM9EsxYRRXCvZSO4 zl~P2cmC6#u@63?&>AU{^`@LSj%RTqG&v`%3ea>^vdCs|K?pb)UFaYsyG$)!vFc=K7 z1b@)N;HraWF%*9YA`%rKb_jwvAwC!!VgpnnIQap+8eCJrDF{PAFmPp@;xJa`wHVNA zmgq`A%PnExfW`t@Zg9NF0G}Wt?DC9uQo32AM$YY-~t2R(2#3$-%+S$t}#o&Betn zCMd)wyjEOFa;><81WHB)jasiHEg>PNC9ec}q`JBknxKcr>8hxy;}}X{92^|nT->5O zJfgUD66mtjr`V;9FE)xec8<%a`O*?N zI9II}6%*eeiv87bLd*xYL;R9JVU^$^tY+DSWyBz3(|w?$t_|B8}Muk;XQ!yRZQHpAO=i+Lmo< zN9fpjyf6R4r2Be9Sf6|>4|t= z6{?25GgZRx=dG$=U77PNjUD^&c-3$L?`}@pA~VJ7DT7pEgGBH3^hE!9!_Qhd2cG5mde(VW z;rP6Istd;W{uPUJ~7QyVP|>8Bd425QKu(9nfdInqi@v^ z>SWC{xMk68AFOwFQ^#74hW_{R34<4`*zf4oJWjqSs1;+jv;DO9PWf|Xncp=^FK57X zGoPH_81eE>s}c;+9quxj6t%j0sOQ*ypZWXhW6uM8TB{Uot#ZA1_CpI$)G1ttC!b>G z6VB=_)(7@kRTw_m?!z9>uIZ*&JZdd5oDKV0b#R|z;ZZ4%_*-#NXVcbYyt(4d zW6+oWeqzj1Cmz3PZc;WQHjvjoz~J_aD^p$Q%wsQ~-kvLB=1cmHO1d|GWN#fZisVZu zRM8$u4_SXzESWwZ=WO0Enst3U=bNe42a2tg~p7HA!Q=EOf&6}=GT=*L7 z`u=dsxz8zOw3ya0haXiOu?I$V`Hl?JA54mD=}C2n*1`{h+q0bDPd&s?gmYOMa@lKy?yOFqL|6$9&J=f{!9hKYi+ zuM=v*WxBNHAEbArUtV`F_^5Oid+X}y-j)Js}ov(wWZW-@V8H3#MmJ{_lJCP~yazPl}`keiw%f>0VQmli9ZvK5A3qHd|v zTYwa*MMJzJa;6cnyRwoTUk%_1VtQ}+uDnIcJol=2;1cZodBYtkCA-2hI0jr^$FW+xN9;>xA@~n%eh)*W@(sR*(94z z*(l zdtt&dSk%ZfLKu42S2Ox>jc3&{bdp_z)KKc7oadVQ|}Cc&$7r<6|TCGX034rbbV6Zoc_yt&7p_B@ggne z_i`J)3i2zKa;?F(Z#3YWziwGGaU+WFVe$d{F4gwex4cGkM+~OAyT@-wV9CVi7g&Zm zyyg+Ju613*wN6u373-x5KHbB^qtfV;QbwDwGrq6-H<{^DZFfI5Tys8r>_bY#gOc#> z^X?&1@5c2woy)D_3isb^uhu-Fo1xWr%AJ(Xearj&<@|tWUFZ6YwA)@QpQyZFyKSU} z7Zcvd^;S`K0V4KZo1D{|Qicl$x!jc%-SOo0!M0Y!qXb3y%Icfw_tVK=JSQ$*dqHG@ zh=kU=>_dgSit0u^XuG}&^xLMYpA%32q{CX1BXawxAs6|w%5j8HVN^Q4sLt=v$K(+! z{D~xkoi&*c=9FNQ9cuSQ3ajT|&86--FZFF^egT?6R;)S#7i(nkmOA2_(UP7WOpAra zcW->?sUgFE{o>yE9~7G$4#$JYIYQN^;2jIlWq-fS#%h&@eo7R zIl)(Dh4>*KwLVO&;j2uhMeVxgBm0xX>+0}tJ*!QpG~1&NKxygmnKQjP7~I-RJB4?9 zkXT#SrFX}Fuc$QAO}V`j-VrTH$qJpa-nVylV#cwMx9^4U#kBtXi{Bj9NOe@_x*X$^ zx6|bw)wo&K7*uMirQG_gFe|6?tngM_qsp^Gq>Wl{qrJy_E{X^{byBr#yDD|jrPo}0 zClpCLK40eP(xMK<18eVlx7U%Gz$Kp*)Y=PVAxmm@YU8VTT+%)3!tMm&^ z*vx`S)k0B;!?TWS>f#MBTjKZ?JEu~{dt|U7_HK(~78wYljrc|I{Gn}?$x$JA@=_TuGogT{H_qcTdFXdoV>qrot zs(hYVWPLK&O%IyoRQU9iKh4g-$;=L?KAkG1RI3yIe)kW0EG;FnLH}mg5u&`*ZsZv8 zYSOx6LTN)9_dht;!g2V}X@VBVvW#cRDw%uK4ic7%3&Kc-E!V@N{PFOZ9g_+qbvG zl-9>gJUp12onHE$G+v^Rf98Exj#$r8CF0k!R%EBhh-_&7(+JlB1Rwq~`#Hj8#tH%-5C|h1NTRxI8ExF9kKT6A1j4O|=hS zU3+`yy4{qg&m_K|Y_J=e-czSdkv7$_oeCSz>OuWX^mfIUB zw8`RHD~8Sr%!^!?ND#{;PY^T835i^M+36?sCR{X>HAOS4LN<`Zzo3s5Y@%%vr~Qk+(BKIMoMVy7 z-Jh>I$zhI$E49BEepFOoHUz~#PoKZO8HtuCyp@=WL{F4Fc)nv_P*h`B7&2J=V6Msr z(cD(i2E4HDn&SjioHOp;l*<<>9Wy&^^%OC>r^N2o+q-0w&-0#==sZANgM8>Fab$SRIjKYugYsI^44ziH3Ambh`sNi|+EqEu>lh5l<=x~n(R*20+5lx5DzrgyN} zRCr*pQLjPbA*?^G|4ZYUk)6$VEhPqqC9lI@<$WH+G}li!sHL#2N!w}RktN#PeQN!! z;RR@oH(%V2{-&ZA2lr=fznatgGVZSm|D9!=H}~icr?|-H(#LF$9L>0`96ESX*7X&mWz`Qs%ded2P!{Rk#FP{7`6^)J{d9T|BLjjMlq}MTM6l9 zqrG#*77vl(e4kTC6kD9Ln@+VEY#pznen51DofDI0bzw8mpX>`-rP&)*xxSIq>X1M0 za;J|{Ij!Pq?v$UwKA(L~E{Im<;Ayh4MM|N&Zhz0wxrUTkn*8T0)>=5cFK!bSZE&#c z+1RIjt3GG7Kf$L&edw*0y>KlDJ7T9O+S0ux3|Z#2D?7r?&*(|VfqQ8pFGmz=!or?t zIQn0~>g)D5Z|5GG&_)KmUlSM2x)?u?u}&z&Xa#VS&$&Ltro z2KQ5CTO@?On^|a_%u5jvj`IDB*Idfyhg|oS(2JWb6t6h$CwzV~AXUX~aLz4xLvC*F zo;F<-a|2jkeJr)IolyBPE$gvoO}2H=s_&U~$DUq(bY4v3qQudo-o+T>42;6ytGC#P z331|W4}EH%4QpdacWdls9tZg;eSHIy4)%#&eXHoaQ5tsBoCdAmMiW0q?^L&*61a28 z6svq%wf)9Wx2uQdE7;xHypPmsMXom1-t6$;$K$8i9XzbROnXvVcy+$MbDyorSN+iU zO}V#a!Y#7-YA&W}TH;6C5PrF8SHHcFIJR1VnRByyBPI;o9RfH4t2~-#L^g#h+fNn zZY?bNZScvAQq4PSwUgz_9(Em=5Qu*@;BIKUzp7J__85n(n?=FW_z5u%w1DguC()_;~RQ@zZE zFX)l7r9BGud)~jj6Cj1r=wPwP9w62H$Ppw!?*nvjG(C*L9|yFsZxDlKthhnKG+-d0 zMHzI!BCW*4EYgMy+AoCa2QUnq!~8=07<3b$4@O0j0gV9b+)!c^g&Ym&Za~WfM}|-U zJp*Xr5VB7MSY2i;tIN>Iq(DGp0L@Kvb~FLBE(Ec22dtodSI~5F46qZdv4@7m(kKCe zbd(%P9)%?k@F+`ibTFAtSGM;d1^LkYP$r=vVLsGY2wE&NLkbdP_!b46tcfLPYAWMY zfcuw^f0?9GG7!F-N{w-h``Ok?J&r@MMZ`pS!Q?kEL zWH24Ya4IP@I5d)miU{)|kx|NjZ^Zv=!4+n$utUj_>`$hVso+trpp{Xm0ie36eiS+- zl!~HI|5*+HFU3~az#tdfH6VnF=ON*(DiHqzVF>Y|9bysUf)HK>AP2TwZ`K^H5Tob0 zNsTVHdq9JHX8KzKPY1W~2uc8o!8UPpMv)?EQ4AXN2}Z)o_*o15ZXyOrLeh{NqzvI8 zEl3A4gv_9gkR9XC+5yFbpT7=48BjKq2NgoaPz6*4U4m+%YfvN9 z0^NfiLOswkXaIT*y@SS~DQFHX(js9zFd>*2ObWIErU=8q@GyOt8O$2y2y=&d!vbJb zSR`x*Y%lB(EDM$gI|ZwNU4&hQHNx6pov^2{LD(4VGi(-)fOErz;cMZta8)=SZUnc2 zJHt1_1K{ECIQTwz8vGc%2wn-Vfj7eM!F%8X@OSV@1ccy1tU|0qC?oI)6NDYY1L2RL zA$B2>5jlt=L=~bQ0p5}k1BmyCZ!ByqLM$j2WfmP4OBNRv5=%JCE|wIQJeG2n8kQ!O zM=XOZ<1F*6JggF|imckKMAl8L0j$xiiL5!SrL5Jg&8$7FuUV(q*w|LFq1m+9EZN-I zwy|wzOJO^~cAl+)t&?qtZ4&%KAcj;#>LKlrK1ez;5t)muMBYSpAzveB*tyuH*m3OU z>>lh?_5}7E_A~4^*t^(A+2=U;IW}-;bJ%hCal~?@a};ybb3EX9%`wL*z=`Gr>jj+K zIO92waaM6Qb3W(%!o|h4o{PZcz(wKO!*z`70@od`L9TDyg4_z+Cfr`!k=$wAW!w$i zPr1MF@bJj;81i`V(0S5$DtK=5yx^JP72;LlCGz_5?&dwtTf^JM`;m`>Zv&qZ-xj_& zz8t>Gd=L2E^KWI`$skbN*lo2Wzbqv*n8V5heSgwm) zcWPb7y1Dh}^)BlZ)}LSBFU={9m-d%FB7IAGTt-U9MrMc1S(&~K92@W(0yiAp(7It- z7A@;0dqB2U_6=GTO+?3`&!V5p@yHp-h07Jmb<4BKo>1&mVpSq2QI(369xHPz8!1OApH&`IS*2pFlAuzn@=;Yr)k8H?wN-T5x!|_YSXnZX@AmD)(O%%qw_`=txMJ| z)*aGYujiw8N^d|PrSGkOQh&f;oq>-*k-;lNX+uB5Qo~Urd82Jcl|~@~@~FR_f0qB70KI_20b_v%fjNQiDJGOW z%EUIyZH3#WgY1IJf);|^g3pJrhj@ovr3zAms7;}gp)sMIVTxgiVFTgX;o0HiG%H$Z z1T126L>*liOs3i+dy;bU$tjCdHAS*TIj7l9N1Yxib}eo# z!IxB&ikBWL{ZU3O8z^@yZ>rF$s5m2WCjBh@Y~YX|Dw>pHJ)yn3r%r@s0xmA}fa zt-E&oy2$nP8=N=x-B`F8cXP5Ktl@oQK;zJ@t+$@t-gLXW$)TyE*{biZh^t2;D1Y944ksC!6wSl_AJd85mqtMQTPqn2)B z_q`t5p3YvE-Y1W}9uGY6d-CRK@Y9LDsJ^*pd!DmCPwwaM&v_y7qUfdk%ku-81J?&l z2ispcy?Qo88hSSzG5r1Yz7d|0oKdOKiZ{47*T&4p9=`Q_`}$qjySev?9|S(+f0X-p z<&)v3_VG>QuP0~|3!hWIhEs!)neuP=Z>_U#v!ip--;v*s z{gD4rKW{bvd?5sUa4;zJWNDs9jZpFN3-u+dkU~RLV|>C?u__o6u8-5uRKjU!ffEl-f)Y*>r-ai|2Pfd+)s-~VFyO=j8u0OIN;rb1k{Sl9q=vsHe`jY;?v6taCK0#!C<_Azq zKqli?cBmgYcrpKvPosVcFd@ql|7Yr4SC!FCRy6T{(d|ZNtT9IGVscl8nqQ1x+5a-sED9`#8s_cF(Ci!< zrW{Q6r!QHsGHdw>D`Tt{PGvFrGBvHtS%$9+ufM}r<}Abi7Ga5066hyP?SIQzmT(I7 zr$_tH$p5ahF_kQXm&FemRsptEifGYrw;#V&@b;XCl>$6qyK@g8K#9& z!RQ_V)}8z*0g<%DVIG*dIM;C^hcomq-33$h-BBjKC=(h=c`>p94in1^b4%bA#r`L{ z7hAp=2xG2f8q;-*k9?QpR=PS<5meU@4F0#8}89 zTESX|*;8W3!JAA$lc6$A(Zs0Zm*9WT`d!BTmkgMx{d@#dW<}Pr%+FQ_Iw6?Z`UEou zLcB5-tBfN!|NL-Q5sP6CgiK@q5coqCm_vfORvg*yKi2h2Vp*A4Oju|{ByF+NU{%3q z-pduSB4ZhDZW|fmOQz{#)l`2L`6p``^HZfgEi{1U6T+BoF-!D!{<45mU?iPR29-r7 z`?>lAN0Rk5G&R8>p{l8&p{BVc{U>Ny-YASrqk}Rm$})y8-0!l#1D54}8e~lIiCC0X z#;U6Q&Dvj?%W`0%K&F8iE|}H;Q_=Z zWq44-ih%u57ev-$>8mRF~L^V0G|N=UCm0|KTz{4*!-M5|ILiQ z0hU#mL`Kj;rrSy7y1$Z_H(= z6NyYEoA`t=8i;dj*f01W8OtKSP1pZ_a`k_9az%8qW5yImFsxAnA{NIg#tY6L;~MB1 z|8_+C2ig`#GUgD3V(1O?p+ztonI4+aCn9Mi^5RR-QjcQ{W5A04=C#0&wER*K7D)?c zu2T4sRLQ|)Fk;aozXg`v+e<&@)hptZv@d+TSZVMv^=&JtW{&&v}`}vl$ z{P^GA@s_h!yqGNWw3!1fvm1kG`j6NQdw=o4ugzer@TUZm_5b*3bEW(W-0uo3DIn0$ zVo|F9rE|gSrH#!$F5u6X*pQGV_cF2CL}~<`5p;;A`jL?oKW#H}0!Bki!(7YA$jHAeb3}ko|`o!}LX_X(%a@QHw-VX6vCXH5onhqH42$ts2cN?y@d0 zB1Mt?%xR$^|E4J}J^L5_pEzx?m4YQRuz04ryu=1>mKW(jVAn&_7)x->T`64b?40aK zE=~?kupfm-KtfP}k56E|n7FWnyo{oPoD3SRj5E|y#^_3!zV-><0Ijr{)4$r*k*w`r|jWaf(of?c! z$O2Mgi~B(ql`W>s{U9I<4IT`KvB1HxsE?sh0KwSb!s6f)FJ!zv9G5x1@Y~*tclV5c zpbtEIoPIslbGx20?kgwdLy?T${d&CE~#;dXSIedGpf<4-BNc*`e;*R>${ zkvF;t4n0FNmmY`_>m9a!MufM-6nhKTq3^zxz4BC%FyWg@c#OH$C~25 zk(92E5zWph#9s8ziD@Bjc6on-7AJRNhIV#CM|GVF=WO!a*eCO&X4l5u7oe2G9*!f2 z=ibOA1YD|4+hof+uz^leyjgyJn|vpqCf+~)v_a?e_F21&&)8pZ9q)-bB5f&Vg+i1$ z%%6Ex8-P5Tqt=|%Umra`C;PH*^A3}Z`DIQDV=y9$_k?C^UXr<6!0x2snS0_b9~`5b zHNVY$#l{jWFSqSKsIwov`&>x9MRqc#zy_DC>R0?oEPEd}|AmK@4FV%6;oDDa*)g<% zWjH^pze-=9PtFaG<=>e~t^Dk1VcXe%Ce3bj9oH)}x7lx1AzRsw&#u<|Zn}jlGd5Jc zBmcq{p}LOn_?PNl!8h9sT4xp@&zl#nz59IfdUgJLUM|J#x#SX=03to;Rbh@RRnTsC zfBQbEY8~05cg}V^fB4WSQc}}T|J?jGn*Na~g&t($hwN~gzx6aM{js8XKj-<&T`&6L z-_)&%?5;By=AH#f9C&)64t;n5BD z>2m_=3J!csotdZH^?EM4SRU~ELS9vqd9P^tb&AAeje0$<8nxS9Lr2TK zY)|IH{iHPnpLc3VT4lDyt^exP&hgPxJqvgE(3?KHW^wC7rIBL^d-l#NWFCI>M$s$W zuz}aLVD_-V3H5_?PH7p(y1+~go+m+~`q=9(n>?V$l@V3_wmR=lTWao4ZKA=gH}KAF zV>@eQ+Q(uY+wEu_*(NsUQrxMdMv-xp8X!w<)EaY5@*aw>+F(jMG-^9}>vcd`*~S{T zEg}Sjm4!^e_*JR6z*|mJ_?*?2d#>Q5KA=8KmX+kbGe6zFT^pmP=O8fItR0y19v|aQ zYgT)?!?!%~*ttd*)_sj`FGT`PV*{Ly#FCuyhtF^As^T|G z9C*;@WKDXJU>Iigj=Uno8tFa85>Z`ICtagf)o6yV4X;Q|w=WAV2`g`Udt`X(Vz-Ob z{la6-f#q$vZDKzHevo)erm{H~)^l50^m-Xm*rA)~3qn0EDD!KAT7ZJOo71&IIUDRuU@?S@~` zvRCC^I_zD8NZqW{X;eN}nPAwU-t1kP{kY|;eJ5^Y;;7>VsQgQBo}oof%&`Bhj9o5t zDAHU5m*eD|kuGMwN96JXM6%s?OUZVp*Da*C;ThSMPOpL0=$BF-<3$Fqy|T_bKIq31 z94$8Mwm$gHHuTNF{-MWy(cgLbUYvQVA!OCWpV_|uVP;Rpy!$CEKIuV|k7HEN-kWDj zHaVG6*ELD|D`Y|D1LDQ|+8)rc1%bXhT#wi!5btTe^qMR)eakJop^>Z~-j&SOS~*Ny a(>tQM_R2^+jUX|RM5`oDq*ib*JpCU-Et=T? literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile44.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile44.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ba18b30f5a6d4f90677b232795c94a0f366774ec GIT binary patch literal 15915 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 literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile45.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile45.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b856d66a3f9f2970296f182cfa96bc9924829195 GIT binary patch literal 15903 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 Q*DXT~ZGC8BIQRH}08lA>WdHyG literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile46.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile46.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f474cc2f9fda969691dc3c5361ff881f1b9a02f0 GIT binary patch literal 15927 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 Io6J7?AM7RnQ~&?~ literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile49.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/pushed/tile49.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6a33cf3725198340eab2d0f3cf9f19a03044bc4e GIT binary patch literal 15612 zcmeHOc|26#`@b`TvG0T|6Eb!)W1F#WgR$?07L37!!Pu2VrIKt#NJQBprLyGHLX@Lt1s(27eKIH0jWmK$8pgA)g6VQ_5)rx1Km4+BHp!@|_l2k2S^1eyEN!f1;risUec52Et*mT6!3Q9#$8H)z#C&>EZCuYN#+} z(N1abu%k<~0-$C0{;~nRMBf0~p)4fC0xlRF4vWZIv{@bCS(a!sK*O>ZF)&6sayXe5 zc>q2Hnm9cy9@M~}yuK5RHU)!@1t6K%1#kd!Ai*U);PU?m z@W2jNeJGUSm;-3Y60`w0@EIIpv_uyI9#Fqp1jAiy5MrsHG5YDxzW5Eu|Llu!AiA9U!r3el>m;Pz3Jz5VP%|PR2~pqcm$b4>}+gEHdb~d63M~A&dDvz!_CFT zEiNd;CoCl)BP}H%DT$I-N26p_B)b(K^>kJQqVK@$w|I0JP}EgVA$jDv%Nn~PhF zher&zMsf}AzdjaPApv&C59&a`WFfc!3?Tqp=zwH^ova|LF#_At0lFs(D;tuXgOdvY z8u%eN41s{NAXr&hfD>SejPA=Kz$&-~Ysx0%?2D9*5XL2Em$J*5)whYb+@F@$^rNS6 zaEh)H6PH-4fL2se*3#C|#S`?*Er^y@)+8HOH+PS9>pi{5{*-{gAnKOLsOXs3xcG#f zyLPAU*}E?-=is5-yu>6=XJJ9GAQ|O?!DGRR z5MW_lgJlymbw>I|2+87-*@exrOY7S>6pdN3|Eyps|ErQM z7wlKLh9GVPXx{<|0mv9?40SBm8Y;1wGHQEcKZk5cG;Xl!-Y{{XTB`a$h~t(ihmtpT zdPxo1?nvzIBd$|+bJ)7v^9#@{wdCOf^t%6K)rL;OEP2X4m^`&{&6x!#;CL|K?3P>$ zM?!bvk)fh9XNy^BN4TSfd;P44ch>3oO=54*@!|gQtuFD}=R-1{u<>9|<%i43_eT!b zg=wO1%$D=}`)C+7)aE_PV26HC?*#75;Oi;9W2o*vv@YmcFkRo(AZw0Bh%<-9bMBi^BS>AEea_>c$ZhZy)zbR613e{aw4_To%kA zd+@YPaBtW$BYE!e=b8(M&3N^#xZd@9+4 zoDG}o_Uy8)HW}RN%Co0ovq!d6{^iJ*_=>~1JARDLJr5>&<{PNsN=TN-)bB54UR&=( ze3<+(<~4Hma%>8pyZndmFWm|v52uqa4=g|u4W6g=Mxh&S$MziGMzb3|G}X$xDs2!4 z$JNV}(n`v8Z{L-6oHtOKixUZ+6g_9y3Od(heWPan3=l`-?B{U)V7+upKwj+sXDrIe`a zO=O13UJy@v=7$W z<=ZTCAZ!%rq2I!5XUD-^h$bM=alo0I*#}~lLmZ;#-r|j64}&$AX1!*cEqatz*~0U4;80dTZ{+%b(>_m z)3*0#<`$id>vV0jLO8{!U3U=6ev{ofaQxDY`=_4H@jH>y{X-G@l*`SC?}aDS1o*Lg zF7vuYf1HncknFzieHV!PRdq6fTu-i@Jljy$*6_nU^ZWLvd?rom#&&6L52EwONrK;= zr!+>$_vp^w&Fs!Rx8_#JVYwXk&i7T625!kcSw4bn?VV%pU8`DHohy$k9PB9kOKR3{VoVlZU^FrHg zhsqa3>f2d#en@P~A=mi*(F)JHH&1+zuTr>p`Ie@AkaApZRypf+O zwQF+P%ma~^B%)aLX(nmgE}qZeJFY82yK8@;C1qA=xK}2*gd~wk?WSX z3R*u3@~c#Et;XKAG3J}UWZgJ(Ifn0E+8(DKjoZ(!ZkQ~XFrMw}n{JQ9Qb@yRSjM_H z%p<;eH1~`*xz5^F%gPaa`^Lv7<DeZPc)!8Bt0-$eL7<5O?u?r@`%3E zo}n_YrVTmWs%#TVc3-*OpmWq9OLwT$lbp$Y)#voNqQFNzr-n@Rx}K{Yt-aHjpGY0hv~4K5t)ep5kg+u-xPU7d*D6cwe~hAZgTpVK~g&(vRhLSlhP zgwC7nV54(XklAXo@KBjc%Q9BAt1HpdC2c83km^6X-jTNfa&dl;Jj zQNB7`#1Ewy*>LgJkMh}8O>3?l+MO2B+>C$e-C!}Rb30}al#!X7{be8zgOfU&D4gU) zX6;;)*_Zsiy4KVny*&}$9V<=E37fUswe#D|7nc&=p(n!i8IOzVzdEm$>24@+&*xKe zG~k}pzEas1Tw$TB*7>L;C+~iZ@MZ_o+L|%4jqb}>pXvU35n&MUR0Q z74o+C=XiQ_X=BM75-(T0J30LHjr)hyC#{cv74Vu@>^EpOy2Y*6a(<`G#T~E(=-2~e z?ickZOt#!oFbZ2^V*HImd(6Dh#vT2aQ9aS@-o6V}TY7I%W~27rxFhhq{BcQp;=1X> zd!?TcQ=Yk5b_R`%$Rs_9s>C*7k2|Vg58@r+4Sjtd2Fatl!M`J?3*35Jx2YQk~m{rRwAqVCz@Vg zOKk&5#8cRxKZs-1wzNi@k!=;})hO5(P6vu&?!~Ii@-L zxl3-QhC4msZRbFtYsQY8h`U;m@j|cfyLGIpe)A-soSt#wn|p=H@$9CjTCak)ZoM8? z(GoXvZ*M_vX2omrbh&cT$=5x3;{AtJNgr!$DX!6xxzPN(39bbQKK|j0%Vuv+gzAAr z^^NgEcT=tu8{2r-?5{ac|7dv15}zS&n-}!u~N{BRH5vD6ldRD{KWpH@qlcjO-YrLv2vFJ zuCscqMqpm#l4Oc_Hf4sCRYgeU;>*oEW;o-nt)?TET^G8REb#%IU%YAfoBsk-^1RpV zuP^?0RaPh4em->gq-L(O~J;Kqa{0Zq2%XkkKbP>S?8mM|n#ob+&I;NIT72DtUhoX*Kemvu#1)oMt%( zDl+Fptk&r(v8J8n3XeN>yt<+1Fc?pFS1O2_vOjb!&k;|#oO`kL90?91(pYAr$6Ti3 zM6La-?qTU#l*K-udrC2wUg_*@ZgX8mxvzP5|KYjT^lx;f_vh_&adMrA$aVgvgWY>>Wr#eTP;LwlAJldU zIFB_lczkUu_t=bfpySz=>;ieA>?^Rwnaqys~b-3;!RoF+)k7PBJJU})S?WqQfevGkqY zlOk@RFZ~zfk`>mo556S4=21$=oy{)lV>7|HWxDX-t$RB2=nT z^_-o;N%*k2NiCNv1u?oO<~kpr#I{3t@v^iDbMm326R6i)EGEMBX;VZIYco6|R*L<3UByk!)Fgv_|<4y&!j7u+z(@6*R$rtgSciz}Hi!Gb9H#rVx0f}`v;=%h|*iv0pZmVS$xvP{mFcGpUcvw_U&9t zny}5~QmGo^MF>2(1b;H{_+X@|fR8TRDbOI2C?YuAQ17=nhoaJ^)+Mq(?zXKUNx{r4ZlP@{gm+p@;4+{Wt?t`wnG63Gj^$ zi9#`)N)8JNi>9L@!+psVl-l1L@&8(Ig;^`?P<5dMQ0NpIc$5cdWmH-qsBW4+HHsQW zLs4n}tcL%WVk>N5kc;ga5JD&BA>qyH5dU3a2=V4N#3IB6AvP3)9N2Qb*>QM4jGnhn zW^%FJ0~+Kr)87(!Cb)%1QUg&8wz-QNiX2UkVbGvYFcMbA&syMj6LClyl7kc>H3$dk zLi&&iM1*W0N5~EGgf>BBC z=oWMj>W3adBhYi`6*LXaLUUk|7762l3Bkl+GO)EU6&MbNhZ(_$FgutF%oFAV3xv^N z(Xef>ov{6|99SW&6jlwZhh2cR!Mb4gVGm)?U{kR7uy1e#oEt6-mx3$6HQ;!-DclzB z2Hyw|gh#*=;Je@%@O*d~ycXUFZ-d{0_rpivui&2$5P}OKidchCL*NnS2uFk$A^<^0 zY)7Oa@(^W+Iz$Tsyd@(>5U&wmS=d;FSWql#Ecz_gEbc61mI#*ZEa@zTELALxEFCPp zEYDb`S>{=JSS49iSoK&*tm|0=Sz}pKS@T#cSQ}WcvG%h*XPsqZV-saVv+1%~vw5;@ zVcW`<&UTdTG+Qg%eYP>SPv92d&TvVI!9v%fA6CN*~D4qrW2+r*c^ zm&bRG?=IhKehz*Gesg|b{_Xrn_%HB3;Qu5bB!Crg6bKba7dR=($j5u=EuiJcbf7yBlT5+{lW zi)V{Bi9eQLkx-U!mWY)ombfPIdbRLs{ng~vX{*n!9+HGhDoeUZZk0SPd0X<6l(dwU zRJc@uRJ+t`X;Ent=`GTSq%TWP$q309$xvku$y||ni4sAXqC!yls1DRL_&LUUP4t@5 zHQj6GWYMzjvMI8sWgp9N%Hib#d&#!&1W$Bb1Sk(J`YD<2A;<#%0E%CUPeJCKV==rb?z;OlwWwnqkc7W({Va&GpO^ z&08%H7FHH%7TrWaqC2sW_{37yGSITda@tDUYO7VNHLJC)b++{bk`#$dswPd_=-MRO zbl7s+y4n`nj@l{P(d}C7;r2H62knO));iD}nj9fVlH)hvUlQ+>j*ZcVf?G33L1~#HM#%=7{B(W)E)1}RPn|(K*^I`Q_=X28MyRV~f zneQh*l3$VEG})YdnEcA$#Q%{06vdE|LzxUP49E$15oj2AFmNi!I4CdZHPxJ2NS)bY zy`^N!=U~U+%HV~Nbs?ui*+YFoFVF;O!L*Jr>9Dx4`{63#so^6LdJ(x1({x*UMIx%JKW+Clf3a%D1v?C2zgDZOyji zZO;--6HB+lw)<_rnk1LBE9voGy|q+E?`XsQ=NPVw2*!5~-5?CEt!wj}4Yul{S_sl;s^~J05d<;)KVE&T@Qt zb%jL5{)!)!w91hxm#U6x-RkO-k|#53;5E@TQ?(mv@13$Z)m*1mS9)6X^!_u@ndmbw z>wW46&f1>6dJccC?)=*GMGZm?X^pVP_{Qm`pr(;#&*u9VY%W}F(Qj$^OZ~6Pi)$_( zxg>Ha^D^hgKDD+Sj-DbvSo)U$eb-n>p zT2AEMd$;)>;a5LZ=!{%?X7TLy zsO#vXG4j}}@yPM-&v#AmOyo_C z-rb&FKmB}$KC|#X{e$?2%8yzfuYPj+^kg=4cJ6c97x6DuU-4f%zpeW=IT!mK`91%K z(vOyT+xg*zQ1HRQGoityc^)lN-Pb?NkD^Wv3)P784cEY`V>BQG<2W$$3Z_J%{3wA` znj!k#Usup5s=pz6owhy3KHQuVM72(!Q(O}q+{g*RWP(51*of00PA@JrJd_gUi;4>k zp+)M&8KRlW^#ILaYoGy$?jNA%Vqv)i0e6Pz6?4v5P4zH(paz(O6ErY54IEAlAk-q` zX;Hp$YP3i>pn(&`)L{W$KIqhN<|J+LaZ%KOU&?+f7$b*SG9ZpZ88B}39DJjwF%%1G zASE)&2&b*1iqqBwCmx&xRh$k^6{o8OPQb%!scLItz=;Jk;Nvw_aRePzO$=656N?8Y zK~)WLe_>Or0BOFuKW#CjKv)J?L}GUFf$~|Fb(UHhKWlvk&v9 z2E<$VMp2AFEfdthKTgXHe59nUqldw&fRDvAL7o2%$?zV7WlpC6!T&^f1@fn`J?Q@a zzEQsaiTH2Wp9+{d!{}~dVIfAQjEIGD3JQw~iwp`2N13~7qU?RiR2t)c(f*Zi5HS7= zcT&K|fB)E@jGPrwgmL?O6w%bd>u9NJVF)@HW(wkiE*7_#;+UMpR1@&Ev5OcjRfd!< zGX=2(#0kIwDd1?~wK2>T#1TM)C<0OtcXTyDRAQt64^jdn#RDWr8PC!LF^Z8go}vk2 z7b69-AZ5ye$fbqV0=bM7SPRlcjxLkNGg1Pu6QoQGK^xFu^_7_t7%7N_T37T8=?np;?ynVXsu zHSt7(CPvrN42QuH38q?_7*njdfySRT#%Ntk?#fW}i}5S_Uxu1Rf#p!cyj>ZZ-NM4v zLMQ=IOBSrmT7JUH7^{m@UyQy?O)GPj;VZ-I@9>p5%kaNNSQ3p4`Uz9}-*T2ET*CsQ zVtwh9f7jWVN|wRP;*5UoMh#tzGmNiLaayRKac1es%aCQEKL#lCFf}lYQT&%A{>WdJ zWGv6oDUp$k!O_NvXayFKmKjUJD`=0Tw|UB~#ycS&v~d)ZxUi=76m0Y39y zu80*G%WzAF=ukfj-3Y6x@w3Q3S<9H8DxK(Ifpp(c#&nBWqQCQ(1zdxoqoOFFvM3aP z58sezijlUC4j3dfbhNcKb(W<61TD*(hEwQKpbU$$jNuFSyX^0PWx1aQnNfWs7iHD3 z8k&Ez_E+Yz9GECj=wOBmrZvFSCH!y9WvFEs**C5V!d7U+_ONmPLM>uK)k!>i_KIisb6Zj43W)Sfd3-E{;`<7o0!FHPAKw z?TGddv@MQg%pnNH&>QYck7PD7Lo}mLMAONX#h0L^9>*BQfEEAEYk@y``K2H{njXSj zrSKmb#{5EK@Fmg1b7D*P&)|T8qF;3vMw+p z#Zde$>0zP&rYSBx`xpM7IBl_&f+aJsc&4$u#0GAb7wJG?H$>AJOK{9xDO~LAoa{(0 zP7Y46AB9IiQc!@8Pe4{&LReBsUPW0^9*tJRndquv3^8al-i}~sPP8UjtLQkoJ6O7z zSXmR9yHdD$cm((a)(8r&A*!R*iU0Mnz}%JM1UVsKU_-{wT`A(=r|A_tQos+9%uOe( zU}J~s;(ina0>Q%A_QT8)fDmg0v8F7}zCyAQthi*k?D{rgvr>ST_YAU;xxzCW$>KdHAp%ywazPN0SY=guY6wm(A2Hq@~2XXbL=$ldmA=1@{6f% zFws7Gv+jAq9(uIq-dytNIqo`- z!sk-ah3+ZXqO~o!ss8Av$lAP8GNP@naa(T{9J(P_UweP&?U!k%`^#%rw|`G+5Vh_a zz1T~oJl~9*(uz2A#lFhlX?tbpgWZ!qzQZ3Md90jBcT0-Nx7&1W#>5JTZY4OLJKi^zQS-$?4Zrr*iFviC z`KxNf+w-m(7V%aIaR^@Ml5}l!P|_YZj@To8Ty z$f+0Fg~lYm-0R0xrWBqgm9W2g`R3J&LG8wt2&FK#t!eKL=F@Ax3~{GP92!%rDfVjk zINz11d%~#6IX-X1O3v|ADz>XG&gV_n*UyGmP6qC(A8i*sa^T6+SGT65(;lr>-rhg_ zWmmERQ7Rwzu<2Do>-M^%ng(GW*S20fdL_cx_WqTz*v;DJT|93Gdoob&%Kio4`-o08 zI$j(L(4ir@EpWdi;f-lLt+gI9xZX$?KUK}fcF>$`c3y}lRqEu(_EcNq-X5{8ds2d99lyeX7*2jsx!4 zbz;XjRqE@>`>eYCCrqw?@)mYAEl8UP*d6bDbRwtJ&=oJZwQw&LEq}Y;FR+>OO_TNs zGamn~rh=%im1W9m&BL&-Cp%;xqGb*4J>-bA4wb)AkZdN9i}5ij=FzcSmC}(CtnYcG zRI2b%A9x&tKaBJJls?Zx+KcaHfKNP&!zxu+;u)6;g931x+6l zfIFt`D9-5=GQOi@{*tQF7$m?2mlxN1groF1K7!ucyAATFTf?sQn#sxM_wmTPt96hN zqp6B7h!U0GO6vvR=Z1w?&3L}fpmWH2A(KhG?KV3qIJy){k7WsSA2@loG<3DKT{U^A zc(Wyb;{p1Yh~2XK1J$`j<2jW2(Yn25yXEdC+$vLI;V8NnL}_O8rd=)5?&L17NJeqU dAD!bgN#s8m_bv-|jC4jzsdG0ySIrq#x(+{TmAaMsbt)3<1Hwm2)nJ@p7+6 z0lj3FJ`QNLSqvP|SRgA5u4loC1GF@_HiAOBm>gfXXaRdZeQy7tqnG{8G z^+f4oFg7R~vK`sW(h1@i%tg;#?a0nK}L}3k$^l?Tw0<;7= zkUC@MGEmrGv$QIpm3REK0X<7!2il=*B*X(Q7#t3Z$eyuT2jF>TX)8d(a%M0vPCar3 zxfb~VK0J4(thF5cEI$~~$lRH-c!6GlSy~*>g0pxAz@+6OA%5W4SU^LEXKlb@FtEcj zvK*ho56}3D<4D9DEeJupvvel#pJ>U94VwThHcO`gTD;_EePKsuXrK~;FQ5?%XubtB z2gfNt8zdsLG!BCy7-6wSI3fzGXQYQS!V}d=|a2&H64cUS=00%yUL!6fAK_md`XN%ytiw{D~_A^dDo$HGeKz^<-!hz_* zz6b*h&NCDQeE2LK%XtXknCne**Xf0Eg7ffz;PfNN8sg{UL-O(RBauh}0e(SYX%S%| zAz^t*DKY7#ixrnGU96yhQqe)9l(m*CD5x2#Yk?lAudj$Eni6m(I(qszjuM!FfPk=& zu$+j998O6=3HM(g(~Xb>KNJYHAYjT6TmpuWfK9hRioi}@5Y;$=ZT0}&lZTfN$uA%% z1OPSS5FCa;zJzXBrV;MlxQhsTnfLmRZj7#CV}O89wRb=%)5fJD*F8^MmbgE*6cs4SnB_( zX7d&MS+8D57y;V11VREbhiW5SOZ9t;9EQ!B-Z@VqYm&@s?Alik?yg)~xjWo-{jf{X zJ13*$8oW0Wd+VU*u+t>AI`8Z>G)^yiI1Rn+K31{1l{iivb`GNsuTlDY8VWuVCN{o4 z&&HM5o^-JH(BG#Ec^Ll{Lt|+U(O%|6-1pL|X%%^-J*c1DsmaBBL z9#==|p|6jZiU$Sgn$;Z7f0W4&{h;3t*^(*NS$x}6$GdlB=+!W$v8PFTuR%!Xfe6t= z?@Iq@*5m6RMRargA|M~=%h*xb7loq9f-WT_&5IfR405AF&&8~?;0BA2hWXnc9SHQV zUtNt8Tisn#I4rJ~GoIPlgGalcjOx_Ud@iwZQG#+>rj15YMddxu%R6I_jD8?dHoCIc z;ic~K=9pix>v9V>tyKP71xPfEIVBp^xsM0w74eA$_7 zm`ToqlMbwB*ITtR4&ubW<@BUlTT+)C3@L54&`3lAoyy$wL`O&nb^e$biJ^03LclNArosxmP5KA6?It7 zVftGs^@_VApCf&Y8$@mGD_XME8efPpUEjDqGBDN;FgT?0t$XC+sSK~cF58w1qkn%6 z_j$YL+KG=D<;?ijVfP=^0twrPOvLuSV0VnktnJ>-k{4nev}@n}o*(!_)9vPZ^PV8X zI>q+%P2E{}hmOU!de+(@++ww_xya?b%W3U7a&gpqth4pSZPv2x-e_amrFz8o14p$b z#If5iiF(C+o?_om@!t8d4aEJ5YQ+$tr&o`iuBmRS`Qe@QebX~Bi#i>1r*yCTG5cST zB`02{)<&yz8cua&wP&4Cx*5K2c`kqJ#|m1Fu)_B207<^))&cLfMGd^}Z5Z z^nBu_i{S3lhVm0%zO<86631(|Pnmx>$jsTPP~Y^XSyH25cdiUVYp7zme8sqnH2gGU zZN2FD6nQ~9Gg;5ar2G@kDsi(v za;zL*mD^+;g1oqx&1=lCNZ)wjY^K-|Lm5Vg^SOr94Yk6x*=1{k2}2b^ttt~t%%(i5 z^Ig>*9UuObM){m?#>h%2uIfNvTBl=#Y!9DArKH2g*6)Nqv9)gbcohAOA_r34EQ?PH=Q_^x=HiW)Ba67t>CF*EZb@%P0S;zG^ zx1vOzmaaJVB<|A~K|*-@aadNhKzV6xVq^IfVvV5hd0mO@<}DriAI{kGIehxCb|BX* ztXJV{t!HcY`mmzqD(u(5=TBCVOc*Z7_bry3j2?cM!RjcD z?mFokq4;LRRM4x!F|ladU^jYGG%WnM>i?co$^Va6eG(WTuqMgw>h>$9Z%_5Z42i<$0 zKIlmJYo~cqZBECe7Hq6t@3u@)&D8VB-J4D-e)~E#4Shvc%I<~BH}M20?hVYomX#OI zOn^p`9q#z!Rm3lz+LH2v?wl{+u>(0tto{(baT+=k9F)^kj|&@Yi!01ZpChgGP6>0zV_r}a7#}o zNhkYJd0UmTx>CMZ9=9~fXikE+$1S7hMvgme-7+!y)uTwX_o?)$%qNFVeRE%;*j`iM zy2)ra$EyaY4u-Ge0!F$|$w+(NWf;129ydXk zUGV7{)ue9xct)htkTH<5I_Xl`hhvYQz4QLG@q zC#@XWw`18;Qfj}KZEI*>pJMV;b~&~Vd&E`eq6E51eHs#MOjdj9EbiZy_P%W6rbF&7 z8sw{ao{r?D?NuRztM!Yi!b7p$mZ_$8?X7QoZ^9IG?tb>$5rYPPVmz~6` zGsZCLYB*j)mhqJi5)Tzvv@yNbp>JbZMkNaNRnUc|mUp2dt5m7Z^N#v$zbk(e6x}WB zJcx4R#~zX-q}%*msR$bI#Q}}=-AuNeEIHuTIA1Fjx^60Rvt9T)tGH+kJ?b#Bj1H%9WQl|0Pn!^n@)`wk#XNJrCe`3r(7|NCK)a{*$@8t+@vj`>|*YfiW#zsVzdOY6A+bB#d> zxP73cT`K~uT+42GSf5OP_(5F?Mg`@!}<#E>3#WhH@LeZ79-AMFkX~*M@Tf<_TqN0%D zi+2?0tdJ{cm1`tOn=Cmj)h8{R-&{)^p&!;0l_x9ij;=I&$;|QzK)Tpj(OYtD z1^G;OT(yzz>u)k`RJa3slKJFQ)3L#%t2gWw`d%!%2!DRyV?X9GGn2H%p*Bs;%c zxvO2p%2!@YLrVh05;s0+DS5hMTkeK)`904P&sPQ~l?z_pZ1N(*TfKli?7VkhcC&uS z<`oZGHoV+yo)>GVO)aHY)_uujN~G|d8tbTGCPr>Q{-K(7si4rJ^+nEuLF*^ysgG(k zbB}*3r9NwN^Qv6yC-dSErFgIAHLtvu;x_YjBh`#|i1w%x^2>R>`OMA6dc$N5dSZ_& zH&I*N4^4Sr@1-CAqT^%hc_^EID({3^7_;2n-`e4tqDEK!w(fnCjTsY6^^a$r3~_`& z+$t>Ed`J1C;SXD7Kjz+gK*)%F*HfeV_l11ypsS|bwXU^M$nw>j@>nZ_EFZLQznLlX zY*3>%D(V5=Bls-V%;d?{4Z;JX_z>6A4LJoWQaP7lwWC=r-4)+W7+x{5AGYYU1LyJCwRdFN`qM8?~-q?}r6xeSQU79!`l{bfx5^WhQpjB%b-i*?=(2 zzN_yvE^+;|4OaVz?yXA$T|RyW&tW$v4!md7XbQFQ_T)u}-ybRFclUGp^u?ckP1N}F z8{diAL%Q#JziIbe8(kT5^;`j_<%`0P!~w#vpOsIiUD3`f<&!tUx)nxmZj zrR7+s`j_9R;4?LHcZ(tuA}5FRJuVl-8Xlc&eR2%j3=!l@(g&@ndy|i%-mbS9j51~n zlVt3z2#C0){3n*e(qD%k&Mq^!zSJmPt^7{s_ECwH=Yy0DNonS<2TYT8>u%SnR=HvF z@pKxBk7?(iR_(Ci%&V$oLG$}o`*y9_qAHhp@pWMadG}70L!xKh*K~~&RMz{RKlrzh zfmuzWt)_4(8ChsIK$1DMi?kG#n~O!q6()S}ba~fczW?L*PbBF+_4l4V(o3SV#g-Lz zr-(UyDM=sRxn%`;&@oR)v!YiNA@TGg{AtgVJuFL!07JN2hzW}%Bl)=IRN%T?nr4%B zo6N5GTaJ=sRa@(hk?$9hh$b8El317Z$Uwke}*Kl^7emMPHcoo^&dYzkxt1a1y z1eT~FNZ8(q&WMDA#nK1{o9SU^g#ybhC}cZW5EB54U|0yEP+3vVp0-|`r4cg88pQ&j z+4Y~<%XklW;S0K_ZSRIc{hIf0?b;HnW43~m>JrFLkC4Lf&hkNb5u}75QlC7^p4mV8lVwiof}GvrPJa7 z-34fs@R$fXpuYlII)X-Ffz@TsvbqYJMhyit2GGJxFAr-#n?Ml1aL58WZ~@Jx#REIR z8hd0^0+SvR%0{VC)lpa?k$|$N#f8(@Y;89RHH^XxLRm*fL{S(C5HwR~jua%x@hu8C z*#Jv4Fwn;70Qb)y|1xtP^{)xevzL3U$^OX;EP$Z8 zZ4e~a`;(_~7_5eGg`nDw1@)2Tw3kpeJIY8`H!dzthfbsFa2z^+{9C|0@}Cn6%G2SL zH}5-?4K0`w6V66)oJx%hkBniWSWy%z4W<2iBmQ3tE--6>9acLg07dzLRz_!p zfXB@UqO<9d3>2O5&z|uAQfz?@9CD^z148QP6ePV)2NLg)h7j*=K|E4I5Mp&9$brp2 zHzxrfh|}{{Dh|!Gdq9JHZu(0C&jPn_7Ci*TVOx86p{Oy;SPl*P1SjF;{Hz6jH<5>y zLCYaENE^aIhLAC20g)gF$QAN}e4(`v6$*tSASM(CZG=+5&tJQsY$y*p02M(;p-QM4 zIt|r97oaBS8gvu719d}>pg!m&^adJ%#-T~DNQ;Dtz@%XEFh$r3m?jJdBf!jHB$yM- z1Lg}0fQ7&quo&1z*cR9>ST5`UtQb}aI|VxjYl5}G?!q3z`eDPckFW_i0xk@fhA)My z!gb*UxFy^X?gd{14}nL+6X9FonehGa68Ld=ExZYS6W$H)gTH}~As~bhLKdNf&_)mt z)(BUGA0imRL~KH&Bk~a?h-yRw0=y+7`Vems-+1_VqX&wq^n5`QQE5dWlrxWEblBLP=|Ab|vdEP}HvM3Y4iiq?vDioO>U z5L+Q;DYjNDQ7m8Vj97=*TX6w#RdH)^iufk+gW~7J?~9K~NJ(HNTqPnTG9->kv`M^@ zL`tekk|cv9w@Mz7Y?d65LP#x_vX-JsZIL=Ebye!6G@mqDnk>CudYAMG>2~Q68A%y^ z86TN=nS(MHW%^_hvZ}IV*>Kq`+0(K;vJ;DzF0x!iTeNdg^`g#2U*wj^S;*1k(&bLd zb<0i2qvT2QVe&chb@ERZ^DNd_?7lc|apB^ti{CDhUShn2x+Hzc=_S1ia0Lwo4}}d1 zM-*-;j4fTZ)NX0i(t@SUOW!V&U1qUt{j$BwE-f2Ylu|TPq$}=KysY>dC4;g=g`@VP zT2Le4=NNmX7^PyRcBM&Ww6eEys`5$YC(8ww6P5=r-@E+E@(~q96=#)=Dpe}ID+E>$ zR)ntDx1x2$7gea%eI-5nY9TtR|vnt`@CUqSmF(r%q50Q~yi-js{!< zuMw(oP@_W=u4$l2*ZfQKt`@HrQH!BfqIF+eSld!NR=Y~OUq@EQNhejOPUpR@imsn- zj&7^&6b6q8#~j5x!b)Ntu&LMv>_?mij*2^o>(Ud^v(-!1tJfRV*VL!!7wSL2OW~dI z+woWMKMV{FmTK8V8k{$ZS=ud+c?bl znDHwUvxkZpgnZ=N$y5)Myf8mlkXM%GEzjW!4yJDYTyc9JB?n{M7;9&hwb(xR;ezme(V1HSZYjD?VaAt9>eczO1xanY*%Y zmF}vfRX2SX`_g^u{g8e>er0}R{v`iA|Cg)rtJ7BZtU<4dU(>dB@!IgU7uSicqpUj< zz#FhK;8?(SiYujrG8RY@%Ew}JUi$++IQ;hEZ8}f9+ZA#7iL%fuJ0Mtj2pYLyZ7&& z&J50M&oam=%;w8xWcTEl=TziM~Y>xyBD=LeedVIb$M<1`uRos1oy@4>)Y?N zzrH}FAiLoEfzSip2dxgCJfv`F=bQw6thf(Zpf;;RnTb#kD1>CHY7A zj>H}rJnD0_wUkg=S+=-rSJ{tpMtNU_M@37eVP)kpg=1M&@T!=q;p3~1-#KA(qP|+Y zy7;8*$z6X#f5-g&`c%NFp3{z}ubd&AsXn{n?4cT|n)F&&Z9?rxU1(ijy>I>9a}MXO zG#EG3oYy&DenIKN!HY5%vn~l<+IngFa^mH&#;C@(O(9JKSJqv5)V!*>tHr&g{i@^D z>(^|rHMd%}UcPR6{X&~j+qoNr8+A7gZr0wy->SKdzg^RA&|ceN*inCnc&Fj6$=yqx z=ABLVZ0=p_B6r>FcIm#`B_KQ2)^||q18F+c$OzqdN|Y{E2crv_ zn8$;eR~U_r3Z#Y58K&qD=P#pC^dM98O1v}1Im((AO1DpB(mWGgyr_v`RALa?+)U6U z-Y7mIDuTwQpyDIK87!lCQ#4n(5uiD2T{HkOgMy7bY;0#C;La4iV9pt*rxVEx(FJpG zqAmufi^FLHgf=UI!KTD(Gg!-k20;{8hYfi7VA7+wleC%Q*!19^%6_RBCx=@zAdW(r zaBhuUC~SHx&4wOAW3kO}cmpjQ-VmGwa1ym}1~@I8p*}bPkD#xG*TaAl3uwS6=xO1I z23mR;td<^@08XM79tTc69N+;xh8S=HtRd$f5AwmOr>}=IHXvGAT46~fYb?&11Sl+t zXo$1M+7hg-2pD}^LlfOUEt|L3l}V=oFL3H>hMh$&N31-G<<+6%0lfd}@Qi%nB~do|p0c*D?#U=VgA1cr)n0>$CcQW6#5# zDPc4-?gvn_KrZ8Fc4QDOd?x>oPow4pxR7~?|1;0rM3>V|7BumH(dmg|hb4Lpf8ZbOz^s z#{PwH5HS7=chbPefB)E@oSX$wgme3A6wxyv80c&1V~7SAZVKXqAr?22;<%idR1fg+ z*cptz7DvjEn}S#Z;soG;6mawjcnmiMaRksHihvZv9YZ}3l{hKDgOtcg2>=OFPFZ>& zMsZS3DS9AwaZ(@)Qm!nBT>4mjkjqJdwIH407;CnW+qLCVDt@qh-auiTW#NkJ^s z#}a`~PRfxb5;(F%0tZ7Ra4KtX z%}V@{KQGByo@3HjEY9HQ;6}0o3rO>fS>c6{dAWtYH3Y-af9mi#gMNCCJF)mj9{mq| z%`q*K0Y-NgSa%Ahhr}>vhIwG-%v{Hl7R}K=dlybO^F>()qO6%H?U~31I9x0@%*}!q zRQsRko@x0c5XO9HOs?xVANkJ8Eo9HT%Ql?C3XP)2&*Q-?VHW>;*1U{0SjuC1au)J9 z(-y9W7O>`FZuEFs_$nLFWEfmi3^4kHS@`c+zsmUjlmRofxgxkS3$o^A=2{)-gkWY% z3Fi!i1Z^x<8%Ok-`*2qii{TE0Tx0(b_(K(#LxQYW6fjcRJt)ELzt8Z&UA}gqu=@S0-m8UY&H!% zEE+AyhY}t`Gs7DgfI&jn0FT!*n3et$G%s%%MPsr-9cE-X!x!#X*YP&T#-FpxiQ593~P)K*34MNdBOQ(TmxO> z-;QYiK-FvXcjZRSz2(do z|I0hxeD;DDlX;#IccA5VV-QXM5u0Q0Pd@my8H^Rd^l+NlA3tp_lwW}RRe?Pn1R7=n zO838XE_l6kcK*i|oO_9lh?sRR7i&aju-KfSL$)!CiJ=D>k!*<=ydmD!(9+VLDSAdV>0hfxbE`YA3yesy zv>;n%WW>K|inC?^#Qzhg&9qXmWCj+`bmy1Yz|H(39SH2EXeMU~j=L*Gh@W4OA1Nd# zAPDxOh)5_%N{ER`D9bOFR!~>b)KF7FqqT7shT0fY3>r;vBAQy0?8)|;2Cm*Nwq6!? z_9X7E6k!n&2{8#JNl7J=4qAuwUmw%lT`6vm8v+J4WX$bKk>hN%gZ=V@B@App0sA^u zfbA%7upxy9?1Pv+Bsm*9B*4B6DNA>XbaYM=fGy@ogAE{a7;ptBULHP>rwIz0+0w$< zTE#tJU`qv$5|&reQp#PL5}l)r^C)iWAD#YXzs0d6iB|nq*V{%?buvXRI+xIs6XPag z6%@a#M#{*Q!vl<(?F3}x-v zQ{1^;THo{Hk4kJyWc!@jpa!O4@|K zp6|!2#rdz-F#g_oZr%OB^*FS=r`H=$wayXE}yiBv}QIpUahddTZ@dIR}K?4&-m*d(XD-*dE3^eYrLD85Qrwy9DW;&Pm9U{=a1f*NoMNaao~ zT!^m}zZaAxcv!-0v%qJ_jsLQRKyf&atO$Pq&C=d&vzyfeexP1$k7dx|)T?FnK?5%*_nDg)eVW++mLM_ z7z_s4fj?-fM`kA}jvfR-WU?B>2SJbkBnpE=ynxycPBB0)0M}G-O27~h3|u+qLKu>J zJp$-OGjt`ORc0`7K;wX{5V)QNCmzsJ;93t(N%*uL4u-yihpVRz(3J=XvZ62|7}F`5 z1VCs;Ai+jfJS9aKMM);^3Tv>fEJj+GXQ3L77F46j*S5{bZEu~92N^Z zG%d^VIsDMHuQ-lG%+dl7gq)$%fd52_r)}5-XweyZJD|mif0h?^WSRylA^1ERF^}e* zM{{tT=VyRKc!tJfu>?aL&Ja&T<8%yl@P;@7sDVFuZAUn53I-htLUFHC-~i@85;JfWfIl0lK`+x#l$TdAP}KlEQdd_2Lo^}ajkR=i@f;;Eetv!- zK_OXTVOjiA`K9>(`k1PR#QC5As0jgE2EoN)2yxg{6Qls_M1rWs32ZY5=$<@CUKAg{ zfFJ-=i$QQ00s-eiAdx)439yZv?#m;Nlvs+h;FWZvpq52R;gd3p_!KQqHAuTZ7*Wy* zU?%em$SjbRTew^qqoS&&tEX>3AR1a(lWc75$o3wdUfw<{R{Bx{X+gmu^mQzDbWChq ze8QHk+qUo6xho}W@4oDu{keJh#fOiSl$MoO96f#JY;{d--MRBu8=J0OZ@zK!R_nvI z_Kru7pLF*0_VvFU7<@JKdi2A`Ph;bszkL11DHjY>_3SZSu;0oh4$1}R;X&}AIOT%D zW5J0K=Rq#T@k&^@p(s(3%kW8jQkI!TryBSbb=*g!1DFp4WR!G!mXC6Z#x2=@RM}V6RY*SP`En>Jcb-6a8=o7r=W3q;gcz7xczAPs%GLib;v1{I<$J}$tfu4aH#0` zx@>C~V#~$@odqXPA4D<^2t`Xh46r5L^)U<>#NA-V-z+^Z^J=qqeKo55y6W)&zju8K z_S$0OsaHc>N~}93+s}76^FlYr^dwiBmFcachixy}_+)qgdiQv}vtemnyu!HSxsi%W z?W|{25jvO~<0WE&{@SM1l{rt-_@E#3yTM!1L|cpQnrL}-`h;8yWg2-Hr*!HEx8{e7 zBzRT$MX{dU_#mvE=^GAtL!ZZv$n+l+NfdA{CaGUa?O~AXfR5HiY>O1r>ZYLsJwbXmXH!O%>wmr>SZDV=GLys%FVh)eKCs8)I zu-EEIK0s!gUA1j<4Kq2mqGd6E{qtefMnU7D{vtHb2)_@_}qP)g-#Vly+YtOw)nY^nz^emXtT&3>pkhe;B8#D#Q6yaO^ zMAb7N3skRh+_BZ6!u;`i58)lvYrQiU=U!&LCY0{a-u$C?;$a4%8S!eq2=#ZaL5@E%}m~uvZh=^;jS!37P@m5S`?u#dl6UE&8B>{s=RyMrjYwoj% z7ELbHG8{+`Uv^F|g*}dnlAHb#KUMW0D{Y@(WUj44?b<&09$ zVmXECZl%;I?1|__c^jPv*FFRxMv7VNrq^mKJqL-h}@^zWNqh?>`GnK`C-K8num zCrf;LnOqa4)M_w!FTExG%+lLo`xUeJnm?4&s)gitWcW+)Ha7Qp-CS@U=~i}Fd2dtx z1;gHNFP#PUoHmgA_W5%QNhx8xX2+!2`vc6(UGjAeZyF`k^7dp&BQyrf73Ipuou%NX z8EfiHrXaOy*>HbW&SymYrmS7=z1;+&oXKm^v#(J~&sJ63^9u9)u>6LChD%`?YV@wo zt#b``oXcL3=^Ia=~V$Z%pi_ zY^u`(`hEI zajfN6OMmLnvv#+RU*kjj>qepJ_*YFzc54L(M0(;T8XwkGrYjH09=1KTuRE>#n~%dW zk_N7`%ZjI`xIuV;*!QNhX7K(Zzt%@y58uk$HR>$Ya9A>kxzcwxs+%R^V0rAp@Pz5T z8*lDJ3U`$(Kl(iO;}}6)Xh$V1y^6oAq$Z)hY!b0rVC4mE@r=eT_jKQ%vE#M>_a1ri~y_x9bCA*r@%Q2$&Q+BwvYTtQzb=6?rfZ2Fk+ejk|ML^?0X+;mwzte^uVC zT{m!D1RK>L_*z|g3LH=zL=YtgwYU06NYJF#ce35Bm;Ca0h;s0x{VaJdE^e}#Pk8Q0UZ z!azOl0jae2cSN};V)3$c$wSC)CqU!1hul=g6$Mx^T?10kJlQO?_A?TcprZ zo#&M+s_J4aG^lr_tRb}2+Ca1UX<=5*gJV)_oh>Sl^-=8&UdQ^6w4aie@_4{7aBi(M z#*|+4?if|4ZuoFUxYdBsm$Yi*<@pS^hN^>NWryTf0_eJ54gjq6Nr3mKk2yG7yR zX4n*T=#iPwt5ZkJ*WFe&jaX`K_Kjb!&nn;E3va61&)CxLVv{7p+U;m}9C_kU)K+N|%$E-_7msN;hmO zaC258U(5DzATMq?7Cf*@w}>h<7}IW%Y+~Ee{AT5Cn7r15PriGd&!6$5**>hg+Y*yw zV7}xp&v$06)!P0x+k(&59wh7wFmE}LY%~~Ebx|4b_M_eQRb2c*p9K@&)Y~_e63z%Q z$)9`EPd4E)4~}UU%F6Hk=Dw&d$qc(DK}`L@cJ<+ubw0* zxLMS?6J^IMpJf(19t!g@fxZc-y?-K>=3?eSa>46<-lL#VYZNt{_=6qKOpUHLz0$gm ztg4WR8X}+DwKP{UtxxanTQ_Goo)B?h?|Ua~A@z&rEx)3O+bMfPx7R;^c&@AkBbaTC zL2qgJL^#xtbW?Hkm{4lMyXKC3kF?EMQTKFN@sh(2Jew9&yzR=RrluYF=2dEbIJ5SJ z?wio{>#xU^o{t;7zcVj8y>ys5QleIHbhtG~u6@4-`O`56nnyG%8=8DSAUFlV`#*kh zU+cFLp|NM9*6R4qd&yT0n%Vmu+kI@$si)6|YzS#e4mlxTK3(rBbley!=u7u~A)%k0 zOcb+gs=eQP@%8>=T66`c?IjDv`nTYa*_Yl`Hl4jC7QX1VTq@^Ik$Ip-lQB1-Pb1YAWqA;7B|$=|DiJ2(kv&m)FnqvMe^daymaY3oll4T zcD+PQ9+Zz7#;R36i^VyY+VxvrNn~Xut{tt)>bUL7NId1k!|WYGeURFpk1!@3NxAE1 z_Hksz6!Z%5wXM;XS)$aQdPA$V%QN@;A>niWqD$%@S<#OA-dJNsxGJ~FJ!(tyIe#;? z0dcrpfP`%`0<)}!-Sng`k^bbV{P#okE`6Ujm#xW)$ZvA5C{w5Hej4Xgcd*OpwOPlq z8vDX>S2MMn%J}ArzGLE((wF3ul6Cy?O4|b3j zq3*jmu@v#lk) zDa%HH*JS54YpL#@29tXE`>^L}&p$RC9oTs7mYsZe|B_4a-uw?e*lXuU-E>lU7o}~q z_05vK)>gFaYX20p$X_&J!}F%%uASSm)}PDic#&|SB4}foz?IF${i$B6dF&ylefu*S zb%QrAf84bGgDnl}}#G!40^m%U*9=6NxHYwJDqB6KL_cWykF_ z=@$cPHIb2z_1uHb;!KU7Ut2HKH>wxxa{7E`o|0td6+-$wlKxLcu$ z&+NEQ%JRItyv;X_wQS5_o#*2jm3N4h@6xg!`PF1QhRS@;tjm3J=HUrBy;Ji0_xm5g zT4rF?dU{{u?k6WKym_Be`?TK>OTAU&^5s!zpvI?HFvT!R?1HPsCoIx%D<<@q&zT`ilq71}O8LR>t45Q)No zp>cD$#if-lMeiNE3WOxTw4YNxVJWY}4rP?;-&kyzqEdFhb;qc9Qttp|{l@KPulr0k?$O?%Ri$*x z_`~V#XkMnRy-Jn+`ZKSNB?_25vfRIW^%iB>v`eoKrjqyUQYsKR>$bXWoS?LB<%I($ z1@%p<6KvFlO30{#wtXb&g59LW=&URpCiYQ;T{(IK@cDnoS1 z!S*Ck$IrznL%X&tCl5Ge3#ylQiXg45$MXsK`-g#}iZIm_xwY#KEL&{#kVF+JU_0BsCGd_uwV=zw`Nn-&M` z1Z(UOk?~A=a0nZ%LRCfMh(rR~jusn6W3x3~Db!F3GZ1YR5gtil#6!?@nK@FB1jn~% z;ADLqQD0vZuLayccl=}K9O|D5PTfu;KDu%13?_5uC-0~1PhLbZ1QEu7Z!&)J0`efJ zb{hoAcK+lk9RjQ2TOp|C-n{b2aN0`nVznjQG zOCUu^1=57@kO5=_nL{MV9&&*^p_R}Yhzf;3;Sdvwg*HG*;ODR1PzIC@Lx(?lj?nCX+Q>Yty3B7?vpmAsdEYhN2!Z1mg983YW9HtJ#!w4`_7zySG zbBC>j`NM)?3|KU518fUyH!KU54=aLIz)r!=!5UyUVGm$WU_G!Q*az4*I07yNmx3>b zE5o(n1h@s<0qzN34G)G#!4u$H;c4((crm;ZUITA{--frtyWwx(V+aT#h>$@nMQ9=j z2rGmO!WR*QU?MglQV=}lNMWQrQXOfCBqLWKgORbw?Z_NtDY6=Q4cU%-i5%zU<(1*Z z@EY*i@vh`u$Ge_4mG>{+6TJ1j4|w}{$G|THawv6_3CaaUL9tQWQF*9J)D=`K>Luz6 zpCF$CAD+*K&zFzEm&}*Lca-lkUn}1r-vqxH|8jmqei!~g{&@a${v-V7`S0<+;;^E?{;zz}AiocRT zNhnK@BmyP2N*tDGl<1R0NGeKNNm3=ZNFI^ACizl|R|+FVmRcvZTk5z}i`0m;gtV@- zw{)EJ0qIN9-7*LnWf`(em`u9NX_*e0ZwnSLuvkD_uxmlpg4P9}Wf#es%hF_1WKYPp z%YKtX%aPFDQOcszi#p}u@@n$#^6TXf z%iob7TfAhk?c&JAd5aqt4=<5fV!mYEl6^}qFBwvhR4`SbE9_IaqVO6mjkZ9Cp>xqq z=n?R9jNQ`crA13ymQF0gEc05HyzIoX=ZXS~1jQi5eTr8VN0bzloRl^w9aHLD&cB?n zJY@O)<;}}KD`S*>ly@lCD!;R+n&)!=G+ zY9VR|)b6Rn)%Dfs>VK&}&_HSsH5eMj8jm!EG%Yk^G>>WaXvt_fY9(vcYQ57|()QKP z)Na70m<9OU(x@R4vw7R9d{V z#9A^ft1UlU8Cq?$s<%d1+ghhsw~!=AUZi|dm(4PpV4Gt$Ber_B>uu}pkaiAsnRbuJ zi^)`S1$o5Yz<#5BlY@|hheN(YucMkH)A76$+{xZ)uhTQ<<<1P}S{KNL?6TM8xhuvs z%JqU9ubZpe0k;8nocjj%W)De^wH`-3#yu@P(>YG6Gzm7S0PCSkgcPCygepmcpf=xon zdY<*v^;b76-H^1QXQRc&qD`<(0h_KSDkg4Ce7V_nb9s_LQfSiMWNdO)@~166Th4D? zymj-|zHPSKj%^p&&fNZJhtZD0ojg0~J6m???8@6UnG%?CYd3ax&hGE2)YMyhaC>t1 zOr-^-wWRB(AI#v*U}SV;nq`(}iDxBb_3d@qTeAN=w$TC*QfkXb)0rMef13COx4-tXA7z&t5a%VHSskgwIQ|Lbt~&0oU=c7 z^}Nyf>I+&I$}TRwc;J%srS!`Jm$zP?x{`2ZtUj`SxFNWq@9NsCPa9V>wl%pmwOn(! zcH_Fu^~Pq4<|{W$Zd|-+c=OyX!mZld`nPND=-sKlt9Q4$MZcxyp25Ak`^5X_9~eKl z+-la^@X-3<^)_sZ%#`1r)lMa*5rozDo2R)LHXXbefmKG&2B7mkvjR@C{qeN=sw6NNc zu~{6Ld4fc*2Vx3Gcd@|-P&db0`5#O^X8nfI$9CTU~Mo5 zCu(Ey+IYMsKxne!8Ei_NCWEC2Gzg%%I;_FV2a_Jjouo}aj!h5xsqB}6adNmN1LA13 zG3VCMnZl;W(5&gfG#1+wucxnp*E0Yo0h~k)ygpt7Z=efKz$55t=;>g=i32p?6Ld82 zM12h%EKWlQM*t^LLk|y59X#LxJqB2C0;~b&UJvAhQ%6?^Z=_GOw6MgHNLDz!6$wx{ z643x}g|i`8SrV|iHU`Gpe_A$YuM3k-176^i*AzE{oX?vBuCxlUV!B4Kxbs&qnKi}F zp#R926ItO-V?~5Tb9EbluU4>FZSbiL7CR$2KYLDQcQKpV<+Cw6N*?KR%6`72rbVB>vCTxv@5WpA|Mnr^}T5uv3+BGDC9l;8Th(ud?=%Ae_R62um zKW+bfI0zX3g*$2B%wAc}w##2o`25S2J7z=M>?NeKW6QqHq< zK#by~oTuo3*u_bKEJ(SsAad#AbU`jB1=fOenq$DF37nJ&>;x$nL(~H_SbgQDL{17~ zp)QUHbaGOTERn#GB@#FoB7uV;61f;6j!Sd25xLrkM9ve5T%AOYCEz1PeGL#VH9(Bj z(AO}~Ab{yFa19Qpfx~OybTn|f8X$fG5Y9jY1Y117f)ETMFbK|iAWnlY3nDTIv_^Us zHdfZwmR1&4Bpm{YsDm}IvBYEXB%+0`4%Px^Wvu;Yjd5BRmpeby{AB#h{>M-=Eie~q zxVQ5|vu8x4W*9AqJ!8TAthp!5kFf@Lt?B5?)igh64n9A;{tBO;GY9`I!jc(O&`-G9 zf6JMZ@Q4Uv$5NQIf7jW#O6I_G;+%f&Ne`cnGn}tb@w({QI5YF)Imn#Q9|M$CgeDlq zXn`{lf8@_ea+c?qG!~09INH0CY{3H39Aid!K4eaAzHbe{aP*%#eAb|!-s4Uz{?4QS zfv-8HMKHkV&I0RBLG<8g=JYTR%$%OtRsm=$CR%ekvH=bk%MEig z;CaRV7rLigJ_&>|ZyJ;9I?hMFGjj9UbMCSUqp(6E>2Y&-FiV)h|DH7`V+EG-m>!&k zJkGR*>!Eq9IhZRwjuy7U8Z;RO*A#uME@1}#d)BWqD}Ty>ncD0lxH9vy=457D9q5E$ zW=jd<41@$t98MEY^ql>0R~?7t4uo7|{}A{?6_`VUxmE%#@ITh|Q({gTDK0XC70sOP zG&pVWnfF{p%*&XA+c-yu2hfNn-XfC5WP>tH%W{S<{I9aV0_Nmq4YH(D zSktnaIBlKZto@leCkG}9G$xqgf@uvfbq4Dn|0h@fXD3%=4;OAsaRtRNC}Q&`gix3}e8G|K_zIkUIBL5E;!3 zU|@$64M0Lv$e3FeriB{py~w@3#9y9tKLS%Ty4N)hDa6W~J$ z3h)bn{V2lX@)F{rqTRV2} z6Zr*!*=N~}eNXq@q~)`dWzVK1)I5K5HKhMhG(WveXhqQr;%fEd`;GDfno0|f3@L68 zQXDybq&d@V)wPa?{jVRqE~;#hAL+G`Lyz9P_C@#QD4s$g8Mjz(Nb?+eb7B346C=G3 z#=?B>I*#=Ek=DjuS){$zBgj!yUaF-3`tzXvsXgqr4wwmHrr*-C0nip z?=5OwC#CCfs!Td(eEr%5ufH$)(7&z!#fKg0+48wh78Cu~D|@$#x3DfGbR9TR)T+C3 zY2ktm$|WLi!b!G?M}5>MhV|q;QY(%|zng*@Q3?+aAk@V_+6s0JT&l`s?>=F2Z*7G} zZggC9C111aeL)XFRQ(CKZ0fog&<3;%NPoBtu0Iegnm z#?gh9w^EXCNNMgX*GRJPSAA7u>79TSfCCv7jYTT!&fpK7mNpSoaXq32Nkio@bpiNR MDJjjY^r869zN(QcexCcB=bZDLbI;r}b$_Z45_hzt+Cc~e z0&)O<(9|>8?UV#&Fa%Mlnh-w(L4uGN0txW}Dh-_CfL;Ku8Q_#epdbXe!skK+FZWss z=tVPhHK5gJFi1e-fvhmNo(3lY(9+=A1WqaBv>q74+{VMz(*@{i6a?AOSdpyh6hm?I z!WiIiwisKgJ=NRF8G;SwqGzx6R2O#)L6@Y9$3X-f-oO|~HpUY%c%rcZ!I(gT7D0Pc zr|nb%4?8eJs{wlP_MbLjXXtA{JCuWlc)$fmAmC9s(>Ch@JkJbm4QNE}GzI~eBTtZP zkuTsQ^QND*8phA?g8_}sn|>BA&?_)QivwD42G0VRv^+G#4;&i@Xz0+44R{<5acEi= z_Bry^yXah*#Gb9AJL{BmaP(ND)>@GeCHPg@FemdJ1$ASE8Uqk}Y`F#-q z7;qg50zPtvj)N-!9J9S?_Bu5`PQaB11g9QCHV{7_ADWMsAB{!}2=EIEON$5#2?@(f zN{LA;E>u=hT&SRcQPss_7Hcn2P*5joXoDVUV4#d8n~?~ny7~qLSP4QvKtNbXSWZMl zj-aBTLin$bsU}E*9}0w8P>974QUZaJKuonj%D_%u5Y^zoHgkaP$-~Qs<`)nY0)RSk z2#G+UkUS_}ULN2CL^9lcc_erxRq$4PQXVw);%I3?N^UX#66+JqGM;xvRP_VdsRDws z3*_V%E>**-YiJr68WBlkV;ftFoxKCq(aYP%ciHk4e)J$la7ZX~Ehi>6EXB#hFZn<)`^;+BY&bwXRJ@@WE z=zaFQf8fR7(97XhqaQwg`aJgK>$h>ZTnJFrv&VG7ek+#*C>N542gQSi%Y{J3gA*me z!>fYlleF?c)1svo6H@r4t#gY{Gz%=z_Z*Q4WZw~#RW*3FbQCTcw`BiW!BYQMC7UbQ z&vNxb!YI(bB~TKO1ymp5R%Xy!fUyh`Qx6cl_oOl)jzzO5U% zJ$Ya6{^KVP^0M{`$4cJ~w5Qx!W*j(#zs62%JJKloa+7aU9lGMG#<4*Ecm2wadg5al zmqOjjY>OC#ffs3of1oJTd0 z`q*n@W#T~rdggW21rM|Mp&!g!A)B+rI*V_a>H7383%wG?Ht{k|?==eP+#4a9M_K6)JApcYCV@&zaVjOT9&P5az*7`uZuh44v)U4(AK-ftTB|j z!<%by$-c`y+^lMOyP`nT<2M?q&yG9sUo)$}mwrN$m_SLsc{m_h<5+p_cf%v6auBAu z_iG(FPp`FVBT!w@KA(5QE$Hg+-gApKdCOq}*Km{Qg7o_Y zB;kZ|F{`MIc(X&vZPHX@B2oUFX@>pX)-ucSs82Q9w`djZR`yG|loVH$rIPdVbbyFO zZ}ywfVSkep((;MVYB`CaqV6FUm!F&->%`{ndHUe;LMe{rU)_$v4vD6}6 z4}QXHJzvyP}nKIWA!a zTWAf+nUT-Yz9x;Lb`BLSIqFRVVr;k9ZV!!23<8YytA6VqIe#L^2fM~y(@wjoooT>~*4pJi5eXL96)So`eTGvDz8e`vYiSZmP}L_DM1 zp1z?wJAePtgjTP5dz5>e&Q({r+_$-{J%`Va`h4ze9k|6&>h6s;VO(fHecxNEBO#98 zc0trT_S0m{y%e7vAKF0Nuc%QD5qfgv=*ha8=DHs~+21!j6|+2}YvG*keJ^&;09A7Q zMQVMtYA12BBfCBOl**0p-AnTLTR&7V>Vy@xrsAK?XQ--e;Yp^ zf8i>ad6FnU{^d(MMKx)xe%qwQ`+e-(9SRN2uP;k#7G~zjptOf7mdIC(xk@8XvQ{^k zO+lJt~y9 zx|YABFyD<~_k_nc?($098LRfJea+za#0s@@7jEdga8x4CNoQ@;_cbm5NU%=Yk#?*E7!u2^vRtJ-YDuh~9$Jw|I`E-{% zYCXEX{3%WHx!+9Cl~8=u-oCU>rwG{|K8Z?6$Mvn>Nqu6g^N0FJk80h&{Cs1one!O| z&FqIx&(`eJ_rHAC@v2E!HsNK9s>2$gLD6Rk6PNEcRA;LV%N@2qv8ykuZ+w|k6-680 z`^1LlSxK|VAi4i_Z~f5iMgE=leD1zgaJa0mqV1$KguU2*E4q&(>SSGY=gow9$Fz2=;5~+oQ|^S zuG$q5%CASv1idSql8Uxoyjf>-z%+;0TfBmvEqp1U_SF86hn>fIt&H1V=p3lNb!P40 zRZ(1Yv(PIowJC_&bMEtm*_aMeI?U&~n%w&PFSfU}qVA?@X;jx;#J>5G{?UK*#JML_ z9*9bAz0TiXw4tP7$dA3@lf+}!OoL+!v)`NW))&ZJeqbrYIHkJ}B~=ucO)6;!y8AAD z(1~$T98)}umeVv?wzM?B-cOm7Qc>g#^FDn26m%*$D7U%JXY=OaHq_`9z5BcJmp}o1 zgikUFy}al!M8_BEcQ;ab3F)PP(&lUsEF_=NNz8NsEOSk>+n~l}*Oku-oZ~f{rXauD zIF`u)u^K1T4~@9RQSwcnRCDdms9f2#H9fkaf%MA1&UVb`X52O?D?26kYfk}=pm;J_ zdZQnmw^b#(E9HA-wUuecGxfo5v#M_PRo! zJz^Serouyp7t5Q&j@S})S|1kW72K(kUgK(2UDZ!_B)*Cd80kJCBkgsEMRe_~HpLz} z=i4)?MPL8nlt?F$)t|C5`NEO+M;|?X>+^BZQHR6dB>X1TyG#W-I91w_PFSB z^0JZL+m)VBQlEL-wTAZfDQ|odQ;t7_KkTM^UIJUCF$D=WZB&2aBJSUo_U_2~4f{P@ zHK|wfy_~3u?NuRzD-DY2!b5T0R;gz8?X9m@+(0Pk-udMBmuurGe}?_tnp^F01w>1w z1K#f}I_vZT?6!uSK68+?E6}pNHq~S(y5^i3!Q)4_{mX>JgUc37jB9moI6^ul%%*;M zo_)LppL_7LUXh%_U*n#O8d5B9tCPgF?u=!ObgSYc++$qLw&LGzVr?_;#uX)45OR`v zbw9I=yBe$Os4^?0gTzAx7OYRNckEk#B%>08_$uhiP|rVCkzJ;8#_P7mEx$|0Ny;8p zXFSPrW7UsxOPmjdFEfM21vTG45YKY6@S?a848CM4Yo9TRezWmMOd>lYw#ocr=Ps&- z@<#M9_3RFnJyKcyhPU2&xFQLp$bEmkcfl3Wzj|NyFOIyC{#RI9)8o5m%iFO+`LP>cxW=j{a3Ut1?eKd^BuF%2IVI2>tr$>XRbpFeSVi*87L4N2GgRJTBwe(peAR=Z`# zT^YL5>Fv}#&sP;r$|ErSU4nNKbWP9Thua4}REJnw6oegdE6`MzI`^nBTPCyj;T!)Q zFHn;Q6{6qZH0vJ4<6Vz93|L>>$jRHdX0#@+=Y~6L9_nX zK8`G(f?lG&bzQb+m#KDVT+{7*;=SklA(68IVoFW-Y#2xVuC2BpU6SA68NIpnY=DL4 zpajw(P}05?gP}-WzH+4%I7jhsW}zoG$FD4>_cXwK889*a=A4TOX&+gV)q_%-^1F1)!v%f zl4mE#XSV%{t#seBX0s-R+la?mk3Tja9ZbG*-9e#mK~dThNAr_Ju(m8<<^2KLj6cWGVq&TlDhvsg1y!+MKqk2)s5gx819!u)e@n5$;I<@2H3h;w&T5?yrR!5=BS8mAXEDN%_-@ffe zmdw*Z&HAXQ`-Yyur}5^dkFTr~?jJP_aXZu2;FaeaL6J64^RP#bSgesuV~kz#%iKj)8M{Fzrp zO+LL|F>w{1 z3x>u`6;|h0xD~&5ezIR!>TCDeB`Ipl`R~6XzY)>MAe_wI-^FK%^Un4ZAvtum7N}y6 zXmHHaa&1!m%dc1PnHhVyM^QkK!*wB>9r`K^yw6jis13Yi$Rk zOjyGd83$_;DqfNQm?9$mRrsNtBSzO0jnmc3Z+C7Rl}LF$NL!bjX7Q@uEICten{JKj zb<+(Wa#`nJ9n7F+?oiz?dfH=E*BD84?TWwm z9Tze(uS>Gi5-y{n58C%rWcKf*C}Q&R@Ywi+iSNB!-!@w8`SATCMY>PpomY?aqUank zrGwomV$NSm(ua3!UP>Kw$`{hA=oLjtJUNei()0K)j+I0J5$PUc%Av?eKB_wrxF(OG z)vVJdvoqnQlO$Em&ZcAJ`?+MY>AE`EZ zM|GxvC29x~c5r60B9UORG=dew_O!RgfaMkpx*aTt34lc~JOt6`oG2GBJ8yVtgi5i& zZ~$m#{b%Mf*27)+g6`@#xMMKC=Kb3{33?Pe1}ql21EhWsgF^@O7C?u`$3(&W5kN}^ zhQTzv;s!~xfq{UQgXxfITAPcRrY&JQD1sFPFtE*0K@mYP-2&+Caj^_Qqrf^hlorQi z!~?nu(5m6F5lldT1+;VogT?`?%kZ+gY7B!O3TPalh1uSoHh?yTAb#PHd34}BI);$| z>;!AZkrppXVL6Wd< zF~G@2c(Rd^4nY^VfA08~nRBRrO~7?Kjri!ntuut|&7ZuVvOjr|B@jgV41AOGlNVSB zL1(r?kX-Lip6Vg68omXB>O1C@M;2}`p)oO0#(H}3@$tG$23;3+=-lyd0dvTIPRx6r zF8sVX-(hSS!L-=$7!2%GdSrNHEE~g#qR|-`o!=Yr|5|XKS@Z1B_GAPz*bEkUlrLyy zOjZb}ZdMR8h8fAiFj@bshX0ph^K5|0>2?hWsnSVEdW|k5-XRU4-rj_Gq=X>U%7Y*W zF;{QS0=^L3^Oh+OO}BeMgM4oKOM=V>w@3~%1Ou~eJiRgWSauvtgFXQ#yztLj;CB;w zNC{d3sY5yt0U|;skR?Qc93eNz8(IOahUicz6alfJcxXM80)GD53FScf&|atrDupVc z8t5c+208~dLsy|2&~2z2dI_p@t_9BWAm539FvxsIy8{!V)0pb~A81Vryjzl4akt2K9}Hk4K6J!=uAv!sEc>!$aqZ=GnlL z!LygAf~TIRh378MGoBHiNnR0N1zs&)V_quna^4W$c-}PL0^TFMb-Y)2yLn&mj`8vF z$?{?Oheq!JHS`V*Ti>+ub=NT_=P|ot%Wv2yP;|57<3xC5M7PFi0(wc zK!4>I;#cM;@Z0hG@w50-`3v}u@?YTZ z6B7_yDrP0NS}aMdK0{FE(jzjG zG6pifG6^#KWX{X<$)aS{WT~>@ve~jHWqV}D7bq^UTEJMaV?oV=&IMoO7Rg!4G33(a zYUR4+#^o{c6!|duT=_Hdj~DVR)LiJXFn-~|g;y57StPy4WD$K)`l6GIdKHignhKr@ z>l6+v+*J6isHAAG7^PUKcv?3s%bqn=q^%C_i4L%K$MwrF{joX?? zO+(F4&3&33T1YJ;EvD81tvlMh+GK5(c8T^q9bp|Soj9E;ooBkTy3V?(x@UCX>8a}Z z>E-IR>P_MdapAa9+(W!1-VvXQZ^VBfXcFjzeS|K35q&%TjrtAxqXt?A41)ff#ERfz$_{lt4DX_7lBgLI9|OSUF&B%dRHGd41gF+OSh-bBYF z%;c!aOH-^V!?e`2-)ylN&8*n0&m3bOV1CHF&qBq5W>I4C+;WL!kmV7}AuA25wN}+u z@2qjwZ0kDfFE++D$u>>4C|i5mblY}{B*llam-57Jv0aE=mEDNFq5V4hCI?;zCx={z zdsIa#omxp9aU?n>JGM9pJ9#CAR+bV0f}y8Pwx$aSeJ%k_*KQeJDkj(UxGTYG1FKlD-eiS@bUE9Se>x6=2^GTUW& z%lek2+k@_LyR<-Ox=uCkKG#kEY3V`U)iDBLlb9+ZB4CvIoK&hHuYjO&^B z%srV?S;1ND*+$t1bNF&tIX$@+xfOX5c}aQwf4TfszYDV~eb=Y_HTi7?1_edC1$W2p z?%U(Mr=d`_FsJbQ-q5|>`>glX?pN5qWB-=}K?gbyS{|$^QY_k8G=7MA=zg(%aeawe zNx@;h!*PcPOMOdQ%SdIFM;0F0dE`eqtGutmv!bPvSXp^g;b?XhvMRP}xO!#v?PIpb z8ftWEifd(ScOHk1#~y!mBH%>NNvD&SPLWR4oL+i*f1OladOe~(v3}%C=$XET6%BXJ zI-b4MXwq2sx9;EN=Ty$^J1=uS`-0$wEf=OPCSCm86xH;mIi$J&(wa*TFE78`)#B08 ze#Pm^wX1elFSlB?Uc6>@?OdC2+u7@+>t}8l-Kf85c(d-7;jOxMqxSj^Vn@Sm^6kbu zrgtuMT68wwwY___i`sRg+qL^nk5AA2dn@nt-4D9|@a*8`k^?=Q9tiVPMEDGyb?B)mK~Y&U%SmH(?3 zucKa1yh(d2@pk_^^>?S=TfVkL8~XK3)3k{`tvR#Ms1_^sn+? zE54DwwT>?vADW2&j{d&qhsKY_NvFw2QxV{UgJ)9rXXbeqd4(}zFoBE^Cd&-_{_l%e3^T|KyUfr9=MrVZ2xU4Xu^C=TuHN*dFgiI1 zYhf;EnqZs|5f#CRpS1T!pGxiO$&wjLIM*g?U@p0;)~5O8OPoj2!<*Vm0? zhvU~mq% zWI!B)F@4QIEF1Vgu#h1Cm0%O6AX#qB!QExO)w&86Nm=j1U!;~wxK=_oOnP3 zK1pAjKsM6W$Kkd0@g#7PwG9d2)F%KQ&_l$56JUw(y&=d4r@nzc!NiDcZDoz8P;Br7 z8w#NC6f%)ugSR8uSd(xDc0^OXKP{WH*Nx3&058DhHOJ2&=kw-(D{KO7*zS=W?)()@ zX3YsR=sz;%M3#FpIFaG8T-`+Q)d~)$2R^mI;bsKqXV1y3_}4P?v*%=fiukaY!0R*m ze`C+VU1(tpbM6OFGe9omXLe)|BYZmlk58j!1-OtoiT^WoZmI`&lX*@2Uo?Br=jOW5 zFRgyIJ5M)y05f)p3}OZ++R|be=Af3zI^dsR;0-=fGBh&A;kCfWV)~%Ye?!9FgIP9g z1`zyDgy$h=g1a3=$N{P&Oj3FpjxG9JaL_A?SC2%>@ zsXpKv;-@hN+OQOnn}S#Z;soG;6mSekhB$5t;s~HY6aguSJ4AgDmEaWMK}v>G5yTxo`@s1?e=0$fZeeN(Od>yPCj*^u3d@p7uq>GbW5^^JLnd=EWIUIKwUN2n$Yl75WUfv!Yzg>C(MTJ_OKlKi zwT-li+9WXj1+Ky4webXPyuLQxKpVtQ0KyZsL9itNEC|6M0)yae2;wvdvmhdaKx<-X zWoKh+Yi(m?L(wNu$oe>>Z)U`FoPobWB^Y34LvV zcwVvpiSFr^PXS@fm%-+`4*tk@Ms7ZP&RurlG)`y~Ghq%7W(hO+-?QdqY`{_;+Y4UE zgQqQA56xrE!Q7b%jPT{QpvkbfrWoN2NHg%?vwoFX@lyuO)Mg*Sm6?|{Co|jXKqmw< zTUt0g5R!E8cpU=Sd-lU!Ej*4p5OR(EL*NfpU=9i9T1kwc|5(>gi8*DIgs4bPEPJ}s z;Pt>~-g6Z(FJlgF=NcOk$Y7h}_4Q_pJjewFYT>*@by?a$0PIWSRRu)z!$OlyFtGx%Sab5OfTIxXCp!3v29HOG^F`fMKiSFC$D zjm4NB9`s2h5`5y3p2>qoB6KXCs*&HRuI-Y(z?ww@vQ1o(F~^Kt(`&Cg&nJA3}k zjNbrrDr{moF_96T3|0_W<_pLO9c775swp$G8T% z#=jlW{(-jXk&HV8VPL&cG&YCZ$jq>CpNM7C8PhL8Gd&I-#()+7&1*preeR_oDwZA2 zU8M-3>oLL^V8n{yfcFACyu1O1=pedra3ni|7GutgpoK8>)yt(XoFD7$5W9~r9?ZzOQ{v$SQ?@vDXwHb^R!OU=m`5!-R&X=Eu`&EGh69gJ| zB1Z4ObS`+kbaDB|1)P0}jfj|WFBfY}WpQHQphLAakBwyp8B^@YI76bL9ns3l%9eu1 zTaj$Q8;1?vnnWU#DOMn4{~-sPKFzd^q{qUwNVVm*9`;O=F~d%)ru=KwSZ;CWbb%2m zjuB+Xj*R#>O>ySgKk@&>Y16F~ESZ7DGrhSbHgGeyNCyJD8I}z%!Etw`2=Vg^@}q?W z1q8u<6cGspNeMAAiN*2@r4=+(wKUaLu~;2~B~b@whQne>&SWziiUZX_%gD{g)y~_} z-hsm1l_D%6A|WQBA}Ogt(Z%Xg{_A6kyDP;Vaz`P+hK$)=De~|}J8-~1LCzcq9u$%n zZ0uMn4k37uNU(Jd-u1&hz`hMhupvc73UB2>i{>K~1K2`X8ted>#egf=?ZJ!U19_U@ zN${Q)6cXNB#XS&UPX&((o>$UJ$|HI)4c^c4Y-Dumm;DwUhlfQC!RLJk9vS3p)|{Ms6Iq9@auSt5RUJ$BetOzBa@ivngR|Kd*atM2%<=hZTG;x70xFUx8tf6VV}e89Ol z*5SN-*WFzQRHJWP+MpD)r^9<0KKAsdHJj=7RcWMI)`z3O0l$>1!<9iRwp>wnDep29`87f<4^Y0HsATQ0zY)+sX(Po<3kURr|RT@c^RSCgT+a;KT{FMt^opTQ7fZ)Z>&3SXpT~^!?QmBf3TS@3@aC(Z`RUt= zDM7bYp4wr&uSp&f+@uLrAP-3%T3(@|$-7rQ(8FE8otKj0Zl~#wENQAZ?zl^Ej6bw; zYoVYJrr1go5vslA5OR~?CPzfjA-+0V3f0oe7Fr?{pv7o zpPum)BO1xT@S5Uz15IXxM zGT}ym`*K6~52sW-0zMpLC#hY@+_8fAa<8n9ePfJv(=K%#%cam#NLpvJkC>J||E2rV bvX0WS>h8;nt%6G6G;oJ?x-^f))Pw&4t#0~_ literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile03.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile03.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fcb7b194af972c17485fcc9a029294c21e352efc GIT binary patch literal 15276 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 literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile04.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile04.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0cc4933277d74b34064f1a2f9beff0611631d9de GIT binary patch literal 15235 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 GJpLbx>mUmN literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile05.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile05.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6a6d66fb418ba85ae39878217cd21eb105b3fcce GIT binary patch literal 15254 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 Y!n-w!D|?;xSSP>N>nM%+Kl}850I3)?*#H0l literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile06.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile06.jpg new file mode 100644 index 0000000000000000000000000000000000000000..89ddd6406e6f3a21a6e8547d412a7fcf2ccedbb0 GIT binary patch literal 15292 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 V2`WYPfL8(4xB7Ojr{Vn5{{hKJQ5paM literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile07.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile07.jpg new file mode 100644 index 0000000000000000000000000000000000000000..967931796eda211bf3d3d77169ffaf9edcdf23f7 GIT binary patch literal 15227 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 literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile08.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile08.jpg new file mode 100644 index 0000000000000000000000000000000000000000..aa895c49695d0e771ed89f047204b30379021507 GIT binary patch literal 15282 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 F{6FriNHPEb literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile09.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile09.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9c234cacf3509de39ea144244fb2465714ccaca7 GIT binary patch literal 15532 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%0AB;MfKgXu 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%0AGRb{S*2d za}nxBr7>t0ln7P>aZiZ&1^**sQRKJj`u|U^{?AUXY~7sLF~t=OYmDHi`LT-ig7e3? z2D-+-9nt=Qw)v5aJp^G`dLt;zD0U+=!m|2A43kQme+gRXajansSn=Py76eikUkV~( zn4#=dia@F+EtCdEtmr84UVvvUZ-5~>kg6XP&J3eO8`Hxm!8FZ{5wu`K&0pOA?zs^^ z-*Ofo|Jys>V)l|3lSQ6Bd!S`^V-QXM5u0W2FFyFS8H^P{^iZ1dA3tp_m0yDUU4ac9 z1R7=nM)SXPE_l6kaQMds{P_|a7PjDCHdf!35f#k}I<}U^F){Q&eXyHa@h_=E(6NJ~P$<0~;)acEam5v(bibj+A!%c2Xz&hpe{*9PZz=iMz$K|NnAKkaOT>zZEjw4 z#k``XN6*Im&bS`9+&lcRQ1bJ-oy0V^13MLttnJ=(d>PlOFl23Dd|AhBQi?IpiVKWq zv=8529;P&ZyqQ^+*>-XP4zuht740>zwy^b|qsf$0-}m9P{5NCUZ!wx^G8%&&AL~PZA24jk*OJyT?9sol4L7fY|?yAKA9O z+jiZ0o*mkh)>G=*XV-OwyxW?-w?i+xC=pN5RqEe-E~S7l{6G>bbt!xODZkSr{&!Rz z6dS(H%0;NJY~gwG$|ZqlpOxC+c&VV#E#2~ka?sn;$Z8j-?l}mhHDLeno2j2f?M=g0 z=6#p$gO4wqKOFCRX-q=XvojIJ2@E`%ntp7}kcA(YboW)4?upvxA**5#{;QN-T-w8< zYSRL#H<^I6+FN2?yY#-6OC0Tx6YT*7! zp`z-{`h7NJWcKElw@XZA+={)ms;=DLdgAjrJ^9kh)77tg6dfu(f4ErGO4~^7Hb`T^Iw1yV#Tp)D*4agXcb{6sG0)dtTR(_4?dYNxQSsWfETkjlaGw z*r z`*-*`JQH)UWUTI%E(jPs+tm4B##pJehd$7PMKG;9k93`NNjxgoIO^Fc_e@6q*$$ND zILflf>))yUZSRd3mok zykgp3i9bXYn`Fv--psweySnnpVN(K~y8puj1$nH|EqUbakMz1$y&o+yCy!-Vi^%MW zE+uxXc%j>9&^z$MOV>-;$=&7k_)X#j(P8$Q)Z^tSn;SEPwU#a03t}Mg9WB}dO#^xd zf+7RcB7eC2;2-_#PJiMadzV|%TkVRb+54M{aiMdM{s&C) B{|*2E literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile12.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile12.jpg new file mode 100644 index 0000000000000000000000000000000000000000..448faa253305ca41341071de4fd66d11f6974c1d GIT binary patch literal 15605 zcmeHOc|26n+rKk|vG0T|Q)KLB#x`T$24mk_v|tP-48~3pl}h#^v?yhZl(MCbkX9nJ zQADJbii+YrGbGh-egAtupZ9XleeQF<&vT#ioO7OY?wNb$AI%R!{MHsE3kU{-K~~@o znjaF~ON^%aLJ*0h1hGR9#0l}i;1C<2lEKLj=oR3a3Qj>70)l}ngMR z1hm2;1`cQ}kmUx~8gSwOEex(r;1q%{=wV=}dsvuydH`LCfFLt3S}<)PMG+lbP?{Kw zIm(=5Npdx@g%}1i(Lb-2Bs)hGP7SYy#XvX=R#O*4(8cPYusXV$I9(hbS_u^-E!Zgy z9(Ht*mIw6ey}xWgFVZ)Gb|@1Gv49H(hr=Q=7i?Aoc$P)l6wt7&1q_T)j%-e*MecwP z&t7=eW(Izd?+a*T_QJDRfnJV9njg@di+CEqBxfTbcHr1ZKtsnDZNOqMu;UA|44=b~ zFZhb#NW@Q?6M|S5>2%;f-l7E?wgZ}Pkxm9Qf6=e(Et_DsQiw8CEH?QXuqfNn}Gq*_(b|=a#LmWsWMgGVB9R;%?3~=fJltGd+~R^l ze8N%^GSX5Kl9DJnH8g6qimarhf{vmJ=#iS5GH8MU9;dISp^0NCfpKtfaC32s@$iV@ zR!OeH{ny8Q6C}V6c|)xT*lGwa07D4C=35~dU?(exYK*|Pc!2K7!peqZ=iuZ5fLeYC z4nrW|EC^Os7T^R}Jfr)v2(SvS!kVxNIe8&hhYI5ovre$fnpQW9ICo9RX?W91(Akw+^iDrstI>);8xX68f-ODmGKi>sUa+I8zUkbNk=e*V<0Vc`*xQPDB6J9q6) z-m`aKO7?++Ik|`O@(YSio+>UWEh|5L{z6S{U46sF#_KJuZ8zF)-n!lWpr^O*;iJb- zhF%Phyc`{SHU4_?;6G;uR=8)BJo)Gb-cX5i!X%4NKWE72Avj41LN&l;oEfwrn zxt>7W2++O-5CV`9R2Sq>tofwSdfc%2o!u<5Hr}Y#vSZ_DTDeqtTA;($ar?q|wz>(m zT5d?}-NP>9wzJr(oSJ!Pnp*gH9(vn*x@==RVVXQ{7eF50wCdbE;7WZ4bHGu|HWhh?nQ`KME_ z`#Thy_s#V-_Sv&Rx2Cj&)*F>-ZKVb#7so%|KN9C0qg4}_{)~+WdnPYLR<1X!zbaS* zeRH~)-^Wwku(mSyX*xUfgL=fwxD)#wr}& z#@^avC@qF9f!&h*_PSZmtwxR{p@{+P=t>lOKp)+Z@8TXbE+( zW*J?#>~RbJ>wP6lxBU`D!Gse)6aTz z);jF@&2|+Sq9@dCYG34vp5flSdtP(*G{^h>yxOaj?QQZm^6ZA@p~w@s4i7%%tVf)+ zn{D^(vMD!y6z9UTr*?~bmQ>!=u-EvKLpeKsyqJ9%KwO`vuZ$}sSs;_Yzm|DxwG;7a z?8oqi!Sh$6lK9-@K7D`fnjdy3m3*~t9+IeCe`aquy7q2V_sMNE+ZPANn|N2GJi@_o z)iNh&g~d8|JEa}w^c81g#4qcoT0UqmHl7LjT(x(Xa^WGF4T;xdBP-HZWxlHMCn#kyKFjb&en<9s#U-l^P9kL{s(J6v8n2aifn5{WbfI@Q&?$D;M}Jlpfta?)0OIBWBN-KHtHS@Xjfb&An-1;#{=DmK|yD*?~Wl9q(*4>hsa5m+45^ z-kXtgS0>%2y4y$Yg-Q253Du)qZ9sf4IHfAUkKJ>X z*EQnvT=>I8w|yUPfw*5*CF95StnKvq+N$Q-A8r}nw?F4Iu2(a%O>un~kvBpToOzj4 z7b@4SGuN5Xk#S+woxnq~+3f8f%P6(nl6x{e1=(8KhuvLNJt=>nwcwKOiT>&G=WWmFsrOOccJG;UvPzH49l zib#Dwjm`^<@mo$sWM2(8oZVq;QV9d})3$!1^tSQ%Hl+zcJFJ%?=9CEus! z&YsvLp7l)+Sq?>26bvSJ+XRXBu?dt5T5oIrjvwUPoHI5&d0P2V%Zr301KWBIrHrRG zLtFN1c(gpQzM&V8fqT^|XSIcEly@k4w&g)XWrqB?*h$OkgM;aVGiz-sh$`48&&*hc zikf*w3Bzxm)Q#O=>Cyer?ZG=qs}_w_DmKz%=xf9GLI=ZmZA>e=-p(3!-h6X6gy&iD zn$rVOpQi8v+l%_YxulTd-%S0qR0)4JNZqY z1^JarxK?8CS{w1rU9qa0yc)@OKV^?&xBA_e*Ef#kj~Y$)^h~sbVJW2kb1cIh8|M%+ z?hV}|^)Ayk<*Q{0UOgitW3uSuGA8S=U%g)ptRw2v>=PauuRI$%{w_7FvpBTp?D`;? zHxmY&u4Oi{g}blat<^rNpQ-cY#CmcD_jS*+7moNn?LPCwMEBN9)uWa7>bH*G;KhVC zbG=rUpNB|&m#1b8rd8p>0dBYD#kM_qx%XB(;z5$KVrA_$^xH2fpFAe3FFzx(KqNx@ zZT8{9?L`e^8|d3V3k=w&X`YeD_@KvHmn+ip*qDoQLG3U?s4y}EU)139;C;%d4gTmp zqxib4&RG@MRENerk;2-!7qe;G&&quJIyVn}MV5;mgo`(`c*-2~&b*P46G)GNCK9ag zduYk=U#Z@i_=9Sf%i+8iIZLSe5V&m~y5Q@R)m-bgbLaRi#AKWLqk}oJkmn%ovquyn zWrO&k7`Zw`yy>%CmSz2_wu8Gy(NArOhj&CtQ?rAoZFlXQnf&Tp$ou4(aCQ2?k?L!`LrEzHjCsu12{Z&Fz?Ot#i}9pyRETP-5&(nZs;@2=EGmt1!5n^Y!m z`*?w;TZcBBxH0}}$%oVZ&)>OyT6x;)1=eopONP}%)BF1_mTi4-hcX?$_vSr;m&F5xE%9q74(*kG zMob!VwP^Pr9F$3T7G8?2$DVXhyCQ(DP@IQ2n-UbB+3|baN`7CmZTk@?dnHm^j*AUR zs-wbhbfe}8GWS?yuSt@DWk>s)^><*BYF(c<9I$V^;6br`P<5{(GFQh~`l#!BqwZRD zPmA4tHTB2v2fd9u&L-)Ng;rgb$2tAzwR{yFb8PL3*%{^D?IrjN+;q~H7a8YTv02Ba z)Cwqu?wU@W=#|3;IfmOC?8d&^LEB^4iz$pY!ez#@s!h>!dm1Zi zNh0foefa%3R%}bDvmV@5l3I>}edV;LDCAr&%P3w|?{Z)9-iGVv@G?#&_09yb>B|1B zBHQDEYYm_oPNffz`O_VYT!;=h%`a&(D)oAyZxeom$Iw$FnhdXXA0#QtBp}C07x%5o z6G|V}y7$h>9*)BYA3pHG4pT_}>U!JbMDU%I0|CiR0}n2icA&X(%+aWw&7bkdn-g!z zPF8TI#=dXwD{x8QksaEp85Se-w#&73MftmDdF0geQ!{QQ#wWAtpKHDeh>N=sUD6mm zd4F$yPDaUF@k%qx@oJl5 zo^&Q%J7#3&IxCK64&L-FY>*pKK3T@*9xHeEXJQvi?Nh0uDwbtK%aryP3 z3N@;XU2?Gm|Hw`(YtEJTm8~_m`GZ#85&G(vqW-yd1vmNf(a7`;nWSg43q`ViK z^5?{1sNo*Yu4py=dhB?|$j3@QQ={B~5{FzR1)!f zKMHGKVl`rVEg>vBVasGycHbRGT0-?&7W#{EBVxrsW;WSpSk9JKhAT}lTW(op&8}$hMSv>@Ylt+JBRLUzvyYCG%5hM@)opgN1#{N zg||L#NT5D`D*63*lf&?r9i^MIg9}=n%S)9h`=3VJH5_|p_u8m$b)9u#nWK@?EqPpf z`EZ56oX8c)B=IcDBq_6ukj%xGlX2W&(oIWMTP&+8XboB76FTqM=KdLKj+mF~ZTw*(EBy5l``31 z2`{X_@-P7v>x#QG?e{C&iXRk$>v=_?{wC;FwBX)v%T!UAfB$F0eZupX(;qHmFw=|`;W?OKw8SHH{7akmH zHfWN(4;x4y_|$xQG`{V&mE_=v^cDDvf{#O(w#G>(ja0Uk>G75uvc=kZPOQE@G7qiv zZX__S(*+V)s;4?@>9N6kDnC?FuI3*zZXd~dG-@_*iTt!qIlJ;(G5LA3qigx* z4I(2)yiOcczTukFdg7MRmWe9bJ48pw8F5)wH#Q@~sV4!V+I^9gtDDK~PDke4Za$$_ zeo=F`a5<96K9_SwA%I@$7Gy4!Q*&?SmthRIoGNgk!Q%h(Kb zog70*_~6+wP3LR*kvgYl+Xqf#TOho6QOc+p`C!5+)Z4A*qak{tQrbSbBfK?En|h}4a$DD5I(&{x+psp)LYcdmggjpnj>UX%v47WSl=t!bC!+A6;(M1q;gzA8eA35y6Zvev6s3&s+qs4` zYLmmIT=s+)A@J-9{8`_?fiM#RPaU|UpMDroM6kcM+IvejMY&n^mdO6-yEcL(c?+}7 ziSL)=3Hote#IW{+{PRyo+$JTR+RD5&U8Of1QE1=VroS8AouXWNV#wwNkCURliL_p*&7RkWG$tqus1TO6GoEJ(IQutW_(+*YCTp>D6rguLUw=!F%GZ@hJ_$6a#)C+i-jv=X@o>HLxlm*;`-0xWx9{K@C7|k zwQ@wEe$V^2cLL-PdN^1tas)^XA4(V*(7ON~7!@AE;7^8px!%^^NPJ`B1Q(0d~zD1b(Qb#5p*l1hmJbPu5A0waQ`fc^?- z;UJ1v7+76qEUU|fQ^@{+#sHd|?&@p?XnhD`=k{AhdoQEIDbc`Au*M!75<{o@`G=zv z$ciW|fq+L@QKAAV;o+)|UgQ8Tx(~`MI4H!676U;GWoAf0f(+lHfRnYc1Z{0qoEmWd z((x}dmr(zjVASmb;*%4z&S0WUTqs*ppv8x#M6p zd=~`ObuKH9D5Jgjhlhvgs;fsuMX6CKWHp9EmyUl6SVI1DV%hW57|&bs9m<^I>lG0g zj$$~K92^)NK}Us!c#$b6)ju2Y|5|XFS>{igb_>=8dkhOGXSL-XgxSu(1fSpjcE{^jNG|+*rsgp)A{3QdtUE%2?`HT3H^j z46#hG%(3#YO0p`m>avnp*RlGsMzJQd=CYQs*0Q#-_OiZYon~WW6J9AR`t!LZH z7RQ##c9iWbTN7Is+c4V{_=P|msf;v0Iv~A};mBlUKC%*d4cU!+iTujW#V*5+W4B=6 zz)oXNV$Wqi&3=`=n|+LZmV=*T4Tmm=1BVYs3`YjXDUL>tPL7uxvz!8)Xil(Rz`2z( zku#68inEQgpYszJ7uRYo0+$mPm1_rA9@japn_NR&-?#<2mAK8gH*!aCr*oHbH*r7a z{=~z>BhO>Zvwnjh_)PdV^TqPz z@?GHTMhlaKw+inUJ|o;AJRu?|qAB7o5-oCA|i5 zC0;K+Ai*M`B;h0xC2>rmP2%lJ;gxzT$tzPsuQa^ zR?V(PuXbCVwEFDo0a;F2ysWS6LD}oF6LKU~D&(H5;aG!T*YN8scTA@0mCaPwumZVm%_Fi31eS>&IfZ$O z6~tO&ldz50k2ob98Fv`hqrszLp^>1`pfRbbtVz*4ruj%qNXt%ZkJfdqAKE(FbnPnb zaUD4wPn{z=5AniyM|>*&CV`b;N=P7FCVbP?)(zJ^ulqqyRWCsAwB9Ryv_3`ul>V^6 zY6CBW69$8ZC__)f04X zYcfZeTbiescMt`MZo~rOGmF(0eijuL6P8+*ah6S1tX4KwSym58QY12|oHSvrV;yhZ zYQt^gVpCxA!dA(aZrf-Fx3ji8VApTI#-3(h?*KWF91b`PIHDax9WObtIXOBVb{ciY zI&X7scM)>g;&R$$+SSxG!}Y0~f?I^!b$34Zjqc^{U)Gwh&0agWPJLbcx;yJ7)>GFv zY(Q>s-%zq)%7f^Uv`JqyO)Dk zk=K+r$@_@+1lf#yi2TOK*yo_nIK_aHO&Rkw@XhvpQSF46>P0kqa&>EP(#t`Oyr`P;y9K#a@_T8tF|R> z8;UoHKd~LQ-Fy4>1lfdL2`_h8?kG#-Obkf8mxM{mPWrra?aszsQoDBS8s2TWyCRu4 znV$S`kKUfby)1jFdpq`N?91Obm*SIhdp~A>?*8woJFj~rX2j7vnA(Nu4ZoGAeMi0=^*P#o%=lPUp;Y1i!kOdLcm;mv-{6M=OWI% zuJ)|%J8yIT`UU)js+u)5M{0#?Q|e%KF?AF5{`G?m>l?Z*T3@{0sMlC~N$pbUTZ5u{@_Lrspn3w zeQ#HvTi>IH8y^ln@_F>?ap2?0Cy`HPpYG^q?N1rtAIN9qq$=;W96@KuP%>UjNgCl@%rVPkTPtJc#{UrXW^t0yY>r;+X&!&T>XTPL;75`fH4galuX6?+_Y}9w;_q-p9 zKN{z3=KAM@zy}9ILXQ^bd9*M!FP~s;S!QHS)6qQT57fD(@Krub25 z2Ivo$uAxy>9|QDSEjx@|h#AG7Y86YTxWwAKl4AqN1Ru1KA*X({Zgfyc5GC9T6&(~v z3)77@Kr@x=0-C{AM*|Su$5+?c++q;|?hMe&=A2O)YQc0rbub4fsAF*IIGid#sD{PR z!o8wZX<@QJ11E~9!yLSP(5WHJN!r5W!l}N$l>JsPMh>%NKpchAXWZ)AdxcXYDdto^ zN?5odPD@(_r= ziWUx>8aTiMdUP=01Xvx$y%xv^r-r5mPEVU)YGR5d63wtUGa{g{M1l^^3~PZmGsR;x zEp+tN|F&$&UI#jr0=&Q|uOW63xtzBITyN%WMt2MjW6oc}WY!S3i2f^MNo1WfB`i2F zf~i{ve6@nXsDn>!FqlQb<=IOz>;JXP^6Vv3SE(WU%J{Hpeb^ZsE;XMY+j7|Z9|B3K2SW<{un_GIP;D*?EzvG{*gc{mbDXVEh;E zq=1kA{;@w9Im@C5P5N5VSGO6vPJ|EN&siF*ysV2H%9I6>OB1UJav3SG7NiRt9VU%uqy%6mNSPRd7NEiED>Ef9QV_!#kuqcn zc!n$i&%hAy3=Dz5#1ODdnxT!r)J7mMo=9NoBrq%iA1P|9fOx3_Vyud`ijE2%On-rE zuvir=P6ex>g4I+3@e_csIw~O8;s6$eU=V>paMl8G8iZL8kwKu<(=xFzGdDLiGchA- z;E4ncjE;pV4uc~SOf)qxCRj6l^}lP3(Yl!2<)P*m<5%{-3^fY^OQD8&yF4_z28XBy zQhdV~Em)ql^n~RxRtKlH5Pg}Nmgg+NmxtHi;mdQD;D1C|5{(S{2~+zYIZF~Q!M@>9 zUUbU8>ugLVOW-AOMn89@1}(%H##g8~P1Mggv-spC$db@s1C&{?Dj3EnK8q56<kkpLs7; z#IlSfxP^U0kT->Hh}BU4S>&IrCCpEij`UzZx>pcmy2UKfpZp~O7ypRxa0;j_3dP6W zD=>m$sHLq91_^a-EiDc0Md`mmOY$Zm6nZ!)!-6bh_`>}z`#WGs?x#VfRIjiFSyimM z#vj)H%3P8I69oz#%y7Z92AH~t|Bbl>wFoAA1=><*e&POxSo|-aEo1+Vbqw^PQI>}X z4Llytm>A=Az&xGlN2bodaQ{Hf@{kPPE*KMRbuI7-@Skdym~QoDdIZKcK z?Hz9^d)bT05>J;o&@#I*h^GID&9L_uAN<-3#tL6*AjR;npEj4vFT?$=z={e24Lt^> z{$Dy5yk6Sb{o?}ue2EPTT68ZHt4pGVg)@Q<$=om^g6gA7v>;%#bhIpVOiWD7iCC-& z-VD5Pm|;!vcmjcF0z&p*atzZKnC8Lc2u3ZE%$co+zSv|8&WTT^c3{lz zUr`LcqGNd+#j#QSY-?k2cm0&xTw$Tn&dql(#doq6V5AD`@G97C2acR;YfBJXD`=-` z6mj9&#Kv>ZhaJ8Jw9#d_6Xir+nU%E>@TR2N+jbi;(IYuE9twx@9>rEUWPan0=T)T- zTwQT(_KrF)GS_tnwR!hr?t;nkS$E%I`{dV_69iI~&k^ zc_u>aqQ%;cNo{Y}-y^O&x?8YyUD@kuif@f}ci7_q6T;wS+jCtXLvIgJZ&WozjrBdV zzlql8Reto^Db7TV`pNW@u0G*ltXpxZNcMExmKCpF-OYY)6pkE`I$!Z(W_^pcL1=c! zrN?W2yndN*G<7C*-woGZ-7g8p#U6zy4}8PxAC)JH(2m9tFb0IxkBQmVV7iCw3P_Djm1Acx_>-yun1T# zvQ@=3R^|YyFwaCe=b}#axP?GUQF3*qKexaCu+m14kzQT%DC#<|p>y$^0b)nq9LRGe zH>93PM@mf#?G3d0YFmKOI?*a7y_c3I%W}WGm)vgwle}*`T-f4Ta(aly(I zgg*TnAAdC3`+nNCmC?6v-+N`=6NE2KPgJw!(Q(@ux+8jZQfpAAneCL_x}yTkodaEl zXT zM}a9K=Q8cBY|Z)cu(vx@GFgtVRB74M-}OFo)2_O%1_NB-YM+ZIBT4qV%XD~lq@NHL zU6-f9e*D5nXZ&fadaJqIjOnvAKeC?fJE`|{OB+v5m#(i=|7*Hs%}cYikhitjhLL*V zS1y*li+3u1teIxBGVEStil?7=_Q~jqEb0yWqS*Av3cHi#YdxHI>+XEV6K50OAsi6& zcK-vGt0mj|Dy|fq(K7Ai%-J$p>L~Ezkol_e^>_47tBgg(O}#^1A|ZAd6&165KQLf( zQ0Tcq)WqFy^U%KOt1mvBwW!g%^F%lI;ysB-OU|5v>#AGJ{CXxXAx!Oc#mO6o5Qq3Y zYfl#JKYCMkNS9NI^tfe6SU4$IPc&~NMZyZ%Cd+zd6{2icO6!kdF&k^?Hc{6Mtu4D+ zf=yJy=X!Rv*B4dFuP*;)%J*th)!xawvDR^{Ur)y6VSS{xx$YJm literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile13.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile13.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4f4f9012e0666af5232364f67e4996937202d246 GIT binary patch literal 15664 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 literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile14.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile14.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e03120b27caf8787e05f161bb21b3ee086675baa GIT binary patch literal 15730 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 literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile17.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile17.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2da3aa8ef0a61cf43bce70240bd6c0ef914e967c GIT binary patch literal 15533 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 E0IKT1_5c6? literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile18.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile18.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e934ce5ec1a79daba8687611d7aa8909068021d0 GIT binary patch literal 15615 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 literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile19.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile19.jpg new file mode 100644 index 0000000000000000000000000000000000000000..15707bc73e29e931b1941f8ec2f8309573d9080f GIT binary patch literal 16092 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 literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile20.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile20.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4b940f13d4bad508b36c4798a8a8e64da68c8c31 GIT binary patch literal 15844 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 literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile22.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile22.jpg new file mode 100644 index 0000000000000000000000000000000000000000..39b08249da72c31bf9c138f9b2d04b88f0d7cd37 GIT binary patch literal 15788 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 Gd-Oj}?I|1p literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile23.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile23.jpg new file mode 100644 index 0000000000000000000000000000000000000000..42adae167e747807dff13271396f0d9df61b924b GIT binary patch literal 15994 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 literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile25.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile25.jpg new file mode 100644 index 0000000000000000000000000000000000000000..43692f079524e4dcd8e5f8260b67be60b77efcdb GIT binary patch literal 16403 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 literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile27.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile27.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f6c210ac9d8faafbfcdaad12f536a737e96e000d GIT binary patch literal 15603 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 literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile28.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile28.jpg new file mode 100644 index 0000000000000000000000000000000000000000..39f262204c985e3e52ed82364e2920b9a533fcb2 GIT binary patch literal 15748 zcmeHOc|26n+rKk|v1TtqrpVaMjBUog4aU9;EykFbFc`ZQQBjgD6p6^%qEwc&5hX2@ zP^m;jTB$6hc+U(;^;_Tn-p}W~+;gA%obU78=RD_}=bU@yp1H?!{g8l_ImsM?!C;Uj z_=DyKmhUA-(>6m8iKGZ|LJ-6a@x$N{2cQzcDFEmd;FD-#L$x1SbDkueF_0Vreu0BeLh7I?VV8? z7>pUpjATJ_F}8u22D8vVuNEX*2NX^fuZqP$I1E-p2Sd=oYNN2)IvO|~93GN@@)G9l zTm>F>bb*!w^qRfDY(Ov2w}5si9SO043kHY7BGTt=Rt0#r1=ka0LRbX^z%BmG)^!p4+zdZg-jt%4h|#-J0}u}BEK z7ZT8Yw~;FwBaTwGkdJiKCj zd}6rOlB;q5^)c57335U{PzwUK27(L15Q4C|7DyV{$qu3#Gq5cjpnI~hb09gnxOo7e zMgW4t5C}LMf}NcWI03eu*?rjr*@aePjX8uJ$;dUKBDnaBB2F2T>LyXA2V=78K8yq| z?&T}Q#8sUaIuB2ZFLkq@KW$4`ctm7WbWH57 z-Fp&~_U=p0JeZZ8bLenxUh(mg(z5c3$`j|#U#O|AtH0QAy}9Ma&DL9Ow>uwpb@x1a z{G@l_<>1h(;gQ#)Z^l1;{xUH+HU0G)vs^Gx)jyB zARGH?EQgS>Ba$2{yapG~DPodQRNcfSqwX{&>ce=zy&i( zPiWtMq&NTUxdL|j5#9)qhdvg>d+s_uBiLJvn6|Qp<*#?THP#?2ZYrGi@%k_*ZKWnK znR4CVzSOK|w!5Lnjst3&&=mGCEZ5vZ3rsBC{$&5qR;L)v3xTP99DLZ*heKp!yThJU z1*@ZPO_mDydaD`KoXUBc$_f3T-SgX(%HLUZPhZuw*WLd{07KVVFS%FCuQM-bS*&ZN zS7_L?Tc7yUGCYGIH)v|2Wcg6RvN&$LVxrQOlmR-aQL^VsTH@vgqfgp7Nl){9yz19i z;rQ2g*A$Ej$Y)HZHuh+u9nXYxsw%w{+_oZSO=7B<;`WNlhtAjbMIImjNF;Bw58tFI z{D3{f@VZ5pL!kc2b?s7IjnCgHBn+Ij=Dej}`zX0uNIROi{qAw^?Fy&MGrntIo21>=l}3-ZI`bvfY;wzxI(#+k4ZiG9_Rb$KXI=#mJr3(B;R;FS$i(k&q~BTY zLVOPoyzKw44GHbMP*^1=H zI5@6ax`9uP=D> z8TO{V8z1%3jmNK>nUG75@n7cPXV~0#VX_mQarniP=9ywv{wkl5RUSR1IyCT^>ao9wG^S zdzDZdD%+_&+mY6uc7FApz(X>boUNZKs5QKjN$K7~9L=qRu5BwC*d5D{%N=aVyQK5- z+bcWn1Lw5GzfDcG6J=v3Ym;UTKOSLZ?31i-dfP0dn0p{o6rns)AtPQfX(s|dN8ebl zKL;t+hy{6vhijnAr?t1FmF)}5k>mH&Z(nS> zYghi7Nc%8}J{%a;kmVe+KSFMxebeywmso-gq{l>uT$fnpn zkn2%(0~0x29j)P#o`H-*&)g4hnHM^G+B1B!?`jbkmC!!%Qo1) zeire~t-f=p&UwA3Pxl(@w7pU}dg@-?mf@Sr zFriI6Zuou)ZI55=ZEHn5Oi)rdRdWsfZYue+*Ld~iJ`x*5 zBDCJ-94y>XTtDK;*zra1x!nPc(<{?H>ay47h∇-Q{bJ%SJ}j7-B9*ZV&FkUVUS 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­2YM1rZhJ;3$^_*7!!z^lw_oawxLmzlclFv8rQ} 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 literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile30.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile30.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e699a53cd49ecbbb69d1e31fc9e42c0d5f976e3a GIT binary patch literal 15476 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 b0R-nR<0-VSzVYW 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 I#JMN`1K)Xt1ONa4 literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile32.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile32.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fde5e8b9b34beb14b7375dc54917d3c198d046eb GIT binary patch literal 15509 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 literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile34.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile34.jpg new file mode 100644 index 0000000000000000000000000000000000000000..37e24f05c20e1d283f07bf653e96187162b96e17 GIT binary patch literal 16062 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 adbK0!izx#$Fwg7BQLSb@v1~z0&;AGW`o2m4 literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile35.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile35.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b147697ed6c5b6b72a1055c1bb7b331bd38cc32f GIT binary patch literal 16064 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
    0`NOszQqP@U z``!nAZhifaHa;5a_w9fEIOy@@K;*#elU+~QpQb!p@ho>xa&?rzp>Jp3CA}AXU-&`c!^Mxr zAMZ}ApLjV*pPc`c`dQ+0#TU&lH>Mn?2B(9kXTPR=llWHo9sj+3#$#q|HtGlRN8V4x zpN(@ib5G}k!3zh&!u^ZuJQ_pI+c(6AqDBr0R*&`$RmZAf)FFMNXt44Mq=chu40!*8uIIWrwi~HKPPjtzzjEmsopOa%>=(;EOggIy?N7sB5pJ)@cN?uzp$6!cHV&$L)HsWi$E6R zcXo&`C1@f4Z?8uE5@11=B>vB|xxPAcm@Mn!|DxN2F}E~?{^<3;hVw$F2QXc`5MQca zjJbC>#SpYIK^6StG+n_ZfUkvJz-X#4rF2*+ zh$SFS01ik2M-#7wVWl9B02)LQkb=0QqXD84GX;2%5|}9-AVJD3O9R9xX38u@1H>+7 z3S>dbk_C}V6RQbwnJKUqqzfD!7L8}71YjpfSr~#Apuz4dDf5DPW21fY|dGGz&P zrYr%^#1QaI41vJH5U?zosg1zWMj$XtB(QW6n3jN-6tz`Ayi@@(Rz+JyM+Fa-zrZzE ztO^#Vg4IyLYN~+v2|!pK6%cH301HAeh`=B?Yk@co!Yqi$AkgY*nOK;ao12=Mm=QJb zM1lrJ$HEkc!4U~2ni?1rteL+0-&>5?yI9=iq2@Q^clN&wH46eup@wz4JT$w8gsKKn z{K6M4Se~_1!txlagHv0GzAR14bC%%C!|R{$6Cvr*jP%Iz)RxHaqdbDUWhZyw@`7Ks9$krvE(JllF;8KD6fvr5c3v(lnxoly1Xc=n> z=17gE1g$p*U53UoMH{1uUxfcP>ra^tzh%Hm?N<>jnPpi^GQWBq7=&PD>m9_L2=S^| ztSXM+`s>AAWh{m@5weW^o50^xfi)ypYsFH0|6^UhC6<&CqeDX&5%h&YgH;Ewc`r4@ zvWz9Tg?&V@4~1@s)lmOchX)v9@xOhxjQuCpG02-nSsorV z@OV6PVT{)S>vWbMSvvoQ`v+>4hh*?{!CYXgYk^mQ|Egv=?jNZ69c+HBp8sOTUjR!g z%pw@!A;HcRnlISq_BH&~v;M+fg8ym`mRo;;|6ndbJpw>@HuVO+0>k?k^bh6|)P+o; zQOvwUnH|J6CiFM_Zy8G>e=OJke{uDHc5y{=abU$1XE3eN{22>#74re-Z}S=$8vk}i z`v=+>X}M4fgh@sBa6U`0K0x=Kgo(hW>iWSt|aI zXS}8CWe+AxJYCjA%NoWYn*Jj;)85~F@M|-eEBvTI6vMy$w7Fb<8SYO7R#XsZ=rJhu z|I)eO@zT!jA6M|#Lu_#HqI+3bT@sBE&I~#vbHj)Ts;@55f`HM|(X!AnF)=YGVzDN8 zGw{S=hBd|G2?U}E2-$y=W17CeG!G$1Fk6vi&gwn%#V%ujUQkW^*Q(L1>MrR5Gg2hQ z*Mc4r{BOGAV%fj(|HNqvy%cPjfz31Zr7bpav$RPE0=og4&fJ1yeU-w)$;r)$nZR)Jq7!RH&yUmzC`1RDfr zhjD-}JG59Iq`=wWoE+f$Ip(qOL5h$JyD-*dtrLd`&O4!``GKgcPgvHuVL4N?(i=cb zV&Q|3MRiMJ>>x`ClnjTlffLUB*lHn9PzWJ|bz(DF3qAzFvCDxET!ts-|M>o5iYKzn z%kF00lhETo9`32T z&$d9tmgJ&=ei;M@YQz3dQq&z8vM$z#P%obST(`vse=I6$P04oE>LPU7S!+~HhP0>d zj8kt0?-7ff2^&t6>}eOQ+m8}KN5<~7P1+m{J3@3^I*zIurO&>&8b{YsNHJ-DY~|5V zfE~LhJHDy=z(&z@m8Z2MecjQc#Yf~@U-=uUxULS&Ive7fR5lv0ha(^QG$|Cn?eXp} z#g?f7(dXMpiG$cj;y1M{Zd2+J`*j3EEH*1t)Y+W4#8-C2FT){dicrHvZ8O~AH%e`* zX^8qb!};`*RbN9)dBdo#@7|H9dQ8H#W7ty_oWM)EhoTe-b2jik=yHCi}2Sb*4~=CU=4aR#If@?$IHHT zbMk$fIYO6?bE(t6BXC-Kt)HZ2U~g$*!c4|sk^*zMd|u zRN_3f-qTX)aBXJMoaGqN-t879t)JmfK8Lv#HD1UYuUW6wMinU%V?zwO`tk{p z%I(Ln)+fUxZFd{&byAG9YxtsH{TO)!^%>UQwavHKxA3ru)0ebob)Ok_iSVPb=4h9r zR(EwjkVFa8pNGIfRzM{-JxJ{9K8T@!v z7iBP?2*YU>ZhlE3NrewZpS@IlTzV`0wWM^)_{)o_!wz*n=OOZ7fwf_RgpqILHN`uR z&VEpkOF9?O9obuByZu?ll$&|p%d?!xE|e>u?f2cuO80JRDg06mA1F-SpSS(OhLVG9 zwn5<+-!{m1spm&Ldi0Oa^gZ3QmnOsicq$kAcz%~1 zE?gsE2;JMq=iNkLJH{K&=*zFWNm9Kzh=H*gZvPhW_$W^Y{Os+w$T9mPf+yTWkO&o% z3fqHb*xM!#?*ACy!+-Kcg!hEVdfEZ4%NU6$sQY5FGnsF#IDPscZ*d&M=gt6UWn;I4 zdtS+NnNL>V`fSc_euBJIfW4zVZI|$#v5{sdrEswr@_4`h`R2DTKUEJO*x^e$IN4GD zI6;Ld*KpLe_*TbrLWx;Q?Yjz@Y@EYZhHd8QU1N7!q@_;oJF?0|7P3F1|D}Abh%85E zSW%JsXJRt_>FF3YM6vdCg)fVi!NIKqsApYS% zZPG3ht>y@QvhdxZj>ME4mB!Gp{(Y|tGB2MByv-23dW#*$UP6@#edaY1W}BBfbRa0m zs>0S{-_d694z(vQzwVTQ_C1bOsyZh;95Z9E=ci$I4DX$t#Th0CyQ@64di{>^H;B)@ z#$UT}XbWt0cl7O}kz_>0D@Rf?=WfI7G`tP$KaE5`-uQ)$yG$lHEA{f*%=Fo-t1Rkh0%q@zL^kBTe|5~mAmbqKwSE03 zQ}1b{hO1#ei6#^#$$p7S_Bx+8!uI`Kr%^#qU`ST~`x3KxNJ6%}$)^rgC+s|{+iRIQ zkYXivU*cj?+X-1Ii!Wg&b?d@(gAX3nOI1_$NegPfl#3JJ))v+O@I+cr*F#Z>)LhSZ0fEeIA63;MoXn0r-+0I)=QTiJ_+l&}R`4Wa>i+r7VRgb9-l$ z1{!UKG9y?LT#RiXdWIS3Ur$Sdtpf_Hic`g)AuJlBp@YWjV6;&fZ5<7)4i*Qkg$fdv z^0^Kqc6^zZ1@!s@zw;osOy2|Ap$sI%1RiKC7K6xG%CjoKGcD65fQDr*VPN!fWU(`H zoP~r~fnlQn4V_%h0|t$Tom`To z+Z=v!$yRhjB7V{A5X8JprvdwMmn`LB7od5T>106jmi#U+?DP^1R6_7oG-4IavWlkT z=;>#GfNPn?qR}`V3`PfwM`6@;)Ui4k9H@c6^LkFx+Y}5s>5pVQ7r_Ox1Mx5G0gwN` zfCctA>p~%P!yJ}_EI=E81DnAidP{V|;{f%$MbOQ~0wI?B8NHwW>Wgzg{#Rdw1JTue z5e69aI%EfY_%aP^3%i>v(vz4CzI;xot;Vr5}LvM{qEkw`W+R(4K7E=~>( zPEmdV9zh8)$#oK9;^HV7RXNmpWodD7d2Iz{&?7Z8B<1k>IINzkx(1f61jfe3#>v4c z!o?+ml@gc2{@2%HBgDrF`9duS*m?-g2Sf0|7F!@mkWXgd)##3G`2yXOiJ1k-%ErzC z0JXdj9EL!^nGnp(Ouz`RczX9`;$!BQ!Wgp%IQk&hhYMm8GK*QIO=_BioI0mu)P1Rm zZ0y2oL`20l$jZqpC~9bGY2)xZre@|AmR5v~&MvNQ?wdR|6a7g30fFT0w1~*4=$P2J zq`mu+_a8Wzl653IC--PxenH8p)1_tQ6_sZ$Ubh0#1J9k^}wcYRP?&OxH@Ii)9U5I_D#z4`=NrR^Mw)4o^c*9!DcCS~5DX@sqkvW&k!eVgNTz`q%JH%{-=U?X13>; z+2h;ej|~)FxOjq@a*Q)lu-n(t{E@qk?*!%^HMXtns_^SQZjH6bin|Kud_6ymNp4i* zok_hNXkTj9x6pgF&yEFZ`>HA6VOXxYogAE88vpdr_ztI7&C9`QLo8gFb9rIXGQG6t z)uHNg_hw3Y{k+u-YR~3AOJjw8k{<;mrSWtXKhjrq9dHl46GYW@)=L@C3g{{b;f`~y z^bDsxzxRntEpu}Srz!3<=eR?c71Z1nPPlJWw-OKgHflZKbrgOw2#=P zDbUHBX?WYR$01n1YE!!eTjSt6g~X8y)~xsR>z<_4@N37I$3Hyf9j|b%JoATU*`*Aa zUS|LKjkFi{T9sjlo^aQ%2cy>XjP>R{@>zJKG5I{er?pzi&N|)Xt87MWAh$z+Ve`=CnJ&4^ycbWK=Svvz>wG8Hc{F`sZ5=a?+TEi=-rA!Q3eDNq#aUHm1Y~H zvDc?T@^I)V(oOd&w}n+jONM;oI1knSt^G4CT@7!oLYeQqQ#Wc-U3_~iTCPuD_!8{) z?#SJ9pHj=IF|Cu1Kdaed_fP2YWRFL5d==W-dzdE5K{;mGe)t0`u!oYvgYAZWe%ket z?J2u@({l>X#I!osSt1;wRPNe|WWLXA?K^d2+VyK!>-Zzuy550sUDC}4#E*j0Dtx?{ z{WrN?BEKv|JV|gp_^A!}{fcVI0FI$MXD-%OH`V@hP5-g$1&>j^s-aDa%ah2waRUF` z%fz~HnJ(>xj`a5QOHvPlk4k5;wtlK0)pCmO&+z7FX>J{JZCi7d*|GeT?2(p&YdWKI zFYVY5U(^)qz`9R&4CKY;JFm1QGn_l^6tV!G+PvqBf z&8n;>(*WcRu?S{eic!kW>zC7bPH78KI&808P25q(S(j0^)gL!e!Oe%D1-LeW(P!^vINA;Ntue3kqgceeh(4fAZxnHZZsqtxF#x;s(drk+hP z{h9U1wnOTk&D|UC>IS7_U$@9uZR2>wJrXnD+}&_CU3OCBlx0o!aN6*kyLFYhGG<`N zlxd`-iR%@9?Cn6^#N)M|T~A!Q--}x{t4k?cubYs&HTEccn8s~wQq}ox-k{^&+lOIX zL!}$e3`T$cisR$le-@Tr%~oDo7uQ(6fY`$BaZQacqdBQV| zH-6#gRVw3Hi+Q-wkZ0kBRo(Q>D4xeD`yIN}9=^QoHIe_yaHgkcs+oo%5uRUQ8f*7j zK+L%{bdA?L&sbNkm&W__jE_%9%bk=o-h}z)J36?@T#sV6`-#!o^Wl^4Q)wNg;XUU) zLL}c#>9f03SjQFZyY;YE>$qNq_CT=*F`e_a_xVeO0nfV54H)aRy;M1V_EG)zS9iJ5 z;Y}QGlw=nnLf`eT^ZGL?aKRwg`?4ZC`(GYtYejS?Dk+?;y(RZkHqY-C>P$V(A}snd@?enx7hRkS?hnLkJW_rQ=E({JKA+yt2j} zKWG?Vm)S9|4Ex%y{z#~(c42h>@UHWc-@h#^Lf?>;!r5@qCMIvmY~PH#={dpFSZHeZ z#>bwTGQ2lxk`jKBZFAY24j||8)gOa*E<%_5{W6=&V! z0cK&f_u%jE-4*23;q!zj-0|KN8Z+C|4O0R&TAysCR(v^5!(D1P0>Bh zQ*Dj-sSve3OtkTfOr~YM)Sc{oDd7zbxHq1)W;0q3qxM5-=?R(N`f|}&iHq@qyEhY= zTcy%_5`I*kHP%aQj)%8LuOnxL&e-ftnw$RSRKz_nBv_L+SXlGjajj%~ZN6(BkAl4( z=Y;01@}{6NGi{aDXGK}LomGO{?2OM=jS)9$zlrvq>a7tHbnc{R+jX7QlPkOK);Fz0 z-1+GeSC=+rEWs=OX4%Ix&tJTE{k-;!)v51%n-}DJ^%@KwaOzyWoFsXD4{Q-S`NWX( zb_Wh>JyQ;P9~I?xBcsOg-k5z3L4s90k6C?iiw;PQN6~A`j+jjZ#^Es#8o@LY(8Rl^^zyavb*|GdsMEr(YoU<9}K%{ z)x0hC1zfH_fy?$aYCoT-I}u)eT^8&3v)A%yy}QbAmpG|}+0pb1Ett#` zU)734#E;B5t!+p!L~o7bRqC8co$8grgg8Xl>F>k5-$U7N(2FjLF~nxXGpl~3==5AY zTT2k~5cJ~>WLvW{rEcT!&a%`>6zm(j9Z5dtdPRDvRK4?Kg-4rjU%*K^8rM7FMP|-E z&n&Sy8SJhP&9N(fe9D_g5+-GB;m?I zsXT$SG0jKs9qr&)TtATR4cf8eZcN$L znCZs{@^jM5-Vvut6${V2>&g}FJ*rIjQe{nYj-=&43m;!`EJE<{&)=N3d3qp}567!+ zi5=)jymi8Gqi5Bjs>3zUo=;lf(qyc21HXN_J5*#7AH=bly!i#cR!$#|JI{kJ5ep~8!{4D5 zYoAAB?8>ahO>XU`W$oTJU7gkUz=5*6#+`{eI*I%wc(eeaXMQ^6k*DG3sZEQ}YsB}S zW=m?ROmFHv)vh6zydNjIu6XmTYkXo#I+2#~o;K_zKYb?t<7A`#*zBJ2ty!T3El!o?N~A;2Vr&~u4B5Uh>|0;Av8ckqP_a!G z+gdqR#kU}ILp)J5lQd1psK6(4@Z_YQ)Sq_MRM8U2tPa^g6#FcfcVg@FIlo1y=w-Lb zwQqhMN^29WXS0uu98kMtF+vZf)Krn{uAJSiEyTVJ-5pqji44ym~1c zik5XcTI2k!XyevW*};}QZ|}xUkx!~~ixMOchgTZBq^7%hBke3r$SqkG>@4~R?wAP< zk2L8wia&-8rVV~>I`b<2&V4KK;qi4h;G+efM$mVzPCKfnvaC&ux7?g1a;K+w{q6BZ zXstI-+|I$4lA#0pvUXg_?Ryb-t3h3-H5v6^%<|AbNNcxL}A)4^-RXLU+hXTO&cUo<(m zRBqiYG+yXaoUL@%C8wph&2ZaPHRU~`J?xyQG_xyx=3$FJD z$Y*C&-7K67GgudL&dCQ+%N;#UH{O+0>}lB7dvv}rb&jg=>9UPB7UzrIgpo5mQ2uQ4 z<6hxUSr7YhsZsCyYGp56&&9m5R}#6~vo#D^?zJn2=I&?Q-@gArn$U|^igjUO{hChx zmoWxQzl}|9r_Zqi_q|ZJMF`N>K~S@W;HzLp0Xi7KYve~ zo~nf*Y~X4v=uz*}Xp;=I;>hS5%;Us3 zv9`xP_0Pt2(8T+7_TQca`6+*S4U-P`iC%NNeP->Mp+L-%t!h@eo6|*{Sw)s5kNxsXi`{k|2TwS5s`+@H& zeRrqbBkx?vN4Lz1|BM^MO>Q=E>#{65c)NW5eo(LY6kcNH1T#_&42^p#jBj|@7k{)F zD&!RS)_X-dL3R^s{~P=}E`?O=#mvGU79+Gvx)T@9s;f0uMy^bOW{{j|n&?@6zk)?y z$I&5-fD4_U&~UnyAEkYIzIE^nrWwMCmZZEgC1&qFje57;>{Xa9Wzt;8$^?gqmS8<6 z0ZVxkd@`d<>z;&8ihTLwuKm+|38SxkcEl$ez8TYxKdiQ2wOZ!B-lvPnC>E;aM)~TE zJ1)Jh+RbkG#N_CqElIK>X*b@SNF^LTC{xIN*>OwH3{Gad$F*Y@IJ69E<1CaoO9{vm zmSg5Zg@?=~P+3_Rx#$zIAD!*qUp378^y9O+;IP66=RU!;;Tb&ZPV^@5*vyusOdd?y zKzL=H!=Y3$z>VM=x&a^R8$3cY=JVEuI|S&_%!T-$*Vg!M%OWW?sk8|lig{?wPmr}R z?U?#;Jsz*Oqtl$$x;y{kvvJpHamPCqz8Ws;HW$jbZoi|qPp&IPsl0f^dX#hp@{F+( zX~Vb1MOoGF6qB#qA;ixj!#w__Bt_BIe(~wzDCZ`Esp&QcCwmKmjX7ANh9FKW8!{yn z4i-y8C=paAOA{1WZb2d2!GahYSOmjBkPnd-X6tO>LSGson46+#0JOaRv;3IpV=R0@ z-6~cNDAb>E|MrcK7)Ff%i$x9qsqROj5dpmy(819WVRZf!pap${=rn!B4HBe+3<6q& zP6sT}$_&gBZA7R2LMVO!L(g-VUx*)_ZUOXxs7Ml^5n!DgN{%9vq5<6lXqn*15Hg^@ z0a`GG7gwkTj^Eeu{uO9iV6 z%)fH|%g7bfzb5E)yM*}c$fz@z@WbD{-?G1Xp(PN6`wDE6@tfzH4?*?&AV_53H&5my zSPkC`L3JIg$|Fo~FM$yeVLEDR(b3VWWD-%8ZqSwM-vU;U|D0Hro+>@P72BcANd7*N z!4W9BQHi0!p^;P+EzE~VLaF?<5&y3RS7mEeK9rqE{v;}i0+MnAt&B_w0M$+LBS(-! zDJU}KpVjdHQfyTo=;Ttn286)r1xRq4D#Y6%2qE4-gqQ?4AcWTm5CdDOHybuLh~D$u zB`22JJ)l86Bm5(Pr-N5GjU0fYvrU~`P{c@T6rBcrf*vr_f7SxOn}|Z|AZbV*Qh~6L zHlzy~LFUj#$R2WmJfN)*5ekGtASx6M?SvA*&tHe23@8UGfQq2gP$g6iU4-hP>rfMP z7kU6ahI*lA&@l88dJ9cKGtfL(q(#EGU;;2vm?UfiObLdC;a~mmiCKr4z`Th$fH|5unK_rajJcNi4s$Q_OXe9C78YR^ITmdeD;5uy z?JPT3Qdy3(oM&ld>0}vW`3im^5Jf5>^^x{SA7lhF8JUkfi@b&GLcT< z$CJx*iKm0-9WNWNEUziA5AQDCW4u>*pYVR=6X3(}+4F_)rShHOYvX&(kK~u-H|O`` z-^+iBznOnb03jePU@AZqND??La7W;!Ad8@!AVF}u;32_tg6)D+Li|D+LT*AaLdS$| z2n`D(gk^;Z!okAn!WV`6gy+^stTA3gT61ts^_s3Vvm$Fnj6_HxDI(`ZdPU|$QKIIe zL86(W^`e7fOk#>+j$+YbC&ccEy<01|R(CCNZOYn!<;yc7oi9ZzoDzQ$& zQX)(uU!qy!-8$iQM(eh(%U*YL-K3;|q=6(^GF$SN{8& zH2N8aAF~mYh`EaSgjK{6vB$7I>Rjp;>bun&)TcF+G)NjJH2O6KG;KBaYu?uUsimz& z)vDH-)Rxiq)-Ke3f)m6!;8Jn-@XUA<{BHbp{C6EKod}(aIv;gabc1xy=)TsI(6Pr&w?%GC%$ByTVq1f^-q^;o&1c&sZ)R_I?=#*% zeC&Nne7^b;d<%W2h^EA&#J7G%e%XGLBz;mAX~JLMKg<7hfPTP{fXP6^z}&!hWK(hh zd3w9m_M+{xLH0rAL5soe!RJF*L%c(-Q1~f9l$OwSp)sMIVM<}iVZ-4%;W^<`RBLJ( z4My8SYlsj8lc|T1@{#)@N1_a(jzxWoc8k6g!xuw|c^Io0doXq)&LXaK2h$GXj@vt> zb|&l`i8qcf-UZv`yX*FD>D_yGzuaTFry_wpAt>QdB04cE@k^3>($&2Zd-v=e+h@72 zDw#W(n*3zH?*5_!Ob5sZ+7GH9%s;r0;+Jy&5c*K=p&zNl)cc1qhw~0ErunC}r)#C3 z$Y9B!Wb|bkW>#eJWyNKU9kD%9myOC!$^MeFEvGG4Be&=%`_agw!+AD&4f!(p8Tmg7 z0teQ>#Zl_yI zaix`IVr7TQewI_phbx>aS}L_GE6<3ZNw0!eMOICo^*a0boY}dCYL)8Z^TOv3U4Sk` zUU*aEUDJ2b`r_?NxJ%WSH(V~P6{tP1Oh14>x!;bY9tb<@QzGtF_luua#ey zx_<12(2ew)>^Jw`T)Y)`>uY0JGVL%x8t<&A20X5;(C=kAvsa`8vFYCq{ZapH=b`^z72aj|1SAG-}}N3@*ggL zH2U~(YSYxqY3lUir_|4)pUb~!e7XJA;p@;$$jtm~$~Vz(72k2+Tj$*8Cg!7mAb;fj zRQP#y!Fu8OVhGr9Fe1>uJkO)hRDJwHeMzds&=9p4pD;CyDq0QFGmHTN(RJHC_VbCj-5{gIf`UP z4j|DY46vG7%2-WpaO1#@SH@~#m9g3y;08RLhO(wQ8r&E_13pe&8H?9aR!3u$)iF45 z19?+wW1~9xndTM{q*-C!xsbmtc0=>Kjm}TT@-U`se)Yp{i5K3drU%_P70K1I-Tf~aUCMOat zG&qu>TN~_JL8H~cRvR>WS#Wjqij2pDY$) zk^N)Md?H8&pqB9};2*2u0yZgWYU!XcN?@~?I;iu%AnDelvrMTZAo!mMuR{J3wguha z&nLp?KN0@}`%3{sXDHPrG&I=2nC`Jq4uPQ&p|rrzFqEmYI?C3ENT$%=m-4?F4jjgR zVNMd*{P&OTNsn3OMd+`8dJ%OkoR)^N1{$x0W`w{$Xk)NTA(p{e3e^E$6SIWTP^L?1 zGeY1?fS&*y5CV<{P7}=tfgb@h@FE}ten(p!cqMuW@F2v~LmWVYke-%0@KN-To{Bo~ zUGxyhf{-B#JeLMW1H{rpkXsNgakLpUjvnGcK0(O9;57jaR$mz*o*n{UsDZ%)o%E0{ zi^tJr@i;mLkE3Jocm@WKVbFAKc!oASo}MC}p%YKf3D~5lr40O~GVrm=TFTnWI57PM zromv8F<51cx-v#X8Td~C!e}c4XNv_`;DUh%2F_U%_-Wu~fky_8R#(&5!qm*n#MIc- zTpeeQS4V4Gm|)RZbG)&JI@%axs;Bn%8l$%^26wfq`OWwp{V!e3lE8|qVZ5$(%`Tx~ zD#0ZGh~*rtj#^1!wU5=tsxEn7hNjgqEAZ9s^-uWfm=*Y6JS>4i1pS1e{Vy>q63(Ii z5z#(W(!c9$3?(bz6>)k$cOi!?`5F2yRICQ-m!DZqc?Gf}^tS=ZG*kr)Vk9GZ)SW#vk6BbH~q%L(Dj2hVH zy;2dYB39rQc99{zB&q>MUF}zqf3a3Dzf?LBEn-FFkLmjVPpf_6(om1co(A0BvckqJQB0ZCnFg zG|3KT)NX8g~P;|XvK2#c`k?G6P`$Qy_NLu;`TJCZ5VGQKrzxgciBd&ZDghf(= z8LJe2L^V<{35-|~H1J)3p)YTMA=;0q;~z>5@rf`Xhxi1L)V7C_0`$~=bN{<^!+w3` ztR(-(H{MG0st=PDo(^N6WprcUP5%*_p5Nbm@M|*|EBwj9B!j>Gw7FV-74A<3R%GC4 zsIe%u|I)bN^U~J#9~bb~M{G#QvUwR;9Rh_GL3cU?GlR%TvY(E*1s<)bt!bfcY;0_1 zj=>n?Ou-k2DaHhc!{g13fy@4z96jqxOtVm8B)t|1W{lQDU2Zb^a!abs|Fvp4MsZhk zfe|T+qW%`#T8_f6y;^)QZ2Qq%0N)ZFUN`m7X^j{#C zFR)7h!3<*oCp)y34y3@CkgVYR9R2!zAcaN1*ijJc6JA`?DYRbN#3>=O$rmZ?OdJ7n zV&LG!pE|+OIs`KlU8gcg8=P`s0ta1~ms5p-Qx^OJ#!ONeM<3?(*zklJ`caUP>BT?J zV03=T!v~CBYDn3Zp{gUZ+>&Ay{zB9MOIkyajUn7ew*ivs^5~Kxxc3*@H9V6Ob z*Nr_UrC2>R`f$N+h<&2eHT{i{a;W{worCV5l&2o7FZ0#2Wqo*X-|Mwsd31&*=dR=5 zn^$>>>eB|r*)r}o3`*JrOn4nv>)Gb0xoIHj>e0#f z;Zq(*xc7aJ_G16&W>E3nt=uSbT9+LYpvaopecpBV*t&HKIZb+zeF~G8d$-VbF5I#6(A+-0-7x@tWqsgt z8)vC)_&edlP5L-j&F|Y~UaCdA~4mdoPv^A-!?F_rx+QJPJ_Bn6se`02~Di`HwzT>#N_eGnG zhtf5Dog&G@9-ZFzuUa*0lie3~mz?cNdz-zJ=Y^NKhCoe$HbE;dJMR8Y+Dot*(S8y; z_8?71+$+?1q+01|Z;=d1RM%GLlxWFoQ}^=60ZA7UY4WW%Q(W({4PIfIZT?i zez;tVIg4AVgT?>x{*PI>ANOg~=Qgzw;?Fi^Z$LV^gs~D(HBF}+dBrW;If(c7zZPT4 zvp2omb6HG^{lP4)fRp8-HO2ForT>&J&+MkJQ*qaJHDjO256!%7*mc?~eZ1?NTy&fB zcHv_eO)5X#XHV#s*&;D6O-}2t`8>D?a7|Ctb8bcU+&|Bv0b?|i7 zz}Dyc{H?>rK37sK8&WO_2OJ*2e;!HR)cwhya1}3xcPC3&po1JqslBJTV@lIDdpIkH zzTu9qmB7fKJr#4fF^13bgWqZQdpHNn3q3YTGu9#d+!`_qzZl3~EaZC=d6NrXhl>9& zou1omUCeRDTfZ*vjaeq4`;?Y}Xi1h?ua-x7hks_;G^b6uNUX7B%_Cbhg0{REMM@Gl#c zJt9$pxxy2vrH4@vYLdCJ?y%N(@q^4QqU7c?)3vAHx;m0_O8*(j*1w`T zdcCgELtXTzlIWb%v+*qkhYWga>k!w;jdFTYbDgre#?|D=^D%5I_2?}|OzZ>&r`ECo;XMjQ<{_~CojLt#eogvfo34xLWy zHKoI6%bB#9+&9l(JGCw>W>CG@)xug$NaJyGL~gLf>)vSYlkCNvSM#JoWg8x%TlUI( zt$#1>>O?w|@x@Ii4ejB&GkZ*Pn@_FOfiEwe^FKJQZNZH9aF^d+YpW%lYJcz3R(U7H zu`gRS^0N8wOvQ2C*p6@>zC1ZIqJ_%)Af7R@J^u6+ZrLK}0B7C1&$S(W1&@y^>1yo@ zGT1^?!i(_7?|gG_JbS00U#sYu%vT>mCN3Q>IgV~_4P}Z=d7E08nR8@qQkh$3jWlXc z+_+eA=Y=F~-p!VS8;K$z&vMvyho31b>AzD?ixa1YNp{s zlY0JNtua56@H6A=M|lJDyU$IclOMDt{P*`zTJ3DEjuNql3|lUCHZ#<2uV|FnDY>Hh$;r!3I` literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile37.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile37.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e2d40eeb9d27bead4488731944a33087d15de11e GIT binary patch literal 15679 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 literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile38.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile38.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1363781678b146cd01f06c44db6501789ed5192d GIT binary patch literal 15502 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
    Wg`#O4*FUo0)@9r2?cYnJ| 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 literal 0 HcmV?d00001 diff --git a/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile43.jpg b/jetty-ee10/jetty-ee10-demos/embedded/src/main/resources/docroot/tiles/tile43.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6230abd253ce064836f6afc4a160131225d48309 GIT binary patch literal 15896 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