This is an automated email from the ASF dual-hosted git repository.
reschke pushed a commit to branch OAK-11571
in repository https://gitbox.apache.org/repos/asf/jackrabbit-oak.git
The following commit(s) were added to refs/heads/OAK-11571 by this push:
new 80fd3120ce OAK-11571: commons: add Closer class (similar to Guava
Closer) - add replacement and switch tests to new class
80fd3120ce is described below
commit 80fd3120ced0339405ce92bfe65766cdc3c5a21c
Author: Julian Reschke <[email protected]>
AuthorDate: Thu Mar 13 16:31:17 2025 +0100
OAK-11571: commons: add Closer class (similar to Guava Closer) - add
replacement and switch tests to new class
---
oak-commons/pom.xml | 3 +-
.../apache/jackrabbit/oak/commons/pio/Closer.java | 128 +++++++++++++++++++++
.../jackrabbit/oak/commons/pio/package-info.java | 28 +++++
.../jackrabbit/oak/commons/io/CloserTest.java | 20 ++--
4 files changed, 169 insertions(+), 10 deletions(-)
diff --git a/oak-commons/pom.xml b/oak-commons/pom.xml
index 385104b03c..c25643f766 100644
--- a/oak-commons/pom.xml
+++ b/oak-commons/pom.xml
@@ -56,7 +56,8 @@
org.apache.jackrabbit.oak.commons.log,
org.apache.jackrabbit.oak.commons.sort,
org.apache.jackrabbit.oak.commons.properties,
- org.apache.jackrabbit.oak.commons.jdkcompat
+ org.apache.jackrabbit.oak.commons.jdkcompat,
+ org.apache.jackrabbit.oak.commons.io
</Export-Package>
</instructions>
</configuration>
diff --git
a/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/pio/Closer.java
b/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/pio/Closer.java
new file mode 100755
index 0000000000..3cc3cf3101
--- /dev/null
+++
b/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/pio/Closer.java
@@ -0,0 +1,128 @@
+/*
+ * 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.jackrabbit.oak.commons.pio;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.Objects;
+
+/**
+ * Convenience utility to close a list of {@link Closeable}s.
+ * <p>
+ * Inspired by and replacing Guava's Closer.
+ */
+public class Closer {
+
+ private Closer() {
+ // no instances for you
+ }
+
+ // stack of closeables to close
+ private Deque<Closeable> closeables = new ArrayDeque<>();
+
+ // set by rethrow method
+ private Throwable rethrow = null;
+
+ /**
+ * Create instance of Closer.
+ */
+ public static Closer create() {
+ return new Closer();
+ }
+
+ /**
+ * Add a {@link Closeable} to the list.
+ * @param closeable
+ */
+ public void register(@Nullable Closeable closeable) {
+ if (closeable != null) {
+ closeables.add(closeable);
+ }
+ }
+
+ /**
+ * Closes the set of {@link Closeable}s in reverse order.
+ * <p>
+ * Swallows all {@link IOException}s except the first that
+ * was thrown.
+ * <p>
+ * If {@link #rethrow} was called before, throw <em>that</em>
+ * exception instead (wrapped into a {@link RuntimeException}
+ * when necessary).
+ */
+ public void close() throws IOException {
+ // keep track of the IOException to throw
+ IOException toThrow = null;
+
+ // close all in reverse order
+ while (!closeables.isEmpty()) {
+ Closeable closeable = closeables.removeLast();
+ try {
+ closeable.close();
+ } catch (IOException exception) {
+ // remember the first one that occured
+ if (toThrow == null) {
+ toThrow = exception;
+ }
+ }
+ }
+
+ // consider exceptions passed to rethrow()
+ if (rethrow instanceof IOException) {
+ throw (IOException) rethrow;
+ } else if (rethrow instanceof RuntimeException) {
+ throw (RuntimeException) rethrow;
+ } else if (rethrow != null) {
+ throw new RuntimeException(rethrow);
+ }
+
+ // otherwise throw the IOException we selected
+ if (toThrow != null) {
+ throw toThrow;
+ }
+ }
+
+ /**
+ * Stores a {@link Throwable} for later use in {@link #close()} and
+ * rethrows it (potentially wrapped into {@link RuntimeException} or
+ * {@link Error}.
+ * <p>
+ * {@link #close()} will use the exception passed in the last call of this
+ * method.
+ * @return never returns
+ * @throws IOException
+ */
+ public RuntimeException rethrow(@NotNull Throwable throwable) throws
IOException {
+ rethrow = Objects.requireNonNull(throwable);
+ if (throwable instanceof IOException) {
+ throw (IOException) throwable;
+ } else if (throwable instanceof RuntimeException) {
+ throw (RuntimeException) throwable;
+ } else if (throwable instanceof Error) {
+ throw (Error) throwable;
+ } else {
+ throw new RuntimeException(throwable);
+ }
+ }
+}
diff --git
a/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/pio/package-info.java
b/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/pio/package-info.java
new file mode 100644
index 0000000000..d552c968ab
--- /dev/null
+++
b/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/pio/package-info.java
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+/**
+ * Internal ("private") utilities related to IO..
+ */
+@Internal(since = "1.0.0")
+@Version("1.0.0")
+package org.apache.jackrabbit.oak.commons.pio;
+import org.apache.jackrabbit.oak.commons.annotations.Internal;
+import org.osgi.annotation.versioning.Version;
+
diff --git
a/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/io/CloserTest.java
b/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/io/CloserTest.java
index bc20525288..e6ba84abad 100644
---
a/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/io/CloserTest.java
+++
b/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/io/CloserTest.java
@@ -18,7 +18,7 @@
*/
package org.apache.jackrabbit.oak.commons.io;
-import org.apache.jackrabbit.guava.common.io.Closer;
+import org.apache.jackrabbit.oak.commons.pio.Closer;
import org.junit.Test;
import java.io.Closeable;
@@ -33,11 +33,11 @@ import static org.junit.Assert.fail;
public class CloserTest {
- // tests below confirm what Guava actually does
+ // tests below confirm what actually does
@Test
- public void testGuavaCloserOrder() throws IOException {
- // shows that Guava closes in reverse order
+ public void testCloserOrder() throws IOException {
+ // shows that closes in reverse order
int cnt = 2;
List<Integer> order = new ArrayList<>();
@@ -62,7 +62,7 @@ public class CloserTest {
}
@Test
- public void testGuavaCloseableThrowsRuntimeException() {
+ public void testCloseableThrowsRuntimeException() {
Closer closer = Closer.create();
closer.register(() -> {
throw new RuntimeException();
@@ -71,7 +71,7 @@ public class CloserTest {
}
@Test
- public void testGuavaWhichThrows() throws IOException {
+ public void testWhichThrows() throws IOException {
// shows which exception is not suppressed
int cnt = 2;
@@ -100,7 +100,7 @@ public class CloserTest {
}
@Test
- public void testGuavaRethrowRuntime() {
+ public void testRethrowRuntime() {
try {
Closer closer = Closer.create();
try {
@@ -114,13 +114,15 @@ public class CloserTest {
closer.close();
}
} catch (Exception ex) {
- assertTrue("should throw the (wrapped) unchecked exception",
+ assertTrue(
+ "should throw the (wrapped) unchecked exception, but got "
+
+ ex.getMessage(),
ex.getMessage().contains("unchecked"));
}
}
@Test
- public void testGuavaRethrowChecked() throws IOException {
+ public void testRethrowChecked() throws IOException {
try {
Closer closer = Closer.create();
try {