You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by us...@apache.org on 2012/08/31 00:43:59 UTC

svn commit: r1379200 [10/11] - in /lucene/dev/branches/lucene3312: ./ dev-tools/ dev-tools/eclipse/ dev-tools/idea/.idea/libraries/ dev-tools/maven/ dev-tools/maven/lucene/core/ dev-tools/maven/lucene/test-framework/ dev-tools/scripts/ lucene/ lucene/a...

Modified: lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/cloud/ElectionContext.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/cloud/ElectionContext.java?rev=1379200&r1=1379199&r2=1379200&view=diff
==============================================================================
--- lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/cloud/ElectionContext.java (original)
+++ lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/cloud/ElectionContext.java Thu Aug 30 22:43:41 2012
@@ -2,6 +2,8 @@ package org.apache.solr.cloud;
 
 import java.io.IOException;
 import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
 
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.SolrException.ErrorCode;
@@ -15,7 +17,6 @@ import org.apache.solr.core.CoreContaine
 import org.apache.solr.core.SolrCore;
 import org.apache.zookeeper.CreateMode;
 import org.apache.zookeeper.KeeperException;
-import org.apache.zookeeper.KeeperException.NodeExistsException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -82,31 +83,26 @@ class ShardLeaderElectionContextBase ext
   }
 
   @Override
-  void runLeaderProcess(boolean weAreReplacement)
-      throws KeeperException, InterruptedException, IOException {
-
-    try {
-      zkClient.makePath(leaderPath,
-          leaderProps == null ? null : ZkStateReader.toJSON(leaderProps),
-          CreateMode.EPHEMERAL, true);
-    } catch (NodeExistsException e) {
-      // if a previous leader ephemeral still exists for some reason, try and
-      // remove it
-      zkClient.delete(leaderPath, -1, true);
-      zkClient.makePath(leaderPath,
-          leaderProps == null ? null : ZkStateReader.toJSON(leaderProps),
-          CreateMode.EPHEMERAL, true);
-    }
+  void runLeaderProcess(boolean weAreReplacement) throws KeeperException,
+      InterruptedException, IOException {
+    // this pause is important (and seems to work also at 100ms to 1 second in
+    // many cases),
+    // but I don't know why yet :*( - it must come before this publish call
+    // and can happen at the start of leader election process even
+    Thread.sleep(500);
+    
+    zkClient.makePath(leaderPath, ZkStateReader.toJSON(leaderProps),
+        CreateMode.EPHEMERAL, true);
     
-    // TODO: above we make it looks like leaderProps could be true, but here
-    // you would get an NPE if it was.
-    ZkNodeProps m = new ZkNodeProps(Overseer.QUEUE_OPERATION,
-        "leader", ZkStateReader.SHARD_ID_PROP, shardId,
-        ZkStateReader.COLLECTION_PROP, collection, ZkStateReader.BASE_URL_PROP,
-        leaderProps.getProperties().get(ZkStateReader.BASE_URL_PROP),
-        ZkStateReader.CORE_NAME_PROP, leaderProps.getProperties().get(ZkStateReader.CORE_NAME_PROP));
+    ZkNodeProps m = new ZkNodeProps(Overseer.QUEUE_OPERATION, "leader",
+        ZkStateReader.SHARD_ID_PROP, shardId, ZkStateReader.COLLECTION_PROP,
+        collection, ZkStateReader.BASE_URL_PROP, leaderProps.getProperties()
+            .get(ZkStateReader.BASE_URL_PROP), ZkStateReader.CORE_NAME_PROP,
+        leaderProps.getProperties().get(ZkStateReader.CORE_NAME_PROP),
+        ZkStateReader.STATE_PROP, ZkStateReader.ACTIVE);
     Overseer.getInQueue(zkClient).offer(ZkStateReader.toJSON(m));
-  } 
+    
+  }
 
 }
 
@@ -117,66 +113,182 @@ final class ShardLeaderElectionContext e
   private ZkController zkController;
   private CoreContainer cc;
   private SyncStrategy syncStrategy = new SyncStrategy();
+
+  private boolean afterExpiration;
   
   public ShardLeaderElectionContext(LeaderElector leaderElector, 
       final String shardId, final String collection,
-      final String shardZkNodeName, ZkNodeProps props, ZkController zkController, CoreContainer cc) {
+      final String shardZkNodeName, ZkNodeProps props, ZkController zkController, CoreContainer cc, boolean afterExpiration) {
     super(leaderElector, shardId, collection, shardZkNodeName, props,
         zkController.getZkStateReader());
     this.zkController = zkController;
     this.cc = cc;
+    this.afterExpiration = afterExpiration;
   }
   
   @Override
-  void runLeaderProcess(boolean weAreReplacement)
-      throws KeeperException, InterruptedException, IOException {
-    if (cc != null) {
-      String coreName = leaderProps.get(ZkStateReader.CORE_NAME_PROP);
-      SolrCore core = null;
+  void runLeaderProcess(boolean weAreReplacement) throws KeeperException,
+      InterruptedException, IOException {
+    String coreName = leaderProps.get(ZkStateReader.CORE_NAME_PROP);
+    
+    // clear the leader in clusterstate
+    ZkNodeProps m = new ZkNodeProps(Overseer.QUEUE_OPERATION, "leader",
+        ZkStateReader.SHARD_ID_PROP, shardId, ZkStateReader.COLLECTION_PROP,
+        collection);
+    Overseer.getInQueue(zkClient).offer(ZkStateReader.toJSON(m));
+    
+    waitForReplicasToComeUp(weAreReplacement);
+    
+    // wait for local leader state to clear...
+    // int tries = 0;
+    // while (zkController.getClusterState().getLeader(collection, shardId) !=
+    // null) {
+    // System.out.println("leader still shown " + tries + " " +
+    // zkController.getClusterState().getLeader(collection, shardId));
+    // Thread.sleep(1000);
+    // tries++;
+    // if (tries == 30) {
+    // break;
+    // }
+    // }
+    // Thread.sleep(1000);
+    
+    SolrCore core = null;
+    try {
+      
+      core = cc.getCore(coreName);
+      
+      if (core == null) {
+        cancelElection();
+        throw new SolrException(ErrorCode.SERVER_ERROR,
+            "Fatal Error, SolrCore not found:" + coreName + " in "
+                + cc.getCoreNames());
+      }
+      
+      // should I be leader?
+      if (weAreReplacement && !shouldIBeLeader(leaderProps, core)) {
+        // System.out.println("there is a better leader candidate it appears");
+        rejoinLeaderElection(leaderSeqPath, core);
+        return;
+      }
+      
+      if (weAreReplacement) {
+        log.info("I may be the new leader - try and sync");
+        // we are going to attempt to be the leader
+        // first cancel any current recovery
+        core.getUpdateHandler().getSolrCoreState().cancelRecovery();
+        boolean success = syncStrategy.sync(zkController, core, leaderProps);
+        // solrcloud_debug
+        // try {
+        // RefCounted<SolrIndexSearcher> searchHolder =
+        // core.getNewestSearcher(false);
+        // SolrIndexSearcher searcher = searchHolder.get();
+        // try {
+        // System.out.println(core.getCoreDescriptor().getCoreContainer().getZkController().getNodeName()
+        // + " synched "
+        // + searcher.search(new MatchAllDocsQuery(), 1).totalHits);
+        // } finally {
+        // searchHolder.decref();
+        // }
+        // } catch (Exception e) {
+        //
+        // }
+        if (!success && anyoneElseActive()) {
+          rejoinLeaderElection(leaderSeqPath, core);
+          return;
+        }
+      }
+      
+      log.info("I am the new leader: "
+          + ZkCoreNodeProps.getCoreUrl(leaderProps));
+      
+    } finally {
+      if (core != null) {
+        core.close();
+      }
+    }
+    
+    try {
+      super.runLeaderProcess(weAreReplacement);
+    } catch (Throwable t) {
+      cancelElection();
       try {
-     
         core = cc.getCore(coreName);
-
-        if (core == null) {
-          cancelElection();
-          throw new SolrException(ErrorCode.SERVER_ERROR, "Fatal Error, SolrCore not found:" + coreName + " in " + cc.getCoreNames());
+        core.getCoreDescriptor().getCloudDescriptor().isLeader = false;
+        if (!cc.isShutDown()) {
+          // we could not publish ourselves as leader - rejoin election
+          rejoinLeaderElection(coreName, core);
         }
-        // should I be leader?
-        if (weAreReplacement && !shouldIBeLeader(leaderProps)) {
-          // System.out.println("there is a better leader candidate it appears");
-          rejoinLeaderElection(leaderSeqPath, core);
-          return;
+      } finally {
+        if (core != null) {
+          core.close();
         }
+      }
+      
+    }
+    
+    try {
+      core = cc.getCore(coreName);
+      // we do this after the above super. call so that we don't
+      // briefly think we are the leader and then end up not being
+      // able to publish that we are the leader.
+      core.getCoreDescriptor().getCloudDescriptor().isLeader = true;
+    } finally {
+      if (core != null) {
+        core.close();
+      }
+    }
+    
+  }
 
-        if (weAreReplacement) {
-          if (zkClient.exists(leaderPath, true)) {
-            zkClient.delete(leaderPath, -1, true);
+  private void waitForReplicasToComeUp(boolean weAreReplacement)
+      throws InterruptedException {
+    int retries = 300; // ~ 5 min
+    boolean tryAgain = true;
+    Slice slices = zkController.getClusterState().getSlice(collection, shardId);
+    log.info("Running the leader process. afterExperiation=" + afterExpiration);
+    while (tryAgain || slices == null) {
+      
+      // wait for everyone to be up
+      if (slices != null) {
+        Map<String,ZkNodeProps> shards = slices.getShards();
+        Set<Entry<String,ZkNodeProps>> entrySet = shards.entrySet();
+        int found = 0;
+        tryAgain = false;
+        for (Entry<String,ZkNodeProps> entry : entrySet) {
+          ZkCoreNodeProps props = new ZkCoreNodeProps(entry.getValue());
+          if (props.getState().equals(ZkStateReader.ACTIVE)
+              && zkController.getClusterState().liveNodesContain(
+                  props.getNodeName())) {
+            found++;
           }
-          log.info("I may be the new leader - try and sync");
-          // we are going to attempt to be the leader
-          // first cancel any current recovery
-          core.getUpdateHandler().getSolrCoreState().cancelRecovery();
-          boolean success = syncStrategy.sync(zkController, core, leaderProps);
-          if (!success && anyoneElseActive()) {
-            rejoinLeaderElection(leaderSeqPath, core);
-            return;
-          } 
         }
-        log.info("I am the new leader: " + ZkCoreNodeProps.getCoreUrl(leaderProps));
         
-        // If I am going to be the leader I have to be active
-        core.getUpdateHandler().getSolrCoreState().cancelRecovery();
-        zkController.publish(core.getCoreDescriptor(), ZkStateReader.ACTIVE);
+        // on startup and after connection timeout, wait for all known shards
+        if ((afterExpiration || !weAreReplacement)
+            && found >= slices.getShards().size()) {
+          log.info("Enough replicas found to continue.");
+          tryAgain = false;
+        } else if (!afterExpiration && found >= slices.getShards().size() - 1) {
+          // a previous leader went down - wait for one less than the total
+          // known shards
+          log.info("Enough replicas found to continue.");
+          tryAgain = false;
+        } else {
+          log.info("Waiting until we see more replicas up");
+        }
         
-      } finally {
-        if (core != null ) {
-          core.close();
+        retries--;
+        if (retries == 0) {
+          log.info("Was waiting for replicas to come up, but they are taking too long - assuming they won't come back till later");
+          break;
         }
       }
-      
+      if (tryAgain) {
+        Thread.sleep(1000);
+        slices = zkController.getClusterState().getSlice(collection, shardId);
+      }
     }
-    
-    super.runLeaderProcess(weAreReplacement);
   }
 
   private void rejoinLeaderElection(String leaderSeqPath, SolrCore core)
@@ -195,7 +307,8 @@ final class ShardLeaderElectionContext e
     leaderElector.joinElection(this);
   }
   
-  private boolean shouldIBeLeader(ZkNodeProps leaderProps) {
+  private boolean shouldIBeLeader(ZkNodeProps leaderProps, SolrCore core) {
+    log.info("Checking if I should try and be the leader.");
     ClusterState clusterState = zkController.getZkStateReader().getClusterState();
     Map<String,Slice> slices = clusterState.getSlices(this.collection);
     Slice slice = slices.get(shardId);
@@ -210,6 +323,7 @@ final class ShardLeaderElectionContext e
           && clusterState.liveNodesContain(shard.getValue().get(
               ZkStateReader.NODE_NAME_PROP))) {
           // we are alive
+          log.info("I am Active and live, it's okay to be the leader.");
           return true;
         }
       }
@@ -222,7 +336,19 @@ final class ShardLeaderElectionContext e
         foundSomeoneElseActive = true;
       }
     }
-    
+    if (!foundSomeoneElseActive) {
+      log.info("I am not Active but no one else is either, it's okay to be the leader");
+      try {
+        zkController.publish(core.getCoreDescriptor(), ZkStateReader.ACTIVE);
+      } catch (KeeperException e) {
+        throw new RuntimeException(e);
+      } catch (InterruptedException e) {
+        Thread.currentThread().interrupt();
+        throw new RuntimeException(e);
+      }
+    } else {
+      log.info("I am not Active and someone else appears to be a better leader candidate.");
+    }
     return !foundSomeoneElseActive;
   }
   
@@ -261,24 +387,16 @@ final class OverseerElectionContext exte
   }
 
   @Override
-  void runLeaderProcess(boolean weAreReplacement) throws KeeperException, InterruptedException {
+  void runLeaderProcess(boolean weAreReplacement) throws KeeperException,
+      InterruptedException {
     
-    final String id = leaderSeqPath.substring(leaderSeqPath.lastIndexOf("/")+1);
+    final String id = leaderSeqPath
+        .substring(leaderSeqPath.lastIndexOf("/") + 1);
     ZkNodeProps myProps = new ZkNodeProps("id", id);
-
-    try {
-      zkClient.makePath(leaderPath,
-          ZkStateReader.toJSON(myProps),
-          CreateMode.EPHEMERAL, true);
-    } catch (NodeExistsException e) {
-      // if a previous leader ephemeral still exists for some reason, try and
-      // remove it
-      zkClient.delete(leaderPath, -1, true);
-      zkClient.makePath(leaderPath,
-          ZkStateReader.toJSON(myProps),
-          CreateMode.EPHEMERAL, true);
-    }
-  
+    
+    zkClient.makePath(leaderPath, ZkStateReader.toJSON(myProps),
+        CreateMode.EPHEMERAL, true);
+    
     overseer.start(id);
   }
   

Modified: lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/cloud/LeaderElector.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/cloud/LeaderElector.java?rev=1379200&r1=1379199&r2=1379200&view=diff
==============================================================================
--- lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/cloud/LeaderElector.java (original)
+++ lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/cloud/LeaderElector.java Thu Aug 30 22:43:41 2012
@@ -93,6 +93,13 @@ public  class LeaderElector {
     sortSeqs(seqs);
     List<Integer> intSeqs = getSeqs(seqs);
     if (seq <= intSeqs.get(0)) {
+      // first we delete the node advertising the old leader in case the ephem is still there
+      try {
+        zkClient.delete(context.leaderPath, -1, true);
+      } catch(Exception e) {
+        // fine
+      }
+
       runIamLeaderProcess(context, replacement);
     } else {
       // I am not the leader - watch the node below me
@@ -138,6 +145,7 @@ public  class LeaderElector {
       } catch (KeeperException.SessionExpiredException e) {
         throw e;
       } catch (KeeperException e) {
+        SolrException.log(log, "Failed setting watch", e);
         // we couldn't set our watch - the node before us may already be down?
         // we need to check if we are the leader again
         checkIfIamLeader(seq, context, true);

Modified: lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/cloud/Overseer.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/cloud/Overseer.java?rev=1379200&r1=1379199&r2=1379200&view=diff
==============================================================================
--- lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/cloud/Overseer.java (original)
+++ lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/cloud/Overseer.java Thu Aug 30 22:43:41 2012
@@ -126,8 +126,8 @@ public class Overseer {
                 final String operation = message.get(QUEUE_OPERATION);
                 
                 clusterState = processMessage(clusterState, message, operation);
-                byte[] processed = stateUpdateQueue.remove();
-                workQueue.offer(processed);
+                workQueue.offer(head);
+                stateUpdateQueue.remove();
                 head = stateUpdateQueue.peek();
               }
               zkClient.setData(ZkStateReader.CLUSTER_STATE,
@@ -166,17 +166,19 @@ public class Overseer {
       } else if (DELETECORE.equals(operation)) {
         clusterState = removeCore(clusterState, message);
       } else if (ZkStateReader.LEADER_PROP.equals(operation)) {
+
         StringBuilder sb = new StringBuilder();
         String baseUrl = message.get(ZkStateReader.BASE_URL_PROP);
         String coreName = message.get(ZkStateReader.CORE_NAME_PROP);
         sb.append(baseUrl);
-        if (!baseUrl.endsWith("/")) sb.append("/");
+        if (baseUrl != null && !baseUrl.endsWith("/")) sb.append("/");
         sb.append(coreName == null ? "" : coreName);
-        if (!(sb.substring(sb.length() - 1).equals("/"))) sb
-            .append("/");
+        if (!(sb.substring(sb.length() - 1).equals("/"))) sb.append("/");
         clusterState = setShardLeader(clusterState,
             message.get(ZkStateReader.COLLECTION_PROP),
-            message.get(ZkStateReader.SHARD_ID_PROP), sb.toString());
+            message.get(ZkStateReader.SHARD_ID_PROP),
+            sb.length() > 0 ? sb.toString() : null);
+
       } else {
         throw new RuntimeException("unknown operation:" + operation
             + " contents:" + message.getProperties());

Modified: lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/cloud/RecoveryStrategy.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/cloud/RecoveryStrategy.java?rev=1379200&r1=1379199&r2=1379200&view=diff
==============================================================================
--- lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/cloud/RecoveryStrategy.java (original)
+++ lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/cloud/RecoveryStrategy.java Thu Aug 30 22:43:41 2012
@@ -185,6 +185,7 @@ public class RecoveryStrategy extends Th
     prepCmd.setCoreNodeName(coreZkNodeName);
     prepCmd.setState(ZkStateReader.RECOVERING);
     prepCmd.setCheckLive(true);
+    prepCmd.setOnlyIfLeader(true);
     prepCmd.setPauseFor(6000);
     
     server.request(prepCmd);
@@ -239,6 +240,7 @@ public class RecoveryStrategy extends Th
       return;
     }
 
+    boolean firstTime = true;
 
     List<Long> recentVersions;
     UpdateLog.RecentUpdates recentUpdates = null;
@@ -273,9 +275,6 @@ public class RecoveryStrategy extends Th
       log.info("###### startupVersions=" + startingVersions);
     }
 
-
-    boolean firstTime = true;
-
     if (recoveringAfterStartup) {
       // if we're recovering after startup (i.e. we have been down), then we need to know what the last versions were
       // when we went down.  We may have received updates since then.
@@ -305,7 +304,10 @@ public class RecoveryStrategy extends Th
         String ourUrl = ZkCoreNodeProps.getCoreUrl(baseUrl, coreName);
 
         boolean isLeader = leaderUrl.equals(ourUrl);
-        if (isLeader) {
+        if (isLeader && !cloudDesc.isLeader) {
+          throw new SolrException(ErrorCode.SERVER_ERROR, "Cloud state still says we are leader.");
+        }
+        if (cloudDesc.isLeader) {
           // we are now the leader - no one else must have been suitable
           log.warn("We have not yet recovered - but we are now the leader! core=" + coreName);
           log.info("Finished recovery process. core=" + coreName);
@@ -333,9 +335,6 @@ public class RecoveryStrategy extends Th
                 new ModifiableSolrParams());
             core.getUpdateHandler().commit(new CommitUpdateCommand(req, false));
             log.info("PeerSync Recovery was successful - registering as Active. core=" + coreName);
-            // System.out
-            // .println("Sync Recovery was successful - registering as Active "
-            // + zkController.getNodeName());
 
             // solrcloud_debug
             // try {

Modified: lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/cloud/SyncStrategy.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/cloud/SyncStrategy.java?rev=1379200&r1=1379199&r2=1379200&view=diff
==============================================================================
--- lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/cloud/SyncStrategy.java (original)
+++ lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/cloud/SyncStrategy.java Thu Aug 30 22:43:41 2012
@@ -108,7 +108,8 @@ public class SyncStrategy {
       if (!success
           && !areAnyOtherReplicasActive(zkController, leaderProps, collection,
               shardId)) {
-        log.info("Sync was not a success but no on else i active! I am the leader");
+        log.info("Sync was not a success but no one else is active! I am the leader");
+        zkController.publish(core.getCoreDescriptor(), ZkStateReader.ACTIVE);
         success = true;
       }
       
@@ -224,14 +225,14 @@ public class SyncStrategy {
            
            requestRecovery(((ShardCoreRequest)srsp.getShardRequest()).baseUrl, ((ShardCoreRequest)srsp.getShardRequest()).coreName);
 
-         } catch (Exception e) {
-           SolrException.log(log, ZkCoreNodeProps.getCoreUrl(leaderProps) + ": Could not tell a replica to recover", e);
+         } catch (Throwable t) {
+           SolrException.log(log, ZkCoreNodeProps.getCoreUrl(leaderProps) + ": Could not tell a replica to recover", t);
          }
       } else {
         log.info(ZkCoreNodeProps.getCoreUrl(leaderProps) + ": " + " sync completed with " + srsp.getShardAddress());
       }
+      
     }
-    
 
   }
   

Modified: lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/cloud/ZkController.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/cloud/ZkController.java?rev=1379200&r1=1379199&r2=1379200&view=diff
==============================================================================
--- lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/cloud/ZkController.java (original)
+++ lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/cloud/ZkController.java Thu Aug 30 22:43:41 2012
@@ -41,6 +41,7 @@ import org.apache.solr.common.SolrExcept
 import org.apache.solr.common.cloud.ClusterState;
 import org.apache.solr.common.cloud.OnReconnect;
 import org.apache.solr.common.cloud.SolrZkClient;
+import org.apache.solr.common.cloud.ZkClientConnectionStrategy;
 import org.apache.solr.common.cloud.ZkCmdExecutor;
 import org.apache.solr.common.cloud.ZkCoreNodeProps;
 import org.apache.solr.common.cloud.ZkNodeProps;
@@ -112,8 +113,12 @@ public final class ZkController {
   private final String nodeName;           // example: 127.0.0.1:54065_solr
   private final String baseURL;            // example: http://127.0.0.1:54065/solr
 
+
   private LeaderElector overseerElector;
+  
 
+  // for now, this can be null in tests, in which case recovery will be inactive, and other features
+  // may accept defaults or use mocks rather than pulling things from a CoreContainer
   private CoreContainer cc;
 
   protected volatile Overseer overseer;
@@ -181,7 +186,11 @@ public final class ZkController {
                   // TODO: we need to think carefully about what happens when it was
                   // a leader that was expired - as well as what to do about leaders/overseers
                   // with connection loss
-                  register(descriptor.getName(), descriptor, true);
+                  try {
+                    register(descriptor.getName(), descriptor, true, true);
+                  } catch (Throwable t) {
+                    SolrException.log(log, "Error registering SolrCore", t);
+                  }
                 }
               }
   
@@ -200,6 +209,45 @@ public final class ZkController {
 
  
         });
+    
+    zkClient.getZkClientConnectionStrategy().addDisconnectedListener(new ZkClientConnectionStrategy.DisconnectedListener() {
+      
+      @Override
+      public void disconnected() {
+        List<CoreDescriptor> descriptors = registerOnReconnect.getCurrentDescriptors();
+        // re register all descriptors
+        if (descriptors  != null) {
+          for (CoreDescriptor descriptor : descriptors) {
+            descriptor.getCloudDescriptor().isLeader = false;
+          }
+        }
+      }
+    });
+    
+    zkClient.getZkClientConnectionStrategy().addConnectedListener(new ZkClientConnectionStrategy.ConnectedListener() {
+      
+      @Override
+      public void connected() {
+        List<CoreDescriptor> descriptors = registerOnReconnect.getCurrentDescriptors();
+        if (descriptors  != null) {
+          for (CoreDescriptor descriptor : descriptors) {
+            CloudDescriptor cloudDesc = descriptor.getCloudDescriptor();
+            String leaderUrl;
+            try {
+              leaderUrl = getLeaderProps(cloudDesc.getCollectionName(), cloudDesc.getShardId())
+                  .getCoreUrl();
+            } catch (InterruptedException e) {
+              throw new RuntimeException();
+            }
+            String ourUrl = ZkCoreNodeProps.getCoreUrl(getBaseUrl(), descriptor.getName());
+            boolean isLeader = leaderUrl.equals(ourUrl);
+            log.info("SolrCore connected to ZooKeeper - we are " + ourUrl + " and leader is " + leaderUrl);
+            cloudDesc.isLeader = isLeader;
+          }
+        }
+      }
+    });
+    
     this.overseerJobQueue = Overseer.getInQueue(zkClient);
     this.overseerCollectionQueue = Overseer.getCollectionQueue(zkClient);
     cmdExecutor = new ZkCmdExecutor();
@@ -468,7 +516,7 @@ public final class ZkController {
    * @throws Exception
    */
   public String register(String coreName, final CoreDescriptor desc) throws Exception {  
-    return register(coreName, desc, false);
+    return register(coreName, desc, false, false);
   }
   
 
@@ -478,10 +526,11 @@ public final class ZkController {
    * @param coreName
    * @param desc
    * @param recoverReloadedCores
+   * @param afterExpiration
    * @return the shardId for the SolrCore
    * @throws Exception
    */
-  public String register(String coreName, final CoreDescriptor desc, boolean recoverReloadedCores) throws Exception {  
+  public String register(String coreName, final CoreDescriptor desc, boolean recoverReloadedCores, boolean afterExpiration) throws Exception {  
     final String baseUrl = getBaseUrl();
     
     final CloudDescriptor cloudDesc = desc.getCloudDescriptor();
@@ -506,7 +555,7 @@ public final class ZkController {
     ZkNodeProps leaderProps = new ZkNodeProps(props);
     
     try {
-      joinElection(desc);
+      joinElection(desc, afterExpiration);
     } catch (InterruptedException e) {
       // Restore the interrupted status
       Thread.currentThread().interrupt();
@@ -517,25 +566,7 @@ public final class ZkController {
       throw new ZooKeeperException(SolrException.ErrorCode.SERVER_ERROR, "", e);
     }
     
-    // rather than look in the cluster state file, we go straight to the zknodes
-    // here, because on cluster restart there could be stale leader info in the
-    // cluster state node that won't be updated for a moment
-    String leaderUrl = getLeaderProps(collection, cloudDesc.getShardId()).getCoreUrl();
-    
-    // now wait until our currently cloud state contains the latest leader
-    String clusterStateLeader = zkStateReader.getLeaderUrl(collection, shardId, 30000);
-    int tries = 0;
-    while (!leaderUrl.equals(clusterStateLeader)) {
-      if (tries == 60) {
-        throw new SolrException(ErrorCode.SERVER_ERROR,
-            "There is conflicting information about the leader of shard: "
-                + cloudDesc.getShardId() + " our state says:" + clusterStateLeader + " but zookeeper says:" + leaderUrl);
-      }
-      Thread.sleep(1000);
-      tries++;
-      clusterStateLeader = zkStateReader.getLeaderUrl(collection, shardId, 30000);
-      leaderUrl = getLeaderProps(collection, cloudDesc.getShardId()).getCoreUrl();
-    }
+    String leaderUrl = getLeader(cloudDesc);
     
     String ourUrl = ZkCoreNodeProps.getCoreUrl(baseUrl, coreName);
     log.info("We are " + ourUrl + " and leader is " + leaderUrl);
@@ -568,8 +599,7 @@ public final class ZkController {
         } else {
           log.info("No LogReplay needed for core="+core.getName() + " baseURL=" + baseUrl);
         }
-      }
-      
+      }      
       boolean didRecovery = checkRecovery(coreName, desc, recoverReloadedCores, isLeader, cloudDesc,
           collection, coreZkNodeName, shardId, leaderProps, core, cc);
       if (!didRecovery) {
@@ -580,12 +610,52 @@ public final class ZkController {
         core.close();
       }
     }
+
     
     // make sure we have an update cluster state right away
     zkStateReader.updateClusterState(true);
 
     return shardId;
   }
+
+  private String getLeader(final CloudDescriptor cloudDesc) {
+    
+    String collection = cloudDesc.getCollectionName();
+    String shardId = cloudDesc.getShardId();
+    // rather than look in the cluster state file, we go straight to the zknodes
+    // here, because on cluster restart there could be stale leader info in the
+    // cluster state node that won't be updated for a moment
+    String leaderUrl;
+    try {
+      leaderUrl = getLeaderProps(collection, cloudDesc.getShardId())
+          .getCoreUrl();
+      
+      // now wait until our currently cloud state contains the latest leader
+      String clusterStateLeader = zkStateReader.getLeaderUrl(collection,
+          shardId, 30000);
+      int tries = 0;
+      while (!leaderUrl.equals(clusterStateLeader)) {
+        if (tries == 60) {
+          throw new SolrException(ErrorCode.SERVER_ERROR,
+              "There is conflicting information about the leader of shard: "
+                  + cloudDesc.getShardId() + " our state says:"
+                  + clusterStateLeader + " but zookeeper says:" + leaderUrl);
+        }
+        Thread.sleep(1000);
+        tries++;
+        clusterStateLeader = zkStateReader.getLeaderUrl(collection, shardId,
+            30000);
+        leaderUrl = getLeaderProps(collection, cloudDesc.getShardId())
+            .getCoreUrl();
+      }
+      
+    } catch (Exception e) {
+      log.error("Error getting leader from zk", e);
+      throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
+          "Error getting leader from zk", e);
+    } 
+    return leaderUrl;
+  }
   
   /**
    * Get leader props directly from zk nodes.
@@ -597,8 +667,9 @@ public final class ZkController {
    * @throws InterruptedException
    */
   private ZkCoreNodeProps getLeaderProps(final String collection,
-      final String slice) throws KeeperException, InterruptedException {
+      final String slice) throws InterruptedException {
     int iterCount = 60;
+    Exception exp = null;
     while (iterCount-- > 0) {
       try {
         byte[] data = zkClient.getData(
@@ -607,15 +678,21 @@ public final class ZkController {
         ZkCoreNodeProps leaderProps = new ZkCoreNodeProps(
             ZkNodeProps.load(data));
         return leaderProps;
-      } catch (NoNodeException e) {
+      } catch (InterruptedException e) {
+        throw e;
+      } catch (Exception e) {
+        exp = e;
         Thread.sleep(500);
       }
+      if (cc.isShutDown()) {
+        throw new RuntimeException("CoreContainer is shutdown");
+      }
     }
-    throw new RuntimeException("Could not get leader props");
+    throw new RuntimeException("Could not get leader props", exp);
   }
 
 
-  private void joinElection(CoreDescriptor cd) throws InterruptedException, KeeperException, IOException {
+  private void joinElection(CoreDescriptor cd, boolean afterExpiration) throws InterruptedException, KeeperException, IOException {
     
     String shardId = cd.getCloudDescriptor().getShardId();
     
@@ -631,7 +708,7 @@ public final class ZkController {
         .getCollectionName();
     
     ElectionContext context = new ShardLeaderElectionContext(leaderElector, shardId,
-        collection, coreZkNodeName, ourProps, this, cc);
+        collection, coreZkNodeName, ourProps, this, cc, afterExpiration);
 
     leaderElector.setup(context);
     electionContexts.put(coreZkNodeName, context);

Modified: lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/core/CoreContainer.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/core/CoreContainer.java?rev=1379200&r1=1379199&r2=1379200&view=diff
==============================================================================
--- lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/core/CoreContainer.java (original)
+++ lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/core/CoreContainer.java Thu Aug 30 22:43:41 2012
@@ -598,15 +598,16 @@ public class CoreContainer 
         }
         cores.clear();
       } finally {
+        if (shardHandlerFactory != null) {
+          shardHandlerFactory.close();
+        }
+        // we want to close zk stuff last
         if(zkController != null) {
           zkController.close();
         }
         if (zkServer != null) {
           zkServer.stop();
         }
-        if (shardHandlerFactory != null) {
-          shardHandlerFactory.close();
-        }
       }
     }
   }

Modified: lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/core/SolrCore.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/core/SolrCore.java?rev=1379200&r1=1379199&r2=1379200&view=diff
==============================================================================
--- lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/core/SolrCore.java (original)
+++ lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/core/SolrCore.java Thu Aug 30 22:43:41 2012
@@ -65,6 +65,7 @@ import org.apache.solr.search.ValueSourc
 import org.apache.solr.update.DirectUpdateHandler2;
 import org.apache.solr.update.SolrIndexWriter;
 import org.apache.solr.update.UpdateHandler;
+import org.apache.solr.update.VersionInfo;
 import org.apache.solr.update.processor.DistributedUpdateProcessorFactory;
 import org.apache.solr.update.processor.LogUpdateProcessorFactory;
 import org.apache.solr.update.processor.RunUpdateProcessorFactory;
@@ -593,6 +594,25 @@ public final class SolrCore implements S
       schema = new IndexSchema(config, IndexSchema.DEFAULT_SCHEMA_FILE, null);
     }
 
+    if (null != cd && null != cd.getCloudDescriptor()) {
+      // we are evidently running in cloud mode.  
+      //
+      // In cloud mode, version field is required for correct consistency
+      // ideally this check would be more fine grained, and individual features
+      // would assert it when they initialize, but DistribuedUpdateProcessor
+      // is currently a big ball of wax that does more then just distributing
+      // updates (ie: partial document updates), so it needs to work in no cloud
+      // mode as well, and can't assert version field support on init.
+
+      try {
+        Object ignored = VersionInfo.getAndCheckVersionField(schema);
+      } catch (SolrException e) {
+        throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
+                                "Schema will not work with SolrCloud mode: " +
+                                e.getMessage(), e);
+      }
+    }
+
     //Initialize JMX
     if (config.jmxConfig.enabled) {
       infoRegistry = new JmxMonitoredMap<String, SolrInfoMBean>(name, String.valueOf(this.hashCode()), config.jmxConfig);

Modified: lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/handler/admin/CoreAdminHandler.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/handler/admin/CoreAdminHandler.java?rev=1379200&r1=1379199&r2=1379200&view=diff
==============================================================================
--- lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/handler/admin/CoreAdminHandler.java (original)
+++ lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/handler/admin/CoreAdminHandler.java Thu Aug 30 22:43:41 2012
@@ -19,10 +19,13 @@ package org.apache.solr.handler.admin;
 
 import java.io.File;
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 import java.util.Properties;
 
@@ -36,6 +39,7 @@ import org.apache.solr.cloud.ZkControlle
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.SolrException.ErrorCode;
 import org.apache.solr.common.cloud.ClusterState;
+import org.apache.solr.common.cloud.HashPartitioner;
 import org.apache.solr.common.cloud.Slice;
 import org.apache.solr.common.cloud.ZkNodeProps;
 import org.apache.solr.common.cloud.ZkStateReader;
@@ -57,6 +61,7 @@ import org.apache.solr.request.SolrQuery
 import org.apache.solr.response.SolrQueryResponse;
 import org.apache.solr.search.SolrIndexSearcher;
 import org.apache.solr.update.MergeIndexesCommand;
+import org.apache.solr.update.SplitIndexCommand;
 import org.apache.solr.update.processor.UpdateRequestProcessor;
 import org.apache.solr.update.processor.UpdateRequestProcessorChain;
 import org.apache.solr.util.NumberUtils;
@@ -171,6 +176,11 @@ public class CoreAdminHandler extends Re
           break;
         }
 
+        case SPLIT: {
+          doPersist = this.handleSplitAction(req, rsp);
+          break;
+        }
+
         case PREPRECOVERY: {
           this.handleWaitForStateAction(req, rsp);
           break;
@@ -202,6 +212,62 @@ public class CoreAdminHandler extends Re
     rsp.setHttpCaching(false);
   }
 
+
+  protected boolean handleSplitAction(SolrQueryRequest adminReq, SolrQueryResponse rsp) throws IOException {
+    SolrParams params = adminReq.getParams();
+     // partitions=N    (split into N partitions, leaving it up to solr what the ranges are and where to put them)
+    // path - multiValued param, or comma separated param?  Only creates indexes, not cores
+
+    List<HashPartitioner.Range> ranges = null;
+    // boolean closeDirectories = true;
+    // DirectoryFactory dirFactory = null;
+
+
+    String cname = params.get(CoreAdminParams.CORE, "");
+    SolrCore core = coreContainer.getCore(cname);
+    SolrQueryRequest req = new LocalSolrQueryRequest(core, params);
+    try {
+
+      String[] pathsArr = params.getParams("path");
+      List<String> paths = null;
+
+      String rangesStr = params.get("ranges");    // ranges=a-b,c-d,e-f
+
+
+      // dirFactory = core.getDirectoryFactory();
+
+
+      if (pathsArr != null) {
+
+        paths = Arrays.asList(pathsArr);
+
+        if (rangesStr == null) {
+          HashPartitioner hp = new HashPartitioner();
+          // should this be static?
+          // TODO: use real range if we know it.  If we don't know it, we should prob
+          // split on every other doc rather than on a hash?
+          ranges = hp.partitionRange(pathsArr.length, Integer.MIN_VALUE, Integer.MAX_VALUE);
+        }
+
+      }
+
+
+      SplitIndexCommand cmd = new SplitIndexCommand(req, paths, ranges);
+      core.getUpdateHandler().split(cmd);
+
+    } catch (Exception e) {
+      log.error("ERROR executing split:", e);
+      throw new RuntimeException(e);
+
+    } finally {
+      if (req != null) req.close();
+      if (core != null) core.close();
+    }
+
+    return false;
+  }
+
+
   protected boolean handleMergeAction(SolrQueryRequest req, SolrQueryResponse rsp) throws IOException {
     SolrParams params = req.getParams();
     String cname = params.required().get(CoreAdminParams.CORE);
@@ -721,6 +787,21 @@ public class CoreAdminHandler extends Re
         props.put(ZkStateReader.NODE_NAME_PROP, zkController.getNodeName());
         
         boolean success = syncStrategy.sync(zkController, core, new ZkNodeProps(props));
+        // solrcloud_debug
+//         try {
+//         RefCounted<SolrIndexSearcher> searchHolder =
+//         core.getNewestSearcher(false);
+//         SolrIndexSearcher searcher = searchHolder.get();
+//         try {
+//         System.out.println(core.getCoreDescriptor().getCoreContainer().getZkController().getNodeName()
+//         + " synched "
+//         + searcher.search(new MatchAllDocsQuery(), 1).totalHits);
+//         } finally {
+//         searchHolder.decref();
+//         }
+//         } catch (Exception e) {
+//        
+//         }
         if (!success) {
           throw new SolrException(ErrorCode.SERVER_ERROR, "Sync Failed");
         }
@@ -750,8 +831,11 @@ public class CoreAdminHandler extends Re
     String coreNodeName = params.get("coreNodeName");
     String waitForState = params.get("state");
     Boolean checkLive = params.getBool("checkLive");
+    Boolean onlyIfLeader = params.getBool("onlyIfLeader");
     int pauseFor = params.getInt("pauseFor", 0);
     
+
+    
     String state = null;
     boolean live = false;
     int retry = 0;
@@ -764,6 +848,12 @@ public class CoreAdminHandler extends Re
               + cname);
         }
         if (core != null) {
+          if (onlyIfLeader != null && onlyIfLeader) {
+           if (!core.getCoreDescriptor().getCloudDescriptor().isLeader()) {
+             throw new SolrException(ErrorCode.BAD_REQUEST, "We are not the leader");
+           }
+          }
+          
           // wait until we are sure the recovering node is ready
           // to accept updates
           CloudDescriptor cloudDescriptor = core.getCoreDescriptor()

Modified: lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/logging/CircularList.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/logging/CircularList.java?rev=1379200&r1=1379199&r2=1379200&view=diff
==============================================================================
--- lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/logging/CircularList.java (original)
+++ lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/logging/CircularList.java Thu Aug 30 22:43:41 2012
@@ -1,154 +1,154 @@
-/*
- * 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.solr.logging;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-/**
- * FIFO Circular List.
- * 
- * Once the size is reached, it will overwrite previous entries
- * 
- */
-public class CircularList<T> implements Iterable<T>
-{
-  private T[] data;
-  private int head=0;
-  private int tail=0;
-  private int size=0;
-
-  @SuppressWarnings("unchecked")
-  public CircularList(int size) {
-    data = (T[])new Object[size];
-  }
-
-  @SuppressWarnings("unchecked")
-  public synchronized void resize(int newsize) {
-    if(newsize==this.size) return;
-    
-    T[] vals = (T[])new Object[newsize];
-    int i = 0;
-    if(newsize>size) {
-      for(i=0; i<size; i++) {
-        vals[i] = data[convert(i)];
-      }
-    }
-    else {
-      int off=size-newsize;
-      for(i=0; i<newsize; i++) {
-        vals[i] = data[convert(i+off)];
-      }
-    }
-    data = vals;
-    head = 0;
-    tail = i;
-  }
-
-  private int convert(int index) {
-    return (index + head) % data.length;
-  }
-
-  public boolean isEmpty() {
-    return head == tail; // or size == 0
-  }
-
-  public int size() {
-    return size;
-  }
-  
-  public int getBufferSize() {
-    return data.length;
-  }
-
-  private void checkIndex(int index) {
-    if (index >= size || index < 0)
-      throw new IndexOutOfBoundsException("Index: "+index+", Size: "+size);
-  }
-
-  public T get(int index) {
-    checkIndex(index);
-    return data[convert(index)];
-  }
-
-  public synchronized void add(T o) {
-    data[tail] = o;
-    tail = (tail+1)%data.length;
-    if( size == data.length ) {
-      head = (head+1)%data.length;
-    }
-    size++;
-    if( size > data.length ) {
-      size = data.length;
-    }
-  }
-
-  public synchronized void clear() {
-    for( int i=0; i<data.length; i++ ) {
-      data[i] = null;  // for GC
-    }
-    head = tail = size = 0;
-  }
-
-  public List<T> toList()
-  {
-    ArrayList<T> list = new ArrayList<T>( size );
-    for( int i=0; i<size; i++ ) {
-      list.add( data[convert(i)] );
-    }
-    return list;
-  }
-
-  @Override
-  public String toString()
-  {
-    StringBuilder str = new StringBuilder();
-    str.append( "[" );
-    for( int i=0; i<size; i++ ) {
-      if( i > 0 ) {
-        str.append( "," );
-      }
-      str.append( data[convert(i)] );
-    }
-    str.append( "]" );
-    return str.toString();
-  }
-
-  @Override
-  public Iterator<T> iterator() {
-    return new Iterator<T>() {
-      int idx = 0;
-
-      @Override
-      public boolean hasNext() {
-        return idx<size;
-      }
-
-      @Override
-      public T next() {
-        return get( idx++ );
-      }
-
-      @Override
-      public void remove() {
-        throw new UnsupportedOperationException();
-      }
-    };
-  }
-}
+/*
+ * 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.solr.logging;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * FIFO Circular List.
+ * 
+ * Once the size is reached, it will overwrite previous entries
+ * 
+ */
+public class CircularList<T> implements Iterable<T>
+{
+  private T[] data;
+  private int head=0;
+  private int tail=0;
+  private int size=0;
+
+  @SuppressWarnings("unchecked")
+  public CircularList(int size) {
+    data = (T[])new Object[size];
+  }
+
+  @SuppressWarnings("unchecked")
+  public synchronized void resize(int newsize) {
+    if(newsize==this.size) return;
+    
+    T[] vals = (T[])new Object[newsize];
+    int i = 0;
+    if(newsize>size) {
+      for(i=0; i<size; i++) {
+        vals[i] = data[convert(i)];
+      }
+    }
+    else {
+      int off=size-newsize;
+      for(i=0; i<newsize; i++) {
+        vals[i] = data[convert(i+off)];
+      }
+    }
+    data = vals;
+    head = 0;
+    tail = i;
+  }
+
+  private int convert(int index) {
+    return (index + head) % data.length;
+  }
+
+  public boolean isEmpty() {
+    return head == tail; // or size == 0
+  }
+
+  public int size() {
+    return size;
+  }
+  
+  public int getBufferSize() {
+    return data.length;
+  }
+
+  private void checkIndex(int index) {
+    if (index >= size || index < 0)
+      throw new IndexOutOfBoundsException("Index: "+index+", Size: "+size);
+  }
+
+  public T get(int index) {
+    checkIndex(index);
+    return data[convert(index)];
+  }
+
+  public synchronized void add(T o) {
+    data[tail] = o;
+    tail = (tail+1)%data.length;
+    if( size == data.length ) {
+      head = (head+1)%data.length;
+    }
+    size++;
+    if( size > data.length ) {
+      size = data.length;
+    }
+  }
+
+  public synchronized void clear() {
+    for( int i=0; i<data.length; i++ ) {
+      data[i] = null;  // for GC
+    }
+    head = tail = size = 0;
+  }
+
+  public List<T> toList()
+  {
+    ArrayList<T> list = new ArrayList<T>( size );
+    for( int i=0; i<size; i++ ) {
+      list.add( data[convert(i)] );
+    }
+    return list;
+  }
+
+  @Override
+  public String toString()
+  {
+    StringBuilder str = new StringBuilder();
+    str.append( "[" );
+    for( int i=0; i<size; i++ ) {
+      if( i > 0 ) {
+        str.append( "," );
+      }
+      str.append( data[convert(i)] );
+    }
+    str.append( "]" );
+    return str.toString();
+  }
+
+  @Override
+  public Iterator<T> iterator() {
+    return new Iterator<T>() {
+      int idx = 0;
+
+      @Override
+      public boolean hasNext() {
+        return idx<size;
+      }
+
+      @Override
+      public T next() {
+        return get( idx++ );
+      }
+
+      @Override
+      public void remove() {
+        throw new UnsupportedOperationException();
+      }
+    };
+  }
+}

Modified: lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/logging/LogWatcher.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/logging/LogWatcher.java?rev=1379200&r1=1379199&r2=1379200&view=diff
==============================================================================
--- lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/logging/LogWatcher.java (original)
+++ lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/logging/LogWatcher.java Thu Aug 30 22:43:41 2012
@@ -1,107 +1,107 @@
-/*
- * 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.solr.logging;
-
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import org.apache.solr.common.SolrDocument;
-import org.apache.solr.common.SolrDocumentList;
-import org.apache.solr.core.CoreContainer;
-
-/**
- * A Class to monitor Logging events and hold N events in memory
- * 
- * This is abstract so we can support both JUL and Log4j (and other logging platforms)
- */
-public abstract class LogWatcher<E> {
-  
-  protected CircularList<E> history;
-  protected long last = -1;
-  
-  /**
-   * @return The implementation name
-   */
-  public abstract String getName();
-  
-  /**
-   * @return The valid level names for this framework
-   */
-  public abstract List<String> getAllLevels();
-  
-  /**
-   * Sets the log level within this framework
-   */
-  public abstract void setLogLevel(String category, String level);
-  
-  /**
-   * @return all registered loggers
-   */
-  public abstract Collection<LoggerInfo> getAllLoggers();
-  
-  public abstract void setThreshold(String level);
-  public abstract String getThreshold();
-
-  public void add(E event, long timstamp) {
-    history.add(event);
-    last = timstamp;
-  }
-  
-  public long getLastEvent() {
-    return last;
-  }
-  
-  public int getHistorySize() {
-    return (history==null) ? -1 : history.getBufferSize();
-  }
-  
-  public SolrDocumentList getHistory(long since, AtomicBoolean found) {
-    if(history==null) {
-      return null;
-    }
-    
-    SolrDocumentList docs = new SolrDocumentList();
-    Iterator<E> iter = history.iterator();
-    while(iter.hasNext()) {
-      E e = iter.next();
-      long ts = getTimestamp(e);
-      if(ts == since) {
-        if(found!=null) {
-          found.set(true);
-        }
-      }
-      if(ts>since) {
-        docs.add(toSolrDocument(e));
-      }
-    }
-    docs.setNumFound(docs.size()); // make it not look too funny
-    return docs;
-  }
-  
-  public abstract long getTimestamp(E event);
-  public abstract SolrDocument toSolrDocument(E event);
-  
-  public abstract void registerListener(ListenerConfig cfg, CoreContainer container);
-
-  public void reset() {
-    history.clear();
-    last = -1;
-  }
+/*
+ * 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.solr.logging;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.solr.common.SolrDocument;
+import org.apache.solr.common.SolrDocumentList;
+import org.apache.solr.core.CoreContainer;
+
+/**
+ * A Class to monitor Logging events and hold N events in memory
+ * 
+ * This is abstract so we can support both JUL and Log4j (and other logging platforms)
+ */
+public abstract class LogWatcher<E> {
+  
+  protected CircularList<E> history;
+  protected long last = -1;
+  
+  /**
+   * @return The implementation name
+   */
+  public abstract String getName();
+  
+  /**
+   * @return The valid level names for this framework
+   */
+  public abstract List<String> getAllLevels();
+  
+  /**
+   * Sets the log level within this framework
+   */
+  public abstract void setLogLevel(String category, String level);
+  
+  /**
+   * @return all registered loggers
+   */
+  public abstract Collection<LoggerInfo> getAllLoggers();
+  
+  public abstract void setThreshold(String level);
+  public abstract String getThreshold();
+
+  public void add(E event, long timstamp) {
+    history.add(event);
+    last = timstamp;
+  }
+  
+  public long getLastEvent() {
+    return last;
+  }
+  
+  public int getHistorySize() {
+    return (history==null) ? -1 : history.getBufferSize();
+  }
+  
+  public SolrDocumentList getHistory(long since, AtomicBoolean found) {
+    if(history==null) {
+      return null;
+    }
+    
+    SolrDocumentList docs = new SolrDocumentList();
+    Iterator<E> iter = history.iterator();
+    while(iter.hasNext()) {
+      E e = iter.next();
+      long ts = getTimestamp(e);
+      if(ts == since) {
+        if(found!=null) {
+          found.set(true);
+        }
+      }
+      if(ts>since) {
+        docs.add(toSolrDocument(e));
+      }
+    }
+    docs.setNumFound(docs.size()); // make it not look too funny
+    return docs;
+  }
+  
+  public abstract long getTimestamp(E event);
+  public abstract SolrDocument toSolrDocument(E event);
+  
+  public abstract void registerListener(ListenerConfig cfg, CoreContainer container);
+
+  public void reset() {
+    history.clear();
+    last = -1;
+  }
 }
\ No newline at end of file

Modified: lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/logging/LoggerInfo.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/logging/LoggerInfo.java?rev=1379200&r1=1379199&r2=1379200&view=diff
==============================================================================
--- lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/logging/LoggerInfo.java (original)
+++ lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/logging/LoggerInfo.java Thu Aug 30 22:43:41 2012
@@ -1,68 +1,68 @@
-/*
- * 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.solr.logging;
-
-import org.apache.solr.common.util.SimpleOrderedMap;
-
-/**
- * Wrapper class for Logger implementaions
- */
-public abstract class LoggerInfo implements Comparable<LoggerInfo> {
-  public static final String ROOT_NAME = "root";
-
-  protected final String name;
-  protected String level;
-
-  public LoggerInfo(String name) {
-    this.name = name;
-  }
-
-  public String getLevel() {
-    return level;
-  }
-
-  public String getName() {
-    return name;
-  }
-  
-  public abstract boolean isSet();
-
-  public SimpleOrderedMap<?> getInfo() {
-    SimpleOrderedMap<Object> info = new SimpleOrderedMap<Object>();
-    info.add("name", getName());
-    info.add("level", getLevel());
-    info.add("set", isSet());
-    return info;
-  }
-
-  @Override
-  public int compareTo(LoggerInfo other) {
-    if (this.equals(other))
-      return 0;
-
-    String tN = this.getName();
-    String oN = other.getName();
-
-    if(ROOT_NAME.equals(tN))
-      return -1;
-    if(ROOT_NAME.equals(oN))
-      return 1;
-
-    return tN.compareTo(oN);
-  }
+/*
+ * 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.solr.logging;
+
+import org.apache.solr.common.util.SimpleOrderedMap;
+
+/**
+ * Wrapper class for Logger implementaions
+ */
+public abstract class LoggerInfo implements Comparable<LoggerInfo> {
+  public static final String ROOT_NAME = "root";
+
+  protected final String name;
+  protected String level;
+
+  public LoggerInfo(String name) {
+    this.name = name;
+  }
+
+  public String getLevel() {
+    return level;
+  }
+
+  public String getName() {
+    return name;
+  }
+  
+  public abstract boolean isSet();
+
+  public SimpleOrderedMap<?> getInfo() {
+    SimpleOrderedMap<Object> info = new SimpleOrderedMap<Object>();
+    info.add("name", getName());
+    info.add("level", getLevel());
+    info.add("set", isSet());
+    return info;
+  }
+
+  @Override
+  public int compareTo(LoggerInfo other) {
+    if (this.equals(other))
+      return 0;
+
+    String tN = this.getName();
+    String oN = other.getName();
+
+    if(ROOT_NAME.equals(tN))
+      return -1;
+    if(ROOT_NAME.equals(oN))
+      return 1;
+
+    return tN.compareTo(oN);
+  }
 }
\ No newline at end of file

Modified: lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/logging/jul/JulInfo.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/logging/jul/JulInfo.java?rev=1379200&r1=1379199&r2=1379200&view=diff
==============================================================================
--- lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/logging/jul/JulInfo.java (original)
+++ lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/logging/jul/JulInfo.java Thu Aug 30 22:43:41 2012
@@ -1,70 +1,70 @@
-/*
- * 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.solr.logging.jul;
-
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import org.apache.solr.logging.LoggerInfo;
-
-public class JulInfo extends LoggerInfo {
-  private static final Level[] LEVELS = {
-      null, // aka unset
-      Level.FINEST,
-      Level.FINE,
-      Level.CONFIG,
-      Level.INFO,
-      Level.WARNING,
-      Level.SEVERE,
-      Level.OFF
-      // Level.ALL -- ignore. It is useless.
-  };
-
-  final Logger logger;
-
-  public JulInfo(String name, Logger logger) {
-    super(name);
-    this.logger = logger;
-  }
-
-  @Override
-  public String getLevel() {
-    if(logger==null) {
-      return null;
-    }
-    Level level = logger.getLevel();
-    if (level != null) {
-      return level.getName();
-    }
-    for (Level l : LEVELS) {
-      if (l == null) {
-        // avoid NPE
-        continue;
-      }
-      if (logger.isLoggable(l)) {
-        // return first level loggable
-        return l.getName();
-      }
-    }
-    return Level.OFF.getName();
-  }
-  
-  @Override
-  public boolean isSet() {
-    return (logger!=null && logger.getLevel()!=null);
-  }
+/*
+ * 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.solr.logging.jul;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.solr.logging.LoggerInfo;
+
+public class JulInfo extends LoggerInfo {
+  private static final Level[] LEVELS = {
+      null, // aka unset
+      Level.FINEST,
+      Level.FINE,
+      Level.CONFIG,
+      Level.INFO,
+      Level.WARNING,
+      Level.SEVERE,
+      Level.OFF
+      // Level.ALL -- ignore. It is useless.
+  };
+
+  final Logger logger;
+
+  public JulInfo(String name, Logger logger) {
+    super(name);
+    this.logger = logger;
+  }
+
+  @Override
+  public String getLevel() {
+    if(logger==null) {
+      return null;
+    }
+    Level level = logger.getLevel();
+    if (level != null) {
+      return level.getName();
+    }
+    for (Level l : LEVELS) {
+      if (l == null) {
+        // avoid NPE
+        continue;
+      }
+      if (logger.isLoggable(l)) {
+        // return first level loggable
+        return l.getName();
+      }
+    }
+    return Level.OFF.getName();
+  }
+  
+  @Override
+  public boolean isSet() {
+    return (logger!=null && logger.getLevel()!=null);
+  }
 }
\ No newline at end of file

Modified: lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/logging/jul/JulWatcher.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/logging/jul/JulWatcher.java?rev=1379200&r1=1379199&r2=1379200&view=diff
==============================================================================
--- lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/logging/jul/JulWatcher.java (original)
+++ lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/logging/jul/JulWatcher.java Thu Aug 30 22:43:41 2012
@@ -1,169 +1,169 @@
-/*
- * 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.solr.logging.jul;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Date;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.logging.Level;
-import java.util.logging.LogManager;
-import java.util.logging.LogRecord;
-import java.util.logging.Logger;
-
-import org.apache.solr.common.SolrDocument;
-import org.apache.solr.core.CoreContainer;
-import org.apache.solr.logging.CircularList;
-import org.apache.solr.logging.ListenerConfig;
-import org.apache.solr.logging.LoggerInfo;
-import org.apache.solr.logging.LogWatcher;
-
-import com.google.common.base.Throwables;
-
-public class JulWatcher extends LogWatcher<LogRecord> {
-
-  final String name;
-  RecordHandler handler = null;
-  
-  public JulWatcher(String name) {
-    this.name = name;
-  }
-  
-  @Override
-  public String getName() {
-    return "JUL ("+name+")";
-  }
-
-
-  @Override
-  public List<String> getAllLevels() {
-    return Arrays.asList(
-      Level.FINEST.getName(),
-      Level.FINER.getName(),
-      Level.FINE.getName(),
-      Level.CONFIG.getName(),
-      Level.INFO.getName(),
-      Level.WARNING.getName(),
-      Level.SEVERE.getName(),
-      Level.OFF.getName() );
-  }
-
-  @Override
-  public void setLogLevel(String category, String level) {
-    if(LoggerInfo.ROOT_NAME.equals(category)) {
-      category = "";
-    }
-    
-    Logger log = LogManager.getLogManager().getLogger(category);
-    if(level==null||"unset".equals(level)||"null".equals(level)) {
-      if(log!=null) {
-        log.setLevel(null);
-      }
-    }
-    else {
-      if(log==null) {
-        log = Logger.getLogger(category); // create it
-      }
-      log.setLevel(Level.parse(level));
-    }
-  }
-
-  @Override
-  public Collection<LoggerInfo> getAllLoggers() {
-    LogManager manager = LogManager.getLogManager();
-
-    Logger root = manager.getLogger("");
-    Map<String,LoggerInfo> map = new HashMap<String,LoggerInfo>();
-    Enumeration<String> names = manager.getLoggerNames();
-    while (names.hasMoreElements()) {
-      String name = names.nextElement();
-      Logger logger = Logger.getLogger(name);
-      if( logger == root) {
-        continue;
-      }
-      map.put(name, new JulInfo(name, logger));
-
-      while (true) {
-        int dot = name.lastIndexOf(".");
-        if (dot < 0)
-          break;
-        name = name.substring(0, dot);
-        if(!map.containsKey(name)) {
-          map.put(name, new JulInfo(name, null));
-        }
-      }
-    }
-    map.put(LoggerInfo.ROOT_NAME, new JulInfo(LoggerInfo.ROOT_NAME, root));
-    return map.values();
-  }
-
-  @Override
-  public void setThreshold(String level) {
-    if(handler==null) {
-      throw new IllegalStateException("Must have an handler");
-    }
-    handler.setLevel( Level.parse(level) );
-  }
-
-  @Override
-  public String getThreshold() {
-    if(handler==null) {
-      throw new IllegalStateException("Must have an handler");
-    }
-    return handler.getLevel().toString();
-  }
-
-  @Override
-  public void registerListener(ListenerConfig cfg, CoreContainer container) {
-    if(history!=null) {
-      throw new IllegalStateException("History already registered");
-    }
-    history = new CircularList<LogRecord>(cfg.size);
-    handler = new RecordHandler(this);
-    if(cfg.threshold != null) {
-      handler.setLevel(Level.parse(cfg.threshold));
-    }
-    else {
-      handler.setLevel(Level.WARNING);
-    }
-    
-    Logger log = LogManager.getLogManager().getLogger("");
-    log.addHandler(handler);
-  }
-
-  @Override
-  public long getTimestamp(LogRecord event) {
-    return event.getMillis();
-  }
-
-  @Override
-  public SolrDocument toSolrDocument(LogRecord event) {
-    SolrDocument doc = new SolrDocument();
-    doc.setField("time", new Date(event.getMillis()));
-    doc.setField("level", event.getLevel().toString());
-    doc.setField("logger", event.getLoggerName());
-    doc.setField("message", event.getMessage().toString());
-    Throwable t = event.getThrown();
-    if(t!=null) {
-      doc.setField("trace", Throwables.getStackTraceAsString(t));
-    }
-    return doc;
-  }
+/*
+ * 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.solr.logging.jul;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.LogManager;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+
+import org.apache.solr.common.SolrDocument;
+import org.apache.solr.core.CoreContainer;
+import org.apache.solr.logging.CircularList;
+import org.apache.solr.logging.ListenerConfig;
+import org.apache.solr.logging.LoggerInfo;
+import org.apache.solr.logging.LogWatcher;
+
+import com.google.common.base.Throwables;
+
+public class JulWatcher extends LogWatcher<LogRecord> {
+
+  final String name;
+  RecordHandler handler = null;
+  
+  public JulWatcher(String name) {
+    this.name = name;
+  }
+  
+  @Override
+  public String getName() {
+    return "JUL ("+name+")";
+  }
+
+
+  @Override
+  public List<String> getAllLevels() {
+    return Arrays.asList(
+      Level.FINEST.getName(),
+      Level.FINER.getName(),
+      Level.FINE.getName(),
+      Level.CONFIG.getName(),
+      Level.INFO.getName(),
+      Level.WARNING.getName(),
+      Level.SEVERE.getName(),
+      Level.OFF.getName() );
+  }
+
+  @Override
+  public void setLogLevel(String category, String level) {
+    if(LoggerInfo.ROOT_NAME.equals(category)) {
+      category = "";
+    }
+    
+    Logger log = LogManager.getLogManager().getLogger(category);
+    if(level==null||"unset".equals(level)||"null".equals(level)) {
+      if(log!=null) {
+        log.setLevel(null);
+      }
+    }
+    else {
+      if(log==null) {
+        log = Logger.getLogger(category); // create it
+      }
+      log.setLevel(Level.parse(level));
+    }
+  }
+
+  @Override
+  public Collection<LoggerInfo> getAllLoggers() {
+    LogManager manager = LogManager.getLogManager();
+
+    Logger root = manager.getLogger("");
+    Map<String,LoggerInfo> map = new HashMap<String,LoggerInfo>();
+    Enumeration<String> names = manager.getLoggerNames();
+    while (names.hasMoreElements()) {
+      String name = names.nextElement();
+      Logger logger = Logger.getLogger(name);
+      if( logger == root) {
+        continue;
+      }
+      map.put(name, new JulInfo(name, logger));
+
+      while (true) {
+        int dot = name.lastIndexOf(".");
+        if (dot < 0)
+          break;
+        name = name.substring(0, dot);
+        if(!map.containsKey(name)) {
+          map.put(name, new JulInfo(name, null));
+        }
+      }
+    }
+    map.put(LoggerInfo.ROOT_NAME, new JulInfo(LoggerInfo.ROOT_NAME, root));
+    return map.values();
+  }
+
+  @Override
+  public void setThreshold(String level) {
+    if(handler==null) {
+      throw new IllegalStateException("Must have an handler");
+    }
+    handler.setLevel( Level.parse(level) );
+  }
+
+  @Override
+  public String getThreshold() {
+    if(handler==null) {
+      throw new IllegalStateException("Must have an handler");
+    }
+    return handler.getLevel().toString();
+  }
+
+  @Override
+  public void registerListener(ListenerConfig cfg, CoreContainer container) {
+    if(history!=null) {
+      throw new IllegalStateException("History already registered");
+    }
+    history = new CircularList<LogRecord>(cfg.size);
+    handler = new RecordHandler(this);
+    if(cfg.threshold != null) {
+      handler.setLevel(Level.parse(cfg.threshold));
+    }
+    else {
+      handler.setLevel(Level.WARNING);
+    }
+    
+    Logger log = LogManager.getLogManager().getLogger("");
+    log.addHandler(handler);
+  }
+
+  @Override
+  public long getTimestamp(LogRecord event) {
+    return event.getMillis();
+  }
+
+  @Override
+  public SolrDocument toSolrDocument(LogRecord event) {
+    SolrDocument doc = new SolrDocument();
+    doc.setField("time", new Date(event.getMillis()));
+    doc.setField("level", event.getLevel().toString());
+    doc.setField("logger", event.getLoggerName());
+    doc.setField("message", event.getMessage().toString());
+    Throwable t = event.getThrown();
+    if(t!=null) {
+      doc.setField("trace", Throwables.getStackTraceAsString(t));
+    }
+    return doc;
+  }
 }
\ No newline at end of file

Modified: lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/logging/jul/RecordHandler.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/logging/jul/RecordHandler.java?rev=1379200&r1=1379199&r2=1379200&view=diff
==============================================================================
--- lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/logging/jul/RecordHandler.java (original)
+++ lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/logging/jul/RecordHandler.java Thu Aug 30 22:43:41 2012
@@ -1,47 +1,47 @@
-/*
- * 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.solr.logging.jul;
-
-
-import java.util.logging.LogRecord;
-
-import org.apache.solr.logging.LogWatcher;
-
-public final class RecordHandler extends java.util.logging.Handler {
-  final LogWatcher<LogRecord> framework;
-  
-  public RecordHandler(LogWatcher<LogRecord> framework) {
-    this.framework = framework;
-  }
-  
-  @Override
-  public void close() throws SecurityException {
-    //history.reset();
-  }
-  
-  @Override
-  public void flush() {
-    // nothing
-  }
-  
-  @Override
-  public void publish(LogRecord r) {
-    if(isLoggable(r)) {
-      framework.add(r, r.getMillis());
-    }
-  }
+/*
+ * 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.solr.logging.jul;
+
+
+import java.util.logging.LogRecord;
+
+import org.apache.solr.logging.LogWatcher;
+
+public final class RecordHandler extends java.util.logging.Handler {
+  final LogWatcher<LogRecord> framework;
+  
+  public RecordHandler(LogWatcher<LogRecord> framework) {
+    this.framework = framework;
+  }
+  
+  @Override
+  public void close() throws SecurityException {
+    //history.reset();
+  }
+  
+  @Override
+  public void flush() {
+    // nothing
+  }
+  
+  @Override
+  public void publish(LogRecord r) {
+    if(isLoggable(r)) {
+      framework.add(r, r.getMillis());
+    }
+  }
 }
\ No newline at end of file

Modified: lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/schema/BoolField.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/schema/BoolField.java?rev=1379200&r1=1379199&r2=1379200&view=diff
==============================================================================
--- lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/schema/BoolField.java (original)
+++ lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/schema/BoolField.java Thu Aug 30 22:43:41 2012
@@ -72,9 +72,8 @@ public class BoolField extends Primitive
         boolean done = false;
 
         @Override
-        public void setReader(Reader input) throws IOException {
+        public void reset() throws IOException {
           done = false;
-          super.setReader(input);
         }
 
         @Override

Modified: lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/schema/PreAnalyzedField.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/schema/PreAnalyzedField.java?rev=1379200&r1=1379199&r2=1379200&view=diff
==============================================================================
--- lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/schema/PreAnalyzedField.java (original)
+++ lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/schema/PreAnalyzedField.java Thu Aug 30 22:43:41 2012
@@ -83,13 +83,8 @@ public class PreAnalyzedField extends Fi
     return new SolrAnalyzer() {
       
       @Override
-      protected TokenStreamComponents createComponents(String fieldName,
-          Reader reader) {
-        try {
-          return new TokenStreamComponents(new PreAnalyzedTokenizer(reader, parser));
-        } catch (IOException e) {
-          return null;
-        }
+      protected TokenStreamComponents createComponents(String fieldName, Reader reader) {
+        return new TokenStreamComponents(new PreAnalyzedTokenizer(reader, parser));
       }
       
     };
@@ -171,6 +166,7 @@ public class PreAnalyzedField extends Fi
       return null;
     }
     PreAnalyzedTokenizer parse = new PreAnalyzedTokenizer(new StringReader(val), parser);
+    parse.reset(); // consume
     Field f = (Field)super.createField(field, val, boost);
     if (parse.getStringValue() != null) {
       f.setStringValue(parse.getStringValue());
@@ -197,11 +193,11 @@ public class PreAnalyzedField extends Fi
     private String stringValue = null;
     private byte[] binaryValue = null;
     private PreAnalyzedParser parser;
+    private Reader lastReader;
     
-    public PreAnalyzedTokenizer(Reader reader, PreAnalyzedParser parser) throws IOException {
+    public PreAnalyzedTokenizer(Reader reader, PreAnalyzedParser parser) {
       super(reader);
       this.parser = parser;
-      setReader(reader);
     }
     
     public boolean hasTokenStream() {
@@ -231,24 +227,30 @@ public class PreAnalyzedField extends Fi
       return true;
     }
   
-    public final void reset() {
+    @Override
+    public final void reset() throws IOException {
+      // NOTE: this acts like rewind if you call it again
+      if (input != lastReader) {
+        lastReader = input;
+        cachedStates.clear();
+        stringValue = null;
+        binaryValue = null;
+        ParseResult res = parser.parse(input, this);
+        if (res != null) {
+          stringValue = res.str;
+          binaryValue = res.bin;
+          if (res.states != null) {
+            cachedStates.addAll(res.states);
+          }
+        }
+      }
       it = cachedStates.iterator();
     }
 
     @Override
-    public void setReader(Reader input) throws IOException {
-      super.setReader(input);
-      cachedStates.clear();
-      stringValue = null;
-      binaryValue = null;
-      ParseResult res = parser.parse(input, this);
-      if (res != null) {
-        stringValue = res.str;
-        binaryValue = res.bin;
-        if (res.states != null) {
-          cachedStates.addAll(res.states);
-        }
-      }
+    public void close() throws IOException {
+      super.close();
+      lastReader = null; // just a ref, null for gc
     }
   }
   

Modified: lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/update/DefaultSolrCoreState.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/update/DefaultSolrCoreState.java?rev=1379200&r1=1379199&r2=1379200&view=diff
==============================================================================
--- lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/update/DefaultSolrCoreState.java (original)
+++ lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/update/DefaultSolrCoreState.java Thu Aug 30 22:43:41 2012
@@ -242,10 +242,14 @@ public final class DefaultSolrCoreState 
     synchronized (recoveryLock) {
       if (recoveryStrat != null) {
         recoveryStrat.close();
-        try {
-          recoveryStrat.join();
-        } catch (InterruptedException e) {
-          
+        while (true) {
+          try {
+            recoveryStrat.join();
+          } catch (InterruptedException e) {
+            // not interruptible - keep waiting
+            continue;
+          }
+          break;
         }
         
         recoveryRunning = false;

Modified: lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/update/DirectUpdateHandler2.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/update/DirectUpdateHandler2.java?rev=1379200&r1=1379199&r2=1379200&view=diff
==============================================================================
--- lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/update/DirectUpdateHandler2.java (original)
+++ lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/update/DirectUpdateHandler2.java Thu Aug 30 22:43:41 2012
@@ -738,6 +738,13 @@ public class DirectUpdateHandler2 extend
     }
   }
 
+  @Override
+  public void split(SplitIndexCommand cmd) throws IOException {
+    // TODO: do a commit first?
+    SolrIndexSplitter splitter = new SolrIndexSplitter(cmd);
+    splitter.split();
+  }
+
   /////////////////////////////////////////////////////////////////////
   // SolrInfoMBean stuff: Statistics and Module Info
   /////////////////////////////////////////////////////////////////////

Modified: lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/update/SolrCmdDistributor.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/update/SolrCmdDistributor.java?rev=1379200&r1=1379199&r2=1379200&view=diff
==============================================================================
--- lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/update/SolrCmdDistributor.java (original)
+++ lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/update/SolrCmdDistributor.java Thu Aug 30 22:43:41 2012
@@ -166,6 +166,8 @@ public class SolrCmdDistributor {
     
     addCommit(ureq, cmd);
     
+    log.info("Distrib commit to:" + nodes);
+    
     for (Node node : nodes) {
       submit(ureq, node);
     }
@@ -345,7 +347,8 @@ public class SolrCmdDistributor {
     try {
       semaphore.acquire();
     } catch (InterruptedException e) {
-      throw new RuntimeException();
+      Thread.currentThread().interrupt();
+      throw new RuntimeException("Update thread interrupted");
     }
     pending.add(completionService.submit(task));
     

Modified: lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/update/UpdateHandler.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/update/UpdateHandler.java?rev=1379200&r1=1379199&r2=1379200&view=diff
==============================================================================
--- lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/update/UpdateHandler.java (original)
+++ lucene/dev/branches/lucene3312/solr/core/src/java/org/apache/solr/update/UpdateHandler.java Thu Aug 30 22:43:41 2012
@@ -179,4 +179,6 @@ public abstract class UpdateHandler impl
   {
     optimizeCallbacks.add( listener );
   }
+
+  public abstract void split(SplitIndexCommand cmd) throws IOException;
 }