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