package org.apache.solr.common.cloud;

/*
 * 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 java.io.IOException;
import java.lang.reflect.Field;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;

import org.apache.zookeeper.ClientCnxn;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;

// we use this class to expose nasty stuff for tests
public class SolrZooKeeper extends ZooKeeper {
  final Set<Thread> spawnedThreads = new CopyOnWriteArraySet<Thread>();
  
  // for test debug
  //static Map<SolrZooKeeper,Exception> clients = new ConcurrentHashMap<SolrZooKeeper,Exception>();

  public SolrZooKeeper(String connectString, int sessionTimeout,
      Watcher watcher) throws IOException {
    super(connectString, sessionTimeout, watcher);
    //clients.put(this, new RuntimeException());
  }
  
  public ClientCnxn getConnection() {
    return cnxn;
  }
  
  SelectableChannel getSendThreadChannel() throws Exception {
    final Field sendThreadFld = cnxn.getClass().getDeclaredField("sendThread");
    sendThreadFld.setAccessible(true);
    Object sendThread = sendThreadFld.get(cnxn);
    final Field sockKeyFld = sendThread.getClass().getDeclaredField("sockKey");
    sockKeyFld.setAccessible(true);
    final SelectionKey sockKey = (SelectionKey) sockKeyFld.get(sendThread);
    return sockKey.channel();
  }
  
  /**
   * Cause this ZooKeeper object to stop receiving from the ZooKeeperServer
   * for the given number of milliseconds.
   * @param ms the number of milliseconds to pause.
   */
  public void pauseCnxn(final long ms) {
    final Thread t = new Thread() {
      public void run() {
        try {
          synchronized (cnxn) {
            try {
              getSendThreadChannel().close();
            } catch (Exception e) {
              throw new RuntimeException("Closing Zookeeper send channel failed.", e);
            }
            Thread.sleep(ms);
          }
        } catch (InterruptedException e) {
          // ignore
        } finally {
          spawnedThreads.remove(this);
        }
      }
    };
    spawnedThreads.add(t);
    t.start();
  }

  @Override
  public synchronized void close() throws InterruptedException {
    for (Thread t : spawnedThreads) {
      if (t.isAlive()) t.interrupt();
    }
    super.close();
  }
  
//  public static void assertCloses() {
//    if (clients.size() > 0) {
//      Iterator<Exception> stacktraces = clients.values().iterator();
//      Exception cause = null;
//      cause = stacktraces.next();
//      throw new RuntimeException("Found a bad one!", cause);
//    }
//  }
  
}

@@ -899,52 +899,8 @@
 ld;%0A
-import java.nio.channels.SelectableChannel;%0A
 impo
@@ -1703,68 +1703,417 @@
  %0A  
-SelectableChannel getSendThreadChannel() throws Exception %7B%0A
+/**%0A   * Cause this ZooKeeper object to stop receiving from the ZooKeeperServer%0A   * for the given number of milliseconds.%0A   * @param ms the number of milliseconds to pause.%0A   */%0A  public void pauseCnxn(final long ms) %7B%0A    final Thread t = new Thread() %7B%0A      public void run() %7B%0A        try %7B%0A          final ClientCnxn cnxn = getConnection();%0A          synchronized (cnxn) %7B%0A            try %7B%0A          
     
@@ -2184,24 +2184,34 @@
 ndThread%22);%0A
+          
     sendThre
@@ -2233,24 +2233,34 @@
 ible(true);%0A
+          
     Object s
@@ -2296,16 +2296,68 @@
 (cnxn);%0A
+              if (sendThread != null) %7B%0A            
     fina
@@ -2424,24 +2424,36 @@
 %22sockKey%22);%0A
+            
     sockKeyF
@@ -2472,24 +2472,36 @@
 ible(true);%0A
+            
     final Se
@@ -2572,326 +2572,87 @@
     
-return sockKey.channel();%0A  %7D%0A
+   
   
-%0A
   
-/**%0A   * Cause this ZooKeeper object to stop receiving from the ZooKeeperServer%0A   * for the given number of milliseconds.%0A   * @param ms the number of milliseconds to pause.%0A   */%0A  public void pauseCnxn(final long ms) %7B%0A    final Thread t = new Thread() %7B%0A      public void run() %7B
+     if (sockKey != null) %7B%0A                  sockKey.channel().close();
 %0A   
@@ -2656,30 +2656,24 @@
         
-try %7B%0A
         
   synchr
@@ -2668,95 +2668,25 @@
     
-  synchronized (cnxn) %7B%0A            try %7B%0A              getSendThreadChannel().close();
+%7D%0A              %7D
 %0A   

Fix NPE, now it seems to work - sorry