All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.datacleaner.util.UsageAwareCloseable Maven / Gradle / Ivy

/**
 * DataCleaner (community edition)
 * Copyright (C) 2014 Free Software Foundation, Inc.
 *
 * This copyrighted material is made available to anyone wishing to use, modify,
 * copy, or redistribute it subject to the terms and conditions of the GNU
 * Lesser General Public License, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this distribution; if not, write to:
 * Free Software Foundation, Inc.
 * 51 Franklin Street, Fifth Floor
 * Boston, MA  02110-1301  USA
 */
package org.datacleaner.util;

import java.io.Closeable;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A utility {@link Closeable} implementation of {@link Closeable} which is
 * aware of usage/consumption by more than one consumer. Therefore the abstract
 * {@link #closeInternal()} method will only be invoked once, when all usage has
 * stopped.
 */
public abstract class UsageAwareCloseable implements Closeable {

    private static class CloseEvent {
        final String threadName;
        final StackTraceElement[] stackTrace;

        CloseEvent(final String name, final StackTraceElement[] trace) {
            threadName = name;
            stackTrace = trace;
        }
    }

    private static final int STACK_TRACE_ELEMENTS_TO_LOG = 20;

    private final Logger _logger;

    private final AtomicInteger _usageCount;
    private final AtomicBoolean _closed;
    private final List _closeEvents;

    public UsageAwareCloseable() {
        this(LoggerFactory.getLogger(UsageAwareCloseable.class));
    }

    public UsageAwareCloseable(final Logger logger) {
        _logger = logger;
        _usageCount = new AtomicInteger(1);
        _closed = new AtomicBoolean(false);

        if (logger.isDebugEnabled()) {
            _closeEvents = new ArrayList<>(2);
            logger.debug("{} instantiated by:", this.getClass().getSimpleName());
            logNearestStack();
        } else {
            _closeEvents = null;
        }
    }

    private void logNearestStack() {
        final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
        for (int i = 1; i < stackTrace.length && i < STACK_TRACE_ELEMENTS_TO_LOG; i++) {
            final StackTraceElement ste = stackTrace[i];
            _logger.debug(" - {} @ line {}", ste.getClassName(), ste.getLineNumber());
        }
    }

    private void logStack(final StackTraceElement[] stackTrace) {
        for (int i = 1; i < stackTrace.length && i < STACK_TRACE_ELEMENTS_TO_LOG; i++) {
            final StackTraceElement ste = stackTrace[i];
            _logger.debug(" - {} @ line {}", ste.getClassName(), ste.getLineNumber());
        }
    }

    /**
     * Subclasses should implement this method to do the actual closing logic
     */
    protected abstract void closeInternal();

    public final boolean requestUsage() {
        if (isClosed()) {
            return false;
        }

        final int usage;
        synchronized (this) {
            usage = _usageCount.incrementAndGet();
            if (usage == 1) {
                _usageCount.decrementAndGet();
                _logger.debug("{} is closed, request for more usage refused", this.getClass().getSimpleName());
                return false;
            }
        }

        _logger.debug("Usage incremented to {} for {}", usage, this);

        if (_logger.isDebugEnabled()) {
            _logger.debug("Incremented usage by:");
            logNearestStack();
        }
        return true;
    }

    public final boolean isClosed() {
        return _closed.get();
    }

    @Override
    public final void close() {
        if (isClosed()) {
            _logger.warn(this + " is already closed, but close() was invoked!");
            if (_logger.isDebugEnabled()) {
                _closeEvents.add(new CloseEvent(Thread.currentThread().getName(), new Throwable().getStackTrace()));
                final int numCloses = _closeEvents.size();
                int i = 1;
                for (final CloseEvent closeEvent : _closeEvents) {
                    _logger.debug("Stack trace when close() no. {} of {}: ", i, numCloses);
                    _logger.debug("Thread name: {}", closeEvent.threadName);
                    logStack(closeEvent.stackTrace);
                    i++;
                }
            }
            return;
        }

        final int usage;

        synchronized (this) {
            usage = _usageCount.decrementAndGet();

            _logger.debug("Method close() invoked, usage decremented to {} for {}", _usageCount, this);
            if (usage == 0) {
                if (_logger.isDebugEnabled()) {
                    _closeEvents.add(new CloseEvent(Thread.currentThread().getName(), new Throwable().getStackTrace()));
                }
                _logger.debug("Closing {}", this);
                closeInternal();
                _closed.set(true);
            }
        }

    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        if (!isClosed()) {
            if (_logger.isWarnEnabled()) {
                _logger.warn("Method finalize() invoked but not all usages closed ({} remaining) (for {}). Closing.",
                        _usageCount, this);
            }
            // in case of gc, also do the closing
            closeInternal();
        }
    }

    /**
     * Gets the amount of usages this datacontext provider currently has.
     *
     * @return
     */
    protected int getUsageCount() {
        return _usageCount.get();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy