/*
* 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.flink.migration.runtime.state;
import java.io.Closeable;
import java.io.IOException;
import java.io.Serializable;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
/**
* A simple base for closable handles.
*
* Offers to register a stream (or other closable object) that close calls are delegated to if
* the handle is closed or was already closed.
*
* @deprecated Internal class for savepoint backwards compatibility. Don't use for other purposes.
*/
@Deprecated
@SuppressWarnings("deprecation")
public abstract class AbstractCloseableHandle implements Closeable, Serializable {
/** Serial Version UID must be constant to maintain format compatibility */
private static final long serialVersionUID = 1L;
/** To atomically update the "closable" field without needing to add a member class like "AtomicBoolean */
private static final AtomicIntegerFieldUpdater<AbstractCloseableHandle> CLOSER =
AtomicIntegerFieldUpdater.newUpdater(AbstractCloseableHandle.class, "isClosed");
// ------------------------------------------------------------------------
/** The closeable to close if this handle is closed late */
private transient volatile Closeable toClose;
/** Flag to remember if this handle was already closed */
@SuppressWarnings("unused") // this field is actually updated, but via the "CLOSER" updater
private transient volatile int isClosed;
// ------------------------------------------------------------------------
protected final void registerCloseable(Closeable toClose) throws IOException {
if (toClose == null) {
return;
}
// NOTE: The order of operations matters here:
// (1) first setting the closeable
// (2) checking the flag.
// Because the order in the {@link #close()} method is the opposite, and
// both variables are volatile (reordering barriers), we can be sure that
// one of the methods always notices the effect of a concurrent call to the
// other method.
this.toClose = toClose;
// check if we were closed early
if (this.isClosed != 0) {
toClose.close();
throw new IOException("handle is closed");
}
}
/**
* Closes the handle.
*
* <p>If a "Closeable" has been registered via {@link #registerCloseable(Closeable)},
* then this will be closes.
*
* <p>If any "Closeable" will be registered via {@link #registerCloseable(Closeable)} in the future,
* it will immediately be closed and that method will throw an exception.
*
* @throws IOException Exceptions occurring while closing an already registered {@code Closeable}
* are forwarded.
*
* @see #registerCloseable(Closeable)
*/
@Override
public final void close() throws IOException {
// NOTE: The order of operations matters here:
// (1) first setting the closed flag
// (2) checking whether there is already a closeable
// Because the order in the {@link #registerCloseable(Closeable)} method is the opposite, and
// both variables are volatile (reordering barriers), we can be sure that
// one of the methods always notices the effect of a concurrent call to the
// other method.
if (CLOSER.compareAndSet(this, 0, 1)) {
final Closeable toClose = this.toClose;
if (toClose != null) {
this.toClose = null;
toClose.close();
}
}
}
/**
* Checks whether this handle has been closed.
*
* @return True is the handle is closed, false otherwise.
*/
public boolean isClosed() {
return isClosed != 0;
}
/**
* This method checks whether the handle is closed and throws an exception if it is closed.
* If the handle is not closed, this method does nothing.
*
* @throws IOException Thrown, if the handle has been closed.
*/
public void ensureNotClosed() throws IOException {
if (isClosed != 0) {
throw new IOException("handle is closed");
}
}
}