From c445784957660b10fb2ec07e3546765c8b33bccf Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Mon, 31 Jul 2023 10:59:34 -0500 Subject: [PATCH 1/2] Fix demo-spec webapp failures --- .../example/initializer/FooInitializer.java | 4 +- .../jetty-ee10-demo-spec-webapp/pom.xml | 34 ++-- .../jetty/ee10/demos/SpecWebAppTest.java | 177 ++++++++++++++++++ .../test/resources/ee10-demo-realm.properties | 21 +++ .../test/resources/jetty-logging.properties | 11 ++ 5 files changed, 233 insertions(+), 14 deletions(-) create mode 100644 jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-spec/jetty-ee10-demo-spec-webapp/src/test/java/org/eclipse/jetty/ee10/demos/SpecWebAppTest.java create mode 100644 jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-spec/jetty-ee10-demo-spec-webapp/src/test/resources/ee10-demo-realm.properties create mode 100644 jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-spec/jetty-ee10-demo-spec-webapp/src/test/resources/jetty-logging.properties diff --git a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-spec/jetty-ee10-demo-container-initializer/src/main/java/org/example/initializer/FooInitializer.java b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-spec/jetty-ee10-demo-container-initializer/src/main/java/org/example/initializer/FooInitializer.java index 23ee4dda8e5e..74f5dc47bf94 100644 --- a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-spec/jetty-ee10-demo-container-initializer/src/main/java/org/example/initializer/FooInitializer.java +++ b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-spec/jetty-ee10-demo-container-initializer/src/main/java/org/example/initializer/FooInitializer.java @@ -83,8 +83,8 @@ 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.Foo", new ArrayList<>(classes)); + ServletRegistration.Dynamic reg = context.addServlet("AnnotationTest", "org.example.test.AnnotationTest"); context.setAttribute("org.example.AnnotationTest.complete", (reg == null)); context.addListener(new FooListener()); diff --git a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-spec/jetty-ee10-demo-spec-webapp/pom.xml b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-spec/jetty-ee10-demo-spec-webapp/pom.xml index d729e46c7ca0..cdb030e36db3 100644 --- a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-spec/jetty-ee10-demo-spec-webapp/pom.xml +++ b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-spec/jetty-ee10-demo-spec-webapp/pom.xml @@ -207,20 +207,30 @@ org.eclipse.jetty.ee10.demos jetty-ee10-demo-container-initializer - - - diff --git a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-spec/jetty-ee10-demo-spec-webapp/src/test/java/org/eclipse/jetty/ee10/demos/SpecWebAppTest.java b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-spec/jetty-ee10-demo-spec-webapp/src/test/java/org/eclipse/jetty/ee10/demos/SpecWebAppTest.java new file mode 100644 index 000000000000..3efae9bc25be --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-spec/jetty-ee10-demo-spec-webapp/src/test/java/org/eclipse/jetty/ee10/demos/SpecWebAppTest.java @@ -0,0 +1,177 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee10.demos; + +import java.io.IOException; +import java.net.URI; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; +import java.util.stream.Stream; + +import org.eclipse.jetty.client.ContentResponse; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.ee10.webapp.WebAppContext; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.security.HashLoginService; +import org.eclipse.jetty.security.SecurityHandler; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.toolchain.test.FS; +import org.eclipse.jetty.toolchain.test.IO; +import org.eclipse.jetty.toolchain.test.MavenPaths; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; +import org.eclipse.jetty.util.component.LifeCycle; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.util.resource.ResourceFactory; +import org.example.MockDataSource; +import org.example.MockUserTransaction; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.junit.jupiter.api.Assertions.fail; + +@ExtendWith(WorkDirExtension.class) +public class SpecWebAppTest +{ + private Server server; + private HttpClient client; + + @BeforeEach + public void setup(WorkDir workDir) throws Exception + { + server = new Server(); + + ServerConnector connector = new ServerConnector(server); + connector.setPort(0); + server.addConnector(connector); + + Path webappDir = prepareWebAppDir(workDir); + + WebAppContext webapp = new WebAppContext(); + ResourceFactory resourceFactory = ResourceFactory.of(webapp); + webapp.setContextPath("/"); + webapp.setWarResource(resourceFactory.newResource(webappDir)); + webapp.setAttribute( + "org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern", + ".*/jakarta.servlet-api-[^/]*\\.jar$|.*/[^/]*taglibs.*\\.jar$"); + + HashLoginService hashLoginService = new HashLoginService(); + hashLoginService.setName("Test Realm"); + Path realmFile = MavenPaths.findTestResourceFile("ee10-demo-realm.properties"); + Resource realmResource = ResourceFactory.of(server).newResource(realmFile); + hashLoginService.setConfig(realmResource); + SecurityHandler securityHandler = webapp.getSecurityHandler(); + securityHandler.setLoginService(hashLoginService); + + new org.eclipse.jetty.ee10.plus.jndi.Resource(webapp, "jdbc/mydatasource", new MockDataSource()); + new org.eclipse.jetty.ee10.plus.jndi.Transaction("ee10", new MockUserTransaction()); + + server.setHandler(webapp); + server.start(); + + client = new HttpClient(); + client.start(); + } + + private Path prepareWebAppDir(WorkDir workDir) throws IOException + { + Path webappDir = workDir.getEmptyPathDir(); + Path srcWebapp = MavenPaths.projectBase().resolve("src/main/webapp"); + IO.copyDir(srcWebapp, webappDir); + + Path webappClassesDir = webappDir.resolve("WEB-INF/classes"); + FS.ensureDirExists(webappClassesDir); + Path classesDir = MavenPaths.projectBase().resolve("target/classes"); + IO.copyDir(classesDir, webappClassesDir); + + Path libDir = webappDir.resolve("WEB-INF/lib"); + FS.ensureDirExists(libDir); + copyDependency("jetty-ee10-demo-container-initializer", libDir); + copyDependency("jetty-ee10-demo-web-fragment", libDir); + + return webappDir; + } + + private void copyDependency(String depName, Path libDir) throws IOException + { + Path depPath = MavenPaths.projectBase().resolve("../" + depName).normalize(); + if (!Files.isDirectory(depPath)) + fail("Dependency not found: " + depPath); + Path outputJar = libDir.resolve(depName + ".jar"); + Map env = new HashMap<>(); + env.put("create", "true"); + + URI uri = URI.create("jar:" + outputJar.toUri().toASCIIString()); + try (FileSystem fs = FileSystems.newFileSystem(uri, env)) + { + Path root = fs.getPath("/"); + copyContents(depPath.resolve("target/classes"), root); + copyContents(depPath.resolve("src/main/resources"), root); + } + } + + public void copyContents(Path srcPath, Path destPath) throws IOException + { + try (Stream srcStream = Files.walk(srcPath)) + { + Iterator iter = srcStream + .filter(Files::isRegularFile) + .iterator(); + while (iter.hasNext()) + { + Path path = iter.next(); + URI relativeSrc = srcPath.toUri().relativize(path.toUri()); + Path destFile = destPath.resolve(relativeSrc.toASCIIString()); + System.err.printf("Copy %s (%s) -> %s%n", path, relativeSrc, destFile); + if (!Files.exists(destFile.getParent())) + Files.createDirectories(destFile.getParent()); + Files.copy(path, destFile, StandardCopyOption.REPLACE_EXISTING); + } + } + } + + @AfterEach + public void teardown() + { + LifeCycle.stop(client); + LifeCycle.stop(server); + } + + @Test + public void testNoFailures() throws InterruptedException, ExecutionException, TimeoutException + { + ContentResponse response = client.newRequest(server.getURI().resolve("/test/")) + .followRedirects(false) + .send(); + + assertThat("response status", response.getStatus(), is(HttpStatus.OK_200)); + // Look for 0 entries that fail. + assertThat("response", response.getContentAsString(), not(containsString(">FAIL<"))); + } +} diff --git a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-spec/jetty-ee10-demo-spec-webapp/src/test/resources/ee10-demo-realm.properties b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-spec/jetty-ee10-demo-spec-webapp/src/test/resources/ee10-demo-realm.properties new file mode 100644 index 000000000000..9d88b852b7f8 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-spec/jetty-ee10-demo-spec-webapp/src/test/resources/ee10-demo-realm.properties @@ -0,0 +1,21 @@ +# +# This file defines users passwords and roles for a HashUserRealm +# +# The format is +# : [, ...] +# +# Passwords may be clear text, obfuscated or checksummed. The class +# org.eclipse.util.Password should be used to generate obfuscated +# passwords or password checksums +# +# If DIGEST Authentication is used, the password must be in a recoverable +# format, either plain text or OBF:. +# +jetty: MD5:164c88b302622e17050af52c89945d44,user +admin: CRYPT:adpexzg3FUZAk,server-administrator,content-administrator,admin,user +other: OBF:1xmk1w261u9r1w1c1xmq,user +plain: plain,user +user: password,user + +# This entry is for digest auth. The credential is a MD5 hash of username:realmname:password +digest: MD5:6e120743ad67abfbc385bc2bb754e297,user diff --git a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-spec/jetty-ee10-demo-spec-webapp/src/test/resources/jetty-logging.properties b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-spec/jetty-ee10-demo-spec-webapp/src/test/resources/jetty-logging.properties new file mode 100644 index 000000000000..61e4e736f8c4 --- /dev/null +++ b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-spec/jetty-ee10-demo-spec-webapp/src/test/resources/jetty-logging.properties @@ -0,0 +1,11 @@ +## Jetty Logging using jetty-slf4j-impl +org.eclipse.jetty.LEVEL=INFO +#org.eclipse.jetty.STACKS=true +org.eclipse.jetty.ee10.annotations.LEVEL=DEBUG +#org.eclipse.jetty.STACKS=false +#org.eclipse.jetty.io.LEVEL=DEBUG +#org.eclipse.jetty.io.ssl.LEVEL=DEBUG +#org.eclipse.jetty.server.LEVEL=DEBUG +#org.eclipse.jetty.ee10.servlets.LEVEL=DEBUG +#org.eclipse.jetty.alpn.LEVEL=DEBUG +#org.eclipse.jetty.jmx.LEVEL=DEBUG From a157cf7cb8e9a7f019cd97cca20fe1842b5f757d Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Mon, 31 Jul 2023 11:10:51 -0500 Subject: [PATCH 2/2] Create IO.copyDir(Path, Path, CopyOptions...) and use it --- .../main/java/org/eclipse/jetty/util/IO.java | 36 +++++++++++++++++++ .../jetty/ee10/demos/SpecWebAppTest.java | 28 ++------------- .../test/resources/jetty-logging.properties | 2 +- 3 files changed, 40 insertions(+), 26 deletions(-) diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/IO.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/IO.java index 28bb4ee71e33..8c0c1c798abc 100644 --- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/IO.java +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/IO.java @@ -27,16 +27,21 @@ import java.io.Reader; import java.io.StringWriter; import java.io.Writer; +import java.net.URI; import java.nio.ByteBuffer; import java.nio.channels.GatheringByteChannel; import java.nio.charset.Charset; +import java.nio.file.CopyOption; 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.Iterator; +import java.util.Objects; import java.util.concurrent.CompletionException; import java.util.concurrent.ExecutionException; +import java.util.stream.Stream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -218,6 +223,37 @@ public static void copyDir(File from, File to) throws IOException } } + /** + * Copy the contents of a directory from one directory to another. + * + * @param srcDir the source directory + * @param destDir the destination directory + * @throws IOException if unable to copy the file + */ + public static void copyDir(Path srcDir, Path destDir, CopyOption... copyOptions) throws IOException + { + if (!Files.isDirectory(Objects.requireNonNull(srcDir))) + throw new IllegalArgumentException("Source is not a directory: " + srcDir); + if (!Files.isDirectory(Objects.requireNonNull(destDir))) + throw new IllegalArgumentException("Dest is not a directory: " + destDir); + + try (Stream sourceStream = Files.walk(srcDir, 20)) + { + Iterator iterFiles = sourceStream + .filter(Files::isRegularFile) + .iterator(); + while (iterFiles.hasNext()) + { + Path sourceFile = iterFiles.next(); + URI relativeSrc = srcDir.toUri().relativize(sourceFile.toUri()); + Path destFile = destDir.resolve(relativeSrc.toASCIIString()); + if (!Files.exists(destFile.getParent())) + Files.createDirectories(destFile.getParent()); + Files.copy(sourceFile, destFile, copyOptions); + } + } + } + public static void copyFile(File from, File to) throws IOException { try (InputStream in = new FileInputStream(from); diff --git a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-spec/jetty-ee10-demo-spec-webapp/src/test/java/org/eclipse/jetty/ee10/demos/SpecWebAppTest.java b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-spec/jetty-ee10-demo-spec-webapp/src/test/java/org/eclipse/jetty/ee10/demos/SpecWebAppTest.java index 3efae9bc25be..308f7e8248f1 100644 --- a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-spec/jetty-ee10-demo-spec-webapp/src/test/java/org/eclipse/jetty/ee10/demos/SpecWebAppTest.java +++ b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-spec/jetty-ee10-demo-spec-webapp/src/test/java/org/eclipse/jetty/ee10/demos/SpecWebAppTest.java @@ -21,11 +21,9 @@ import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.util.HashMap; -import java.util.Iterator; import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; -import java.util.stream.Stream; import org.eclipse.jetty.client.ContentResponse; import org.eclipse.jetty.client.HttpClient; @@ -36,10 +34,10 @@ 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.MavenPaths; import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; +import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.component.LifeCycle; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.resource.ResourceFactory; @@ -131,28 +129,8 @@ private void copyDependency(String depName, Path libDir) throws IOException try (FileSystem fs = FileSystems.newFileSystem(uri, env)) { Path root = fs.getPath("/"); - copyContents(depPath.resolve("target/classes"), root); - copyContents(depPath.resolve("src/main/resources"), root); - } - } - - public void copyContents(Path srcPath, Path destPath) throws IOException - { - try (Stream srcStream = Files.walk(srcPath)) - { - Iterator iter = srcStream - .filter(Files::isRegularFile) - .iterator(); - while (iter.hasNext()) - { - Path path = iter.next(); - URI relativeSrc = srcPath.toUri().relativize(path.toUri()); - Path destFile = destPath.resolve(relativeSrc.toASCIIString()); - System.err.printf("Copy %s (%s) -> %s%n", path, relativeSrc, destFile); - if (!Files.exists(destFile.getParent())) - Files.createDirectories(destFile.getParent()); - Files.copy(path, destFile, StandardCopyOption.REPLACE_EXISTING); - } + IO.copyDir(depPath.resolve("target/classes"), root); + IO.copyDir(depPath.resolve("src/main/resources"), root, StandardCopyOption.REPLACE_EXISTING); } } diff --git a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-spec/jetty-ee10-demo-spec-webapp/src/test/resources/jetty-logging.properties b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-spec/jetty-ee10-demo-spec-webapp/src/test/resources/jetty-logging.properties index 61e4e736f8c4..2062ff2064dc 100644 --- a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-spec/jetty-ee10-demo-spec-webapp/src/test/resources/jetty-logging.properties +++ b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-spec/jetty-ee10-demo-spec-webapp/src/test/resources/jetty-logging.properties @@ -1,7 +1,7 @@ ## Jetty Logging using jetty-slf4j-impl org.eclipse.jetty.LEVEL=INFO #org.eclipse.jetty.STACKS=true -org.eclipse.jetty.ee10.annotations.LEVEL=DEBUG +#org.eclipse.jetty.ee10.annotations.LEVEL=DEBUG #org.eclipse.jetty.STACKS=false #org.eclipse.jetty.io.LEVEL=DEBUG #org.eclipse.jetty.io.ssl.LEVEL=DEBUG