This is an automated email from the ASF dual-hosted git repository. ggregory pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/juneau.git
The following commit(s) were added to refs/heads/master by this push: new 4078378df org.apache.juneau.microservice.jetty.JettyMicroservice.Builder.jettyXml(Object, boolean) now handles java.nio.file.Path. 4078378df is described below commit 4078378dff0fe97f44dae6d27a7cb8b3649e72ed Author: Gary Gregory <garydgreg...@gmail.com> AuthorDate: Fri Jun 6 12:29:23 2025 -0400 org.apache.juneau.microservice.jetty.JettyMicroservice.Builder.jettyXml(Object, boolean) now handles java.nio.file.Path. - Add org.apache.juneau.common.internal.PathReaderBuilder - Add org.apache.juneau.common.internal.IOUtils.read(Path) --- RELEASE-NOTES.txt | 3 + .../org/apache/juneau/common/internal/IOUtils.java | 86 +++++++++----- .../juneau/common/internal/PathReaderBuilder.java | 125 +++++++++++++++++++++ .../microservice/jetty/JettyMicroservice.java | 18 +-- .../apache/juneau/common/internal/IOUtilsTest.java | 36 ++++++ .../common/internal/PathReaderBuilderTest.java | 116 +++++++++++++++++++ 6 files changed, 352 insertions(+), 32 deletions(-) diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index 323e38eab..3e7618739 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -48,6 +48,9 @@ Release Notes - Juneau - Version 9.1.0 * Add org.apache.juneau.rest.mock.MockServletResponse.sendRedirect(String, int, boolean) for jakarta.servlet:jakarta.servlet-api 6.1.0. * Bump org.junit:junit-bom from 5.10.3 to 5.11.4 #161, #168, #171, #173, #185. * Flip console output logic on shutdown from a system property called 'SystemUtils.quiet' to 'SystemUtils.verbose'. + * org.apache.juneau.microservice.jetty.JettyMicroservice.Builder.jettyXml(Object, boolean) now handles java.nio.file.Path. + * Add org.apache.juneau.common.internal.PathReaderBuilder + * Add org.apache.juneau.common.internal.IOUtils.read(Path) Release Notes - Juneau - Version 9.0.1 diff --git a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/internal/IOUtils.java b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/internal/IOUtils.java index 9c935a3eb..79aea8565 100644 --- a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/internal/IOUtils.java +++ b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/internal/IOUtils.java @@ -12,11 +12,24 @@ // *************************************************************************************************************************** package org.apache.juneau.common.internal; -import java.io.*; -import java.nio.charset.*; -import java.util.*; -import java.util.concurrent.atomic.*; -import java.util.function.*; +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.StringReader; +import java.io.Writer; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Scanner; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; /** * Various I/O related utility methods. @@ -461,26 +474,49 @@ public final class IOUtils { return new String(in, charset); } - /** - * Reads the contents of a file into a string. - * - * <p> - * Assumes default character encoding. - * - * @param in - * The file to read. - * <br>Can be <jk>null</jk>. - * @return - * The contents of the reader as a string, or <jk>null</jk> if file does not exist. - * @throws IOException If a problem occurred trying to read from the reader. - */ - public static String read(File in) throws IOException { - if (in == null || ! in.exists()) - return null; - try (Reader r = FileReaderBuilder.create(in).build()) { - return read(r, in.length()); - } - } + /** + * Reads the contents of a file into a string. + * + * <p> + * Assumes default character encoding. + * + * @param in + * The file to read. + * <br>Can be <jk>null</jk>. + * @return + * The contents of the reader as a string, or <jk>null</jk> if file does not exist. + * @throws IOException If a problem occurred trying to read from the reader. + */ + public static String read(File in) throws IOException { + if (in == null || ! in.exists()) + return null; + try (Reader r = FileReaderBuilder.create(in).build()) { + return read(r, in.length()); + } + } + + /** + * Reads the contents of a path into a string. + * + * <p> + * Assumes default character encoding. + * + * @param in + * The path to read. + * <br>Can be <jk>null</jk>. + * @return + * The contents of the reader as a string, or <jk>null</jk> if path does not exist. + * @throws IOException If a problem occurred trying to read from the reader. + * @since 9.1.0 + */ + public static String read(Path in) throws IOException { + if (in == null || !Files.exists(in)) { + return null; + } + try (Reader r = PathReaderBuilder.create(in).build()) { + return read(r, Files.size(in)); + } + } /** * Reads the contents of a reader into a string. diff --git a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/internal/PathReaderBuilder.java b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/internal/PathReaderBuilder.java new file mode 100644 index 000000000..c2c6bcb8a --- /dev/null +++ b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/internal/PathReaderBuilder.java @@ -0,0 +1,125 @@ +// *************************************************************************************************************************** +// * 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. * +// *************************************************************************************************************************** +package org.apache.juneau.common.internal; + +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; +import java.nio.file.Paths; + +/** + * Utility class for creating {@link Path}-based {@link Reader} objects. + * + * @since 9.1.0 + */ +public final class PathReaderBuilder { + + /** + * Creates a new builder. + * + * @return A new builder. + */ + public static PathReaderBuilder create() { + return new PathReaderBuilder(); + } + + /** + * Creates a new builder initialized with the specified path. + * + * @param path The path being written to. + * @return A new builder. + */ + public static PathReaderBuilder create(final Path path) { + return new PathReaderBuilder().path(path); + } + + private Path path; + + private Charset charset = Charset.defaultCharset(); + + private boolean allowNoFile; + + /** + * If called and the path is <jk>null</jk> or non-existent, then the {@link #build()} command will return an empty reader instead of a {@link IOException}. + * + * @return This object. + */ + public PathReaderBuilder allowNoFile() { + this.allowNoFile = true; + return this; + } + + /** + * Creates a new File reader. + * + * @return A new File reader. + * @throws IOException if an I/O error occurs opening the path + */ + public Reader build() throws IOException { + if (!allowNoFile && path == null) { + throw new IllegalStateException("No path"); + } + if (!allowNoFile && !Files.exists(path)) { + throw new NoSuchFileException(path.toString()); + } + return allowNoFile ? new StringReader("") : Files.newBufferedReader(path, charset != null ? charset : Charset.defaultCharset()); + } + + /** + * Sets the character encoding of the path. + * + * @param charset The character encoding. The default is {@link Charset#defaultCharset()}. Null resets to the default. + * @return This object. + */ + public PathReaderBuilder charset(final Charset charset) { + this.charset = charset; + return this; + } + + /** + * Sets the character encoding of the path. + * + * @param charset The character encoding. The default is {@link Charset#defaultCharset()}. Null resets to the default. + * @return This object. + */ + public PathReaderBuilder charset(final String charset) { + this.charset = charset != null ? Charset.forName(charset) : null; + return this; + } + + /** + * Sets the path being written from. + * + * @param path The path being written from. + * @return This object. + */ + public PathReaderBuilder path(final Path path) { + this.path = path; + return this; + } + + /** + * Sets the path of the path being written from. + * + * @param path The path of the path being written from. + * @return This object. + */ + public PathReaderBuilder path(final String path) { + this.path = Paths.get(path); + return this; + } +} diff --git a/juneau-microservice/juneau-microservice-jetty/src/main/java/org/apache/juneau/microservice/jetty/JettyMicroservice.java b/juneau-microservice/juneau-microservice-jetty/src/main/java/org/apache/juneau/microservice/jetty/JettyMicroservice.java index ccc9890a7..261d0bebc 100644 --- a/juneau-microservice/juneau-microservice-jetty/src/main/java/org/apache/juneau/microservice/jetty/JettyMicroservice.java +++ b/juneau-microservice/juneau-microservice-jetty/src/main/java/org/apache/juneau/microservice/jetty/JettyMicroservice.java @@ -30,6 +30,7 @@ import java.net.ServerSocket; import java.net.URI; import java.net.URISyntaxException; import java.net.UnknownHostException; +import java.nio.file.Path; import java.util.Map; import java.util.Objects; import java.util.Random; @@ -203,7 +204,8 @@ public class JettyMicroservice extends Microservice { * <br>Can be any of the following: * <ul> * <li>{@link String} - Relative path to file on file system or classpath. - * <li>{@link File} - File on file system. + * <li>{@link File} - File on file system. + * <li>{@link Path} - Path on file system. * <li>{@link InputStream} - Raw contents as <c>UTF-8</c> encoded stream. * <li>{@link Reader} - Raw contents. * </ul> @@ -216,12 +218,14 @@ public class JettyMicroservice extends Microservice { public Builder jettyXml(Object jettyXml, boolean resolveVars) throws IOException { if (jettyXml instanceof String) this.jettyXml = read(resolveFile(jettyXml.toString())); - else if (jettyXml instanceof File) - this.jettyXml = read((File)jettyXml); - else if (jettyXml instanceof InputStream) - this.jettyXml = read((InputStream)jettyXml); - else if (jettyXml instanceof Reader) - this.jettyXml = read((Reader)jettyXml); + else if (jettyXml instanceof File file) + this.jettyXml = read(file); + else if (jettyXml instanceof Path path) + this.jettyXml = read(path); + else if (jettyXml instanceof InputStream inputStream) + this.jettyXml = read(inputStream); + else if (jettyXml instanceof Reader reader) + this.jettyXml = read(reader); else throw new BasicRuntimeException("Invalid object type passed to jettyXml(Object): {0}", className(jettyXml)); this.jettyXmlResolveVars = resolveVars; diff --git a/juneau-utest/src/test/java/org/apache/juneau/common/internal/IOUtilsTest.java b/juneau-utest/src/test/java/org/apache/juneau/common/internal/IOUtilsTest.java new file mode 100644 index 000000000..c2dedf9a1 --- /dev/null +++ b/juneau-utest/src/test/java/org/apache/juneau/common/internal/IOUtilsTest.java @@ -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. * +// *************************************************************************************************************************** + +package org.apache.juneau.common.internal; + +import static org.junit.Assert.assertEquals; + +import java.io.IOException; +import java.io.StringReader; +import java.nio.file.Paths; +import java.util.Properties; + +import org.junit.Test; + +/** + * Tests {@link IOUtils}. + */ +public class IOUtilsTest { + + @Test + public void testReadPath() throws IOException { + var p = new Properties(); + p.load(new StringReader(IOUtils.read(Paths.get("src/test/resources/files/Test3.properties")))); + assertEquals("files/Test3.properties", p.get("file")); + } +} diff --git a/juneau-utest/src/test/java/org/apache/juneau/common/internal/PathReaderBuilderTest.java b/juneau-utest/src/test/java/org/apache/juneau/common/internal/PathReaderBuilderTest.java new file mode 100644 index 000000000..ad9d6f142 --- /dev/null +++ b/juneau-utest/src/test/java/org/apache/juneau/common/internal/PathReaderBuilderTest.java @@ -0,0 +1,116 @@ +// *************************************************************************************************************************** +// * 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. * +// *************************************************************************************************************************** + +package org.apache.juneau.common.internal; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThrows; + +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Properties; + +import org.junit.Test; + +/** + * Tests {@link PathReaderBuilder}. + */ +public class PathReaderBuilderTest { + + private static final Path PATH = Paths.get("src/test/resources/files/Test3.properties"); + + @Test + public void testAllowNoFile() throws IOException { + final var p = new Properties(); + try (Reader r = PathReaderBuilder.create().allowNoFile().build()) { + p.load(new StringReader(IOUtils.read(r, Files.size(PATH)))); + } + assertNull(p.get("file")); + p.clear(); + try (Reader r = PathReaderBuilder.create().allowNoFile().path("this file does not exist, at all.").build()) { + p.load(new StringReader(IOUtils.read(r, Files.size(PATH)))); + } + assertNull(p.get("file")); + } + + @Test + public void testAllowNoFileException() throws IOException { + final var p = new Properties(); + assertThrows(IllegalStateException.class, () -> PathReaderBuilder.create().build()); + assertThrows(NoSuchFileException.class, () -> PathReaderBuilder.create().path("this file does not exist, at all.").build()); + } + + @Test + public void testCharsetCharset() throws IOException { + final var p = new Properties(); + try (Reader r = PathReaderBuilder.create().path(PATH).charset(StandardCharsets.UTF_8).build()) { + p.load(new StringReader(IOUtils.read(r, Files.size(PATH)))); + } + assertEquals("files/Test3.properties", p.get("file")); + p.clear(); + try (Reader r = PathReaderBuilder.create().path(PATH).charset((Charset) null).build()) { + p.load(new StringReader(IOUtils.read(r, Files.size(PATH)))); + } + assertEquals("files/Test3.properties", p.get("file")); + } + + @Test + public void testCharsetString() throws IOException { + final var p = new Properties(); + try (Reader r = PathReaderBuilder.create().path(PATH).charset(StandardCharsets.UTF_8.name()).build()) { + p.load(new StringReader(IOUtils.read(r, Files.size(PATH)))); + } + assertEquals("files/Test3.properties", p.get("file")); + p.clear(); + try (Reader r = PathReaderBuilder.create().path(PATH).charset((String) null).build()) { + p.load(new StringReader(IOUtils.read(r, Files.size(PATH)))); + } + assertEquals("files/Test3.properties", p.get("file")); + } + + @Test + public void testCreate() throws IOException { + final var p = new Properties(); + try (Reader r = PathReaderBuilder.create(PATH).build()) { + p.load(new StringReader(IOUtils.read(r, Files.size(PATH)))); + } + assertEquals("files/Test3.properties", p.get("file")); + } + + @Test + public void testPathPath() throws IOException { + final var p = new Properties(); + try (Reader r = PathReaderBuilder.create().path(PATH).build()) { + p.load(new StringReader(IOUtils.read(r, Files.size(PATH)))); + } + assertEquals("files/Test3.properties", p.get("file")); + } + + @Test + public void testPathString() throws IOException { + final var p = new Properties(); + try (Reader r = PathReaderBuilder.create().path(PATH.toString()).build()) { + p.load(new StringReader(IOUtils.read(r, Files.size(PATH)))); + } + assertEquals("files/Test3.properties", p.get("file")); + } + +}