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.concurrent.ConcurrentHashMap;
027import java.util.concurrent.ConcurrentMap;
028import java.util.concurrent.atomic.AtomicBoolean;
029import java.util.concurrent.atomic.AtomicInteger;
030
031/**
032 * Support class for {@link NamedLockFactory} implementations providing reference counting.
033 */
034public abstract class NamedLockFactorySupport implements NamedLockFactory
035{
036    protected final Logger logger = LoggerFactory.getLogger( getClass() );
037
038    private final ConcurrentMap<String, NamedLockHolder> locks;
039
040    public NamedLockFactorySupport()
041    {
042        this.locks = new ConcurrentHashMap<>();
043    }
044
045    @Override
046    public NamedLockSupport getLock( final String name )
047    {
048        return locks.compute( name, ( k, v ) ->
049        {
050            if ( v == null )
051            {
052                v = new NamedLockHolder( createLock( k ) );
053            }
054            v.incRef();
055            return v;
056        } ).namedLock;
057    }
058
059    @Override
060    public void shutdown()
061    {
062        // override if needed
063    }
064
065    public boolean closeLock( final NamedLockSupport lock )
066    {
067        AtomicBoolean destroyed = new AtomicBoolean( false );
068        locks.compute( lock.name(), ( k, v ) ->
069        {
070            if ( v != null && v.decRef() == 0 )
071            {
072                destroyLock( v.namedLock );
073                destroyed.set( true );
074                return null;
075            }
076            return v;
077        } );
078        return destroyed.get();
079    }
080
081
082    @Override
083    protected void finalize() throws Throwable
084    {
085        try
086        {
087            if ( !locks.isEmpty() )
088            {
089                // report leak
090                logger.warn( "Lock leak, referenced locks still exist {}", locks );
091            }
092        }
093        finally
094        {
095            super.finalize();
096        }
097    }
098
099    protected abstract NamedLockSupport createLock( final String name );
100
101    protected void destroyLock( final NamedLockSupport lock )
102    {
103        // override if needed
104    }
105
106    private static final class NamedLockHolder
107    {
108        private final NamedLockSupport namedLock;
109
110        private final AtomicInteger referenceCount;
111
112        private NamedLockHolder( NamedLockSupport namedLock )
113        {
114            this.namedLock = namedLock;
115            this.referenceCount = new AtomicInteger( 0 );
116        }
117
118        private int incRef()
119        {
120            return referenceCount.incrementAndGet();
121        }
122
123        private int decRef()
124        {
125            return referenceCount.decrementAndGet();
126        }
127
128        @Override
129        public String toString()
130        {
131            return "[refCount=" + referenceCount.get() + ", lock=" + namedLock + "]";
132        }
133    }
134}