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}