001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.eclipse.aether.named.support;
020
021import java.util.Objects;
022import java.util.concurrent.ConcurrentHashMap;
023import java.util.concurrent.ConcurrentMap;
024import java.util.concurrent.atomic.AtomicInteger;
025
026import org.eclipse.aether.named.NamedLockFactory;
027import org.slf4j.Logger;
028import org.slf4j.LoggerFactory;
029
030/**
031 * Support class for {@link NamedLockFactory} implementations providing reference counting.
032 */
033public abstract class NamedLockFactorySupport implements NamedLockFactory {
034    protected final Logger logger = LoggerFactory.getLogger(getClass());
035
036    private final ConcurrentMap<String, NamedLockHolder> locks;
037
038    public NamedLockFactorySupport() {
039        this.locks = new ConcurrentHashMap<>();
040    }
041
042    @Override
043    public NamedLockSupport getLock(final String name) {
044        return locks.compute(name, (k, v) -> {
045                    if (v == null) {
046                        v = new NamedLockHolder(createLock(k));
047                    }
048                    v.incRef();
049                    return v;
050                })
051                .namedLock;
052    }
053
054    @Override
055    public void shutdown() {
056        // override if needed
057    }
058
059    public void closeLock(final String name) {
060        locks.compute(name, (k, v) -> {
061            if (v != null && v.decRef() == 0) {
062                destroyLock(v.namedLock.name());
063                return null;
064            }
065            return v;
066        });
067    }
068
069    /**
070     * Implementations shall create and return {@link NamedLockSupport} for given {@code name}, this method must never
071     * return {@code null}.
072     */
073    protected abstract NamedLockSupport createLock(String name);
074
075    /**
076     * Implementation may override this (empty) method to perform some sort of implementation specific cleanup for
077     * given lock name. Invoked when reference count for given name drops to zero and named lock was removed.
078     */
079    protected void destroyLock(final String name) {
080        // override if needed
081    }
082
083    private static final class NamedLockHolder {
084        private final NamedLockSupport namedLock;
085
086        private final AtomicInteger referenceCount;
087
088        private NamedLockHolder(final NamedLockSupport namedLock) {
089            this.namedLock = Objects.requireNonNull(namedLock);
090            this.referenceCount = new AtomicInteger(0);
091        }
092
093        private int incRef() {
094            return referenceCount.incrementAndGet();
095        }
096
097        private int decRef() {
098            return referenceCount.decrementAndGet();
099        }
100
101        @Override
102        public String toString() {
103            return "[refCount=" + referenceCount.get() + ", lock=" + namedLock + "]";
104        }
105    }
106}