001package org.eclipse.aether.internal.impl.synccontext.named; 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.RepositorySystemSession; 023import org.eclipse.aether.SyncContext; 024import org.eclipse.aether.artifact.Artifact; 025import org.eclipse.aether.metadata.Metadata; 026import org.eclipse.aether.named.NamedLock; 027import org.eclipse.aether.named.NamedLockFactory; 028import org.slf4j.Logger; 029import org.slf4j.LoggerFactory; 030 031import java.util.ArrayDeque; 032import java.util.Collection; 033import java.util.Deque; 034import java.util.Objects; 035import java.util.concurrent.TimeUnit; 036 037/** 038 * Adapter to adapt {@link NamedLockFactory} and {@link NamedLock} to {@link SyncContext}. 039 */ 040public final class NamedLockFactoryAdapter 041{ 042 private final NameMapper nameMapper; 043 044 private final NamedLockFactory namedLockFactory; 045 046 private final long time; 047 048 private final TimeUnit timeUnit; 049 050 public NamedLockFactoryAdapter( final NameMapper nameMapper, final NamedLockFactory namedLockFactory, 051 final long time, final TimeUnit timeUnit ) 052 { 053 this.nameMapper = Objects.requireNonNull( nameMapper ); 054 this.namedLockFactory = Objects.requireNonNull( namedLockFactory ); 055 if ( time < 0L ) 056 { 057 throw new IllegalArgumentException( "time cannot be negative" ); 058 } 059 this.time = time; 060 this.timeUnit = Objects.requireNonNull( timeUnit ); 061 } 062 063 public SyncContext newInstance( final RepositorySystemSession session, final boolean shared ) 064 { 065 return new AdaptedLockSyncContext( session, shared, nameMapper, namedLockFactory, time, timeUnit ); 066 } 067 068 public void shutdown() 069 { 070 namedLockFactory.shutdown(); 071 } 072 073 private static class AdaptedLockSyncContext implements SyncContext 074 { 075 private static final Logger LOGGER = LoggerFactory.getLogger( AdaptedLockSyncContext.class ); 076 077 private final RepositorySystemSession session; 078 079 private final boolean shared; 080 081 private final NameMapper lockNaming; 082 083 private final SessionAwareNamedLockFactory sessionAwareNamedLockFactory; 084 085 private final NamedLockFactory namedLockFactory; 086 087 private final long time; 088 089 private final TimeUnit timeUnit; 090 091 private final Deque<NamedLock> locks; 092 093 private AdaptedLockSyncContext( final RepositorySystemSession session, final boolean shared, 094 final NameMapper lockNaming, final NamedLockFactory namedLockFactory, 095 final long time, final TimeUnit timeUnit ) 096 { 097 this.session = session; 098 this.shared = shared; 099 this.lockNaming = lockNaming; 100 this.sessionAwareNamedLockFactory = namedLockFactory instanceof SessionAwareNamedLockFactory 101 ? (SessionAwareNamedLockFactory) namedLockFactory : null; 102 this.namedLockFactory = namedLockFactory; 103 this.time = time; 104 this.timeUnit = timeUnit; 105 this.locks = new ArrayDeque<>(); 106 } 107 108 @Override 109 public void acquire( Collection<? extends Artifact> artifacts, Collection<? extends Metadata> metadatas ) 110 { 111 Collection<String> keys = lockNaming.nameLocks( session, artifacts, metadatas ); 112 if ( keys.isEmpty() ) 113 { 114 return; 115 } 116 117 LOGGER.trace( "Need {} {} lock(s) for {}", keys.size(), shared ? "read" : "write", keys ); 118 int acquiredLockCount = 0; 119 for ( String key : keys ) 120 { 121 NamedLock namedLock = sessionAwareNamedLockFactory != null ? sessionAwareNamedLockFactory 122 .getLock( session, key ) : namedLockFactory.getLock( key ); 123 try 124 { 125 LOGGER.trace( "Acquiring {} lock for '{}'", 126 shared ? "read" : "write", key ); 127 128 boolean locked; 129 if ( shared ) 130 { 131 locked = namedLock.lockShared( time, timeUnit ); 132 } 133 else 134 { 135 locked = namedLock.lockExclusively( time, timeUnit ); 136 } 137 138 if ( !locked ) 139 { 140 LOGGER.trace( "Failed to acquire {} lock for '{}'", 141 shared ? "read" : "write", key ); 142 143 namedLock.close(); 144 throw new IllegalStateException( 145 "Could not acquire " + ( shared ? "read" : "write" ) 146 + " lock for '" + namedLock.name() + "'" ); 147 } 148 149 locks.push( namedLock ); 150 acquiredLockCount++; 151 } 152 catch ( InterruptedException e ) 153 { 154 Thread.currentThread().interrupt(); 155 throw new RuntimeException( e ); 156 } 157 } 158 LOGGER.trace( "Total locks acquired: {}", acquiredLockCount ); 159 } 160 161 @Override 162 public void close() 163 { 164 if ( locks.isEmpty() ) 165 { 166 return; 167 } 168 169 // Release locks in reverse insertion order 170 int released = 0; 171 while ( !locks.isEmpty() ) 172 { 173 try ( NamedLock namedLock = locks.pop() ) 174 { 175 LOGGER.trace( "Releasing {} lock for '{}'", 176 shared ? "read" : "write", namedLock.name() ); 177 namedLock.unlock(); 178 released++; 179 } 180 } 181 LOGGER.trace( "Total locks released: {}", released ); 182 } 183 } 184}