You are viewing a plain text version of this content. The canonical link for it is here.
Posted to solr-commits@lucene.apache.org by ma...@apache.org on 2010/01/01 01:17:33 UTC

svn commit: r894959 [1/2] - in /lucene/solr/branches/cloud/src: java/org/apache/solr/cloud/ java/org/apache/solr/core/ java/org/apache/solr/handler/ java/org/apache/solr/handler/component/ java/org/apache/solr/util/ java/org/apache/solr/util/zookeeper/...

Author: markrmiller
Date: Fri Jan  1 00:17:31 2010
New Revision: 894959

URL: http://svn.apache.org/viewvc?rev=894959&view=rev
Log:
various pushes forward

Added:
    lucene/solr/branches/cloud/src/java/org/apache/solr/cloud/
    lucene/solr/branches/cloud/src/java/org/apache/solr/cloud/CollectionInfo.java
    lucene/solr/branches/cloud/src/java/org/apache/solr/cloud/CountdownWatcher.java
      - copied, changed from r892824, lucene/solr/branches/cloud/src/java/org/apache/solr/util/zookeeper/CountdownWatcher.java
    lucene/solr/branches/cloud/src/java/org/apache/solr/cloud/ReconnectionHandler.java
    lucene/solr/branches/cloud/src/java/org/apache/solr/cloud/ShardInfo.java
    lucene/solr/branches/cloud/src/java/org/apache/solr/cloud/ShardInfoList.java
    lucene/solr/branches/cloud/src/java/org/apache/solr/cloud/ZooKeeperController.java
      - copied, changed from r892824, lucene/solr/branches/cloud/src/java/org/apache/solr/core/ZooKeeperController.java
    lucene/solr/branches/cloud/src/java/org/apache/solr/cloud/ZooKeeperReader.java
    lucene/solr/branches/cloud/src/java/org/apache/solr/cloud/ZooKeeperSolrResourceLoader.java
      - copied, changed from r892824, lucene/solr/branches/cloud/src/java/org/apache/solr/core/ZKSolrResourceLoader.java
    lucene/solr/branches/cloud/src/java/org/apache/solr/cloud/ZooKeeperWriter.java
    lucene/solr/branches/cloud/src/test/org/apache/solr/cloud/
    lucene/solr/branches/cloud/src/test/org/apache/solr/cloud/AbstractDistributedZooKeeperTestCase.java
      - copied, changed from r892824, lucene/solr/branches/cloud/src/test/org/apache/solr/AbstractDistributedZooKeeperTestCase.java
    lucene/solr/branches/cloud/src/test/org/apache/solr/cloud/AbstractZooKeeperTestCase.java
      - copied, changed from r892824, lucene/solr/branches/cloud/src/test/org/apache/solr/AbstractZooKeeperTestCase.java
    lucene/solr/branches/cloud/src/test/org/apache/solr/cloud/BasicDistributedZooKeeperTest.java
      - copied, changed from r892824, lucene/solr/branches/cloud/src/test/org/apache/solr/BasicDistributedZooKeeperTest.java
    lucene/solr/branches/cloud/src/test/org/apache/solr/cloud/BasicZooKeeperTest.java
      - copied, changed from r892824, lucene/solr/branches/cloud/src/test/org/apache/solr/BasicZooKeeperTest.java
    lucene/solr/branches/cloud/src/test/org/apache/solr/cloud/DistributedZooKeeperFailuresTest.java
    lucene/solr/branches/cloud/src/test/org/apache/solr/cloud/TestShardInfoList.java
    lucene/solr/branches/cloud/src/test/org/apache/solr/cloud/ZooKeeperReaderTest.java
    lucene/solr/branches/cloud/src/test/org/apache/solr/cloud/ZooKeeperTestServer.java
    lucene/solr/branches/cloud/src/test/test-files/solr/solr.lowZkTimeout.xml
Removed:
    lucene/solr/branches/cloud/src/java/org/apache/solr/core/ZKSolrResourceLoader.java
    lucene/solr/branches/cloud/src/java/org/apache/solr/core/ZooKeeperController.java
    lucene/solr/branches/cloud/src/java/org/apache/solr/util/ZooPut.java
    lucene/solr/branches/cloud/src/java/org/apache/solr/util/zookeeper/
    lucene/solr/branches/cloud/src/test/org/apache/solr/AbstractDistributedZooKeeperTestCase.java
    lucene/solr/branches/cloud/src/test/org/apache/solr/AbstractZooKeeperTestCase.java
    lucene/solr/branches/cloud/src/test/org/apache/solr/BasicDistributedZooKeeperTest.java
    lucene/solr/branches/cloud/src/test/org/apache/solr/BasicZooKeeperTest.java
Modified:
    lucene/solr/branches/cloud/src/java/org/apache/solr/core/CoreContainer.java
    lucene/solr/branches/cloud/src/java/org/apache/solr/core/CoreDescriptor.java
    lucene/solr/branches/cloud/src/java/org/apache/solr/core/SolrConfig.java
    lucene/solr/branches/cloud/src/java/org/apache/solr/core/SolrCore.java
    lucene/solr/branches/cloud/src/java/org/apache/solr/handler/ReplicationHandler.java
    lucene/solr/branches/cloud/src/java/org/apache/solr/handler/component/QueryComponent.java
    lucene/solr/branches/cloud/src/java/org/apache/solr/handler/component/QueryElevationComponent.java
    lucene/solr/branches/cloud/src/java/org/apache/solr/util/AbstractSolrTestCase.java
    lucene/solr/branches/cloud/src/test/org/apache/solr/BaseDistributedSearchTestCase.java
    lucene/solr/branches/cloud/src/test/org/apache/solr/servlet/NoCacheHeaderTest.java
    lucene/solr/branches/cloud/src/test/test-files/solr/solr.xml
    lucene/solr/branches/cloud/src/webapp/src/org/apache/solr/client/solrj/embedded/JettySolrRunner.java
    lucene/solr/branches/cloud/src/webapp/src/org/apache/solr/servlet/SolrDispatchFilter.java

Added: lucene/solr/branches/cloud/src/java/org/apache/solr/cloud/CollectionInfo.java
URL: http://svn.apache.org/viewvc/lucene/solr/branches/cloud/src/java/org/apache/solr/cloud/CollectionInfo.java?rev=894959&view=auto
==============================================================================
--- lucene/solr/branches/cloud/src/java/org/apache/solr/cloud/CollectionInfo.java (added)
+++ lucene/solr/branches/cloud/src/java/org/apache/solr/cloud/CollectionInfo.java Fri Jan  1 00:17:31 2010
@@ -0,0 +1,65 @@
+package org.apache.solr.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.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Information about the Collection.
+ * 
+ */
+public final class CollectionInfo {
+
+  // maps shard name to the shard addresses and roles
+  private final Map<String,ShardInfoList> shardNameToShardInfoList;
+  private final long updateTime;
+
+  public CollectionInfo(Map<String,ShardInfoList> shardNameToShardInfoList) {
+    //nocommit: defensive copy?
+    this.shardNameToShardInfoList = shardNameToShardInfoList;
+    this.updateTime = System.currentTimeMillis();
+  }
+
+  /**
+   * //nocommit
+   * 
+   * @return
+   */
+  public List<String> getSearchShards() {
+    List<String> nodeList = new ArrayList<String>();
+    for (ShardInfoList nodes : shardNameToShardInfoList.values()) {
+      nodeList.add(nodes.getShardUrl());
+    }
+    return nodeList;
+  }
+
+  public ShardInfoList getShardInfoList(String shardName) {
+    return shardNameToShardInfoList.get(shardName);
+  }
+  
+
+  /**
+   * @return last time info was updated.
+   */
+  public long getUpdateTime() {
+    return updateTime;
+  }
+
+}

Copied: lucene/solr/branches/cloud/src/java/org/apache/solr/cloud/CountdownWatcher.java (from r892824, lucene/solr/branches/cloud/src/java/org/apache/solr/util/zookeeper/CountdownWatcher.java)
URL: http://svn.apache.org/viewvc/lucene/solr/branches/cloud/src/java/org/apache/solr/cloud/CountdownWatcher.java?p2=lucene/solr/branches/cloud/src/java/org/apache/solr/cloud/CountdownWatcher.java&p1=lucene/solr/branches/cloud/src/java/org/apache/solr/util/zookeeper/CountdownWatcher.java&r1=892824&r2=894959&rev=894959&view=diff
==============================================================================
--- lucene/solr/branches/cloud/src/java/org/apache/solr/util/zookeeper/CountdownWatcher.java (original)
+++ lucene/solr/branches/cloud/src/java/org/apache/solr/cloud/CountdownWatcher.java Fri Jan  1 00:17:31 2010
@@ -16,8 +16,9 @@
  * limitations under the License.
  */
 
-package org.apache.solr.util.zookeeper;
+package org.apache.solr.cloud;
 
+import java.io.IOException;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeoutException;
 
@@ -28,7 +29,7 @@
 import org.slf4j.LoggerFactory;
 
 public class CountdownWatcher implements Watcher {
-  protected static final Logger LOG = LoggerFactory
+  protected static final Logger log = LoggerFactory
       .getLogger(CountdownWatcher.class);
 
   private final String name;
@@ -39,8 +40,14 @@
 
   private boolean connected;
 
-  public CountdownWatcher(String name) {
+  private ReconnectionHandler reconnectionHandler;
+
+  //private ZooKeeper keeper;
+
+  public CountdownWatcher(String name, ReconnectionHandler handler) {
+    //this.keeper = keeper;
     this.name = name;
+    this.reconnectionHandler = handler;
     reset();
   }
 
@@ -51,12 +58,28 @@
   }
 
   public synchronized void process(WatchedEvent event) {
-    LOG.info("Watcher " + name + " got event " + event);
+    if(log.isInfoEnabled()) {
+      log.info("Watcher " + name + " got event " + event);
+    }
 
     state = event.getState();
     if (state == KeeperState.SyncConnected) {
       connected = true;
       clientConnected.countDown();
+    } else if(state == KeeperState.Expired) {
+      connected = false;
+      try {
+        reconnectionHandler.handleReconnect();
+      } catch (Exception e) {
+        // nocommit
+        System.out.println("connection failed:");
+        // TODO Auto-generated catch block
+        e.printStackTrace();
+      }
+      // nocommit: start reconnect attempts
+    } else if(state == KeeperState.Disconnected) {
+      connected = false;
+      // nocommit: start reconnect attempts
     } else {
       connected = false;
     }
@@ -81,7 +104,6 @@
     }
     if (!connected) {
       throw new TimeoutException("Did not connect");
-
     }
   }
 
@@ -95,7 +117,6 @@
     }
     if (connected) {
       throw new TimeoutException("Did not disconnect");
-
     }
   }
 }

Added: lucene/solr/branches/cloud/src/java/org/apache/solr/cloud/ReconnectionHandler.java
URL: http://svn.apache.org/viewvc/lucene/solr/branches/cloud/src/java/org/apache/solr/cloud/ReconnectionHandler.java?rev=894959&view=auto
==============================================================================
--- lucene/solr/branches/cloud/src/java/org/apache/solr/cloud/ReconnectionHandler.java (added)
+++ lucene/solr/branches/cloud/src/java/org/apache/solr/cloud/ReconnectionHandler.java Fri Jan  1 00:17:31 2010
@@ -0,0 +1,24 @@
+package org.apache.solr.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;
+
+public abstract class ReconnectionHandler {
+  public abstract boolean handleReconnect() throws IOException;
+}

Added: lucene/solr/branches/cloud/src/java/org/apache/solr/cloud/ShardInfo.java
URL: http://svn.apache.org/viewvc/lucene/solr/branches/cloud/src/java/org/apache/solr/cloud/ShardInfo.java?rev=894959&view=auto
==============================================================================
--- lucene/solr/branches/cloud/src/java/org/apache/solr/cloud/ShardInfo.java (added)
+++ lucene/solr/branches/cloud/src/java/org/apache/solr/cloud/ShardInfo.java Fri Jan  1 00:17:31 2010
@@ -0,0 +1,47 @@
+package org.apache.solr.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.
+ */
+
+/**
+ * Information about a Shard.
+ * 
+ */
+public final class ShardInfo {
+
+  private final String url;
+  //nocommit do role based on existing ReplicationHandler role detection?
+  private final Role role;
+
+  public ShardInfo(String url) {
+    this.url = url;
+    role = Role.SLAVE;
+  }
+
+  public ShardInfo(String url, Role role) {
+    this.url = url;
+    this.role = role;
+  }
+
+  public Role getRole() {
+    return role;
+  }
+
+  public String getUrl() {
+    return url;
+  }
+}

Added: lucene/solr/branches/cloud/src/java/org/apache/solr/cloud/ShardInfoList.java
URL: http://svn.apache.org/viewvc/lucene/solr/branches/cloud/src/java/org/apache/solr/cloud/ShardInfoList.java?rev=894959&view=auto
==============================================================================
--- lucene/solr/branches/cloud/src/java/org/apache/solr/cloud/ShardInfoList.java (added)
+++ lucene/solr/branches/cloud/src/java/org/apache/solr/cloud/ShardInfoList.java Fri Jan  1 00:17:31 2010
@@ -0,0 +1,69 @@
+package org.apache.solr.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.util.Collections;
+import java.util.List;
+
+/**
+ * List of Shards (URL + Role)
+ *
+ */
+public final class ShardInfoList {
+
+  private final List<ShardInfo> shards;
+
+  public ShardInfoList(List<ShardInfo> shards) {
+    //nocommit: defensive copy?
+    this.shards = shards;
+  }
+  
+  public List<ShardInfo> getShards() {
+    return Collections.unmodifiableList(shards);
+  }
+  
+  public String toString() {
+    StringBuilder sb = new StringBuilder();
+    ShardInfo shardInfo;
+    int sz = shards.size();
+    for(int i = 0; i < sz; i++) {
+      shardInfo = shards.get(i);
+      sb.append(shardInfo.getUrl() + ", " + shardInfo.getRole());
+      if(i != sz-1) {
+        sb.append(" : ");
+      }
+    }
+    return sb.toString();
+  }
+  
+  // nocommit: return one URL from list of shards
+  public String getShardUrl() {
+    // nocommit
+    for (ShardInfo shard : shards) {
+      System.out.println("getNode:" + shard.getUrl());
+      if (shard.getRole() != Role.MASTER) {
+        return shard.getUrl();
+      }
+    }
+    throw new IllegalStateException("No slaves for shard");
+  }
+}
+
+enum Role {
+  MASTER, SLAVE
+}

Copied: lucene/solr/branches/cloud/src/java/org/apache/solr/cloud/ZooKeeperController.java (from r892824, lucene/solr/branches/cloud/src/java/org/apache/solr/core/ZooKeeperController.java)
URL: http://svn.apache.org/viewvc/lucene/solr/branches/cloud/src/java/org/apache/solr/cloud/ZooKeeperController.java?p2=lucene/solr/branches/cloud/src/java/org/apache/solr/cloud/ZooKeeperController.java&p1=lucene/solr/branches/cloud/src/java/org/apache/solr/core/ZooKeeperController.java&r1=892824&r2=894959&rev=894959&view=diff
==============================================================================
--- lucene/solr/branches/cloud/src/java/org/apache/solr/core/ZooKeeperController.java (original)
+++ lucene/solr/branches/cloud/src/java/org/apache/solr/cloud/ZooKeeperController.java Fri Jan  1 00:17:31 2010
@@ -1,196 +1,419 @@
-package org.apache.solr.core;
+package org.apache.solr.cloud;
 
-import java.io.ByteArrayInputStream;
+/**
+ * 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.ByteArrayOutputStream;
 import java.io.IOException;
-import java.io.InputStream;
 import java.net.InetAddress;
-import java.net.UnknownHostException;
 import java.util.List;
+import java.util.Map;
+import java.util.Properties;
 import java.util.concurrent.TimeoutException;
-
-import javax.xml.parsers.ParserConfigurationException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import org.apache.solr.common.SolrException;
-import org.apache.solr.schema.IndexSchema;
-import org.apache.solr.util.ZooPut;
-import org.apache.solr.util.zookeeper.CountdownWatcher;
+import org.apache.solr.core.SolrCore;
+import org.apache.zookeeper.CreateMode;
 import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.WatchedEvent;
+import org.apache.zookeeper.Watcher;
 import org.apache.zookeeper.ZooKeeper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.xml.sax.SAXException;
 
 /**
  * Handle ZooKeeper interactions.
+ * 
+ * notes: loads everything on init, creates what's not there - further updates
+ * are prompted with Watches.
+ * 
+ * TODO: handle ZooKeeper goes down / failures, Solr still runs
  */
-public class ZooKeeperController {
-  private static final String CONFIGS_NODE = "configs";
+public final class ZooKeeperController {
+  private static final String COLLECTIONS_ZKNODE = "/collections/";
+
+  static final String NODE_ZKPREFIX = "/node";
+
+  private static final String SHARDS_ZKNODE = "/shards";
+
+  static final String PROPS_DESC = "NodeDesc";
+
+  static final String SHARD_LIST_PROP = "shard_list";
+
+  static final String URL_PROP = "url";
+
+  // nocommit - explore handling shard changes
+  // watches the shards zkNode
+  static class ShardsWatcher implements Watcher {
+
+    private ZooKeeperController controller;
+
+    public ShardsWatcher(ZooKeeperController controller) {
+      this.controller = controller;
+    }
+
+    public void process(WatchedEvent event) {
+      // nocommit : this will be called too often as shards register themselves
+      System.out.println("shards changed");
+
+      try {
+        // refresh watcher
+        controller.getKeeper().exists(event.getPath(), this);
+
+        // TODO: need to load whole state?
+        controller.loadCollectionInfo();
+
+      } catch (KeeperException e) {
+        throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
+            "ZooKeeper Exception", e);
+      } catch (InterruptedException e) {
+        // Restore the interrupted status
+        Thread.currentThread().interrupt();
+      }
+
+    }
+
+  }
+
+  final ShardsWatcher SHARD_WATCHER = new ShardsWatcher(this);
+
+  private final static Pattern URL_POST = Pattern.compile("https?://(.*)");
+
+  private final static Pattern URL_PREFIX = Pattern.compile("(https?://).*");
+
+  private final ReconnectionHandler RECONNECTION_HANDLER = new ReconnectionHandler() {
+    
+    @Override
+    public boolean handleReconnect() throws IOException {
+      // nocommit : reconnection experimentation
+      log.info("Attempting to reconnect to ZooKeeper...");
+      boolean connected = true;
+      CountdownWatcher countdownWatcher = new CountdownWatcher(
+          "ZooKeeperController", RECONNECTION_HANDLER);
+      // nocommit : close old ZooKeeper client?
+      keeper = new ZooKeeper(zooKeeperHost, zkClientTimeout, countdownWatcher);
+      try {
+        countdownWatcher.waitForConnected(5000);
+      } catch (InterruptedException e) {
+        // Restore the interrupted status
+        Thread.currentThread().interrupt();
+        connected = false;
+      } catch (TimeoutException e) {
+        connected = false;
+      }
+      log.info("Connected:" + connected);
+      return connected;
+    }
+  };
 
   private static Logger log = LoggerFactory
       .getLogger(ZooKeeperController.class);
 
-  private ZooKeeper keeper;
+  // nocommit : consider reconnects more closely
+  private volatile ZooKeeper keeper;
 
-  private String configName;
+  ZooKeeper getKeeper() {
+    return keeper;
+  }
+
+  private ZooKeeperReader zkReader;
 
   private String collectionName;
 
-  private String shardAddress;
+  private volatile CollectionInfo collectionInfo;
+
+  private String shardsZkPath;
+
+  private ZooKeeperWriter zkWriter;
+
+  private String zooKeeperHost;
+
+
+  private String hostPort;
+
+  private String hostContext;
+
+  private String configName;
+
+  private int zkClientTimeout;
+
+  private String zooKeeperHostName;
+
 
   /**
-   * @param zookeeperHost ZooKeeper host service
-   * @param shardAddress
+   * 
+   * @param zooKeeperHost ZooKeeper host address
+   * @param collection
+   * @param hostUrl
+   * @param hostPort
+   * @param hostContext
    * @param zkClientTimeout
-   * @param zkSolrPathPrefix Solr ZooKeeper node (default is /solr)
    */
-  public ZooKeeperController(String zookeeperHost, String collection,
-      String shardAddress, int zkClientTimeout) {
+  public ZooKeeperController(String zooKeeperHost, String collection,
+      String hostUrl, String hostPort, String hostContext, int zkClientTimeout) {
+
     this.collectionName = collection;
-    this.shardAddress = shardAddress;
-    CountdownWatcher countdownWatcher = new CountdownWatcher(
-        "ZooKeeperController");
-    System.out.println("timeout:" + zkClientTimeout);
+    this.zooKeeperHost = zooKeeperHost;
+    this.hostPort = hostPort;
+    this.hostContext = hostContext;
+    this.zkClientTimeout = zkClientTimeout;
+
+    shardsZkPath = COLLECTIONS_ZKNODE + collectionName + SHARDS_ZKNODE;
+
+    init();
+  }
+  
+  private void init() {
+
     try {
-      keeper = new ZooKeeper(zookeeperHost, zkClientTimeout, countdownWatcher);
 
+      CountdownWatcher countdownWatcher = new CountdownWatcher(
+          "ZooKeeperController", RECONNECTION_HANDLER);
+      keeper = new ZooKeeper(zooKeeperHost, zkClientTimeout, countdownWatcher);
       countdownWatcher.waitForConnected(5000);
 
-      loadConfigPath();
-      register();
+      zkReader = new ZooKeeperReader(keeper);
+      zkWriter = new ZooKeeperWriter(keeper);
+
+      configName = zkReader.readConfigName(collectionName);
+
+      zooKeeperHostName = getHostAddress();
+      Matcher m = URL_POST.matcher(zooKeeperHostName);
+      if (m.matches()) {
+        String hostName = m.group(1);
+
+        // register host
+        zkWriter.makePath(hostName);
+      } else {
+        // nocommit
+        throw new IllegalStateException("Bad host:" + zooKeeperHostName);
+      }
+
+      // build layout if not exists
+      buildZkLayoutZkNodes();
+
+      // load the state of the cloud
+      loadCollectionInfo();
+
     } catch (IOException e) {
+      log.error("", e);
       throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
-          "Can't create ZooKeeper instance", e);
+          "Can't create ZooKeeperController", e);
     } catch (InterruptedException e) {
-      // nocommit
+      // Restore the interrupted status
+      Thread.currentThread().interrupt();
     } catch (TimeoutException e) {
+      log.error("", e);
       throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
-          "Can't create ZooKeeper instance", e);
+          "Timeout waiting for ZooKeeper connection", e);
+    } catch (KeeperException e) {
+      log.error("KeeperException", e);
+      throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "", e);
     }
 
   }
+  
+  public boolean configFileExists(String fileName) throws KeeperException, InterruptedException {
+    return zkReader.configFileExists(configName, fileName);
+  }
 
-  // nocommit: fooling around
-  private void register() throws IOException {
+  /**
+   * nocommit: adds nodes if they don't exist, eg /shards/ node. consider race
+   * conditions
+   */
+  private void buildZkLayoutZkNodes() throws IOException {
     try {
-      if (shardAddress == null) {
-        shardAddress = InetAddress.getLocalHost().getHostName();
+      // shards node
+      if (!zkReader.exists(shardsZkPath)) {
+        if (log.isInfoEnabled()) {
+          log.info("creating zk shards node:" + shardsZkPath);
+        }
+        // makes shards zkNode if it doesn't exist
+        zkWriter.makePath(shardsZkPath, CreateMode.PERSISTENT, SHARD_WATCHER);
       }
-      ZooPut zooPut = new ZooPut(keeper);
-      zooPut.makePath("/hosts/" + shardAddress);
-    } catch (UnknownHostException e) {
-      throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
-          "Could not determine IP of host", e);
     } catch (KeeperException e) {
-      throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
-          "ZooKeeper Exception", e);
+      // its okay if another beats us creating the node
+      if (e.code() != KeeperException.Code.NODEEXISTS) {
+        log.error("ZooKeeper Exception", e);
+        throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
+            "ZooKeeper Exception", e);
+      }
     } catch (InterruptedException e) {
-      // nocommit: handle
+      // Restore the interrupted status
+      Thread.currentThread().interrupt();
     }
   }
 
-  private void loadConfigPath() {
-    // nocommit: load all config at once or organize differently
+  /**
+   * Closes the underlying ZooKeeper client.
+   */
+  public void close() {
     try {
-      String path = "/collections/" + collectionName;
-      // nocommit
-      System.out.println("look for collection config:" + path);
-      List<String> children = keeper.getChildren(path, null);
-      for (String node : children) {
-        // nocommit
-        System.out.println("check child:" + node);
-        // nocommit: do we actually want to handle settings in the node name?
-        if (node.startsWith("config=")) {
-          configName = node.substring(node.indexOf("=") + 1);
-          // nocommit
-          System.out.println("config:" + configName);
-        }
-      }
-    } catch (KeeperException e) {
-      throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
-          "ZooKeeper Exception", e);
+      keeper.close();
     } catch (InterruptedException e) {
-      // nocommit
-    }
-    if (configName == null) {
-      throw new IllegalStateException("no config specified for collection:"
-          + collectionName);
+      // Restore the interrupted status
+      Thread.currentThread().interrupt();
     }
   }
 
   /**
-   * Load IndexSchema from ZooKeeper.
-   * 
-   * @param resourceLoader
-   * @param schemaName
-   * @param config
-   * @return
+   * @return information about the current collection from ZooKeeper
    */
-  public IndexSchema getSchema(String schemaName, SolrConfig config,
-      SolrResourceLoader resourceLoader) {
-    byte[] configBytes = getFile("/" + CONFIGS_NODE + "/" + configName,
-        schemaName);
-    InputStream is = new ByteArrayInputStream(configBytes);
-    IndexSchema schema = new IndexSchema(config, schemaName, is);
-    return schema;
+  public CollectionInfo getCollectionInfo() {
+    return collectionInfo;
   }
 
   /**
-   * Load SolrConfig from ZooKeeper.
-   * 
-   * @param resourceLoader
-   * @param solrConfigName
-   * @return
-   * @throws IOException
-   * @throws ParserConfigurationException
-   * @throws SAXException
-   */
-  public SolrConfig getConfig(String solrConfigName,
-      SolrResourceLoader resourceLoader) throws IOException,
-      ParserConfigurationException, SAXException {
-    byte[] config = getFile("/" + CONFIGS_NODE + "/" + configName,
-        solrConfigName);
-    InputStream is = new ByteArrayInputStream(config);
-    SolrConfig cfg = solrConfigName == null ? new SolrConfig(resourceLoader,
-        SolrConfig.DEFAULT_CONF_FILE, is) : new SolrConfig(resourceLoader,
-        solrConfigName, is);
+   * @return an object that encapsulates most of the ZooKeeper read util operations.
+   */
+  public ZooKeeperReader getZkReader() {
+    return zkReader;
+  }
 
-    return cfg;
+  /**
+   * @return an object that encapsulates most of the ZooKeeper write util operations.
+   */
+  public ZooKeeperWriter getZkWriter() {
+    return zkWriter;
   }
+  
 
-  public boolean exists(String path) {
-    Object exists = null;
+  /**
+   * @return
+   */
+  public String getZooKeeperHost() {
+    return zooKeeperHost;
+  }
+
+  // load and publish a new CollectionInfo
+  private void loadCollectionInfo() {
+    // build immutable CollectionInfo
+    boolean updateCollectionInfo = false;
+    Map<String,ShardInfoList> shardNameToShardList = null;
     try {
-      exists = keeper.exists(path, null);
+      shardNameToShardList = zkReader.readShardInfo(shardsZkPath);
+      updateCollectionInfo = true;
     } catch (KeeperException e) {
-      throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
-          "ZooKeeper Exception", e);
+      // nocommit: its okay if we cannot access ZK - just log
+      // and continue
+      log.error("", e);
+    } catch (IOException e) {
+      log.error("", e);
     } catch (InterruptedException e) {
-      // nocommit: handle
+      // Restore the interrupted status
+      Thread.currentThread().interrupt();
     }
-    return exists != null;
+
+    if(updateCollectionInfo) {
+      CollectionInfo collectionInfo = new CollectionInfo(shardNameToShardList);
+      // update volatile
+      this.collectionInfo = collectionInfo;
+    }
+  }
+
+  /**
+   * @return name of configuration zkNode to use
+   */
+  public String getConfigName() {
+    return configName;
   }
 
-  public byte[] getFile(String path, String file) {
-    byte[] bytes = null;
-    String configPath = path + "/" + file;
+  // nocommit - testing
+  public String getSearchNodes() {
+    StringBuilder nodeString = new StringBuilder();
+    boolean first = true;
+    List<String> nodes;
+
+    nodes = collectionInfo.getSearchShards();
+    // nocommit
+    System.out.println("there are " + nodes.size() + " node(s)");
+    for (String node : nodes) {
+      nodeString.append(node);
+      if (first) {
+        first = false;
+      } else {
+        nodeString.append(',');
+      }
+    }
+    return nodeString.toString();
+  }
+
+  /**
+   * Register shard. A SolrCore calls this on startup to register with
+   * ZooKeeper.
+   * 
+   * @param core
+   */
+  public void registerShard(SolrCore core) {
+    String coreName = core.getCoreDescriptor().getName();
+    String shardUrl = zooKeeperHost + ":" + hostPort + "/" + hostContext + "/"
+        + coreName;
+
+    // nocommit:
+    if (log.isInfoEnabled()) {
+      log.info("Register shard - core:" + core.getName() + " address:"
+          + shardUrl);
+    }
+
     try {
-      log.info("Reading " + file + " from zookeeper at " + configPath);
-      bytes = keeper.getData(configPath, false, null);
-    } catch (KeeperException e) {
+      // create node
+      ByteArrayOutputStream baos = new ByteArrayOutputStream();
+      // nocommit: could do xml
+      Properties props = new Properties();
+      props.put(URL_PROP, shardUrl);
+      
+      String shardList = core.getCoreDescriptor().getShardList();
+      
+      props.put(SHARD_LIST_PROP, shardList == null ? "" : shardList);
+      props.store(baos, PROPS_DESC);
+
+      zkWriter.makeEphemeralSeqPath(shardsZkPath + NODE_ZKPREFIX, baos
+          .toByteArray(), SHARD_WATCHER);
+
+    } catch (InterruptedException e) {
+      // Restore the interrupted status
+      Thread.currentThread().interrupt();
+    } catch (Exception e) {
+      log.error("ZooKeeper Exception", e);
       throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
           "ZooKeeper Exception", e);
-    } catch (InterruptedException e) {
-      // nocommit: handle
     }
 
-    return bytes;
   }
 
-  public void close() {
-    try {
-      keeper.close();
-    } catch (InterruptedException e) {
-      // nocommit: handle
+  // nocommit: fooling around
+  private String getHostAddress() throws IOException {
+    String host = null;
+
+    if (host == null) {
+      host = "http://" + InetAddress.getLocalHost().getHostName();
+    } else {
+      Matcher m = URL_PREFIX.matcher(host);
+      if (m.matches()) {
+        String prefix = m.group(1);
+        host = prefix + host;
+      }
+    }
+    if (log.isInfoEnabled()) {
+      log.info("Register host with ZooKeeper:" + host);
     }
+
+    return host;
   }
 }

Added: lucene/solr/branches/cloud/src/java/org/apache/solr/cloud/ZooKeeperReader.java
URL: http://svn.apache.org/viewvc/lucene/solr/branches/cloud/src/java/org/apache/solr/cloud/ZooKeeperReader.java?rev=894959&view=auto
==============================================================================
--- lucene/solr/branches/cloud/src/java/org/apache/solr/cloud/ZooKeeperReader.java (added)
+++ lucene/solr/branches/cloud/src/java/org/apache/solr/cloud/ZooKeeperReader.java Fri Jan  1 00:17:31 2010
@@ -0,0 +1,351 @@
+package org.apache.solr.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.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.TimeoutException;
+
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.solr.common.SolrException;
+import org.apache.solr.core.SolrConfig;
+import org.apache.solr.core.SolrResourceLoader;
+import org.apache.solr.schema.IndexSchema;
+import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.ZooKeeper;
+import org.apache.zookeeper.data.Stat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.xml.sax.SAXException;
+
+/**
+ *
+ */
+public class ZooKeeperReader {
+  static final String NEWL = System.getProperty("line.separator");
+
+  private static Logger log = LoggerFactory.getLogger(ZooKeeperReader.class);
+
+  private static final String SHARD_LIST_PROP = "shard_list";
+
+  private static final String URL_PROP = "url";
+
+  private static final String CONFIGS_ZKNODE = "/configs/";
+
+  private static final String COLLECTIONS_ZKNODE = "/collections/";
+
+  private ZooKeeper keeper;
+
+  private boolean closeKeeper;
+
+  /**
+   * @param zooKeeper
+   */
+  ZooKeeperReader(ZooKeeper zooKeeper) {
+    this.keeper = zooKeeper;
+    // this.configName = readConfigName(collection);
+  }
+
+  /**
+   * 
+   * For testing. For regular use see {@link #ZooKeeperReader(ZooKeeper)}.
+   * 
+   * @param zooKeeperHost
+   * @param zkClientTimeout
+   * @throws IOException
+   * @throws InterruptedException
+   * @throws TimeoutException
+   */
+  ZooKeeperReader(String zooKeeperHost, int zkClientTimeout)
+      throws IOException, InterruptedException, TimeoutException {
+    closeKeeper = true;
+    CountdownWatcher countdownWatcher = new CountdownWatcher(
+        "ZooKeeperController", new ReconnectionHandler() {
+          @Override
+          public boolean handleReconnect() throws IOException {
+            return false;
+          }
+        });
+    keeper = new ZooKeeper(zooKeeperHost, zkClientTimeout, countdownWatcher);
+    countdownWatcher.waitForConnected(5000);
+  }
+
+  /**
+   * Check if path exists in ZooKeeper.
+   * 
+   * @param path ZooKeeper path
+   * @return true if path exists in ZooKeeper
+   */
+  public boolean exists(String path) {
+    Object exists = null;
+    try {
+      exists = keeper.exists(path, null);
+    } catch (KeeperException e) {
+      log.error("ZooKeeper Exception", e);
+      throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
+          "ZooKeeper Exception", e);
+    } catch (InterruptedException e) {
+      // nocommit: handle
+    }
+    return exists != null;
+  }
+
+  public void close() throws InterruptedException {
+    if (closeKeeper) {
+      keeper.close();
+    }
+  }
+
+  /**
+   * Load SolrConfig from ZooKeeper.
+   * 
+   * TODO: consider *many* cores firing up at once and loading the same files
+   * from ZooKeeper
+   * 
+   * @param resourceLoader
+   * @param solrConfigFileName
+   * @return
+   * @throws IOException
+   * @throws ParserConfigurationException
+   * @throws SAXException
+   * @throws InterruptedException
+   * @throws KeeperException
+   */
+  public SolrConfig getConfig(String zkConfigName, String solrConfigFileName,
+      SolrResourceLoader resourceLoader) throws IOException,
+      ParserConfigurationException, SAXException, KeeperException,
+      InterruptedException {
+    byte[] config = getFile(CONFIGS_ZKNODE + zkConfigName, solrConfigFileName);
+    InputStream is = new ByteArrayInputStream(config);
+    SolrConfig cfg = solrConfigFileName == null ? new SolrConfig(
+        resourceLoader, SolrConfig.DEFAULT_CONF_FILE, is) : new SolrConfig(
+        resourceLoader, solrConfigFileName, is);
+
+    return cfg;
+  }
+  
+  public byte[] getConfigFileData(String zkConfigName, String fileName) throws KeeperException, InterruptedException {
+    return keeper.getData(CONFIGS_ZKNODE + zkConfigName, null, null);
+  }
+
+  /**
+   * Get data at zkNode path/fileName.
+   * 
+   * @param path to zkNode
+   * @param fileName name of zkNode
+   * @return data at path/file
+   * @throws InterruptedException
+   * @throws KeeperException
+   */
+  public byte[] getFile(String path, String fileName) throws KeeperException,
+      InterruptedException {
+    byte[] bytes = null;
+    String configPath = path + "/" + fileName;
+
+    if (log.isInfoEnabled()) {
+      log.info("Reading " + fileName + " from zookeeper at " + configPath);
+    }
+    bytes = keeper.getData(configPath, false, null);
+
+    return bytes;
+  }
+
+  /**
+   * Load IndexSchema from ZooKeeper.
+   * 
+   * TODO: consider *many* cores firing up at once and loading the same files
+   * from ZooKeeper
+   * 
+   * @param resourceLoader
+   * @param schemaName
+   * @param config
+   * @return
+   * @throws InterruptedException
+   * @throws KeeperException
+   */
+  public IndexSchema getSchema(String zkConfigName, String schemaName,
+      SolrConfig config, SolrResourceLoader resourceLoader)
+      throws KeeperException, InterruptedException {
+    byte[] configBytes = getFile(CONFIGS_ZKNODE + zkConfigName, schemaName);
+    InputStream is = new ByteArrayInputStream(configBytes);
+    IndexSchema schema = new IndexSchema(config, schemaName, is);
+    return schema;
+  }
+
+  /**
+   * Fills string with printout of current ZooKeeper layout.
+   * 
+   * @param path
+   * @param indent
+   * @throws KeeperException
+   * @throws InterruptedException
+   */
+  public void printLayout(String path, int indent, StringBuilder string)
+      throws KeeperException, InterruptedException {
+    byte[] data = keeper.getData(path, null, null);
+    List<String> children = keeper.getChildren(path, false);
+    StringBuilder dent = new StringBuilder();
+    for (int i = 0; i < indent; i++) {
+      dent.append(" ");
+    }
+    string.append(dent + path + " (" + children.size() + ")" + NEWL);
+    if (data != null) {
+      try {
+        String dataString = new String(data, "UTF-8");
+        if (!path.endsWith(".txt") && !path.endsWith(".xml")) {
+          string.append(dent + "DATA:\n" + dent + "    "
+              + dataString.replaceAll("\n", "\n" + dent + "    ") + NEWL);
+        } else {
+          string.append(dent + "DATA: ...supressed..." + NEWL);
+        }
+      } catch (UnsupportedEncodingException e) {
+        throw new RuntimeException(e);
+      }
+    }
+
+    for (String child : children) {
+      if (!child.equals("quota")) {
+        printLayout(path + (path.equals("/") ? "" : "/") + child, indent + 1,
+            string);
+      }
+    }
+
+  }
+
+  /**
+   * Prints current ZooKeeper layout to stdout.
+   * 
+   * @throws KeeperException
+   * @throws InterruptedException
+   */
+  public void printLayoutToStdOut() throws KeeperException,
+      InterruptedException {
+    StringBuilder sb = new StringBuilder();
+    printLayout("/", 0, sb);
+    System.out.println(sb.toString());
+  }
+
+  public String readConfigName(String collection) throws KeeperException,
+      InterruptedException {
+    // nocommit: load all config at once or organize differently (Properties?)
+    String configName = null;
+
+    String path = COLLECTIONS_ZKNODE + collection;
+    if (log.isInfoEnabled()) {
+      log.info("Load collection config from:" + path);
+    }
+    List<String> children = keeper.getChildren(path, null);
+    for (String node : children) {
+      // nocommit
+      System.out.println("check child:" + node);
+      // nocommit: do we actually want to handle settings in the node name?
+      if (node.startsWith("config=")) {
+        configName = node.substring(node.indexOf("=") + 1);
+        if (log.isInfoEnabled()) {
+          log.info("Using collection config:" + configName);
+        }
+      }
+    }
+
+    if (configName == null) {
+      throw new IllegalStateException("no config specified for collection:"
+          + collection);
+    }
+
+    return configName;
+  }
+
+  /**
+   * Read info on the available Shards and Nodes.
+   * 
+   * @param path to the shards zkNode
+   * @return Map from shard name to a {@link ShardInfoList}
+   * @throws InterruptedException
+   * @throws KeeperException
+   * @throws IOException
+   */
+  public Map<String,ShardInfoList> readShardInfo(String path)
+      throws KeeperException, InterruptedException, IOException {
+    // for now, just reparse everything
+    HashMap<String,ShardInfoList> shardNameToShardList = new HashMap<String,ShardInfoList>();
+
+    if (!exists(path)) {
+      throw new IllegalStateException("Cannot find zk node that should exist:"
+          + path);
+    }
+    List<String> nodes = keeper.getChildren(path, null);
+
+    for (String zkNodeName : nodes) {
+      byte[] data = keeper.getData(path + "/" + zkNodeName, null, null);
+
+      Properties props = new Properties();
+      props.load(new ByteArrayInputStream(data));
+
+      String url = (String) props.get(URL_PROP);
+      String shardNameList = (String) props.get(SHARD_LIST_PROP);
+      String[] shardsNames = shardNameList.split(",");
+      for (String shardName : shardsNames) {
+        ShardInfoList sList = shardNameToShardList.get(shardName);
+        List<ShardInfo> shardList;
+        if (sList == null) {
+          shardList = new ArrayList<ShardInfo>(1);
+        } else {
+          List<ShardInfo> oldShards = sList.getShards();
+          shardList = new ArrayList<ShardInfo>(oldShards.size() + 1);
+          shardList.addAll(oldShards);
+        }
+
+        ShardInfo shard = new ShardInfo(url);
+        shardList.add(shard);
+        ShardInfoList list = new ShardInfoList(shardList);
+
+        shardNameToShardList.put(shardName, list);
+      }
+
+    }
+
+    return Collections.unmodifiableMap(shardNameToShardList);
+  }
+
+  /**
+   * Get Stat for path.
+   * 
+   * @param path
+   * @return Stat for path or null if it doesn't exist
+   * @throws KeeperException
+   * @throws InterruptedException
+   */
+  public Stat stat(String path) throws KeeperException, InterruptedException {
+    return keeper.exists(path, null);
+  }
+
+  public boolean configFileExists(String configName, String fileName) throws KeeperException, InterruptedException {
+    Stat stat = keeper.exists(CONFIGS_ZKNODE + configName, null);
+    return stat != null;
+  }
+
+}

Copied: lucene/solr/branches/cloud/src/java/org/apache/solr/cloud/ZooKeeperSolrResourceLoader.java (from r892824, lucene/solr/branches/cloud/src/java/org/apache/solr/core/ZKSolrResourceLoader.java)
URL: http://svn.apache.org/viewvc/lucene/solr/branches/cloud/src/java/org/apache/solr/cloud/ZooKeeperSolrResourceLoader.java?p2=lucene/solr/branches/cloud/src/java/org/apache/solr/cloud/ZooKeeperSolrResourceLoader.java&p1=lucene/solr/branches/cloud/src/java/org/apache/solr/core/ZKSolrResourceLoader.java&r1=892824&r2=894959&rev=894959&view=diff
==============================================================================
--- lucene/solr/branches/cloud/src/java/org/apache/solr/core/ZKSolrResourceLoader.java (original)
+++ lucene/solr/branches/cloud/src/java/org/apache/solr/cloud/ZooKeeperSolrResourceLoader.java Fri Jan  1 00:17:31 2010
@@ -1,45 +1,66 @@
-package org.apache.solr.core;
+package org.apache.solr.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.ByteArrayInputStream;
 import java.io.InputStream;
 import java.util.Properties;
 
-public class ZKSolrResourceLoader extends SolrResourceLoader {
+import org.apache.solr.core.SolrResourceLoader;
+
+/**
+ * ResourceLoader that works with ZooKeeper.
+ *
+ */
+public class ZooKeeperSolrResourceLoader extends SolrResourceLoader {
 
-  private ZooKeeperController zooKeeperController;
+  private String collection;
 
- 
-  String collection;
+  private ZooKeeperReader zkReader;
 
-  public ZKSolrResourceLoader(String instanceDir, String collection,
+  public ZooKeeperSolrResourceLoader(String instanceDir, String collection,
       ZooKeeperController zooKeeperController) {
     super(instanceDir);
-    this.zooKeeperController = zooKeeperController;
+    this.zkReader = zooKeeperController.getZkReader();
     this.collection = collection;
   }
 
   /**
    * <p>
-   * This loader will delegate to the context classloader when possible,
+   * This loader will first attempt to load resources from ZooKeeper, but if not found
+   * will delegate to the context classloader when possible,
    * otherwise it will attempt to resolve resources using any jar files found in
-   * the "lib/" directory in the specified instance directory. If the instance
-   * directory is not specified (=null), SolrResourceLoader#locateInstanceDir
-   * will provide one.
+   * the "lib/" directory in the specified instance directory.
    * <p>
    */
-  public ZKSolrResourceLoader(String instanceDir, String collection, ClassLoader parent,
+  public ZooKeeperSolrResourceLoader(String instanceDir, String collection, ClassLoader parent,
       Properties coreProperties, ZooKeeperController zooKeeperController) {
     super(instanceDir, parent, coreProperties);
-    this.zooKeeperController = zooKeeperController;
     this.collection = collection;
+    this.zkReader = zooKeeperController.getZkReader();
   }
 
   /**
    * Opens any resource by its name. By default, this will look in multiple
-   * locations to load the resource: $configDir/$resource (if resource is not
-   * absolute) $CWD/$resource otherwise, it will look for it in any jar
-   * accessible through the class loader. Override this method to customize
-   * loading resources.
+   * locations to load the resource: $configDir/$resource from ZooKeeper.
+   * It will look for it in any jar
+   * accessible through the class loader if it cannot be found in ZooKeeper. 
+   * Override this method to customize loading resources.
    * 
    * @return the stream for the named resource
    */
@@ -49,8 +70,8 @@
     //nocommit:
     System.out.println("look for:" + file);
     try {
-      if (zooKeeperController.exists(file)) {
-        byte[] bytes = zooKeeperController.getFile(getConfigDir(), resource);
+      if (zkReader.exists(file)) {
+        byte[] bytes = zkReader.getFile(getConfigDir(), resource);
         return new ByteArrayInputStream(bytes);
       }
     } catch (Exception e) {

Added: lucene/solr/branches/cloud/src/java/org/apache/solr/cloud/ZooKeeperWriter.java
URL: http://svn.apache.org/viewvc/lucene/solr/branches/cloud/src/java/org/apache/solr/cloud/ZooKeeperWriter.java?rev=894959&view=auto
==============================================================================
--- lucene/solr/branches/cloud/src/java/org/apache/solr/cloud/ZooKeeperWriter.java (added)
+++ lucene/solr/branches/cloud/src/java/org/apache/solr/cloud/ZooKeeperWriter.java Fri Jan  1 00:17:31 2010
@@ -0,0 +1,251 @@
+package org.apache.solr.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.File;
+import java.io.IOException;
+import java.util.concurrent.TimeoutException;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.Watcher;
+import org.apache.zookeeper.ZooDefs;
+import org.apache.zookeeper.ZooKeeper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ *
+ */
+public class ZooKeeperWriter {
+  private static Logger log = LoggerFactory.getLogger(ZooKeeperWriter.class);
+
+  private ZooKeeper keeper;
+
+  private boolean closeKeeper;
+
+  /**
+   * For testing. For regular use see {@link #ZooKeeperWriter(ZooKeeper)}.
+   * 
+   * @param zooKeeperHost
+   * @param zkClientTimeout
+   * @throws IOException
+   * @throws InterruptedException
+   * @throws TimeoutException
+   */
+  ZooKeeperWriter(String zooKeeperHost, int zkClientTimeout)
+      throws IOException, InterruptedException, TimeoutException {
+    closeKeeper = true;
+    CountdownWatcher countdownWatcher = new CountdownWatcher("ZooKeeperWriter", new ReconnectionHandler() {
+      @Override
+      public boolean handleReconnect() throws IOException {
+        return false;
+      }
+    });
+    keeper = new ZooKeeper(zooKeeperHost, zkClientTimeout, countdownWatcher);
+    countdownWatcher.waitForConnected(5000);
+  }
+
+  /**
+   * @param keeper
+   */
+  ZooKeeperWriter(ZooKeeper keeper) {
+    this.keeper = keeper;
+  }
+
+  /**
+   * Close underling ZooKeeper client if this owns it.
+   * 
+   * Only for tests.
+   * 
+   * @throws InterruptedException
+   */
+  public void close() throws InterruptedException {
+    if (closeKeeper) {
+      keeper.close();
+    }
+  }
+
+  /**
+   * 
+   * @param path
+   * @param data
+   * @param watcher
+   * @return
+   * @throws KeeperException
+   * @throws InterruptedException
+   */
+  public String makeEphemeralSeqPath(String path, byte[] data,
+      Watcher watcher) throws KeeperException, InterruptedException {
+
+    String zkPath = keeper.create(path, data, ZooDefs.Ids.OPEN_ACL_UNSAFE,
+        CreateMode.EPHEMERAL_SEQUENTIAL);
+    
+    keeper.exists(zkPath, watcher);
+    
+    return zkPath;
+  }
+
+  /**
+   * Creates the path in ZooKeeper, creating each node as necessary.
+   * 
+   * e.g. If <code>path=/solr/group/node</code> and none of the nodes, solr,
+   * group, node exist, each will be created.
+   * 
+   * @param path
+   * @throws KeeperException
+   * @throws InterruptedException
+   */
+  public void makePath(String path) throws KeeperException,
+      InterruptedException {
+    makePath(path, null, CreateMode.PERSISTENT);
+  }
+
+  /**
+   * Creates the path in ZooKeeper, creating each node as necessary.
+   * 
+   * @param path
+   * @param data to set on the last zkNode
+   * @throws KeeperException
+   * @throws InterruptedException
+   */
+  public void makePath(String path, byte[] data) throws KeeperException,
+      InterruptedException {
+    makePath(path, data, CreateMode.PERSISTENT);
+  }
+
+  /**
+   * Creates the path in ZooKeeper, creating each node as necessary.
+   * 
+   * e.g. If <code>path=/solr/group/node</code> and none of the nodes, solr,
+   * group, node exist, each will be created.
+   * 
+   * @param path
+   * @param data to set on the last zkNode
+   * @param createMode
+   * @throws KeeperException
+   * @throws InterruptedException
+   */
+  public void makePath(String path, byte[] data, CreateMode createMode)
+      throws KeeperException, InterruptedException {
+    makePath(path, data, createMode, null);
+  }
+
+  /**
+   * Creates the path in ZooKeeper, creating each node as necessary.
+   * 
+   * e.g. If <code>path=/solr/group/node</code> and none of the nodes, solr,
+   * group, node exist, each will be created.
+   * 
+   * @param path
+   * @param data to set on the last zkNode
+   * @param createMode
+   * @param watcher
+   * @throws KeeperException
+   * @throws InterruptedException
+   */
+  public void makePath(String path, byte[] data, CreateMode createMode,
+      Watcher watcher) throws KeeperException, InterruptedException {
+    if (log.isInfoEnabled()) {
+      log.info("makePath: " + path);
+    }
+
+    if (path.startsWith("/")) {
+      path = path.substring(1, path.length());
+    }
+    String[] paths = path.split("/");
+    StringBuilder sbPath = new StringBuilder();
+    for (int i = 0; i < paths.length; i++) {
+      byte[] bytes = null;
+      String pathPiece = paths[i];
+      sbPath.append("/" + pathPiece);
+      String currentPath = sbPath.toString();
+      Object exists = keeper.exists(currentPath, watcher);
+      if (exists == null) {
+        CreateMode mode = CreateMode.PERSISTENT;
+        if (i == paths.length - 1) {
+          mode = createMode;
+          bytes = data;
+        }
+        keeper.create(currentPath, bytes, ZooDefs.Ids.OPEN_ACL_UNSAFE, mode);
+        // set new watch
+        keeper.exists(currentPath, watcher);
+      } else if (i == paths.length - 1) {
+        // nocommit: version ?
+        keeper.setData(currentPath, data, -1);
+        // set new watch
+        keeper.exists(currentPath, watcher);
+      }
+    }
+  }
+
+  /**
+   * @param zkPath
+   * @param createMode
+   * @param watcher
+   * @throws KeeperException
+   * @throws InterruptedException
+   */
+  public void makePath(String zkPath, CreateMode createMode, Watcher watcher)
+      throws KeeperException, InterruptedException {
+    makePath(zkPath, null, createMode, watcher);
+  }
+
+  /**
+   * Write data to ZooKeeper.
+   * 
+   * @param path
+   * @param data
+   * @throws KeeperException
+   * @throws InterruptedException
+   */
+  public void write(String path, byte[] data) throws KeeperException,
+      InterruptedException {
+
+    makePath(path);
+
+    Object exists = keeper.exists(path, null);
+    if (exists != null) {
+      keeper.setData(path, data, -1);
+    } else {
+      keeper.create(path, data, ZooDefs.Ids.OPEN_ACL_UNSAFE,
+          CreateMode.PERSISTENT);
+    }
+  }
+
+  /**
+   * Write file to ZooKeeper - default system encoding used.
+   * 
+   * @param path path to upload file to e.g. /solr/conf/solrconfig.xml
+   * @param file path to file to be uploaded
+   * @throws IOException
+   * @throws KeeperException
+   * @throws InterruptedException
+   */
+  public void write(String path, File file) throws IOException,
+      KeeperException, InterruptedException {
+    if(log.isInfoEnabled()) {
+      log.info("Write to ZooKeepeer " + file.getAbsolutePath() + " to " + path);
+    }
+
+    String data = FileUtils.readFileToString(file);
+    write(path, data.getBytes());
+  }
+  
+}

Modified: lucene/solr/branches/cloud/src/java/org/apache/solr/core/CoreContainer.java
URL: http://svn.apache.org/viewvc/lucene/solr/branches/cloud/src/java/org/apache/solr/core/CoreContainer.java?rev=894959&r1=894958&r2=894959&view=diff
==============================================================================
--- lucene/solr/branches/cloud/src/java/org/apache/solr/core/CoreContainer.java (original)
+++ lucene/solr/branches/cloud/src/java/org/apache/solr/core/CoreContainer.java Fri Jan  1 00:17:31 2010
@@ -31,6 +31,8 @@
 import javax.xml.xpath.XPath;
 import javax.xml.xpath.XPathExpressionException;
 
+import org.apache.solr.cloud.ZooKeeperSolrResourceLoader;
+import org.apache.solr.cloud.ZooKeeperController;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.params.CoreAdminParams;
 import org.apache.solr.common.util.DOMUtil;
@@ -38,6 +40,7 @@
 import org.apache.solr.common.util.FileUtils;
 import org.apache.solr.handler.admin.CoreAdminHandler;
 import org.apache.solr.schema.IndexSchema;
+import org.apache.zookeeper.KeeperException;
 import org.apache.commons.io.IOUtils;
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
@@ -59,7 +62,9 @@
   protected String adminPath = null;
   protected String managementPath = null;
   protected int zkClientTimeout;
-  protected String shardAddress;
+  protected String hostPort;
+  protected String hostContext;
+  protected String host;
   protected CoreAdminHandler coreAdminHandler = null;
   protected File configFile = null;
   protected String libDir = null;
@@ -74,19 +79,29 @@
   protected String solrHome;
   protected String solrConfigFilenameOverride;
   protected String solrDataDirOverride;
+  protected String zkPortOverride;
   protected String collection;
+  private String testShardsListOverride;
   private ZooKeeperController zooKeeperController;
 
+  private String zkHost;
+
+
   public CoreContainer() {
     solrHome = SolrResourceLoader.locateSolrHome();
   }
   
-  private void initZooKeeper(int zkClientTimeout) {
-    //nocommit: pull zookeeper integration into a new CoreContainer? leaning towards no.
-    String zookeeperHost = System.getProperty("zkHost");
+  private void initZooKeeper(String zkHost, int zkClientTimeout) {
+    // nocommit: perhaps get from solr.xml
+    String zookeeperHost;
+    if(zkHost == null) {
+      zookeeperHost = System.getProperty("zkHost");
+    } else {
+      zookeeperHost = zkHost;
+    }
     
     if (zookeeperHost != null) {
-      zooKeeperController = new ZooKeeperController(zookeeperHost, collection, shardAddress, zkClientTimeout);
+      zooKeeperController = new ZooKeeperController(zookeeperHost, collection, host,  hostPort, hostContext, zkClientTimeout);
     }
   }
 
@@ -99,6 +114,8 @@
     protected String solrConfigFilename = null;
     protected boolean abortOnConfigurationError = true;
     protected String dataDir = null; // override datadir for single core mode
+    private String zkPortOverride;
+    protected String testShardListOverride;
 
     public boolean isAbortOnConfigurationError() {
       return abortOnConfigurationError;
@@ -115,22 +132,34 @@
     public void setSolrConfigFilename(String solrConfigFilename) {
       this.solrConfigFilename = solrConfigFilename;
     }
+    
+    public void setZKPortOverride(String port) {
+      this.zkPortOverride = port;
+    }
+    
+    public void setTestShardListOverride(String shardList) {
+      this.testShardListOverride = shardList;
+    }
 
     // core container instantiation
     public CoreContainer initialize() throws IOException, ParserConfigurationException, SAXException {
       CoreContainer cores = null;
       String solrHome = SolrResourceLoader.locateSolrHome();
+      // nocommit : fix broken logic confusing solr.xml with solrconfig.xml
       File fconf = new File(solrHome, solrConfigFilename == null ? "solr.xml"
           : solrConfigFilename);
       log.info("looking for solr.xml: " + fconf.getAbsolutePath());
       cores = new CoreContainer();
-      cores.solrConfigFilenameOverride = solrConfigFilename;
       cores.solrDataDirOverride = dataDir;
+      cores.zkPortOverride = zkPortOverride;
+      cores.testShardsListOverride = testShardListOverride;
       if (fconf.exists())
         cores.load(solrHome, fconf);
       else {
-        cores.load(solrHome, new ByteArrayInputStream(DEF_SOLR_XML.getBytes()));
+        log.info("no solr.xml file found - using default");
+        cores.solrConfigFilenameOverride = solrConfigFilename;
         cores.configFile = fconf;
+        cores.load(solrHome, new ByteArrayInputStream(DEF_SOLR_XML.getBytes()));
       }
       abortOnConfigurationError = false;
       // if any core aborts on startup, then abort
@@ -144,6 +173,7 @@
       
       return cores;
     }
+
   }
 
   private static Properties getCoreProps(String instanceDir, String file, Properties defaults) {
@@ -229,20 +259,29 @@
     try {
       Config cfg = new Config(loader, null, cfgis, null);
 
-      persistent = cfg.getBool( "solr/@persistent", false );
-      libDir     = cfg.get(     "solr/@sharedLib", null);
-      adminPath  = cfg.get(     "solr/cores/@adminPath", null );
-      shareSchema = cfg.getBool("solr/cores/@shareSchema", false );
+      persistent = cfg.getBool("solr/@persistent", false);
+      libDir = cfg.get("solr/@sharedLib", null);
+      zkHost = cfg.get("solr/@zkHost" , null);
+      adminPath = cfg.get("solr/cores/@adminPath", null);
+      shareSchema = cfg.getBool("solr/cores/@shareSchema", false);
       zkClientTimeout = cfg.getInt("solr/cores/@zkClientTimeout", 10000);
-      shardAddress = cfg.get("solr/cores/@shardAddress", null);
+      if (zkPortOverride == null) {
+        hostPort = cfg.get("solr/cores/@hostPort", "8983");
+      } else {
+        hostPort = zkPortOverride;
+      }
+      hostContext = cfg.get("solr/cores/@hostContext", "solr");
+      host = cfg.get("solr/cores/@host", null);
+
       collection = cfg.get("solr/cores/@collection", "collection1"); //nocommit: default collection
+
       if(shareSchema){
         indexSchemaCache = new ConcurrentHashMap<String ,IndexSchema>();
       }
       adminHandler  = cfg.get("solr/cores/@adminHandler", null );
       managementPath  = cfg.get("solr/cores/@managementPath", null );
       
-      initZooKeeper(zkClientTimeout);
+      initZooKeeper(zkHost, zkClientTimeout);
 
       if (libDir != null) {
         File f = FileUtils.resolvePath(new File(dir), libDir);
@@ -280,6 +319,8 @@
 
           // deal with optional settings
           String opt = DOMUtil.getAttr(node, "config", null);
+          //nocommit
+          System.out.println("name:" + name + " c:" + solrConfigFilenameOverride);
           if(solrConfigFilenameOverride != null && name.equals("")) {
             p.setConfigName(solrConfigFilenameOverride);
           } else if (opt != null) {
@@ -289,6 +330,12 @@
           if (opt != null) {
             p.setSchemaName(opt);
           }
+          opt = DOMUtil.getAttr(node, "shardList", null);
+          if(testShardsListOverride != null && name.equals("")) {
+            p.setShardList(testShardsListOverride);
+          } else if(opt != null) {
+            p.setShardList(opt);
+          }
           opt = DOMUtil.getAttr(node, "properties", null);
           if (opt != null) {
             p.setPropertiesName(opt);
@@ -425,13 +472,23 @@
     // Initialize the solr config
     SolrResourceLoader solrLoader ;
     
-    SolrConfig config;
+    SolrConfig config = null;
     if(zooKeeperController == null) {
       solrLoader = new SolrResourceLoader(instanceDir, libLoader, getCoreProps(instanceDir, dcore.getPropertiesName(),dcore.getCoreProperties()));
       config = new SolrConfig(solrLoader, dcore.getConfigName(), null);
     } else {
-      solrLoader = new ZKSolrResourceLoader(instanceDir, collection, libLoader, getCoreProps(instanceDir, dcore.getPropertiesName(),dcore.getCoreProperties()), zooKeeperController);
-      config = zooKeeperController.getConfig(dcore.getConfigName(), solrLoader);
+      solrLoader = new ZooKeeperSolrResourceLoader(instanceDir, collection, libLoader, getCoreProps(instanceDir, dcore.getPropertiesName(),dcore.getCoreProperties()), zooKeeperController);
+      try {
+        config = zooKeeperController.getZkReader().getConfig(zooKeeperController.getConfigName(), dcore.getConfigName(), solrLoader);
+      } catch (KeeperException e) {
+        log.error("ZooKeeper Exception", e);
+        throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
+            "ZooKeeper Exception", e);
+      } catch (InterruptedException e) {
+        // Restore the interrupted status
+        Thread.currentThread().interrupt();
+        //nocommit: we may not know the config name - now what
+      }
     }
     IndexSchema schema = null;
     if (indexSchemaCache != null) {
@@ -464,7 +521,17 @@
     }
     if(schema == null){
       if(zooKeeperController != null) {
-        schema = zooKeeperController.getSchema(dcore.getSchemaName(), config, solrLoader);
+        try {
+          schema = zooKeeperController.getZkReader().getSchema(zooKeeperController.getConfigName(), dcore.getSchemaName(), config, solrLoader);
+        } catch (KeeperException e) {
+          log.error("ZooKeeper Exception", e);
+          throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
+              "ZooKeeper Exception", e);
+        } catch (InterruptedException e) {
+          // Restore the interrupted status
+          Thread.currentThread().interrupt();
+          //nocommit: we may not know have the schema - now what
+        }
       } else {
         schema = new IndexSchema(config, dcore.getSchemaName(), null);
       }

Modified: lucene/solr/branches/cloud/src/java/org/apache/solr/core/CoreDescriptor.java
URL: http://svn.apache.org/viewvc/lucene/solr/branches/cloud/src/java/org/apache/solr/core/CoreDescriptor.java?rev=894959&r1=894958&r2=894959&view=diff
==============================================================================
--- lucene/solr/branches/cloud/src/java/org/apache/solr/core/CoreDescriptor.java (original)
+++ lucene/solr/branches/cloud/src/java/org/apache/solr/core/CoreDescriptor.java Fri Jan  1 00:17:31 2010
@@ -34,6 +34,7 @@
   protected String schemaName;
   private final CoreContainer coreContainer;
   private Properties coreProperties;
+  private String shardList;
 
   public CoreDescriptor(CoreContainer coreContainer, String name, String instanceDir) {
     this.coreContainer = coreContainer;
@@ -171,4 +172,14 @@
         this.coreProperties.putAll(coreProperties);
     }
   }
+
+  public void setShardList(String shardList) {
+    System.out.println("set shard list:" + shardList);
+    this.shardList = shardList;
+  }
+  
+  //nocommit: may be null
+  public String getShardList() {
+    return shardList;
+  }
 }

Modified: lucene/solr/branches/cloud/src/java/org/apache/solr/core/SolrConfig.java
URL: http://svn.apache.org/viewvc/lucene/solr/branches/cloud/src/java/org/apache/solr/core/SolrConfig.java?rev=894959&r1=894958&r2=894959&view=diff
==============================================================================
--- lucene/solr/branches/cloud/src/java/org/apache/solr/core/SolrConfig.java (original)
+++ lucene/solr/branches/cloud/src/java/org/apache/solr/core/SolrConfig.java Fri Jan  1 00:17:31 2010
@@ -125,7 +125,7 @@
    *@param name the configuration name
    *@param is the configuration stream
    */
-  SolrConfig(SolrResourceLoader loader, String name, InputStream is)
+  public SolrConfig(SolrResourceLoader loader, String name, InputStream is)
   throws ParserConfigurationException, IOException, SAXException {
     super(loader, name, is, "/config/");
     initLibs();

Modified: lucene/solr/branches/cloud/src/java/org/apache/solr/core/SolrCore.java
URL: http://svn.apache.org/viewvc/lucene/solr/branches/cloud/src/java/org/apache/solr/core/SolrCore.java?rev=894959&r1=894958&r2=894959&view=diff
==============================================================================
--- lucene/solr/branches/cloud/src/java/org/apache/solr/core/SolrCore.java (original)
+++ lucene/solr/branches/cloud/src/java/org/apache/solr/core/SolrCore.java Fri Jan  1 00:17:31 2010
@@ -22,6 +22,7 @@
 import org.apache.lucene.index.IndexWriter;
 import org.apache.lucene.search.BooleanQuery;
 import org.apache.lucene.store.Directory;
+import org.apache.solr.cloud.ZooKeeperController;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.params.CommonParams;
 import org.apache.solr.common.params.CommonParams.EchoParamStyle;
@@ -283,6 +284,8 @@
   List<SolrEventListener> firstSearcherListeners;
   List<SolrEventListener> newSearcherListeners;
 
+  private ZooKeeperController zooKeeperComponent;
+
   /**
    * NOTE: this function is not thread safe.  However, it is safe to call within the
    * <code>inform( SolrCore core )</code> function for <code>SolrCoreAware</code> classes.
@@ -522,6 +525,11 @@
     if (schema==null) {
       schema = new IndexSchema(config, IndexSchema.DEFAULT_SCHEMA_FILE, null);
     }
+    
+    zooKeeperComponent = cd.getCoreContainer().getZooKeeperController();
+    if(zooKeeperComponent != null) {
+      zooKeeperComponent.registerShard(this);
+    }
 
     //Initialize JMX
     if (config.jmxConfig.enabled) {

Modified: lucene/solr/branches/cloud/src/java/org/apache/solr/handler/ReplicationHandler.java
URL: http://svn.apache.org/viewvc/lucene/solr/branches/cloud/src/java/org/apache/solr/handler/ReplicationHandler.java?rev=894959&r1=894958&r2=894959&view=diff
==============================================================================
--- lucene/solr/branches/cloud/src/java/org/apache/solr/handler/ReplicationHandler.java (original)
+++ lucene/solr/branches/cloud/src/java/org/apache/solr/handler/ReplicationHandler.java Fri Jan  1 00:17:31 2010
@@ -252,6 +252,8 @@
 
   void doFetch(SolrParams solrParams) {
     String masterUrl = solrParams == null ? null : solrParams.get(MASTER_URL);
+    // nocommit : override master url using info from ZooKeeper
+    
     if (!snapPullLock.tryLock())
       return;
     try {

Modified: lucene/solr/branches/cloud/src/java/org/apache/solr/handler/component/QueryComponent.java
URL: http://svn.apache.org/viewvc/lucene/solr/branches/cloud/src/java/org/apache/solr/handler/component/QueryComponent.java?rev=894959&r1=894958&r2=894959&view=diff
==============================================================================
--- lucene/solr/branches/cloud/src/java/org/apache/solr/handler/component/QueryComponent.java (original)
+++ lucene/solr/branches/cloud/src/java/org/apache/solr/handler/component/QueryComponent.java Fri Jan  1 00:17:31 2010
@@ -110,6 +110,8 @@
 
     // TODO: temporary... this should go in a different component.
     String shards = params.get(ShardParams.SHARDS);
+    // nocommit : get shards based on ZooKeeper info, using load balancing
+    // shards = req.getCore().getCoreDescriptor().getCoreContainer().getZooKeeperController().getSearchNodes();
     if (shards != null) {
       List<String> lst = StrUtils.splitSmart(shards, ",", true);
       rb.shards = lst.toArray(new String[lst.size()]);

Modified: lucene/solr/branches/cloud/src/java/org/apache/solr/handler/component/QueryElevationComponent.java
URL: http://svn.apache.org/viewvc/lucene/solr/branches/cloud/src/java/org/apache/solr/handler/component/QueryElevationComponent.java?rev=894959&r1=894958&r2=894959&view=diff
==============================================================================
--- lucene/solr/branches/cloud/src/java/org/apache/solr/handler/component/QueryElevationComponent.java (original)
+++ lucene/solr/branches/cloud/src/java/org/apache/solr/handler/component/QueryElevationComponent.java Fri Jan  1 00:17:31 2010
@@ -44,6 +44,7 @@
 import org.apache.lucene.index.Term;
 import org.apache.lucene.search.*;
 import org.apache.lucene.util.StringHelper;
+import org.apache.solr.cloud.ZooKeeperController;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.params.SolrParams;
 import org.apache.solr.common.util.DOMUtil;
@@ -169,20 +170,29 @@
               "QueryElevationComponent must specify argument: '"+CONFIG_FILE
               +"' -- path to elevate.xml" );
         }
-        //nocommit: add resourceloader.exists? needs to be handled with zookeeper
-        File fC = new File( core.getResourceLoader().getConfigDir(), f );
-        File fD = new File( core.getDataDir(), f );
-        if( fC.exists() == fD.exists() ) {
-          throw new SolrException( SolrException.ErrorCode.SERVER_ERROR,
-              "QueryElevationComponent missing config file: '"+f + "\n"
-              +"either: "+fC.getAbsolutePath() + " or " + fD.getAbsolutePath() + " must exist, but not both." );
-        }
-        if( fC.exists() ) {
-          log.info( "Loading QueryElevation from: "+fC.getAbsolutePath() );
-          Config cfg = new Config( core.getResourceLoader(), f );
-          elevationCache.put(null, loadElevationMap( cfg ));
+        boolean exists = false;
+        //nocommit: double check this how we want to handle this
+        // check if using ZooKeeper
+        ZooKeeperController zooKeeperController = core.getCoreDescriptor().getCoreContainer().getZooKeeperController();
+        if(zooKeeperController != null) {
+          exists = zooKeeperController.configFileExists(f);
+        } else {
+          File fC = new File( core.getResourceLoader().getConfigDir(), f );
+          File fD = new File( core.getDataDir(), f );
+          if( fC.exists() == fD.exists() ) {
+            throw new SolrException( SolrException.ErrorCode.SERVER_ERROR,
+                "QueryElevationComponent missing config file: '"+f + "\n"
+                +"either: "+fC.getAbsolutePath() + " or " + fD.getAbsolutePath() + " must exist, but not both." );
+          }
+          if( fC.exists() ) {
+            exists = true;
+            log.info( "Loading QueryElevation from: "+ fC.getAbsolutePath() );
+            Config cfg = new Config( core.getResourceLoader(), f );
+            elevationCache.put(null, loadElevationMap( cfg ));
+          } 
         }
-        else {
+        
+        if (!exists){
           // preload the first data
           RefCounted<SolrIndexSearcher> searchHolder = null;
           try {
@@ -193,6 +203,7 @@
             if (searchHolder != null) searchHolder.decref();
           }
         }
+      
       }
     }
     catch( Exception ex ) {

Modified: lucene/solr/branches/cloud/src/java/org/apache/solr/util/AbstractSolrTestCase.java
URL: http://svn.apache.org/viewvc/lucene/solr/branches/cloud/src/java/org/apache/solr/util/AbstractSolrTestCase.java?rev=894959&r1=894958&r2=894959&view=diff
==============================================================================
--- lucene/solr/branches/cloud/src/java/org/apache/solr/util/AbstractSolrTestCase.java (original)
+++ lucene/solr/branches/cloud/src/java/org/apache/solr/util/AbstractSolrTestCase.java Fri Jan  1 00:17:31 2010
@@ -110,7 +110,7 @@
     String configFile = getSolrConfigFile();
     if (configFile != null) {
 
-      solrConfig = h.createConfig(getSolrConfigFile());
+      solrConfig = h.createConfig(configFile);
       h = new TestHarness( dataDir.getAbsolutePath(),
               solrConfig,
               getSchemaFile());

Modified: lucene/solr/branches/cloud/src/test/org/apache/solr/BaseDistributedSearchTestCase.java
URL: http://svn.apache.org/viewvc/lucene/solr/branches/cloud/src/test/org/apache/solr/BaseDistributedSearchTestCase.java?rev=894959&r1=894958&r2=894959&view=diff
==============================================================================
--- lucene/solr/branches/cloud/src/test/org/apache/solr/BaseDistributedSearchTestCase.java (original)
+++ lucene/solr/branches/cloud/src/test/org/apache/solr/BaseDistributedSearchTestCase.java Fri Jan  1 00:17:31 2010
@@ -1,6 +1,19 @@
 package org.apache.solr;
 
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+
 import junit.framework.TestCase;
+
 import org.apache.solr.client.solrj.SolrServer;
 import org.apache.solr.client.solrj.SolrServerException;
 import org.apache.solr.client.solrj.embedded.JettySolrRunner;
@@ -14,10 +27,6 @@
 import org.apache.solr.schema.TrieDateField;
 import org.apache.solr.util.AbstractSolrTestCase;
 
-import java.io.File;
-import java.io.IOException;
-import java.util.*;
-
 /**
  * Helper base class for distributed search test cases
  *
@@ -33,6 +42,7 @@
   protected String shards;
   protected File testDir;
   protected SolrServer controlClient;
+  protected int portSeed;
 
   // to stress with higher thread counts and requests, make sure the junit
   // xml formatter is not being used (all output will be buffered before
@@ -126,7 +136,7 @@
     super.tearDown();
   }
 
-  private void createServers(int numShards) throws Exception {
+  protected void createServers(int numShards) throws Exception {
     controlJetty = createJetty(testDir, "control");
     controlClient = createNewSolrServer(controlJetty.getLocalPort());
 
@@ -148,18 +158,38 @@
     clients.clear();
     jettys.clear();
   }
+  
+  public JettySolrRunner createJetty(File baseDir, String dataDirName) throws Exception {
+    return createJetty(baseDir, dataDirName, null, null);
+  }
 
-  public static JettySolrRunner createJetty(File baseDir, String dataDirName) throws Exception {
+  public JettySolrRunner createJetty(File baseDir, String dataDirName, String shardList) throws Exception {
+    return createJetty(baseDir, dataDirName, shardList, null);
+  }
+  
+  public JettySolrRunner createJetty(File baseDir, String dataDirName, String shardList, String solrConfigOverride) throws Exception {
     File subDir = new File(baseDir, dataDirName);
     subDir.mkdirs();
     System.setProperty("solr.data.dir", subDir.toString());
-
-    JettySolrRunner jetty = new JettySolrRunner("/solr", 0);
-
+    int port = getPort();
+    JettySolrRunner jetty = new JettySolrRunner("/solr", port, solrConfigOverride);
+    if(port != 0) {
+      jetty.setPortOverride(port);
+    }
+    if(shardList != null) {
+      jetty.setTestShardListOverride(shardList);
+    }
     jetty.start();
     return jetty;
   }
 
+  private int getPort() {
+    if(portSeed != 0) {
+      return portSeed++;
+    }
+    return 0;
+  }
+  
   protected SolrServer createNewSolrServer(int port) {
     try {
       // setup the server...
@@ -241,7 +271,7 @@
     SolrServer client = clients.get(which);
     QueryResponse rsp = client.query(params);
 
-    compareResponses(rsp, controlRsp);
+    //compareResponses(rsp, controlRsp);
 
     if (stress > 0) {
       log.info("starting stress...");

Copied: lucene/solr/branches/cloud/src/test/org/apache/solr/cloud/AbstractDistributedZooKeeperTestCase.java (from r892824, lucene/solr/branches/cloud/src/test/org/apache/solr/AbstractDistributedZooKeeperTestCase.java)
URL: http://svn.apache.org/viewvc/lucene/solr/branches/cloud/src/test/org/apache/solr/cloud/AbstractDistributedZooKeeperTestCase.java?p2=lucene/solr/branches/cloud/src/test/org/apache/solr/cloud/AbstractDistributedZooKeeperTestCase.java&p1=lucene/solr/branches/cloud/src/test/org/apache/solr/AbstractDistributedZooKeeperTestCase.java&r1=892824&r2=894959&rev=894959&view=diff
==============================================================================
--- lucene/solr/branches/cloud/src/test/org/apache/solr/AbstractDistributedZooKeeperTestCase.java (original)
+++ lucene/solr/branches/cloud/src/test/org/apache/solr/cloud/AbstractDistributedZooKeeperTestCase.java Fri Jan  1 00:17:31 2010
@@ -1,15 +1,31 @@
-package org.apache.solr;
+package org.apache.solr.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.File;
-import java.io.IOException;
+import java.util.HashSet;
 
+import org.apache.solr.BaseDistributedSearchTestCase;
 import org.apache.solr.core.CoreContainer;
 import org.apache.solr.util.TestHarness;
-import org.apache.zookeeper.server.ServerConfig;
-import org.apache.zookeeper.server.ZooKeeperServerMain;
 
 public abstract class AbstractDistributedZooKeeperTestCase extends BaseDistributedSearchTestCase {
-  protected ZooKeeperServerMain zkServer = new ZooKeeperServerMain();
+  private ZooKeeperTestServer zkServer;
 
   protected File tmpDir = new File(System.getProperty("java.io.tmpdir")
       + System.getProperty("file.separator") + getClass().getName() + "-"
@@ -19,31 +35,15 @@
   public void setUp() throws Exception {
     // we don't call super.setUp
     log.info("####SETUP_START " + getName());
+    portSeed = 13000;
     
     System.setProperty("zkHost", AbstractZooKeeperTestCase.ZOO_KEEPER_HOST);
-    Thread zooThread = new Thread() {
-      @Override
-      public void run() {
-        ServerConfig config = new ServerConfig() {
-          {
-            this.clientPort = 2181;
-            this.dataDir = tmpDir.getAbsolutePath() + File.separator
-                + "zookeeper/server1/data";
-            this.dataLogDir = this.dataDir;
-          }
-        };
-
-        try {
-          zkServer.runFromConfig(config);
-        } catch (IOException e) {
-          throw new RuntimeException(e);
-        }
-
-      }
-    };
-    zooThread.setDaemon(true);
-    zooThread.start();
-    Thread.sleep(500); // pause for ZooKeeper to start
+    String zkDir = tmpDir.getAbsolutePath() + File.separator
+    + "zookeeper/server1/data";
+    zkServer = new ZooKeeperTestServer(zkDir);
+    zkServer.run();
+    
+    
     AbstractZooKeeperTestCase.buildZooKeeper(getSolrConfigFile(), getSchemaFile());
 
     dataDir = tmpDir;
@@ -56,6 +56,7 @@
     CoreContainer.Initializer init = new CoreContainer.Initializer() {
       {
         this.dataDir = AbstractDistributedZooKeeperTestCase.this.dataDir.getAbsolutePath();
+        this.solrConfigFilename = AbstractDistributedZooKeeperTestCase.this.getSolrConfigFilename();
       }
     };
 
@@ -70,8 +71,35 @@
     testDir.mkdirs();
     postSetUp();
   }
+  
+  protected String getSolrConfigFilename() {
+    return null;
+  }
+
+  public void testDistribSearch() throws Exception {
+    for (int nServers = 2; nServers < 3; nServers++) {
+      createServers(nServers);
+     
+      RandVal.uniqueValues = new HashSet(); //reset random values
+      doTest();
+      printeLayout();
+
+      destroyServers();
+    }
+  }
 
   public void tearDown() throws Exception {
+    printeLayout();
     super.tearDown();
   }
+  
+  private void printeLayout() throws Exception {
+    ZooKeeperReader zkReader = new ZooKeeperReader(
+        AbstractZooKeeperTestCase.ZOO_KEEPER_HOST.substring(0,
+            AbstractZooKeeperTestCase.ZOO_KEEPER_HOST.indexOf('/')),
+        AbstractZooKeeperTestCase.TIMEOUT);
+
+    zkReader.printLayoutToStdOut();
+    zkReader.close();
+  }
 }