diff --git a/oak-commons/pom.xml b/oak-commons/pom.xml index 385104b03ce..83d64d9a07a 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.pio 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 00000000000..54705d5a3ef --- /dev/null +++ b/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/pio/Closer.java @@ -0,0 +1,126 @@ +/* + * 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 in reverse order, + * suppressing all but the first exception to occur. + *
+ * Inspired by and replacing Guava's Closer.
+ */
+public class Closer implements Closeable {
+
+ private Closer() {
+ // no instances for you
+ }
+
+ // stack of closeables to close, in general will be few
+ private final Deque
+ * Swallows all exceptions except the first that
+ * was thrown.
+ *
+ * If {@link #rethrow} was called before, even the first
+ * exception will be suppressed.
+ */
+ public void close() throws IOException {
+ // keep track of the IOException to throw
+ Throwable toThrow = null;
+
+ // close all in reverse order
+ while (!closeables.isEmpty()) {
+ Closeable closeable = closeables.removeLast();
+ try {
+ closeable.close();
+ } catch (Throwable exception) {
+ // remember the first one that occurred
+ if (toThrow == null) {
+ toThrow = exception;
+ }
+ }
+ }
+
+ // exceptions are suppressed when retrow was called
+ if (!suppressExceptionsOnClose && toThrow != null) {
+ // due to the contract of Closeable, the exception is either
+ // a checked IOException or an unchecked exception
+ if (toThrow instanceof IOException) {
+ throw (IOException) toThrow;
+ } else {
+ throw (RuntimeException) toThrow;
+ }
+ }
+ }
+
+ /**
+ * Sets a flag indicating that this method was called, then rethrows the
+ * given exception (potentially wrapped into {@link Error} or {@link RuntimeException}).
+ *
+ * {@link #close()} will not throw when this method was called before.
+ * @return never returns
+ * @throws IOException wrapping the input, when needed
+ */
+ public RuntimeException rethrow(@NotNull Throwable throwable) throws IOException {
+ Objects.requireNonNull(throwable);
+ suppressExceptionsOnClose = true;
+ 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 00000000000..d552c968ab0
--- /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/pio/CloserTest.java b/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/pio/CloserTest.java
new file mode 100644
index 00000000000..4550026cada
--- /dev/null
+++ b/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/pio/CloserTest.java
@@ -0,0 +1,213 @@
+/*
+ * 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.junit.Test;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public class CloserTest {
+
+ @Test
+ public void testCloserOrder() throws IOException {
+ // shows closes in reverse order
+
+ int cnt = 2;
+ List