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