001package org.eclipse.aether.internal.impl.synccontext; 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.DefaultArtifact; 025import org.eclipse.aether.internal.impl.synccontext.named.*; 026import org.eclipse.aether.named.NamedLockFactory; 027import org.eclipse.aether.repository.LocalRepository; 028import org.eclipse.aether.spi.synccontext.SyncContextFactory; 029import org.junit.AfterClass; 030import org.junit.Assert; 031import org.junit.Before; 032import org.junit.Test; 033 034import java.io.IOException; 035import java.nio.file.Files; 036import java.nio.file.Paths; 037import java.util.Arrays; 038import java.util.Objects; 039import java.util.concurrent.CountDownLatch; 040import java.util.concurrent.TimeUnit; 041 042import static org.mockito.Mockito.mock; 043import static org.mockito.Mockito.when; 044 045/** 046 * UT support for {@link SyncContextFactory}. 047 */ 048public abstract class NamedLockFactoryAdapterTestSupport { 049 private static final long ADAPTER_TIME = 100L; 050 051 private static final TimeUnit ADAPTER_TIME_UNIT = TimeUnit.MILLISECONDS; 052 053 /** 054 * Subclass MAY populate this field but subclass must take care of proper cleanup as well, if needed! 055 */ 056 protected static NameMapper nameMapper = new DiscriminatingNameMapper(new GAVNameMapper()); 057 058 /** 059 * Subclass MUST populate this field but subclass must take care of proper cleanup as well, if needed! Once set, 060 * subclass must MUST call {@link #createAdapter()}. 061 */ 062 protected static NamedLockFactory namedLockFactory; 063 064 private static NamedLockFactoryAdapter adapter; 065 066 private RepositorySystemSession session; 067 068 public static void createAdapter() { 069 Objects.requireNonNull(namedLockFactory, "NamedLockFactory not set"); 070 adapter = new NamedLockFactoryAdapter(nameMapper, namedLockFactory, ADAPTER_TIME, ADAPTER_TIME_UNIT); 071 } 072 073 @AfterClass 074 public static void cleanupAdapter() { 075 if (adapter != null) { 076 adapter.shutdown(); 077 } 078 } 079 080 @Before 081 public void before() throws IOException { 082 Files.createDirectories(Paths.get(System.getProperty("java.io.tmpdir"))); // hack for Surefire 083 LocalRepository localRepository = new LocalRepository(Files.createTempDirectory("test").toFile()); 084 session = mock(RepositorySystemSession.class); 085 when(session.getLocalRepository()).thenReturn(localRepository); 086 } 087 088 @Test 089 public void justCreateAndClose() { 090 adapter.newInstance(session, false).close(); 091 } 092 093 @Test 094 public void justAcquire() { 095 try (SyncContext syncContext = adapter.newInstance(session, false)) { 096 syncContext.acquire( 097 Arrays.asList(new DefaultArtifact("groupId:artifactId:1.0"), new DefaultArtifact("groupId:artifactId:1.1")), 098 null 099 ); 100 } 101 } 102 103 @Test(timeout = 5000) 104 public void sharedAccess() throws InterruptedException { 105 CountDownLatch winners = new CountDownLatch(2); // we expect 2 winners 106 CountDownLatch losers = new CountDownLatch(0); // we expect 0 losers 107 Thread t1 = new Thread(new Access(true, winners, losers, adapter, session, null)); 108 Thread t2 = new Thread(new Access(true, winners, losers, adapter, session, null)); 109 t1.start(); 110 t2.start(); 111 t1.join(); 112 t2.join(); 113 winners.await(); 114 losers.await(); 115 } 116 117 @Test(timeout = 5000) 118 public void exclusiveAccess() throws InterruptedException { 119 CountDownLatch winners = new CountDownLatch(1); // we expect 1 winner 120 CountDownLatch losers = new CountDownLatch(1); // we expect 1 loser 121 Thread t1 = new Thread(new Access(false, winners, losers, adapter, session, null)); 122 Thread t2 = new Thread(new Access(false, winners, losers, adapter, session, null)); 123 t1.start(); 124 t2.start(); 125 t1.join(); 126 t2.join(); 127 winners.await(); 128 losers.await(); 129 } 130 131 @Test(timeout = 5000) 132 public void mixedAccess() throws InterruptedException { 133 CountDownLatch winners = new CountDownLatch(1); // we expect 1 winner 134 CountDownLatch losers = new CountDownLatch(1); // we expect 1 loser 135 Thread t1 = new Thread(new Access(true, winners, losers, adapter, session, null)); 136 Thread t2 = new Thread(new Access(false, winners, losers, adapter, session, null)); 137 t1.start(); 138 t2.start(); 139 t1.join(); 140 t2.join(); 141 winners.await(); 142 losers.await(); 143 } 144 145 @Test(timeout = 5000) 146 public void nestedSharedShared() throws InterruptedException { 147 CountDownLatch winners = new CountDownLatch(2); // we expect 2 winners 148 CountDownLatch losers = new CountDownLatch(0); // we expect 0 losers 149 Thread t1 = new Thread( 150 new Access(true, winners, losers, adapter, session, 151 new Access(true, winners, losers, adapter, session, null) 152 ) 153 ); 154 t1.start(); 155 t1.join(); 156 winners.await(); 157 losers.await(); 158 } 159 160 @Test(timeout = 5000) 161 public void nestedExclusiveShared() throws InterruptedException { 162 CountDownLatch winners = new CountDownLatch(2); // we expect 2 winners 163 CountDownLatch losers = new CountDownLatch(0); // we expect 0 losers 164 Thread t1 = new Thread( 165 new Access(false, winners, losers, adapter, session, 166 new Access(true, winners, losers, adapter, session, null) 167 ) 168 ); 169 t1.start(); 170 t1.join(); 171 winners.await(); 172 losers.await(); 173 } 174 175 @Test(timeout = 5000) 176 public void nestedExclusiveExclusive() throws InterruptedException { 177 CountDownLatch winners = new CountDownLatch(2); // we expect 2 winners 178 CountDownLatch losers = new CountDownLatch(0); // we expect 0 losers 179 Thread t1 = new Thread( 180 new Access(false, winners, losers, adapter, session, 181 new Access(false, winners, losers, adapter, session, null) 182 ) 183 ); 184 t1.start(); 185 t1.join(); 186 winners.await(); 187 losers.await(); 188 } 189 190 @Test(timeout = 5000) 191 public void nestedSharedExclusive() throws InterruptedException { 192 CountDownLatch winners = new CountDownLatch(1); // we expect 1 winner (outer) 193 CountDownLatch losers = new CountDownLatch(1); // we expect 1 loser (inner) 194 Thread t1 = new Thread( 195 new Access(true, winners, losers, adapter, session, 196 new Access(false, winners, losers, adapter, session, null) 197 ) 198 ); 199 t1.start(); 200 t1.join(); 201 winners.await(); 202 losers.await(); 203 } 204 205 private static class Access implements Runnable { 206 final boolean shared; 207 final CountDownLatch winner; 208 final CountDownLatch loser; 209 final NamedLockFactoryAdapter adapter; 210 final RepositorySystemSession session; 211 final Access chained; 212 213 public Access(boolean shared, 214 CountDownLatch winner, 215 CountDownLatch loser, 216 NamedLockFactoryAdapter adapter, 217 RepositorySystemSession session, 218 Access chained) { 219 this.shared = shared; 220 this.winner = winner; 221 this.loser = loser; 222 this.adapter = adapter; 223 this.session = session; 224 this.chained = chained; 225 } 226 227 @Override 228 public void run() { 229 try { 230 try (SyncContext syncContext = adapter.newInstance(session, shared)) { 231 syncContext.acquire( 232 Arrays.asList(new DefaultArtifact("groupId:artifactId:1.0"), new DefaultArtifact("groupId:artifactId:1.1")), 233 null 234 ); 235 winner.countDown(); 236 if (chained != null) { 237 chained.run(); 238 } 239 loser.await(); 240 } catch (IllegalStateException e) { 241 e.printStackTrace(); // for ref purposes 242 loser.countDown(); 243 winner.await(); 244 } 245 } catch (InterruptedException e) { 246 Assert.fail("interrupted"); 247 } 248 } 249 } 250}