package voldemort.client.protocol.admin;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import voldemort.client.ClientConfig;
/**
* AdminClientPool utility class for caching AdminClient.
* Checkout is non-blocking and if AdminClient does not exist in the cache
* a new AdminClient will be created and returned.
* CheckIn is non-blocking as well, when max number of clients are already
* cached, further CheckIn of AdminClient will be closed and discarded.
*/
public class AdminClientPool {
private final AdminClientConfig adminConfig;
private final ClientConfig clientConfig;
private final BlockingQueue<AdminClient> clientCache;
private final AtomicBoolean isClosed;
/**
* Create AdminClientPool
* @param maxClients number of clients to cache, due to concurrent checkout,
* more number of AdminClient may be created. But on CheckIn, additional
* ones are discarded.
* @param adminConfig AdminClientConfig used for creation of AdminClient
* @param clientConfig ClientConfig used for creation of AdminClient.
*/
public AdminClientPool(int maxClients, AdminClientConfig adminConfig, ClientConfig clientConfig) {
if (maxClients <= 0) {
throw new IllegalArgumentException("maxClients should be positive");
}
if (adminConfig == null) {
throw new IllegalArgumentException("AdminClientConfig is null");
}
if (clientConfig == null) {
throw new IllegalArgumentException("ClientConfig is null");
}
String[] bootstrapUrls = clientConfig.getBootstrapUrls();
if (bootstrapUrls == null || bootstrapUrls.length == 0) {
throw new IllegalArgumentException("ClientConfig has no bootstrap Urls specified");
}
this.adminConfig = adminConfig;
this.clientConfig = clientConfig;
this.clientCache = new ArrayBlockingQueue<AdminClient>(maxClients);
this.isClosed = new AtomicBoolean(false);
}
private AdminClient createAdminClient() {
return new AdminClient(adminConfig, clientConfig);
}
/**
* number of AdminClient in the cache
* @return number of AdminClient in the cache.
*/
public int size() {
if (isClosed.get()) {
throw new IllegalStateException("Pool is closing");
}
return clientCache.size();
}
/**
* get an AdminClient from the cache if exists, if not create new one
* and return it. This method is non-blocking.
*
* All AdminClient returned from checkout, once after the completion of
* usage must be returned to the pool by calling checkin. If not,
* there will be leak of AdminClients (connections, threads and file handles).
*
* @return AdminClient
*/
public AdminClient checkout() {
if (isClosed.get()) {
throw new IllegalStateException("Pool is closing");
}
AdminClient client;
// Try to get one from the Cache.
while ((client = clientCache.poll()) != null) {
if (!client.isClusterModified()) {
return client;
} else {
// Cluster is Modified, after the AdminClient is created. Close it
client.close();
}
}
// None is available, create new one.
return createAdminClient();
}
/**
* submit the adminClient after usage is completed.
* Behavior is undefined, if checkin is called with objects not retrieved
* from checkout.
*
* @param client AdminClient retrieved from checkout
*/
public void checkin(AdminClient client) {
if (isClosed.get()) {
throw new IllegalStateException("Pool is closing");
}
if (client == null) {
throw new IllegalArgumentException("client is null");
}
boolean isCheckedIn = clientCache.offer(client);
if (!isCheckedIn) {
// Cache is already full, close this AdminClient
client.close();
}
}
/**
* close the AdminPool, if no long required.
* After closed, all public methods will throw IllegalStateException
*/
public void close() {
boolean isPreviouslyClosed = isClosed.getAndSet(true);
if (isPreviouslyClosed) {
return;
}
AdminClient client;
while ((client = clientCache.poll()) != null) {
client.close();
}
}
}