001package org.eclipse.aether.named.providers;
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 java.io.IOException;
023import java.io.UncheckedIOException;
024import java.nio.channels.FileChannel;
025import java.nio.file.Files;
026import java.nio.file.Path;
027import java.nio.file.Paths;
028import java.nio.file.StandardOpenOption;
029import java.util.concurrent.ConcurrentHashMap;
030import java.util.concurrent.ConcurrentMap;
031
032import javax.inject.Named;
033import javax.inject.Singleton;
034
035import org.eclipse.aether.named.support.FileLockNamedLock;
036import org.eclipse.aether.named.support.FileSystemFriendly;
037import org.eclipse.aether.named.support.NamedLockFactorySupport;
038import org.eclipse.aether.named.support.NamedLockSupport;
039
040/**
041 * Named locks factory of {@link FileLockNamedLock}s. This is a bit special implementation, as it
042 * expects locks names to be fully qualified absolute file system paths.
043 *
044 * @since 1.7.3
045 */
046@Singleton
047@Named( FileLockNamedLockFactory.NAME )
048public class FileLockNamedLockFactory
049    extends NamedLockFactorySupport
050    implements FileSystemFriendly
051{
052    public static final String NAME = "file-lock";
053
054    private final ConcurrentMap<String, FileChannel> fileChannels;
055
056    public FileLockNamedLockFactory()
057    {
058        this.fileChannels = new ConcurrentHashMap<>();
059    }
060
061    @Override
062    protected NamedLockSupport createLock( final String name )
063    {
064        Path path = Paths.get( name );
065        FileChannel fileChannel = fileChannels.computeIfAbsent( name, k ->
066        {
067            try
068            {
069                Files.createDirectories( path.getParent() );
070                return FileChannel.open(
071                        path,
072                        StandardOpenOption.READ, StandardOpenOption.WRITE,
073                        StandardOpenOption.CREATE, StandardOpenOption.DELETE_ON_CLOSE
074                );
075            }
076            catch ( IOException e )
077            {
078                throw new UncheckedIOException( "Failed to open file channel for '"
079                    + name + "'", e );
080            }
081        } );
082        return new FileLockNamedLock( name, fileChannel, this );
083    }
084
085    @Override
086    protected void destroyLock( final String name )
087    {
088        FileChannel fileChannel = fileChannels.remove( name );
089        if ( fileChannel == null )
090        {
091            throw new IllegalStateException( "File channel expected, but does not exist: " + name );
092        }
093
094        try
095        {
096            fileChannel.close();
097        }
098        catch ( IOException e )
099        {
100            throw new UncheckedIOException( "Failed to close file channel for '"
101                    + name + "'", e );
102        }
103    }
104}