/* * 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.sshd.common.util.closeable; import java.util.concurrent.atomic.AtomicReference; import org.apache.sshd.common.future.CloseFuture; import org.apache.sshd.common.future.DefaultCloseFuture; import org.apache.sshd.common.future.SshFuture; import org.apache.sshd.common.future.SshFutureListener; /** * Provides some default implementations * * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a> */ public abstract class AbstractCloseable extends IoBaseCloseable { public enum State { Opened, Graceful, Immediate, Closed } /** * Lock object for this session state */ protected final Object lock = new Object(); /** * State of this object */ protected final AtomicReference<AbstractCloseable.State> state = new AtomicReference<>(State.Opened); /** * A future that will be set 'closed' when the object is actually closed */ protected final CloseFuture closeFuture = new DefaultCloseFuture(lock); protected AbstractCloseable() { this(""); } protected AbstractCloseable(String discriminator) { super(discriminator); } @Override public void addCloseFutureListener(SshFutureListener<CloseFuture> listener) { closeFuture.addListener(listener); } @Override public void removeCloseFutureListener(SshFutureListener<CloseFuture> listener) { closeFuture.removeListener(listener); } @Override public CloseFuture close(boolean immediately) { if (immediately) { if (state.compareAndSet(State.Opened, State.Immediate) || state.compareAndSet(State.Graceful, State.Immediate)) { if (log.isDebugEnabled()) { log.debug("close({}) Closing immediately", this); } preClose(); doCloseImmediately(); if (log.isDebugEnabled()) { log.debug("close({})[Immediately] closed", this); } } else { if (log.isDebugEnabled()) { log.debug("close({})[Immediately] state already {}", this, state.get()); } } } else { if (state.compareAndSet(State.Opened, State.Graceful)) { if (log.isDebugEnabled()) { log.debug("close({}) Closing gracefully", this); } preClose(); SshFuture<CloseFuture> grace = doCloseGracefully(); if (grace != null) { grace.addListener(future -> { if (state.compareAndSet(State.Graceful, State.Immediate)) { doCloseImmediately(); if (log.isDebugEnabled()) { log.debug("close({}][Graceful] - operationComplete() closed", AbstractCloseable.this); } } }); } else { if (state.compareAndSet(State.Graceful, State.Immediate)) { doCloseImmediately(); if (log.isDebugEnabled()) { log.debug("close({})[Graceful] closed", this); } } } } else { if (log.isDebugEnabled()) { log.debug("close({})[Graceful] state already {}", this, state.get()); } } } return closeFuture; } @Override public boolean isClosed() { return state.get() == State.Closed; } @Override public boolean isClosing() { return state.get() != State.Opened; } /** * preClose is guaranteed to be called before doCloseGracefully or doCloseImmediately. * When preClose() is called, isClosing() == true */ protected void preClose() { // nothing } protected CloseFuture doCloseGracefully() { return null; } /** * <P>doCloseImmediately is called once and only once * with state == Immediate</P> * * <P>Overriding methods should always call the base implementation. * It may be called concurrently while preClose() or doCloseGracefully is executing</P> */ protected void doCloseImmediately() { closeFuture.setClosed(); state.set(State.Closed); } protected Builder builder() { return new Builder(lock); } }