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.concurrent.ConcurrentHashMap;
022import java.util.concurrent.ConcurrentMap;
023import java.util.concurrent.atomic.AtomicInteger;
024
025import org.eclipse.aether.named.NamedLockFactory;
026import org.slf4j.Logger;
027import org.slf4j.LoggerFactory;
028
029import static java.util.Objects.requireNonNull;
030
031/**
032 * Support class for {@link NamedLockFactory} implementations providing reference counting.
033 */
034public abstract class NamedLockFactorySupport implements NamedLockFactory {
035    protected final Logger logger = LoggerFactory.getLogger(getClass());
036
037    private final ConcurrentMap<String, NamedLockHolder> locks;
038
039    public NamedLockFactorySupport() {
040        this.locks = new ConcurrentHashMap<>();
041    }
042
043    @Override
044    public NamedLockSupport getLock(final String name) {
045        return locks.compute(name, (k, v) -> {
046                    if (v == null) {
047                        v = new NamedLockHolder(createLock(k));
048                    }
049                    v.incRef();
050                    return v;
051                })
052                .namedLock;
053    }
054
055    @Override
056    public void shutdown() {
057        // override if needed
058    }
059
060    public void closeLock(final String name) {
061        locks.compute(name, (k, v) -> {
062            if (v != null && v.decRef() == 0) {
063                destroyLock(v.namedLock.name());
064                return null;
065            }
066            return v;
067        });
068    }
069
070    /**
071     * Implementations shall create and return {@link NamedLockSupport} for given {@code name}, this method must never
072     * return {@code null}.
073     */
074    protected abstract NamedLockSupport createLock(String name);
075
076    /**
077     * Implementation may override this (empty) method to perform some sort of implementation specific cleanup for
078     * given lock name. Invoked when reference count for given name drops to zero and named lock was removed.
079     */
080    protected void destroyLock(final String name) {
081        // override if needed
082    }
083
084    private static final class NamedLockHolder {
085        private final NamedLockSupport namedLock;
086
087        private final AtomicInteger referenceCount;
088
089        private NamedLockHolder(final NamedLockSupport namedLock) {
090            this.namedLock = requireNonNull(namedLock);
091            this.referenceCount = new AtomicInteger(0);
092        }
093
094        private int incRef() {
095            return referenceCount.incrementAndGet();
096        }
097
098        private int decRef() {
099            return referenceCount.decrementAndGet();
100        }
101
102        @Override
103        public String toString() {
104            return "[refCount=" + referenceCount.get() + ", lock=" + namedLock + "]";
105        }
106    }
107}