From commits-return-6527-archive-asf-public=cust-asf.ponee.io@zookeeper.apache.org Fri Jul 13 15:01:50 2018 Return-Path: X-Original-To: archive-asf-public@cust-asf.ponee.io Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by mx-eu-01.ponee.io (Postfix) with SMTP id B15CA18078A for ; Fri, 13 Jul 2018 15:01:47 +0200 (CEST) Received: (qmail 87662 invoked by uid 500); 13 Jul 2018 13:01:46 -0000 Mailing-List: contact commits-help@zookeeper.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@zookeeper.apache.org Delivered-To: mailing list commits@zookeeper.apache.org Received: (qmail 87315 invoked by uid 99); 13 Jul 2018 13:01:46 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 13 Jul 2018 13:01:46 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id D1BE8E0BD5; Fri, 13 Jul 2018 13:01:45 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: andor@apache.org To: commits@zookeeper.apache.org Date: Fri, 13 Jul 2018 13:01:49 -0000 Message-Id: <0ec5d602d3a34e1fa17f1c969b7b403b@git.apache.org> In-Reply-To: References: X-Mailer: ASF-Git Admin Mailer Subject: [05/10] zookeeper git commit: ZOOKEEPER-3033: MAVEN MIGRATION - Step 1.2 - create zk-recipes maven structure http://git-wip-us.apache.org/repos/asf/zookeeper/blob/4174a0b1/zookeeper-recipes/zookeeper-recipes-election/src/java/org/apache/zookeeper/recipes/leader/LeaderElectionAware.java ---------------------------------------------------------------------- diff --git a/zookeeper-recipes/zookeeper-recipes-election/src/java/org/apache/zookeeper/recipes/leader/LeaderElectionAware.java b/zookeeper-recipes/zookeeper-recipes-election/src/java/org/apache/zookeeper/recipes/leader/LeaderElectionAware.java new file mode 100644 index 0000000..6c32ebc --- /dev/null +++ b/zookeeper-recipes/zookeeper-recipes-election/src/java/org/apache/zookeeper/recipes/leader/LeaderElectionAware.java @@ -0,0 +1,37 @@ +/* + * 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. + */ +package org.apache.zookeeper.recipes.leader; + +import org.apache.zookeeper.recipes.leader.LeaderElectionSupport.EventType; + +/** + * An interface to be implemented by clients that want to receive election + * events. + */ +public interface LeaderElectionAware { + + /** + * Called during each state transition. Current, low level events are provided + * at the beginning and end of each state. For instance, START may be followed + * by OFFER_START, OFFER_COMPLETE, DETERMINE_START, DETERMINE_COMPLETE, and so + * on. + * + * @param eventType + */ + public void onElectionEvent(EventType eventType); + +} http://git-wip-us.apache.org/repos/asf/zookeeper/blob/4174a0b1/zookeeper-recipes/zookeeper-recipes-election/src/java/org/apache/zookeeper/recipes/leader/LeaderElectionSupport.java ---------------------------------------------------------------------- diff --git a/zookeeper-recipes/zookeeper-recipes-election/src/java/org/apache/zookeeper/recipes/leader/LeaderElectionSupport.java b/zookeeper-recipes/zookeeper-recipes-election/src/java/org/apache/zookeeper/recipes/leader/LeaderElectionSupport.java new file mode 100644 index 0000000..8d4096d --- /dev/null +++ b/zookeeper-recipes/zookeeper-recipes-election/src/java/org/apache/zookeeper/recipes/leader/LeaderElectionSupport.java @@ -0,0 +1,461 @@ +/* + * 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. + */ +package org.apache.zookeeper.recipes.leader; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.KeeperException; +import org.apache.zookeeper.WatchedEvent; +import org.apache.zookeeper.Watcher; +import org.apache.zookeeper.ZooDefs; +import org.apache.zookeeper.ZooKeeper; +import org.apache.zookeeper.data.Stat; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + *

+ * A leader election support library implementing the ZooKeeper election recipe. + *

+ *

+ * This support library is meant to simplify the construction of an exclusive + * leader system on top of Apache ZooKeeper. Any application that can become the + * leader (usually a process that provides a service, exclusively) would + * configure an instance of this class with their hostname, at least one + * listener (an implementation of {@link LeaderElectionAware}), and either an + * instance of {@link ZooKeeper} or the proper connection information. Once + * configured, invoking {@link #start()} will cause the client to connect to + * ZooKeeper and create a leader offer. The library then determines if it has + * been elected the leader using the algorithm described below. The client + * application can follow all state transitions via the listener callback. + *

+ *

+ * Leader election algorithm + *

+ *

+ * The library starts in a START state. Through each state transition, a state + * start and a state complete event are sent to all listeners. When + * {@link #start()} is called, a leader offer is created in ZooKeeper. A leader + * offer is an ephemeral sequential node that indicates a process that can act + * as a leader for this service. A read of all leader offers is then performed. + * The offer with the lowest sequence number is said to be the leader. The + * process elected leader will transition to the leader state. All other + * processes will transition to a ready state. Internally, the library creates a + * ZooKeeper watch on the leader offer with the sequence ID of N - 1 (where N is + * the process's sequence ID). If that offer disappears due to a process + * failure, the watching process will run through the election determination + * process again to see if it should become the leader. Note that sequence ID + * may not be contiguous due to failed processes. A process may revoke its offer + * to be the leader at any time by calling {@link #stop()}. + *

+ *

+ * Guarantees (not) Made and Caveats + *

+ *

+ *

    + *
  • It is possible for a (poorly implemented) process to create a leader + * offer, get the lowest sequence ID, but have something terrible occur where it + * maintains its connection to ZK (and thus its ephemeral leader offer node) but + * doesn't actually provide the service in question. It is up to the user to + * ensure any failure to become the leader - and whatever that means in the + * context of the user's application - results in a revocation of its leader + * offer (i.e. that {@link #stop()} is called).
  • + *
  • It is possible for ZK timeouts and retries to play a role in service + * liveliness. In other words, if process A has the lowest sequence ID but + * requires a few attempts to read the other leader offers' sequence IDs, + * election can seem slow. Users should apply timeouts during the determination + * process if they need to hit a specific SLA.
  • + *
  • The library makes a "best effort" to detect catastrophic failures of the + * process. It is possible that an unforeseen event results in (for instance) an + * unchecked exception that propagates passed normal error handling code. This + * normally doesn't matter as the same exception would almost certain destroy + * the entire process and thus the connection to ZK and the leader offer + * resulting in another round of leader determination.
  • + *
+ *

+ */ +public class LeaderElectionSupport implements Watcher { + + private static final Logger logger = LoggerFactory + .getLogger(LeaderElectionSupport.class); + + private ZooKeeper zooKeeper; + + private State state; + private Set listeners; + + private String rootNodeName; + private LeaderOffer leaderOffer; + private String hostName; + + public LeaderElectionSupport() { + state = State.STOP; + listeners = Collections.synchronizedSet(new HashSet()); + } + + /** + *

+ * Start the election process. This method will create a leader offer, + * determine its status, and either become the leader or become ready. If an + * instance of {@link ZooKeeper} has not yet been configured by the user, a + * new instance is created using the connectString and sessionTime specified. + *

+ *

+ * Any (anticipated) failures result in a failed event being sent to all + * listeners. + *

+ */ + public synchronized void start() { + state = State.START; + dispatchEvent(EventType.START); + + logger.info("Starting leader election support"); + + if (zooKeeper == null) { + throw new IllegalStateException( + "No instance of zookeeper provided. Hint: use setZooKeeper()"); + } + + if (hostName == null) { + throw new IllegalStateException( + "No hostname provided. Hint: use setHostName()"); + } + + try { + makeOffer(); + determineElectionStatus(); + } catch (KeeperException e) { + becomeFailed(e); + return; + } catch (InterruptedException e) { + becomeFailed(e); + return; + } + } + + /** + * Stops all election services, revokes any outstanding leader offers, and + * disconnects from ZooKeeper. + */ + public synchronized void stop() { + state = State.STOP; + dispatchEvent(EventType.STOP_START); + + logger.info("Stopping leader election support"); + + if (leaderOffer != null) { + try { + zooKeeper.delete(leaderOffer.getNodePath(), -1); + logger.info("Removed leader offer {}", leaderOffer.getNodePath()); + } catch (InterruptedException e) { + becomeFailed(e); + } catch (KeeperException e) { + becomeFailed(e); + } + } + + dispatchEvent(EventType.STOP_COMPLETE); + } + + private void makeOffer() throws KeeperException, InterruptedException { + state = State.OFFER; + dispatchEvent(EventType.OFFER_START); + + leaderOffer = new LeaderOffer(); + + leaderOffer.setHostName(hostName); + leaderOffer.setNodePath(zooKeeper.create(rootNodeName + "/" + "n_", + hostName.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, + CreateMode.EPHEMERAL_SEQUENTIAL)); + + logger.debug("Created leader offer {}", leaderOffer); + + dispatchEvent(EventType.OFFER_COMPLETE); + } + + private void determineElectionStatus() throws KeeperException, + InterruptedException { + + state = State.DETERMINE; + dispatchEvent(EventType.DETERMINE_START); + + String[] components = leaderOffer.getNodePath().split("/"); + + leaderOffer.setId(Integer.valueOf(components[components.length - 1] + .substring("n_".length()))); + + List leaderOffers = toLeaderOffers(zooKeeper.getChildren( + rootNodeName, false)); + + /* + * For each leader offer, find out where we fit in. If we're first, we + * become the leader. If we're not elected the leader, attempt to stat the + * offer just less than us. If they exist, watch for their failure, but if + * they don't, become the leader. + */ + for (int i = 0; i < leaderOffers.size(); i++) { + LeaderOffer leaderOffer = leaderOffers.get(i); + + if (leaderOffer.getId().equals(this.leaderOffer.getId())) { + logger.debug("There are {} leader offers. I am {} in line.", + leaderOffers.size(), i); + + dispatchEvent(EventType.DETERMINE_COMPLETE); + + if (i == 0) { + becomeLeader(); + } else { + becomeReady(leaderOffers.get(i - 1)); + } + + /* Once we've figured out where we are, we're done. */ + break; + } + } + } + + private void becomeReady(LeaderOffer neighborLeaderOffer) + throws KeeperException, InterruptedException { + + logger.info("{} not elected leader. Watching node:{}", + leaderOffer.getNodePath(), neighborLeaderOffer.getNodePath()); + + /* + * Make sure to pass an explicit Watcher because we could be sharing this + * zooKeeper instance with someone else. + */ + Stat stat = zooKeeper.exists(neighborLeaderOffer.getNodePath(), this); + + if (stat != null) { + dispatchEvent(EventType.READY_START); + logger.debug( + "We're behind {} in line and they're alive. Keeping an eye on them.", + neighborLeaderOffer.getNodePath()); + state = State.READY; + dispatchEvent(EventType.READY_COMPLETE); + } else { + /* + * If the stat fails, the node has gone missing between the call to + * getChildren() and exists(). We need to try and become the leader. + */ + logger + .info( + "We were behind {} but it looks like they died. Back to determination.", + neighborLeaderOffer.getNodePath()); + determineElectionStatus(); + } + + } + + private void becomeLeader() { + state = State.ELECTED; + dispatchEvent(EventType.ELECTED_START); + + logger.info("Becoming leader with node:{}", leaderOffer.getNodePath()); + + dispatchEvent(EventType.ELECTED_COMPLETE); + } + + private void becomeFailed(Exception e) { + logger.error("Failed in state {} - Exception:{}", state, e); + + state = State.FAILED; + dispatchEvent(EventType.FAILED); + } + + /** + * Fetch the (user supplied) hostname of the current leader. Note that by the + * time this method returns, state could have changed so do not depend on this + * to be strongly consistent. This method has to read all leader offers from + * ZooKeeper to deterime who the leader is (i.e. there is no caching) so + * consider the performance implications of frequent invocation. If there are + * no leader offers this method returns null. + * + * @return hostname of the current leader + * @throws KeeperException + * @throws InterruptedException + */ + public String getLeaderHostName() throws KeeperException, + InterruptedException { + + List leaderOffers = toLeaderOffers(zooKeeper.getChildren( + rootNodeName, false)); + + if (leaderOffers.size() > 0) { + return leaderOffers.get(0).getHostName(); + } + + return null; + } + + private List toLeaderOffers(List strings) + throws KeeperException, InterruptedException { + + List leaderOffers = new ArrayList(strings.size()); + + /* + * Turn each child of rootNodeName into a leader offer. This is a tuple of + * the sequence number and the node name. + */ + for (String offer : strings) { + String hostName = new String(zooKeeper.getData( + rootNodeName + "/" + offer, false, null)); + + leaderOffers.add(new LeaderOffer(Integer.valueOf(offer.substring("n_" + .length())), rootNodeName + "/" + offer, hostName)); + } + + /* + * We sort leader offers by sequence number (which may not be zero-based or + * contiguous) and keep their paths handy for setting watches. + */ + Collections.sort(leaderOffers, new LeaderOffer.IdComparator()); + + return leaderOffers; + } + + @Override + public void process(WatchedEvent event) { + if (event.getType().equals(Watcher.Event.EventType.NodeDeleted)) { + if (!event.getPath().equals(leaderOffer.getNodePath()) + && state != State.STOP) { + logger.debug( + "Node {} deleted. Need to run through the election process.", + event.getPath()); + try { + determineElectionStatus(); + } catch (KeeperException e) { + becomeFailed(e); + } catch (InterruptedException e) { + becomeFailed(e); + } + } + } + } + + private void dispatchEvent(EventType eventType) { + logger.debug("Dispatching event:{}", eventType); + + synchronized (listeners) { + if (listeners.size() > 0) { + for (LeaderElectionAware observer : listeners) { + observer.onElectionEvent(eventType); + } + } + } + } + + /** + * Adds {@code listener} to the list of listeners who will receive events. + * + * @param listener + */ + public void addListener(LeaderElectionAware listener) { + listeners.add(listener); + } + + /** + * Remove {@code listener} from the list of listeners who receive events. + * + * @param listener + */ + public void removeListener(LeaderElectionAware listener) { + listeners.remove(listener); + } + + @Override + public String toString() { + return "{ state:" + state + " leaderOffer:" + leaderOffer + " zooKeeper:" + + zooKeeper + " hostName:" + hostName + " listeners:" + listeners + + " }"; + } + + /** + *

+ * Gets the ZooKeeper root node to use for this service. + *

+ *

+ * For instance, a root node of {@code /mycompany/myservice} would be the + * parent of all leader offers for this service. Obviously all processes that + * wish to contend for leader status need to use the same root node. Note: We + * assume this node already exists. + *

+ * + * @return a znode path + */ + public String getRootNodeName() { + return rootNodeName; + } + + /** + *

+ * Sets the ZooKeeper root node to use for this service. + *

+ *

+ * For instance, a root node of {@code /mycompany/myservice} would be the + * parent of all leader offers for this service. Obviously all processes that + * wish to contend for leader status need to use the same root node. Note: We + * assume this node already exists. + *

+ */ + public void setRootNodeName(String rootNodeName) { + this.rootNodeName = rootNodeName; + } + + /** + * The {@link ZooKeeper} instance to use for all operations. Provided this + * overrides any connectString or sessionTimeout set. + */ + public ZooKeeper getZooKeeper() { + return zooKeeper; + } + + public void setZooKeeper(ZooKeeper zooKeeper) { + this.zooKeeper = zooKeeper; + } + + /** + * The hostname of this process. Mostly used as a convenience for logging and + * to respond to {@link #getLeaderHostName()} requests. + */ + public String getHostName() { + return hostName; + } + + public void setHostName(String hostName) { + this.hostName = hostName; + } + + /** + * The type of event. + */ + public static enum EventType { + START, OFFER_START, OFFER_COMPLETE, DETERMINE_START, DETERMINE_COMPLETE, ELECTED_START, ELECTED_COMPLETE, READY_START, READY_COMPLETE, FAILED, STOP_START, STOP_COMPLETE, + } + + /** + * The internal state of the election support service. + */ + public static enum State { + START, OFFER, DETERMINE, ELECTED, READY, FAILED, STOP + } +} http://git-wip-us.apache.org/repos/asf/zookeeper/blob/4174a0b1/zookeeper-recipes/zookeeper-recipes-election/src/java/org/apache/zookeeper/recipes/leader/LeaderOffer.java ---------------------------------------------------------------------- diff --git a/zookeeper-recipes/zookeeper-recipes-election/src/java/org/apache/zookeeper/recipes/leader/LeaderOffer.java b/zookeeper-recipes/zookeeper-recipes-election/src/java/org/apache/zookeeper/recipes/leader/LeaderOffer.java new file mode 100644 index 0000000..188a6d5 --- /dev/null +++ b/zookeeper-recipes/zookeeper-recipes-election/src/java/org/apache/zookeeper/recipes/leader/LeaderOffer.java @@ -0,0 +1,84 @@ +/* + * 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. + */ +package org.apache.zookeeper.recipes.leader; + +import java.util.Comparator; + +/** + * A leader offer is a numeric id / path pair. The id is the sequential node id + * assigned by ZooKeeper where as the path is the absolute path to the ZNode. + */ +public class LeaderOffer { + + private Integer id; + private String nodePath; + private String hostName; + + public LeaderOffer() { + // Default constructor + } + + public LeaderOffer(Integer id, String nodePath, String hostName) { + this.id = id; + this.nodePath = nodePath; + this.hostName = hostName; + } + + @Override + public String toString() { + return "{ id:" + id + " nodePath:" + nodePath + " hostName:" + hostName + + " }"; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getNodePath() { + return nodePath; + } + + public void setNodePath(String nodePath) { + this.nodePath = nodePath; + } + + public String getHostName() { + return hostName; + } + + public void setHostName(String hostName) { + this.hostName = hostName; + } + + /** + * Compare two instances of {@link LeaderOffer} using only the {code}id{code} + * member. + */ + public static class IdComparator implements Comparator { + + @Override + public int compare(LeaderOffer o1, LeaderOffer o2) { + return o1.getId().compareTo(o2.getId()); + } + + } + +} http://git-wip-us.apache.org/repos/asf/zookeeper/blob/4174a0b1/zookeeper-recipes/zookeeper-recipes-election/test/org/apache/zookeeper/recipes/leader/LeaderElectionSupportTest.java ---------------------------------------------------------------------- diff --git a/zookeeper-recipes/zookeeper-recipes-election/test/org/apache/zookeeper/recipes/leader/LeaderElectionSupportTest.java b/zookeeper-recipes/zookeeper-recipes-election/test/org/apache/zookeeper/recipes/leader/LeaderElectionSupportTest.java new file mode 100644 index 0000000..7e19dc7 --- /dev/null +++ b/zookeeper-recipes/zookeeper-recipes-election/test/org/apache/zookeeper/recipes/leader/LeaderElectionSupportTest.java @@ -0,0 +1,298 @@ +/* + * 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. + */ +package org.apache.zookeeper.recipes.leader; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + + +import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.KeeperException; +import org.apache.zookeeper.ZooDefs; +import org.apache.zookeeper.ZooKeeper; +import org.apache.zookeeper.recipes.leader.LeaderElectionSupport.EventType; +import org.apache.zookeeper.test.ClientBase; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class LeaderElectionSupportTest extends ClientBase { + + private static final Logger logger = LoggerFactory + .getLogger(LeaderElectionSupportTest.class); + private static final String testRootNode = "/" + System.currentTimeMillis() + + "_"; + + private ZooKeeper zooKeeper; + + @Before + public void setUp() throws Exception { + super.setUp(); + + zooKeeper = createClient(); + + zooKeeper.create(testRootNode + Thread.currentThread().getId(), + new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); + } + + @After + public void tearDown() throws Exception { + if (zooKeeper != null) { + zooKeeper.delete(testRootNode + Thread.currentThread().getId(), -1); + } + + super.tearDown(); + } + + @Test + public void testNode() throws IOException, InterruptedException, + KeeperException { + + LeaderElectionSupport electionSupport = createLeaderElectionSupport(); + + electionSupport.start(); + Thread.sleep(3000); + electionSupport.stop(); + } + + @Test + public void testNodes3() throws IOException, InterruptedException, + KeeperException { + + int testIterations = 3; + final CountDownLatch latch = new CountDownLatch(testIterations); + final AtomicInteger failureCounter = new AtomicInteger(); + + for (int i = 0; i < testIterations; i++) { + runElectionSupportThread(latch, failureCounter); + } + + Assert.assertEquals(0, failureCounter.get()); + + if (!latch.await(10, TimeUnit.SECONDS)) { + logger + .info( + "Waited for all threads to start, but timed out. We had {} failures.", + failureCounter); + } + } + + @Test + public void testNodes9() throws IOException, InterruptedException, + KeeperException { + + int testIterations = 9; + final CountDownLatch latch = new CountDownLatch(testIterations); + final AtomicInteger failureCounter = new AtomicInteger(); + + for (int i = 0; i < testIterations; i++) { + runElectionSupportThread(latch, failureCounter); + } + + Assert.assertEquals(0, failureCounter.get()); + + if (!latch.await(10, TimeUnit.SECONDS)) { + logger + .info( + "Waited for all threads to start, but timed out. We had {} failures.", + failureCounter); + } + } + + @Test + public void testNodes20() throws IOException, InterruptedException, + KeeperException { + + int testIterations = 20; + final CountDownLatch latch = new CountDownLatch(testIterations); + final AtomicInteger failureCounter = new AtomicInteger(); + + for (int i = 0; i < testIterations; i++) { + runElectionSupportThread(latch, failureCounter); + } + + Assert.assertEquals(0, failureCounter.get()); + + if (!latch.await(10, TimeUnit.SECONDS)) { + logger + .info( + "Waited for all threads to start, but timed out. We had {} failures.", + failureCounter); + } + } + + @Test + public void testNodes100() throws IOException, InterruptedException, + KeeperException { + + int testIterations = 100; + final CountDownLatch latch = new CountDownLatch(testIterations); + final AtomicInteger failureCounter = new AtomicInteger(); + + for (int i = 0; i < testIterations; i++) { + runElectionSupportThread(latch, failureCounter); + } + + Assert.assertEquals(0, failureCounter.get()); + + if (!latch.await(20, TimeUnit.SECONDS)) { + logger + .info( + "Waited for all threads to start, but timed out. We had {} failures.", + failureCounter); + } + } + + @Test + public void testOfferShuffle() throws InterruptedException { + int testIterations = 10; + final CountDownLatch latch = new CountDownLatch(testIterations); + final AtomicInteger failureCounter = new AtomicInteger(); + List threads = new ArrayList(testIterations); + + for (int i = 1; i <= testIterations; i++) { + threads.add(runElectionSupportThread(latch, failureCounter, + Math.min(i * 1200, 10000))); + } + + if (!latch.await(60, TimeUnit.SECONDS)) { + logger + .info( + "Waited for all threads to start, but timed out. We had {} failures.", + failureCounter); + } + } + + @Test + public void testGetLeaderHostName() throws KeeperException, + InterruptedException { + + LeaderElectionSupport electionSupport = createLeaderElectionSupport(); + + electionSupport.start(); + + // Sketchy: We assume there will be a leader (probably us) in 3 seconds. + Thread.sleep(3000); + + String leaderHostName = electionSupport.getLeaderHostName(); + + Assert.assertNotNull(leaderHostName); + Assert.assertEquals("foohost", leaderHostName); + + electionSupport.stop(); + } + + @Test + public void testReadyOffer() throws Exception { + final ArrayList events = new ArrayList(); + final CountDownLatch electedComplete = new CountDownLatch(1); + + final LeaderElectionSupport electionSupport1 = createLeaderElectionSupport(); + electionSupport1.start(); + LeaderElectionSupport electionSupport2 = createLeaderElectionSupport(); + LeaderElectionAware listener = new LeaderElectionAware() { + boolean stoppedElectedNode = false; + @Override + public void onElectionEvent(EventType eventType) { + events.add(eventType); + if (!stoppedElectedNode + && eventType == EventType.DETERMINE_COMPLETE) { + stoppedElectedNode = true; + try { + // stopping the ELECTED node, so re-election will happen. + electionSupport1.stop(); + } catch (Exception e) { + logger.error("Unexpected error", e); + } + } + if (eventType == EventType.ELECTED_COMPLETE) { + electedComplete.countDown(); + } + } + }; + electionSupport2.addListener(listener); + electionSupport2.start(); + // waiting for re-election. + electedComplete.await(CONNECTION_TIMEOUT / 3, TimeUnit.MILLISECONDS); + + final ArrayList expectedevents = new ArrayList(); + expectedevents.add(EventType.START); + expectedevents.add(EventType.OFFER_START); + expectedevents.add(EventType.OFFER_COMPLETE); + expectedevents.add(EventType.DETERMINE_START); + expectedevents.add(EventType.DETERMINE_COMPLETE); + expectedevents.add(EventType.DETERMINE_START); + expectedevents.add(EventType.DETERMINE_COMPLETE); + expectedevents.add(EventType.ELECTED_START); + expectedevents.add(EventType.ELECTED_COMPLETE); + Assert.assertEquals("Events has failed to executed in the order", + expectedevents, events); + electionSupport2.stop(); + } + + private LeaderElectionSupport createLeaderElectionSupport() { + LeaderElectionSupport electionSupport = new LeaderElectionSupport(); + + electionSupport.setZooKeeper(zooKeeper); + electionSupport.setRootNodeName(testRootNode + + Thread.currentThread().getId()); + electionSupport.setHostName("foohost"); + + return electionSupport; + } + + private Thread runElectionSupportThread(final CountDownLatch latch, + final AtomicInteger failureCounter) { + return runElectionSupportThread(latch, failureCounter, 3000); + } + + private Thread runElectionSupportThread(final CountDownLatch latch, + final AtomicInteger failureCounter, final long sleepDuration) { + + final LeaderElectionSupport electionSupport = createLeaderElectionSupport(); + + Thread t = new Thread() { + + @Override + public void run() { + try { + electionSupport.start(); + Thread.sleep(sleepDuration); + electionSupport.stop(); + + latch.countDown(); + } catch (Exception e) { + logger.warn("Failed to run leader election due to: {}", + e.getMessage()); + failureCounter.incrementAndGet(); + } + } + }; + + t.start(); + + return t; + } + +} http://git-wip-us.apache.org/repos/asf/zookeeper/blob/4174a0b1/zookeeper-recipes/zookeeper-recipes-lock/README.txt ---------------------------------------------------------------------- diff --git a/zookeeper-recipes/zookeeper-recipes-lock/README.txt b/zookeeper-recipes/zookeeper-recipes-lock/README.txt new file mode 100644 index 0000000..1fc4fbf --- /dev/null +++ b/zookeeper-recipes/zookeeper-recipes-lock/README.txt @@ -0,0 +1,28 @@ + + +1) This lock interface recipe implements the lock recipe +mentioned in ../../../docs/recipes.[html,pdf]. + +2) To compile the lock java recipe you can just run ant jar from +this directory. For compiling the c libarary go to src/c and read +the INSTALLATION instructions. +Please report any bugs on the jira + +http://issues.apache.org/jira/browse/ZOOKEEPER + + http://git-wip-us.apache.org/repos/asf/zookeeper/blob/4174a0b1/zookeeper-recipes/zookeeper-recipes-lock/build.xml ---------------------------------------------------------------------- diff --git a/zookeeper-recipes/zookeeper-recipes-lock/build.xml b/zookeeper-recipes/zookeeper-recipes-lock/build.xml new file mode 100644 index 0000000..1fa7b22 --- /dev/null +++ b/zookeeper-recipes/zookeeper-recipes-lock/build.xml @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Tests failed! + + + + + + + + + + + + + + + + + + http://git-wip-us.apache.org/repos/asf/zookeeper/blob/4174a0b1/zookeeper-recipes/zookeeper-recipes-lock/src/c/INSTALL ---------------------------------------------------------------------- diff --git a/zookeeper-recipes/zookeeper-recipes-lock/src/c/INSTALL b/zookeeper-recipes/zookeeper-recipes-lock/src/c/INSTALL new file mode 100644 index 0000000..5458714 --- /dev/null +++ b/zookeeper-recipes/zookeeper-recipes-lock/src/c/INSTALL @@ -0,0 +1,234 @@ +Installation Instructions +************************* + +Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, +2006 Free Software Foundation, Inc. + +This file is free documentation; the Free Software Foundation gives +unlimited permission to copy, distribute and modify it. + +Basic Installation +================== + +Briefly, the shell commands `./configure; make; make install' should +configure, build, and install this package. The following +more-detailed instructions are generic; see the `README' file for +instructions specific to this package. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. Caching is +disabled by default to prevent problems with accidental use of stale +cache files. + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You need `configure.ac' if +you want to change it or regenerate `configure' using a newer version +of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. + + Running `configure' might take a while. While running, it prints + some messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + +Some systems require unusual options for compilation or linking that the +`configure' script does not know about. Run `./configure --help' for +details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c99 CFLAGS=-g LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + +You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you can use GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + With a non-GNU `make', it is safer to compile the package for one +architecture at a time in the source code directory. After you have +installed the package for one architecture, use `make distclean' before +reconfiguring for another architecture. + +Installation Names +================== + +By default, `make install' installs the package's commands under +`/usr/local/bin', include files under `/usr/local/include', etc. You +can specify an installation prefix other than `/usr/local' by giving +`configure' the option `--prefix=PREFIX'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +pass the option `--exec-prefix=PREFIX' to `configure', the package uses +PREFIX as the prefix for installing programs and libraries. +Documentation and other data files still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=DIR' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + +Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + +There may be some features `configure' cannot figure out automatically, +but needs to determine by the type of machine the package will run on. +Usually, assuming the package is built to be run on the _same_ +architectures, `configure' can figure that out, but if it prints a +message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the option `--target=TYPE' to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + +If you want to set default values for `configure' scripts to share, you +can create a site shell script called `config.site' that gives default +values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + +Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +causes the specified `gcc' to be used as the C compiler (unless it is +overridden in the site shell script). + +Unfortunately, this technique does not work for `CONFIG_SHELL' due to +an Autoconf bug. Until the bug is fixed you can use this workaround: + + CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash + +`configure' Invocation +====================== + +`configure' recognizes the following options to control how it operates. + +`--help' +`-h' + Print a summary of the options to `configure', and exit. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. + http://git-wip-us.apache.org/repos/asf/zookeeper/blob/4174a0b1/zookeeper-recipes/zookeeper-recipes-lock/src/c/LICENSE ---------------------------------------------------------------------- diff --git a/zookeeper-recipes/zookeeper-recipes-lock/src/c/LICENSE b/zookeeper-recipes/zookeeper-recipes-lock/src/c/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/zookeeper-recipes/zookeeper-recipes-lock/src/c/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. http://git-wip-us.apache.org/repos/asf/zookeeper/blob/4174a0b1/zookeeper-recipes/zookeeper-recipes-lock/src/c/Makefile.am ---------------------------------------------------------------------- diff --git a/zookeeper-recipes/zookeeper-recipes-lock/src/c/Makefile.am b/zookeeper-recipes/zookeeper-recipes-lock/src/c/Makefile.am new file mode 100644 index 0000000..9b36b43 --- /dev/null +++ b/zookeeper-recipes/zookeeper-recipes-lock/src/c/Makefile.am @@ -0,0 +1,46 @@ +# 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. + +include $(top_srcdir)/aminclude.am + +AM_CFLAGS = -Wall -fPIC -I${ZOOKEEPER_PATH}/include -I${ZOOKEEPER_PATH}/generated \ + -I$(top_srcdir)/include -I/usr/include +AM_CPPFLAGS = -Wall -I${ZOOKEEPER_PATH}/include -I${ZOOKEEPER_PATH}/generated\ + -I${top_srcdir}/include -I/usr/include +EXTRA_DIST = LICENSE +lib_LTLIBRARIES = libzoolock.la +libzoolock_la_SOURCES = src/zoo_lock.c include/zoo_lock.h +libzoolock_la_CPPFLAGS = -DDLOPEN_MODULE +libzoolock_la_LDFLAGS = -version-info 0:1:0 + +#run the tests now + +TEST_SOURCES = tests/TestDriver.cc tests/TestClient.cc tests/Util.cc + + +check_PROGRAMS = zklocktest +nodist_zklocktest_SOURCES = ${TEST_SOURCES} +zklocktest_LDADD = ${ZOOKEEPER_LD} libzoolock.la -lpthread ${CPPUNIT_LIBS} +zklocktest_CXXFLAGS = -DUSE_STATIC_LIB ${CPPUNIT_CFLAGS} + +run-check: check + ./zklocktest ${TEST_OPTIONS} + +clean-local: clean-check + ${RM} ${DX_CLEANFILES} + +clean-check: + ${RM} ${nodist_zklocktest_OBJECTS} http://git-wip-us.apache.org/repos/asf/zookeeper/blob/4174a0b1/zookeeper-recipes/zookeeper-recipes-lock/src/c/README.txt ---------------------------------------------------------------------- diff --git a/zookeeper-recipes/zookeeper-recipes-lock/src/c/README.txt b/zookeeper-recipes/zookeeper-recipes-lock/src/c/README.txt new file mode 100644 index 0000000..326bba8 --- /dev/null +++ b/zookeeper-recipes/zookeeper-recipes-lock/src/c/README.txt @@ -0,0 +1,28 @@ + Zookeeper C lock client library + + +INSTALLATION + +If you're building the client from a source checkout you need to +follow the steps outlined below. If you're building from a release +tar downloaded from Apache please skip to step 2. + +1) make sure that you compile the main zookeeper c client library. + +2) change directory to src/recipes/lock/src/c + and do a "autoreconf -if" to bootstrap + autoconf, automake and libtool. Please make sure you have autoconf + version 2.59 or greater installed. +3) do a "./configure [OPTIONS]" to generate the makefile. See INSTALL + for general information about running configure. + +4) do a "make" or "make install" to build the libraries and install them. + Alternatively, you can also build and run a unit test suite (and + you probably should). Please make sure you have cppunit-1.10.x or + higher installed before you execute step 4. Once ./configure has + finished, do a "make run-check". It will build the libraries, build + the tests and run them. +5) to generate doxygen documentation do a "make doxygen-doc". All + documentations will be placed to a new subfolder named docs. By + default only HTML documentation is generated. For information on + other document formats please use "./configure --help" http://git-wip-us.apache.org/repos/asf/zookeeper/blob/4174a0b1/zookeeper-recipes/zookeeper-recipes-lock/src/c/acinclude.m4 ---------------------------------------------------------------------- diff --git a/zookeeper-recipes/zookeeper-recipes-lock/src/c/acinclude.m4 b/zookeeper-recipes/zookeeper-recipes-lock/src/c/acinclude.m4 new file mode 100644 index 0000000..d0041d8 --- /dev/null +++ b/zookeeper-recipes/zookeeper-recipes-lock/src/c/acinclude.m4 @@ -0,0 +1,312 @@ +# This file is part of Autoconf. -*- Autoconf -*- + +# Copyright (C) 2004 Oren Ben-Kiki +# This file is distributed under the same terms as the Autoconf macro files. + +# Generate automatic documentation using Doxygen. Works in concert with the +# aminclude.m4 file and a compatible doxygen configuration file. Defines the +# following public macros: +# +# DX_???_FEATURE(ON|OFF) - control the default setting fo a Doxygen feature. +# Supported features are 'DOXYGEN' itself, 'DOT' for generating graphics, +# 'HTML' for plain HTML, 'CHM' for compressed HTML help (for MS users), 'CHI' +# for generating a seperate .chi file by the .chm file, and 'MAN', 'RTF', +# 'XML', 'PDF' and 'PS' for the appropriate output formats. The environment +# variable DOXYGEN_PAPER_SIZE may be specified to override the default 'a4wide' +# paper size. +# +# By default, HTML, PDF and PS documentation is generated as this seems to be +# the most popular and portable combination. MAN pages created by Doxygen are +# usually problematic, though by picking an appropriate subset and doing some +# massaging they might be better than nothing. CHM and RTF are specific for MS +# (note that you can't generate both HTML and CHM at the same time). The XML is +# rather useless unless you apply specialized post-processing to it. +# +# The macro mainly controls the default state of the feature. The use can +# override the default by specifying --enable or --disable. The macros ensure +# that contradictory flags are not given (e.g., --enable-doxygen-html and +# --enable-doxygen-chm, --enable-doxygen-anything with --disable-doxygen, etc.) +# Finally, each feature will be automatically disabled (with a warning) if the +# required programs are missing. +# +# Once all the feature defaults have been specified, call DX_INIT_DOXYGEN with +# the following parameters: a one-word name for the project for use as a +# filename base etc., an optional configuration file name (the default is +# 'Doxyfile', the same as Doxygen's default), and an optional output directory +# name (the default is 'doxygen-doc'). + +## ----------## +## Defaults. ## +## ----------## + +DX_ENV="" +AC_DEFUN([DX_FEATURE_doc], ON) +AC_DEFUN([DX_FEATURE_dot], ON) +AC_DEFUN([DX_FEATURE_man], OFF) +AC_DEFUN([DX_FEATURE_html], ON) +AC_DEFUN([DX_FEATURE_chm], OFF) +AC_DEFUN([DX_FEATURE_chi], OFF) +AC_DEFUN([DX_FEATURE_rtf], OFF) +AC_DEFUN([DX_FEATURE_xml], OFF) +AC_DEFUN([DX_FEATURE_pdf], ON) +AC_DEFUN([DX_FEATURE_ps], ON) + +## --------------- ## +## Private macros. ## +## --------------- ## + +# DX_ENV_APPEND(VARIABLE, VALUE) +# ------------------------------ +# Append VARIABLE="VALUE" to DX_ENV for invoking doxygen. +AC_DEFUN([DX_ENV_APPEND], [AC_SUBST([DX_ENV], ["$DX_ENV $1='$2'"])]) + +# DX_DIRNAME_EXPR +# --------------- +# Expand into a shell expression prints the directory part of a path. +AC_DEFUN([DX_DIRNAME_EXPR], + [[expr ".$1" : '\(\.\)[^/]*$' \| "x$1" : 'x\(.*\)/[^/]*$']]) + +# DX_IF_FEATURE(FEATURE, IF-ON, IF-OFF) +# ------------------------------------- +# Expands according to the M4 (static) status of the feature. +AC_DEFUN([DX_IF_FEATURE], [ifelse(DX_FEATURE_$1, ON, [$2], [$3])]) + +# DX_REQUIRE_PROG(VARIABLE, PROGRAM) +# ---------------------------------- +# Require the specified program to be found for the DX_CURRENT_FEATURE to work. +AC_DEFUN([DX_REQUIRE_PROG], [ +AC_PATH_TOOL([$1], [$2]) +if test "$DX_FLAG_$[DX_CURRENT_FEATURE$$1]" = 1; then + AC_MSG_WARN([$2 not found - will not DX_CURRENT_DESCRIPTION]) + AC_SUBST([DX_FLAG_]DX_CURRENT_FEATURE, 0) +fi +]) + +# DX_TEST_FEATURE(FEATURE) +# ------------------------ +# Expand to a shell expression testing whether the feature is active. +AC_DEFUN([DX_TEST_FEATURE], [test "$DX_FLAG_$1" = 1]) + +# DX_CHECK_DEPEND(REQUIRED_FEATURE, REQUIRED_STATE) +# ------------------------------------------------- +# Verify that a required features has the right state before trying to turn on +# the DX_CURRENT_FEATURE. +AC_DEFUN([DX_CHECK_DEPEND], [ +test "$DX_FLAG_$1" = "$2" \ +|| AC_MSG_ERROR([doxygen-DX_CURRENT_FEATURE ifelse([$2], 1, + requires, contradicts) doxygen-DX_CURRENT_FEATURE]) +]) + +# DX_CLEAR_DEPEND(FEATURE, REQUIRED_FEATURE, REQUIRED_STATE) +# ---------------------------------------------------------- +# Turn off the DX_CURRENT_FEATURE if the required feature is off. +AC_DEFUN([DX_CLEAR_DEPEND], [ +test "$DX_FLAG_$1" = "$2" || AC_SUBST([DX_FLAG_]DX_CURRENT_FEATURE, 0) +]) + +# DX_FEATURE_ARG(FEATURE, DESCRIPTION, +# CHECK_DEPEND, CLEAR_DEPEND, +# REQUIRE, DO-IF-ON, DO-IF-OFF) +# -------------------------------------------- +# Parse the command-line option controlling a feature. CHECK_DEPEND is called +# if the user explicitly turns the feature on (and invokes DX_CHECK_DEPEND), +# otherwise CLEAR_DEPEND is called to turn off the default state if a required +# feature is disabled (using DX_CLEAR_DEPEND). REQUIRE performs additional +# requirement tests (DX_REQUIRE_PROG). Finally, an automake flag is set and +# DO-IF-ON or DO-IF-OFF are called according to the final state of the feature. +AC_DEFUN([DX_ARG_ABLE], [ + AC_DEFUN([DX_CURRENT_FEATURE], [$1]) + AC_DEFUN([DX_CURRENT_DESCRIPTION], [$2]) + AC_ARG_ENABLE(doxygen-$1, + [AS_HELP_STRING(DX_IF_FEATURE([$1], [--disable-doxygen-$1], + [--enable-doxygen-$1]), + DX_IF_FEATURE([$1], [don't $2], [$2]))], + [ +case "$enableval" in +#( +y|Y|yes|Yes|YES) + AC_SUBST([DX_FLAG_$1], 1) + $3 +;; #( +n|N|no|No|NO) + AC_SUBST([DX_FLAG_$1], 0) +;; #( +*) + AC_MSG_ERROR([invalid value '$enableval' given to doxygen-$1]) +;; +esac +], [ +AC_SUBST([DX_FLAG_$1], [DX_IF_FEATURE([$1], 1, 0)]) +$4 +]) +if DX_TEST_FEATURE([$1]); then + $5 + : +fi +if DX_TEST_FEATURE([$1]); then + AM_CONDITIONAL(DX_COND_$1, :) + $6 + : +else + AM_CONDITIONAL(DX_COND_$1, false) + $7 + : +fi +]) + +## -------------- ## +## Public macros. ## +## -------------- ## + +# DX_XXX_FEATURE(DEFAULT_STATE) +# ----------------------------- +AC_DEFUN([DX_DOXYGEN_FEATURE], [AC_DEFUN([DX_FEATURE_doc], [$1])]) +AC_DEFUN([DX_MAN_FEATURE], [AC_DEFUN([DX_FEATURE_man], [$1])]) +AC_DEFUN([DX_HTML_FEATURE], [AC_DEFUN([DX_FEATURE_html], [$1])]) +AC_DEFUN([DX_CHM_FEATURE], [AC_DEFUN([DX_FEATURE_chm], [$1])]) +AC_DEFUN([DX_CHI_FEATURE], [AC_DEFUN([DX_FEATURE_chi], [$1])]) +AC_DEFUN([DX_RTF_FEATURE], [AC_DEFUN([DX_FEATURE_rtf], [$1])]) +AC_DEFUN([DX_XML_FEATURE], [AC_DEFUN([DX_FEATURE_xml], [$1])]) +AC_DEFUN([DX_XML_FEATURE], [AC_DEFUN([DX_FEATURE_xml], [$1])]) +AC_DEFUN([DX_PDF_FEATURE], [AC_DEFUN([DX_FEATURE_pdf], [$1])]) +AC_DEFUN([DX_PS_FEATURE], [AC_DEFUN([DX_FEATURE_ps], [$1])]) + +# DX_INIT_DOXYGEN(PROJECT, [CONFIG-FILE], [OUTPUT-DOC-DIR]) +# --------------------------------------------------------- +# PROJECT also serves as the base name for the documentation files. +# The default CONFIG-FILE is "Doxyfile" and OUTPUT-DOC-DIR is "doxygen-doc". +AC_DEFUN([DX_INIT_DOXYGEN], [ + +# Files: +AC_SUBST([DX_PROJECT], [$1]) +AC_SUBST([DX_CONFIG], [ifelse([$2], [], Doxyfile, [$2])]) +AC_SUBST([DX_DOCDIR], [ifelse([$3], [], doxygen-doc, [$3])]) + +# Environment variables used inside doxygen.cfg: +DX_ENV_APPEND(SRCDIR, $srcdir) +DX_ENV_APPEND(PROJECT, $DX_PROJECT) +DX_ENV_APPEND(DOCDIR, $DX_DOCDIR) +DX_ENV_APPEND(VERSION, $PACKAGE_VERSION) + +# Doxygen itself: +DX_ARG_ABLE(doc, [generate any doxygen documentation], + [], + [], + [DX_REQUIRE_PROG([DX_DOXYGEN], doxygen) + DX_REQUIRE_PROG([DX_PERL], perl)], + [DX_ENV_APPEND(PERL_PATH, $DX_PERL)]) + +# Dot for graphics: +DX_ARG_ABLE(dot, [generate graphics for doxygen documentation], + [DX_CHECK_DEPEND(doc, 1)], + [DX_CLEAR_DEPEND(doc, 1)], + [DX_REQUIRE_PROG([DX_DOT], dot)], + [DX_ENV_APPEND(HAVE_DOT, YES) + DX_ENV_APPEND(DOT_PATH, [`DX_DIRNAME_EXPR($DX_DOT)`])], + [DX_ENV_APPEND(HAVE_DOT, NO)]) + +# Man pages generation: +DX_ARG_ABLE(man, [generate doxygen manual pages], + [DX_CHECK_DEPEND(doc, 1)], + [DX_CLEAR_DEPEND(doc, 1)], + [], + [DX_ENV_APPEND(GENERATE_MAN, YES)], + [DX_ENV_APPEND(GENERATE_MAN, NO)]) + +# RTF file generation: +DX_ARG_ABLE(rtf, [generate doxygen RTF documentation], + [DX_CHECK_DEPEND(doc, 1)], + [DX_CLEAR_DEPEND(doc, 1)], + [], + [DX_ENV_APPEND(GENERATE_RTF, YES)], + [DX_ENV_APPEND(GENERATE_RTF, NO)]) + +# XML file generation: +DX_ARG_ABLE(xml, [generate doxygen XML documentation], + [DX_CHECK_DEPEND(doc, 1)], + [DX_CLEAR_DEPEND(doc, 1)], + [], + [DX_ENV_APPEND(GENERATE_XML, YES)], + [DX_ENV_APPEND(GENERATE_XML, NO)]) + +# (Compressed) HTML help generation: +DX_ARG_ABLE(chm, [generate doxygen compressed HTML help documentation], + [DX_CHECK_DEPEND(doc, 1)], + [DX_CLEAR_DEPEND(doc, 1)], + [DX_REQUIRE_PROG([DX_HHC], hhc)], + [DX_ENV_APPEND(HHC_PATH, $DX_HHC) + DX_ENV_APPEND(GENERATE_HTML, YES) + DX_ENV_APPEND(GENERATE_HTMLHELP, YES)], + [DX_ENV_APPEND(GENERATE_HTMLHELP, NO)]) + +# Seperate CHI file generation. +DX_ARG_ABLE(chi, [generate doxygen seperate compressed HTML help index file], + [DX_CHECK_DEPEND(chm, 1)], + [DX_CLEAR_DEPEND(chm, 1)], + [], + [DX_ENV_APPEND(GENERATE_CHI, YES)], + [DX_ENV_APPEND(GENERATE_CHI, NO)]) + +# Plain HTML pages generation: +DX_ARG_ABLE(html, [generate doxygen plain HTML documentation], + [DX_CHECK_DEPEND(doc, 1) DX_CHECK_DEPEND(chm, 0)], + [DX_CLEAR_DEPEND(doc, 1) DX_CLEAR_DEPEND(chm, 0)], + [], + [DX_ENV_APPEND(GENERATE_HTML, YES)], + [DX_TEST_FEATURE(chm) || DX_ENV_APPEND(GENERATE_HTML, NO)]) + +# PostScript file generation: +DX_ARG_ABLE(ps, [generate doxygen PostScript documentation], + [DX_CHECK_DEPEND(doc, 1)], + [DX_CLEAR_DEPEND(doc, 1)], + [DX_REQUIRE_PROG([DX_LATEX], latex) + DX_REQUIRE_PROG([DX_MAKEINDEX], makeindex) + DX_REQUIRE_PROG([DX_DVIPS], dvips) + DX_REQUIRE_PROG([DX_EGREP], egrep)]) + +# PDF file generation: +DX_ARG_ABLE(pdf, [generate doxygen PDF documentation], + [DX_CHECK_DEPEND(doc, 1)], + [DX_CLEAR_DEPEND(doc, 1)], + [DX_REQUIRE_PROG([DX_PDFLATEX], pdflatex) + DX_REQUIRE_PROG([DX_MAKEINDEX], makeindex) + DX_REQUIRE_PROG([DX_EGREP], egrep)]) + +# LaTeX generation for PS and/or PDF: +if DX_TEST_FEATURE(ps) || DX_TEST_FEATURE(pdf); then + AM_CONDITIONAL(DX_COND_latex, :) + DX_ENV_APPEND(GENERATE_LATEX, YES) +else + AM_CONDITIONAL(DX_COND_latex, false) + DX_ENV_APPEND(GENERATE_LATEX, NO) +fi + +# Paper size for PS and/or PDF: +AC_ARG_VAR(DOXYGEN_PAPER_SIZE, + [a4wide (default), a4, letter, legal or executive]) +case "$DOXYGEN_PAPER_SIZE" in +#( +"") + AC_SUBST(DOXYGEN_PAPER_SIZE, "") +;; #( +a4wide|a4|letter|legal|executive) + DX_ENV_APPEND(PAPER_SIZE, $DOXYGEN_PAPER_SIZE) +;; #( +*) + AC_MSG_ERROR([unknown DOXYGEN_PAPER_SIZE='$DOXYGEN_PAPER_SIZE']) +;; +esac + +#For debugging: +#echo DX_FLAG_doc=$DX_FLAG_doc +#echo DX_FLAG_dot=$DX_FLAG_dot +#echo DX_FLAG_man=$DX_FLAG_man +#echo DX_FLAG_html=$DX_FLAG_html +#echo DX_FLAG_chm=$DX_FLAG_chm +#echo DX_FLAG_chi=$DX_FLAG_chi +#echo DX_FLAG_rtf=$DX_FLAG_rtf +#echo DX_FLAG_xml=$DX_FLAG_xml +#echo DX_FLAG_pdf=$DX_FLAG_pdf +#echo DX_FLAG_ps=$DX_FLAG_ps +#echo DX_ENV=$DX_ENV +]) http://git-wip-us.apache.org/repos/asf/zookeeper/blob/4174a0b1/zookeeper-recipes/zookeeper-recipes-lock/src/c/aminclude.am ---------------------------------------------------------------------- diff --git a/zookeeper-recipes/zookeeper-recipes-lock/src/c/aminclude.am b/zookeeper-recipes/zookeeper-recipes-lock/src/c/aminclude.am new file mode 100644 index 0000000..420049e --- /dev/null +++ b/zookeeper-recipes/zookeeper-recipes-lock/src/c/aminclude.am @@ -0,0 +1,186 @@ +# Copyright (C) 2004 Oren Ben-Kiki +# This file is distributed under the same terms as the Automake macro files. + +# Generate automatic documentation using Doxygen. Goals and variables values +# are controlled by the various DX_COND_??? conditionals set by autoconf. +# +# The provided goals are: +# doxygen-doc: Generate all doxygen documentation. +# doxygen-run: Run doxygen, which will generate some of the documentation +# (HTML, CHM, CHI, MAN, RTF, XML) but will not do the post +# processing required for the rest of it (PS, PDF, and some MAN). +# doxygen-man: Rename some doxygen generated man pages. +# doxygen-ps: Generate doxygen PostScript documentation. +# doxygen-pdf: Generate doxygen PDF documentation. +# +# Note that by default these are not integrated into the automake goals. If +# doxygen is used to generate man pages, you can achieve this integration by +# setting man3_MANS to the list of man pages generated and then adding the +# dependency: +# +# $(man3_MANS): doxygen-doc +# +# This will cause make to run doxygen and generate all the documentation. +# +# The following variable is intended for use in Makefile.am: +# +# DX_CLEANFILES = everything to clean. +# +# This is usually added to MOSTLYCLEANFILES. + +## --------------------------------- ## +## Format-independent Doxygen rules. ## +## --------------------------------- ## + +if DX_COND_doc + +## ------------------------------- ## +## Rules specific for HTML output. ## +## ------------------------------- ## + +if DX_COND_html + +DX_CLEAN_HTML = @DX_DOCDIR@/html + +endif DX_COND_html + +## ------------------------------ ## +## Rules specific for CHM output. ## +## ------------------------------ ## + +if DX_COND_chm + +DX_CLEAN_CHM = @DX_DOCDIR@/chm + +if DX_COND_chi + +DX_CLEAN_CHI = @DX_DOCDIR@/@PACKAGE@.chi + +endif DX_COND_chi + +endif DX_COND_chm + +## ------------------------------ ## +## Rules specific for MAN output. ## +## ------------------------------ ## + +if DX_COND_man + +DX_CLEAN_MAN = @DX_DOCDIR@/man + +endif DX_COND_man + +## ------------------------------ ## +## Rules specific for RTF output. ## +## ------------------------------ ## + +if DX_COND_rtf + +DX_CLEAN_RTF = @DX_DOCDIR@/rtf + +endif DX_COND_rtf + +## ------------------------------ ## +## Rules specific for XML output. ## +## ------------------------------ ## + +if DX_COND_xml + +DX_CLEAN_XML = @DX_DOCDIR@/xml + +endif DX_COND_xml + +## ----------------------------- ## +## Rules specific for PS output. ## +## ----------------------------- ## + +if DX_COND_ps + +DX_CLEAN_PS = @DX_DOCDIR@/@PACKAGE@.ps + +DX_PS_GOAL = doxygen-ps + +doxygen-ps: @DX_DOCDIR@/@PACKAGE@.ps + +@DX_DOCDIR@/@PACKAGE@.ps: @DX_DOCDIR@/@PACKAGE@.tag + cd @DX_DOCDIR@/latex; \ + rm -f *.aux *.toc *.idx *.ind *.ilg *.log *.out; \ + $(DX_LATEX) refman.tex; \ + $(MAKEINDEX_PATH) refman.idx; \ + $(DX_LATEX) refman.tex; \ + countdown=5; \ + while $(DX_EGREP) 'Rerun (LaTeX|to get cross-references right)' \ + refman.log > /dev/null 2>&1 \ + && test $$countdown -gt 0; do \ + $(DX_LATEX) refman.tex; \ + countdown=`expr $$countdown - 1`; \ + done; \ + $(DX_DVIPS) -o ../@PACKAGE@.ps refman.dvi + +endif DX_COND_ps + +## ------------------------------ ## +## Rules specific for PDF output. ## +## ------------------------------ ## + +if DX_COND_pdf + +DX_CLEAN_PDF = @DX_DOCDIR@/@PACKAGE@.pdf + +DX_PDF_GOAL = doxygen-pdf + +doxygen-pdf: @DX_DOCDIR@/@PACKAGE@.pdf + +@DX_DOCDIR@/@PACKAGE@.pdf: @DX_DOCDIR@/@PACKAGE@.tag + cd @DX_DOCDIR@/latex; \ + rm -f *.aux *.toc *.idx *.ind *.ilg *.log *.out; \ + $(DX_PDFLATEX) refman.tex; \ + $(DX_MAKEINDEX) refman.idx; \ + $(DX_PDFLATEX) refman.tex; \ + countdown=5; \ + while $(DX_EGREP) 'Rerun (LaTeX|to get cross-references right)' \ + refman.log > /dev/null 2>&1 \ + && test $$countdown -gt 0; do \ + $(DX_PDFLATEX) refman.tex; \ + countdown=`expr $$countdown - 1`; \ + done; \ + mv refman.pdf ../@PACKAGE@.pdf + +endif DX_COND_pdf + +## ------------------------------------------------- ## +## Rules specific for LaTeX (shared for PS and PDF). ## +## ------------------------------------------------- ## + +if DX_COND_latex + +DX_CLEAN_LATEX = @DX_DOCDIR@/latex + +endif DX_COND_latex + +.PHONY: doxygen-run doxygen-doc $(DX_PS_GOAL) $(DX_PDF_GOAL) + +.INTERMEDIATE: doxygen-run $(DX_PS_GOAL) $(DX_PDF_GOAL) + +doxygen-run: @DX_DOCDIR@/@PACKAGE@.tag + +doxygen-doc: doxygen-run $(DX_PS_GOAL) $(DX_PDF_GOAL) + +@DX_DOCDIR@/@PACKAGE@.tag: $(DX_CONFIG) $(pkginclude_HEADERS) + rm -rf @DX_DOCDIR@ + $(DX_ENV) $(DX_DOXYGEN) $(srcdir)/$(DX_CONFIG) + +DX_CLEANFILES = \ + @DX_DOCDIR@/@PACKAGE@.tag \ + -r \ + $(DX_CLEAN_HTML) \ + $(DX_CLEAN_CHM) \ + $(DX_CLEAN_CHI) \ + $(DX_CLEAN_MAN) \ + $(DX_CLEAN_RTF) \ + $(DX_CLEAN_XML) \ + $(DX_CLEAN_PS) \ + $(DX_CLEAN_PDF) \ + $(DX_CLEAN_LATEX) + +endif DX_COND_doc