Java tutorial
/* * Copyright 2012 Netflix, Inc. * * Licensed 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. */ package com.netflix.curator.framework.recipes.locks; import java.io.Closeable; import java.io.IOException; import java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicReference; import org.apache.commons.io.IOUtils; import com.netflix.curator.framework.CuratorFramework; import com.netflix.curator.framework.CuratorFrameworkFactory; import com.netflix.curator.framework.state.ConnectionState; import com.netflix.curator.framework.state.ConnectionStateListener; import com.netflix.curator.retry.ExponentialBackoffRetry; import com.netflix.curator.test.Timing; class SemaphoreClient implements Callable<Void>, ConnectionStateListener, Closeable { private final CuratorFramework client; private final String semaphorePath; private final Callable<Void> operation; private volatile boolean shouldRun; private volatile boolean hasAcquired; private static final int CLIENT_EXCEPTION_HANDLER_SLEEP_TIME_SECS = 10; private static final int MAX_SEMAPHORE_LEASES = 1; private static final AtomicReference<SemaphoreClient> activeClient = new AtomicReference<SemaphoreClient>(null); SemaphoreClient(String connectionString, String semaphorePath, Callable<Void> operation) throws IOException { Timing timing = new Timing(); this.client = CuratorFrameworkFactory.newClient(connectionString, timing.session(), timing.connection(), new ExponentialBackoffRetry(100, 3)); client.start(); this.semaphorePath = semaphorePath; this.operation = operation; } @Override public void close() throws IOException { shouldRun = false; } boolean hasAcquired() { return hasAcquired; } @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { hasAcquired = false; } static SemaphoreClient getActiveClient() { return activeClient.get(); } @Override public Void call() throws Exception { shouldRun = true; client.getConnectionStateListenable().addListener(this); try { while (shouldRun) { try { acquireAndRun(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); // propagate up, don't sleep throw e; } catch (Exception e) { Thread.sleep(CLIENT_EXCEPTION_HANDLER_SLEEP_TIME_SECS * 1000L); } } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { IOUtils.closeQuietly(client); } return null; } private void acquireAndRun() throws Exception { InterProcessSemaphoreV2 semaphore = new InterProcessSemaphoreV2(client, semaphorePath, MAX_SEMAPHORE_LEASES); Lease lease = semaphore.acquire(); try { hasAcquired = true; if (activeClient.compareAndSet(null, this)) { throw new Exception("Multiple acquirers"); } try { while (hasAcquired && shouldRun) { operation.call(); } } finally { if (activeClient.compareAndSet(this, null)) { //noinspection ThrowFromFinallyBlock throw new Exception("Bad release"); } } } finally { semaphore.returnLease(lease); } } }