Skip to content

Commit

Permalink
[MRESOLVER-245] Isolate and update tests (#157)
Browse files Browse the repository at this point in the history
Main intent is to properly isolate ITs, but did other cleanup as well
like patch update of Hazelcast and documentation updated as well.

High level changes:
* POM update with new patch release of HZ
* removed obsolete `destroySemaphore` parameter (always false), factored out semaphore provision
* updated all 4 ITs to form random named clusters (hence to prevent outer HZ instance joining)
* provided hazelcast and hazelcast-client XML fixed (used wrong schema)
* Tests reformatted to maven codestyle (changes best viewed with `?w=1`)
  • Loading branch information
cstamas authored Mar 11, 2022
1 parent 9f19f5a commit 374109f
Show file tree
Hide file tree
Showing 17 changed files with 618 additions and 447 deletions.
2 changes: 1 addition & 1 deletion maven-resolver-named-locks-hazelcast/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast</artifactId>
<version>5.0.1</version>
<version>5.0.2</version>
</dependency>
<dependency>
<groupId>javax.inject</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package org.eclipse.aether.named.hazelcast;

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.cp.ISemaphore;

/**
* Direct provider of {@link ISemaphore} instances: it simply uses the passed in lock name to create semaphore name out
* of it. This implies, that as many lock names are requested from it, this class will create as many semaphores in
* Hazelcast.
*/
public class DirectHazelcastSemaphoreProvider extends HazelcastSemaphoreProvider
{
@Override
public ISemaphore acquireSemaphore( HazelcastInstance hazelcastInstance, String name )
{
return hazelcastInstance.getCPSubsystem().getSemaphore( NAME_PREFIX + name );
}

@Override
public void releaseSemaphore( HazelcastInstance hazelcastInstance, String name, ISemaphore semaphore )
{
// nothing
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,32 +19,37 @@
* under the License.
*/

import com.hazelcast.core.Hazelcast;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;

import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;

/**
* {@link HazelcastSemaphoreNamedLockFactory} using Hazelcast and {@link
* com.hazelcast.core.HazelcastInstance#getCPSubsystem()} method to get CP semaphore. For this to work, the Hazelcast
* cluster the client is connecting to must be CP enabled cluster.
* {@link HazelcastSemaphoreNamedLockFactory} using {@link DirectHazelcastSemaphoreProvider} full Hazelcast member.
*/
@Singleton
@Named( HazelcastCPSemaphoreNamedLockFactory.NAME )
public class HazelcastCPSemaphoreNamedLockFactory
extends HazelcastSemaphoreNamedLockFactory
public class HazelcastCPSemaphoreNamedLockFactory extends HazelcastSemaphoreNamedLockFactory
{
public static final String NAME = "semaphore-hazelcast";
public static final String NAME = "semaphore-hazelcast";

/**
* The default constructor: creates own instance of Hazelcast using standard Hazelcast configuration discovery.
*/
@Inject
public HazelcastCPSemaphoreNamedLockFactory()
{
this( Hazelcast.newHazelcastInstance(), true );
}

@Inject
public HazelcastCPSemaphoreNamedLockFactory()
{
super(
Hazelcast.newHazelcastInstance(),
( hazelcastInstance, name ) -> hazelcastInstance.getCPSubsystem().getSemaphore( NAME_PREFIX + name ),
false,
true
);
}
/**
* Constructor for customization.
*/
public HazelcastCPSemaphoreNamedLockFactory( HazelcastInstance hazelcastInstance,
boolean manageHazelcast )
{
super( hazelcastInstance, manageHazelcast, new DirectHazelcastSemaphoreProvider() );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,32 +19,38 @@
* under the License.
*/

import com.hazelcast.client.HazelcastClient;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;

import com.hazelcast.client.HazelcastClient;
import com.hazelcast.core.HazelcastInstance;

/**
* Provider of {@link HazelcastSemaphoreNamedLockFactory} using Hazelcast Client and {@link
* com.hazelcast.core.HazelcastInstance#getCPSubsystem()} method to get CP semaphore. For this to work, the Hazelcast
* cluster the client is connecting to must be CP enabled cluster.
* {@link HazelcastSemaphoreNamedLockFactory} using {@link DirectHazelcastSemaphoreProvider} and Hazelcast client. The
* client must be configured to connect to some existing cluster (w/ proper configuration applied).
*/
@Singleton
@Named( HazelcastClientCPSemaphoreNamedLockFactory.NAME )
public class HazelcastClientCPSemaphoreNamedLockFactory
extends HazelcastSemaphoreNamedLockFactory
public class HazelcastClientCPSemaphoreNamedLockFactory extends HazelcastSemaphoreNamedLockFactory
{
public static final String NAME = "semaphore-hazelcast-client";
public static final String NAME = "semaphore-hazelcast-client";

/**
* The default constructor: creates own instance of Hazelcast using standard Hazelcast configuration discovery.
*/
@Inject
public HazelcastClientCPSemaphoreNamedLockFactory()
{
this( HazelcastClient.newHazelcastClient(), true );
}

@Inject
public HazelcastClientCPSemaphoreNamedLockFactory()
{
super(
HazelcastClient.newHazelcastClient(),
( hazelcastInstance, name ) -> hazelcastInstance.getCPSubsystem().getSemaphore( NAME_PREFIX + name ),
false,
true
);
}
/**
* Constructor for customization.
*/
public HazelcastClientCPSemaphoreNamedLockFactory( HazelcastInstance hazelcastInstance,
boolean manageHazelcast )
{
super( hazelcastInstance, manageHazelcast, new DirectHazelcastSemaphoreProvider() );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,82 +19,66 @@
* under the License.
*/

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;

import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.cp.ISemaphore;
import org.eclipse.aether.named.support.AdaptedSemaphoreNamedLock;
import org.eclipse.aether.named.support.AdaptedSemaphoreNamedLock.AdaptedSemaphore;
import org.eclipse.aether.named.support.NamedLockFactorySupport;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.function.BiFunction;
import static java.util.Objects.requireNonNull;

/**
* Factory of {@link AdaptedSemaphoreNamedLock} instances using adapted Hazelcast {@link ISemaphore}. This class may
* use {@link HazelcastInstance} backed by Hazelcast Server or Hazelcast Client.
* Factory of {@link AdaptedSemaphoreNamedLock} instances, using adapted Hazelcast {@link ISemaphore}. It delegates
* most the work to {@link HazelcastSemaphoreProvider} and this class just adapts the returned semaphore to named lock
* and caches {@link ISemaphore} instances, as recommended by Hazelcast.
*/
public class HazelcastSemaphoreNamedLockFactory
extends NamedLockFactorySupport
extends NamedLockFactorySupport
{
protected static final String NAME_PREFIX = "maven:resolver:";

private final HazelcastInstance hazelcastInstance;
protected final HazelcastInstance hazelcastInstance;

private final BiFunction<HazelcastInstance, String, ISemaphore> semaphoreFunction;
protected final boolean manageHazelcast;

private final boolean destroySemaphore;

private final boolean manageHazelcast;
private final HazelcastSemaphoreProvider hazelcastSemaphoreProvider;

private final ConcurrentMap<String, ISemaphore> semaphores;

public HazelcastSemaphoreNamedLockFactory(
final HazelcastInstance hazelcastInstance,
final BiFunction<HazelcastInstance, String, ISemaphore> semaphoreFunction,
final boolean destroySemaphore,
final boolean manageHazelcast
final HazelcastInstance hazelcastInstance,
final boolean manageHazelcast,
final HazelcastSemaphoreProvider hazelcastSemaphoreProvider
)
{
this.hazelcastInstance = hazelcastInstance;
this.semaphoreFunction = semaphoreFunction;
this.destroySemaphore = destroySemaphore;
this.hazelcastInstance = requireNonNull( hazelcastInstance );
this.manageHazelcast = manageHazelcast;
this.hazelcastSemaphoreProvider = requireNonNull( hazelcastSemaphoreProvider );
this.semaphores = new ConcurrentHashMap<>();
}

@Override
protected AdaptedSemaphoreNamedLock createLock( final String name )
{
ISemaphore semaphore = semaphores.computeIfAbsent( name, k ->
{
ISemaphore result = semaphoreFunction.apply( hazelcastInstance, k );
result.init( Integer.MAX_VALUE );
return result;
} );
ISemaphore semaphore = semaphores.computeIfAbsent( name,
k -> hazelcastSemaphoreProvider.acquireSemaphore( hazelcastInstance, name ) );
return new AdaptedSemaphoreNamedLock( name, this, new HazelcastSemaphore( semaphore ) );
}

@Override
public void shutdown()
protected void destroyLock( final String name )
{
if ( manageHazelcast )
{
hazelcastInstance.shutdown();
}
hazelcastSemaphoreProvider.releaseSemaphore( hazelcastInstance, name, semaphores.remove( name ) );
}

@Override
protected void destroyLock( final String name )
public void shutdown()
{
ISemaphore semaphore = semaphores.remove( name );
if ( destroySemaphore )
if ( manageHazelcast )
{
if ( semaphore == null )
{
throw new IllegalStateException( "Semaphore expected but does not exist: " + name );
}
semaphore.destroy();
hazelcastInstance.shutdown();
}
}

Expand All @@ -109,7 +93,7 @@ private HazelcastSemaphore( final ISemaphore semaphore )

@Override
public boolean tryAcquire( final int perms, final long time, final TimeUnit unit )
throws InterruptedException
throws InterruptedException
{
return semaphore.tryAcquire( perms, time, unit );
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package org.eclipse.aether.named.hazelcast;

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.cp.ISemaphore;

/**
* Support class for providers of {@link ISemaphore} instances.
*/
public abstract class HazelcastSemaphoreProvider
{
/**
* Name prefix recommended using for simpler configuration of Hazelcast.
*/
protected static final String NAME_PREFIX = "maven:resolver:";

/**
* Invoked when new instance of semaphore needed for given name. must not return {@code null}.
*/
public abstract ISemaphore acquireSemaphore( HazelcastInstance hazelcastInstance, String name );

/**
* Invoked when passed in semaphore associated with passed in name is not to be used anymore.
*/
public abstract void releaseSemaphore( HazelcastInstance hazelcastInstance, String name, ISemaphore semaphore );
}
Loading

0 comments on commit 374109f

Please # to comment.