You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ja...@apache.org on 2013/05/30 09:53:46 UTC

svn commit: r1487777 [50/50] - in /lucene/dev/branches/security: ./ dev-tools/ dev-tools/eclipse/dot.settings/ dev-tools/idea/.idea/ dev-tools/idea/.idea/libraries/ dev-tools/idea/lucene/replicator/ dev-tools/maven/ dev-tools/maven/lucene/ dev-tools/ma...

Modified: lucene/dev/branches/security/solr/test-framework/ivy.xml
URL: http://svn.apache.org/viewvc/lucene/dev/branches/security/solr/test-framework/ivy.xml?rev=1487777&r1=1487776&r2=1487777&view=diff
==============================================================================
--- lucene/dev/branches/security/solr/test-framework/ivy.xml (original)
+++ lucene/dev/branches/security/solr/test-framework/ivy.xml Thu May 30 07:53:18 2013
@@ -32,8 +32,8 @@
       <dependency org="org.apache.ant" name="ant" rev="1.8.2" transitive="false" />
 
       <dependency org="junit" name="junit" rev="4.10" transitive="false" conf="default->*;junit4-stdalone->*" />
-      <dependency org="com.carrotsearch.randomizedtesting" name="junit4-ant" rev="2.0.8" transitive="false" conf="default->*;junit4-stdalone->*" />
-      <dependency org="com.carrotsearch.randomizedtesting" name="randomizedtesting-runner" rev="2.0.8" transitive="false" conf="default->*;junit4-stdalone->*" />
+      <dependency org="com.carrotsearch.randomizedtesting" name="junit4-ant" rev="2.0.10" transitive="false" conf="default->*;junit4-stdalone->*" />
+      <dependency org="com.carrotsearch.randomizedtesting" name="randomizedtesting-runner" rev="2.0.10" transitive="false" conf="default->*;junit4-stdalone->*" />
 
       <exclude org="*" ext="*" matcher="regexp" type="${ivy.exclude.types}"/> 
     </dependencies>

Modified: lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/BaseDistributedSearchTestCase.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/BaseDistributedSearchTestCase.java?rev=1487777&r1=1487776&r2=1487777&view=diff
==============================================================================
--- lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/BaseDistributedSearchTestCase.java (original)
+++ lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/BaseDistributedSearchTestCase.java Thu May 30 07:53:18 2013
@@ -29,6 +29,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Random;
 import java.util.Set;
+import java.util.SortedMap;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import junit.framework.Assert;
@@ -52,6 +53,7 @@ import org.apache.solr.common.params.Sol
 import org.apache.solr.common.util.NamedList;
 import org.apache.solr.schema.TrieDateField;
 import org.apache.solr.util.AbstractSolrTestCase;
+import org.eclipse.jetty.servlet.ServletHolder;
 import org.junit.BeforeClass;
 import org.junit.AfterClass;
 import org.junit.Test;
@@ -363,7 +365,9 @@ public abstract class BaseDistributedSea
   
   public JettySolrRunner createJetty(File solrHome, String dataDir, String shardList, String solrConfigOverride, String schemaOverride, boolean explicitCoreNodeName) throws Exception {
 
-    JettySolrRunner jetty = new JettySolrRunner(solrHome.getAbsolutePath(), context, 0, solrConfigOverride, schemaOverride);
+    boolean stopAtShutdown = true;
+    JettySolrRunner jetty = new JettySolrRunner
+        (solrHome.getAbsolutePath(), context, 0, solrConfigOverride, schemaOverride, stopAtShutdown, getExtraServlets());
     jetty.setShards(shardList);
     jetty.setDataDir(dataDir);
     if (explicitCoreNodeName) {
@@ -374,12 +378,17 @@ public abstract class BaseDistributedSea
     return jetty;
   }
   
+  /** Override this method to insert extra servlets into the JettySolrRunners that are created using createJetty() */
+  public SortedMap<ServletHolder,String> getExtraServlets() {
+    return null;
+  }
+  
   protected SolrServer createNewSolrServer(int port) {
     try {
       // setup the server...
       String url = "http://127.0.0.1:" + port + context;
       HttpSolrServer s = new HttpSolrServer(url);
-      s.setConnectionTimeout(DEFAULT_CONNECTION_TIMEOUT);;
+      s.setConnectionTimeout(DEFAULT_CONNECTION_TIMEOUT);
       s.setSoTimeout(60000);
       s.setDefaultMaxConnectionsPerHost(100);
       s.setMaxTotalConnections(100);

Modified: lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/JSONTestUtil.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/JSONTestUtil.java?rev=1487777&r1=1487776&r2=1487777&view=diff
==============================================================================
--- lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/JSONTestUtil.java (original)
+++ lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/JSONTestUtil.java Thu May 30 07:53:18 2013
@@ -17,7 +17,7 @@
 
 package org.apache.solr;
 
-import org.apache.noggit.ObjectBuilder;
+import org.noggit.ObjectBuilder;
 import org.apache.solr.common.util.StrUtils;
 
 import java.util.*;

Modified: lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/SolrJettyTestBase.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/SolrJettyTestBase.java?rev=1487777&r1=1487776&r2=1487777&view=diff
==============================================================================
--- lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/SolrJettyTestBase.java (original)
+++ lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/SolrJettyTestBase.java Thu May 30 07:53:18 2013
@@ -17,25 +17,19 @@ package org.apache.solr;
  * limitations under the License.
  */
 
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.SortedMap;
+
 import org.apache.solr.client.solrj.SolrServer;
 import org.apache.solr.client.solrj.embedded.EmbeddedSolrServer;
 import org.apache.solr.client.solrj.embedded.JettySolrRunner;
 import org.apache.solr.client.solrj.impl.HttpSolrServer;
 import org.apache.solr.util.ExternalPaths;
-
-import java.io.File;
-import java.util.Collections;
-import java.util.Map;
-import java.util.HashMap;
-import java.util.SortedMap;
-import java.util.TreeMap;
-
-import org.apache.solr.util.RESTfulServerProvider;
-import org.apache.solr.util.RestTestHarness;
 import org.eclipse.jetty.servlet.ServletHolder;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
-import org.restlet.ext.servlet.ServerServlet;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -159,7 +153,7 @@ abstract public class SolrJettyTestBase 
     if (jetty != null) {
       try {
         // setup the server...
-        String url = jetty.getBaseUrl().toString();
+        String url = jetty.getBaseUrl().toString() + "/" + "collection1";
         HttpSolrServer s = new HttpSolrServer( url );
         s.setConnectionTimeout(DEFAULT_CONNECTION_TIMEOUT);
         s.setDefaultMaxConnectionsPerHost(100);

Modified: lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java?rev=1487777&r1=1487776&r2=1487777&view=diff
==============================================================================
--- lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java (original)
+++ lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java Thu May 30 07:53:18 2013
@@ -23,10 +23,10 @@ import java.util.logging.*;
 
 import javax.xml.xpath.XPathExpressionException;
 
+import org.apache.commons.io.FileUtils;
 import org.apache.lucene.util.Constants;
 import org.apache.lucene.util.LuceneTestCase;
 import org.apache.lucene.util.QuickPatchThreadsFilter;
-import org.apache.noggit.*;
 import org.apache.solr.client.solrj.util.ClientUtils;
 import org.apache.solr.common.*;
 import org.apache.solr.common.cloud.SolrZkClient;
@@ -47,6 +47,9 @@ import org.apache.solr.util.TestHarness;
 import org.junit.*;
 import org.junit.rules.RuleChain;
 import org.junit.rules.TestRule;
+import org.noggit.CharArr;
+import org.noggit.JSONUtil;
+import org.noggit.ObjectBuilder;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.xml.sax.SAXException;
@@ -83,6 +86,7 @@ public abstract class SolrTestCaseJ4 ext
   @SuppressWarnings("unused")
   private static void beforeClass() {
     System.setProperty("jetty.testMode", "true");
+    System.setProperty("tests.shardhandler.randomSeed", Long.toString(random().nextLong()));
     setupLogging();
     startTrackingSearchers();
     startTrackingZkClients();
@@ -97,7 +101,9 @@ public abstract class SolrTestCaseJ4 ext
     endTrackingSearchers();
     endTrackingZkClients();
     resetFactory();
+    coreName = CoreContainer.DEFAULT_DEFAULT_CORE_NAME;
     System.clearProperty("jetty.testMode");
+    System.clearProperty("tests.shardhandler.randomSeed");
   }
 
   private static boolean changedFactory = false;
@@ -384,7 +390,8 @@ public abstract class SolrTestCaseJ4 ext
   public static void createCore() {
     assertNotNull(testSolrHome);
     solrConfig = TestHarness.createConfig(testSolrHome, coreName, getSolrConfigFile());
-    h = new TestHarness( dataDir.getAbsolutePath(),
+    h = new TestHarness( coreName,
+            dataDir.getAbsolutePath(),
             solrConfig,
             getSchemaFile());
     lrf = h.getRequestFactory
@@ -1436,4 +1443,30 @@ public abstract class SolrTestCaseJ4 ext
     }
     return result;
   }
+
+  public void assertXmlFile(final File file, String... xpath)
+      throws IOException, SAXException {
+
+    try {
+      String xml = FileUtils.readFileToString(file, "UTF-8");
+      String results = h.validateXPath(xml, xpath);
+      if (null != results) {
+        String msg = "File XPath failure: file=" + file.getPath() + " xpath="
+            + results + "\n\nxml was: " + xml;
+        fail(msg);
+      }
+    } catch (XPathExpressionException e2) {
+      throw new RuntimeException("XPath is invalid", e2);
+    }
+  }
+  // Creates a mininmal conf dir.
+  public void copyMinConf(File dstRoot) throws IOException {
+
+    File subHome = new File(dstRoot, "conf");
+    assertTrue("Failed to make subdirectory ", dstRoot.mkdirs());
+    String top = SolrTestCaseJ4.TEST_HOME() + "/collection1/conf";
+    FileUtils.copyFile(new File(top, "schema-tiny.xml"), new File(subHome, "schema-tiny.xml"));
+    FileUtils.copyFile(new File(top, "solrconfig-minimal.xml"), new File(subHome, "solrconfig-minimal.xml"));
+  }
+
 }

Modified: lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/analysis/MockCharFilterFactory.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/analysis/MockCharFilterFactory.java?rev=1487777&r1=1487776&r2=1487777&view=diff
==============================================================================
--- lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/analysis/MockCharFilterFactory.java (original)
+++ lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/analysis/MockCharFilterFactory.java Thu May 30 07:53:18 2013
@@ -20,7 +20,6 @@ package org.apache.solr.analysis;
 import java.io.Reader;
 import java.util.Map;
 
-import org.apache.lucene.analysis.CharFilter;
 import org.apache.lucene.analysis.MockCharFilter;
 import org.apache.lucene.analysis.util.CharFilterFactory;
 
@@ -28,20 +27,19 @@ import org.apache.lucene.analysis.util.C
  * Factory for {@link MockCharFilter} for testing purposes.
  */
 public class MockCharFilterFactory extends CharFilterFactory {
-  int remainder;
+  final int remainder;
 
-  @Override
-  public void init(Map<String,String> args) {
-    super.init(args);
-    String sval = args.get("remainder");
-    if (sval == null) {
-      throw new IllegalArgumentException("remainder is mandatory");
+  /** Creates a new MockCharFilterFactory */
+  public MockCharFilterFactory(Map<String,String> args) {
+    super(args);
+    remainder = requireInt(args, "remainder");
+    if (!args.isEmpty()) {
+      throw new IllegalArgumentException("Unknown parameters: " + args);
     }
-    remainder = Integer.parseInt(sval);
   }
 
   @Override
-  public CharFilter create(Reader input) {
+  public MockCharFilter create(Reader input) {
     return new MockCharFilter(input, remainder);
   }
 }

Modified: lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/analysis/MockTokenizerFactory.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/analysis/MockTokenizerFactory.java?rev=1487777&r1=1487776&r2=1487777&view=diff
==============================================================================
--- lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/analysis/MockTokenizerFactory.java (original)
+++ lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/analysis/MockTokenizerFactory.java Thu May 30 07:53:18 2013
@@ -18,45 +18,42 @@ package org.apache.solr.analysis;
  */
 
 import java.io.Reader;
+import java.util.Arrays;
 import java.util.Map;
 
 import org.apache.lucene.analysis.MockTokenizer;
-import org.apache.lucene.analysis.Tokenizer;
 import org.apache.lucene.analysis.util.TokenizerFactory;
+import org.apache.lucene.util.AttributeSource.AttributeFactory;
 import org.apache.lucene.util.automaton.CharacterRunAutomaton;
 
 /**
  * Factory for {@link MockTokenizer} for testing purposes.
  */
 public class MockTokenizerFactory extends TokenizerFactory {
-  CharacterRunAutomaton pattern;
-  boolean enableChecks;
+  final CharacterRunAutomaton pattern;
+  final boolean enableChecks;
   
-  @Override
-  public void init(Map<String,String> args) {
-    super.init(args);
-    String patternArg = args.get("pattern");
-    if (patternArg == null) {
-      patternArg = "whitespace";
-    }
-    
-    if ("whitespace".equalsIgnoreCase(patternArg)) {
-      pattern = MockTokenizer.WHITESPACE;
-    } else if ("keyword".equalsIgnoreCase(patternArg)) {
+  /** Creates a new MockTokenizerFactory */
+  public MockTokenizerFactory(Map<String,String> args) {
+    super(args);
+    String patternArg = get(args, "pattern", Arrays.asList("keyword", "simple", "whitespace"));
+    if ("keyword".equalsIgnoreCase(patternArg)) {
       pattern = MockTokenizer.KEYWORD;
     } else if ("simple".equalsIgnoreCase(patternArg)) {
       pattern = MockTokenizer.SIMPLE;
     } else {
-      throw new RuntimeException("invalid pattern!");
+      pattern = MockTokenizer.WHITESPACE;
     }
     
-    enableChecks = getBoolean("enableChecks", true);
+    enableChecks = getBoolean(args, "enableChecks", true);
+    if (!args.isEmpty()) {
+      throw new IllegalArgumentException("Unknown parameters: " + args);
+    }
   }
 
-
   @Override
-  public Tokenizer create(Reader input) {
-    MockTokenizer t = new MockTokenizer(input, pattern, false);
+  public MockTokenizer create(AttributeFactory factory, Reader input) {
+    MockTokenizer t = new MockTokenizer(factory, input, pattern, false);
     t.setEnableChecks(enableChecks);
     return t;
   }

Modified: lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/analysis/StringMockSolrResourceLoader.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/analysis/StringMockSolrResourceLoader.java?rev=1487777&r1=1487776&r2=1487777&view=diff
==============================================================================
--- lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/analysis/StringMockSolrResourceLoader.java (original)
+++ lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/analysis/StringMockSolrResourceLoader.java Thu May 30 07:53:18 2013
@@ -20,8 +20,6 @@ package org.apache.solr.analysis;
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.util.Arrays;
-import java.util.List;
 
 import org.apache.lucene.analysis.util.ResourceLoader;
 
@@ -33,8 +31,22 @@ class StringMockSolrResourceLoader imple
   }
 
   @Override
+  public <T> Class<? extends T> findClass(String cname, Class<T> expectedType) {
+    try {
+      return Class.forName(cname).asSubclass(expectedType);
+    } catch (Exception e) {
+      throw new RuntimeException("Cannot load class: " + cname, e);
+    }
+  }
+
+  @Override
   public <T> T newInstance(String cname, Class<T> expectedType) {
-    return null;
+    Class<? extends T> clazz = findClass(cname, expectedType);
+    try {
+      return clazz.newInstance();
+    } catch (Exception e) {
+      throw new RuntimeException("Cannot create instance: " + cname, e);
+    }
   }
 
   @Override

Modified: lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/cloud/AbstractFullDistribZkTestBase.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/cloud/AbstractFullDistribZkTestBase.java?rev=1487777&r1=1487776&r2=1487777&view=diff
==============================================================================
--- lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/cloud/AbstractFullDistribZkTestBase.java (original)
+++ lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/cloud/AbstractFullDistribZkTestBase.java Thu May 30 07:53:18 2013
@@ -33,11 +33,13 @@ import java.util.concurrent.atomic.Atomi
 import org.apache.http.params.CoreConnectionPNames;
 import org.apache.lucene.util.LuceneTestCase.Slow;
 import org.apache.solr.client.solrj.SolrQuery;
+import org.apache.solr.client.solrj.SolrRequest;
 import org.apache.solr.client.solrj.SolrServer;
 import org.apache.solr.client.solrj.SolrServerException;
 import org.apache.solr.client.solrj.embedded.JettySolrRunner;
 import org.apache.solr.client.solrj.impl.CloudSolrServer;
 import org.apache.solr.client.solrj.impl.HttpSolrServer;
+import org.apache.solr.client.solrj.request.QueryRequest;
 import org.apache.solr.client.solrj.request.UpdateRequest;
 import org.apache.solr.client.solrj.response.QueryResponse;
 import org.apache.solr.common.SolrDocument;
@@ -51,6 +53,7 @@ import org.apache.solr.common.cloud.Slic
 import org.apache.solr.common.cloud.ZkCoreNodeProps;
 import org.apache.solr.common.cloud.ZkNodeProps;
 import org.apache.solr.common.cloud.ZkStateReader;
+import org.apache.solr.common.params.CollectionParams.CollectionAction;
 import org.apache.solr.common.params.ModifiableSolrParams;
 import org.apache.solr.common.params.SolrParams;
 import org.junit.After;
@@ -612,6 +615,12 @@ public abstract class AbstractFullDistri
     super.waitForRecoveriesToFinish(DEFAULT_COLLECTION, zkStateReader, verbose);
   }
   
+  protected void waitForRecoveriesToFinish(String collection, boolean verbose)
+      throws Exception {
+    ZkStateReader zkStateReader = cloudClient.getZkStateReader();
+    super.waitForRecoveriesToFinish(collection, zkStateReader, verbose);
+  }
+  
   protected void waitForRecoveriesToFinish(boolean verbose, int timeoutSeconds)
       throws Exception {
     ZkStateReader zkStateReader = cloudClient.getZkStateReader();
@@ -1307,7 +1316,7 @@ public abstract class AbstractFullDistri
         retry  = true;
       }
       cnt++;
-      if (cnt > 10) break;
+      if (cnt > 20) break;
       Thread.sleep(2000);
     } while (retry);
   }
@@ -1318,6 +1327,9 @@ public abstract class AbstractFullDistri
     if (VERBOSE || printLayoutOnTearDown) {
       super.printLayout();
     }
+    if (commondCloudSolrServer != null) {
+      commondCloudSolrServer.shutdown();
+    }
     if (controlClient != null) {
       ((HttpSolrServer) controlClient).shutdown();
     }
@@ -1355,6 +1367,40 @@ public abstract class AbstractFullDistri
     jettys.clear();
   }
   
+  protected void createCollection(String collectionName, int numShards, int numReplicas, int maxShardsPerNode) throws SolrServerException, IOException {
+    createCollection(null, collectionName, numShards, numReplicas, maxShardsPerNode, null, null);
+  }
+  
+  protected void createCollection(Map<String,List<Integer>> collectionInfos,
+      String collectionName, int numShards, int numReplicas, int maxShardsPerNode, SolrServer client, String createNodeSetStr) throws SolrServerException, IOException {
+    ModifiableSolrParams params = new ModifiableSolrParams();
+    params.set("action", CollectionAction.CREATE.toString());
+
+    params.set(OverseerCollectionProcessor.NUM_SLICES, numShards);
+    params.set(OverseerCollectionProcessor.REPLICATION_FACTOR, numReplicas);
+    params.set(OverseerCollectionProcessor.MAX_SHARDS_PER_NODE, maxShardsPerNode);
+    if (createNodeSetStr != null) params.set(OverseerCollectionProcessor.CREATE_NODE_SET, createNodeSetStr);
+
+    int clientIndex = random().nextInt(2);
+    List<Integer> list = new ArrayList<Integer>();
+    list.add(numShards);
+    list.add(numReplicas);
+    if (collectionInfos != null) {
+      collectionInfos.put(collectionName, list);
+    }
+    params.set("name", collectionName);
+    SolrRequest request = new QueryRequest(params);
+    request.setPath("/admin/collections");
+  
+    if (client == null) {
+      final String baseUrl = getBaseUrl((HttpSolrServer) clients.get(clientIndex));
+      
+      createNewSolrServer("", baseUrl).request(request);
+    } else {
+      client.request(request);
+    }
+  }
+  
   @Override
   protected SolrServer createNewSolrServer(int port) {
     try {
@@ -1371,5 +1417,103 @@ public abstract class AbstractFullDistri
       throw new RuntimeException(ex);
     }
   }
+  
+  protected SolrServer createNewSolrServer(String collection, String baseUrl) {
+    try {
+      // setup the server...
+      HttpSolrServer s = new HttpSolrServer(baseUrl + "/" + collection);
+      s.setConnectionTimeout(DEFAULT_CONNECTION_TIMEOUT);
+      s.setDefaultMaxConnectionsPerHost(100);
+      s.setMaxTotalConnections(100);
+      return s;
+    }
+    catch (Exception ex) {
+      throw new RuntimeException(ex);
+    }
+  }
+  
+  protected String getBaseUrl(HttpSolrServer client) {
+    return client .getBaseURL().substring(
+        0, client.getBaseURL().length()
+            - DEFAULT_COLLECTION.length() - 1);
+  }
+  
+  protected SolrInputDocument getDoc(Object... fields) throws Exception {
+    SolrInputDocument doc = new SolrInputDocument();
+    addFields(doc, fields);
+    return doc;
+  }
+
+  private String checkCollectionExpectations(String collectionName, List<Integer> numShardsNumReplicaList, List<String> nodesAllowedToRunShards) {
+    ClusterState clusterState = getCommonCloudSolrServer().getZkStateReader().getClusterState();
+    
+    int expectedSlices = numShardsNumReplicaList.get(0);
+    // The Math.min thing is here, because we expect replication-factor to be reduced to if there are not enough live nodes to spread all shards of a collection over different nodes
+    int expectedShardsPerSlice = numShardsNumReplicaList.get(1);
+    int expectedTotalShards = expectedSlices * expectedShardsPerSlice;
+    
+      Map<String,DocCollection> collections = clusterState
+          .getCollectionStates();
+      if (collections.containsKey(collectionName)) {
+        Map<String,Slice> slices = collections.get(collectionName).getSlicesMap();
+        // did we find expectedSlices slices/shards?
+      if (slices.size() != expectedSlices) {
+        return "Found new collection " + collectionName + ", but mismatch on number of slices. Expected: " + expectedSlices + ", actual: " + slices.size();
+      }
+      int totalShards = 0;
+      for (String sliceName : slices.keySet()) {
+        for (Replica replica : slices.get(sliceName).getReplicas()) {
+          if (nodesAllowedToRunShards != null && !nodesAllowedToRunShards.contains(replica.getStr(ZkStateReader.NODE_NAME_PROP))) {
+            return "Shard " + replica.getName() + " created on node " + replica.getStr(ZkStateReader.NODE_NAME_PROP) + " not allowed to run shards for the created collection " + collectionName;
+          }
+        }
+        totalShards += slices.get(sliceName).getReplicas().size();
+      }
+      if (totalShards != expectedTotalShards) {
+        return "Found new collection " + collectionName + " with correct number of slices, but mismatch on number of shards. Expected: " + expectedTotalShards + ", actual: " + totalShards; 
+        }
+      return null;
+    } else {
+      return "Could not find new collection " + collectionName;
+    }
+  }
+  
+  protected void checkForCollection(String collectionName,
+      List<Integer> numShardsNumReplicaList,
+      List<String> nodesAllowedToRunShards) throws Exception {
+    // check for an expectedSlices new collection - we poll the state
+    long timeoutAt = System.currentTimeMillis() + 120000;
+    boolean success = false;
+    String checkResult = "Didnt get to perform a single check";
+    while (System.currentTimeMillis() < timeoutAt) {
+      checkResult = checkCollectionExpectations(collectionName,
+          numShardsNumReplicaList, nodesAllowedToRunShards);
+      if (checkResult == null) {
+        success = true;
+        break;
+      }
+      Thread.sleep(500);
+    }
+    if (!success) {
+      super.printLayout();
+      fail(checkResult);
+    }
+  }
+  
+  volatile CloudSolrServer commondCloudSolrServer;
+  protected CloudSolrServer getCommonCloudSolrServer() {
+    if (commondCloudSolrServer == null) {
+      synchronized(this) {
+        try {
+          commondCloudSolrServer = new CloudSolrServer(zkServer.getZkAddress());
+          commondCloudSolrServer.setDefaultCollection(DEFAULT_COLLECTION);
+          commondCloudSolrServer.connect();
+        } catch (MalformedURLException e) {
+          throw new RuntimeException(e);
+        }
+      }
+    }
+    return commondCloudSolrServer;
+  }
 
 }

Modified: lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/core/MockDirectoryFactory.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/core/MockDirectoryFactory.java?rev=1487777&r1=1487776&r2=1487777&view=diff
==============================================================================
--- lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/core/MockDirectoryFactory.java (original)
+++ lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/core/MockDirectoryFactory.java Thu May 30 07:53:18 2013
@@ -17,10 +17,14 @@ package org.apache.solr.core;
  * limitations under the License.
  */
 
+import java.io.File;
 import java.io.IOException;
 
 import org.apache.lucene.store.Directory;
 import org.apache.lucene.store.MockDirectoryWrapper;
+import org.apache.lucene.store.NRTCachingDirectory;
+import org.apache.lucene.store.RateLimitedDirectoryWrapper;
+import org.apache.lucene.store.TrackingDirectoryWrapper;
 import org.apache.lucene.util.LuceneTestCase;
 
 /**
@@ -31,8 +35,13 @@ public class MockDirectoryFactory extend
   @Override
   protected Directory create(String path, DirContext dirContext) throws IOException {
     Directory dir = LuceneTestCase.newDirectory();
-    if (dir instanceof MockDirectoryWrapper) {
-      MockDirectoryWrapper mockDirWrapper = (MockDirectoryWrapper) dir;
+    
+    Directory cdir = reduce(dir);
+    cdir = reduce(cdir);
+    cdir = reduce(cdir);
+    
+    if (cdir instanceof MockDirectoryWrapper) {
+      MockDirectoryWrapper mockDirWrapper = (MockDirectoryWrapper) cdir;
       
       // we can't currently do this check because of how
       // Solr has to reboot a new Directory sometimes when replicating
@@ -55,4 +64,25 @@ public class MockDirectoryFactory extend
     return dir;
   }
 
+  private Directory reduce(Directory dir) {
+    Directory cdir = dir;
+    if (dir instanceof NRTCachingDirectory) {
+      cdir = ((NRTCachingDirectory)dir).getDelegate();
+    }
+    if (cdir instanceof RateLimitedDirectoryWrapper) {
+      cdir = ((RateLimitedDirectoryWrapper)dir).getDelegate();
+    }
+    if (cdir instanceof TrackingDirectoryWrapper) {
+      cdir = ((TrackingDirectoryWrapper)dir).getDelegate();
+    }
+    return cdir;
+  }
+  
+  @Override
+  public boolean isAbsolute(String path) {
+    // TODO: kind of a hack - we don't know what the delegate is, so
+    // we treat it as file based since this works on most ephem impls
+    return new File(path).isAbsolute();
+  }
+
 }

Modified: lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/core/MockFSDirectoryFactory.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/core/MockFSDirectoryFactory.java?rev=1487777&r1=1487776&r2=1487777&view=diff
==============================================================================
--- lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/core/MockFSDirectoryFactory.java (original)
+++ lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/core/MockFSDirectoryFactory.java Thu May 30 07:53:18 2013
@@ -22,6 +22,9 @@ import java.io.IOException;
 
 import org.apache.lucene.store.Directory;
 import org.apache.lucene.store.MockDirectoryWrapper;
+import org.apache.lucene.store.NRTCachingDirectory;
+import org.apache.lucene.store.RateLimitedDirectoryWrapper;
+import org.apache.lucene.store.TrackingDirectoryWrapper;
 import org.apache.lucene.util.LuceneTestCase;
 
 /**
@@ -38,9 +41,36 @@ public class MockFSDirectoryFactory exte
     // test assumes it can open an IndexWriter when that happens - we
     // have a new Directory for the same dir and still an open IW at 
     // this point
-    if (dir instanceof MockDirectoryWrapper) {
-      ((MockDirectoryWrapper)dir).setAssertNoUnrefencedFilesOnClose(false);
+    
+    Directory cdir = reduce(dir);
+    cdir = reduce(cdir);
+    cdir = reduce(cdir);
+    
+    if (cdir instanceof MockDirectoryWrapper) {
+      ((MockDirectoryWrapper)cdir).setAssertNoUnrefencedFilesOnClose(false);
+      ((MockDirectoryWrapper)cdir).setPreventDoubleWrite(false);
     }
     return dir;
   }
+  
+  @Override
+  public boolean isAbsolute(String path) {
+    // TODO: kind of a hack - we don't know what the delegate is, so
+    // we treat it as file based since this works on most ephem impls
+    return new File(path).isAbsolute();
+  }
+  
+  private Directory reduce(Directory dir) {
+    Directory cdir = dir;
+    if (dir instanceof NRTCachingDirectory) {
+      cdir = ((NRTCachingDirectory)dir).getDelegate();
+    }
+    if (cdir instanceof RateLimitedDirectoryWrapper) {
+      cdir = ((RateLimitedDirectoryWrapper)dir).getDelegate();
+    }
+    if (cdir instanceof TrackingDirectoryWrapper) {
+      cdir = ((TrackingDirectoryWrapper)dir).getDelegate();
+    }
+    return cdir;
+  }
 }

Modified: lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/util/BaseTestHarness.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/util/BaseTestHarness.java?rev=1487777&r1=1487776&r2=1487777&view=diff
==============================================================================
--- lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/util/BaseTestHarness.java (original)
+++ lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/util/BaseTestHarness.java Thu May 30 07:53:18 2013
@@ -178,7 +178,7 @@ abstract public class BaseTestHarness {
     return simpleTag("optimize", args);
   }
 
-  private static String simpleTag(String tag, String... args) {
+  public static String simpleTag(String tag, String... args) {
     try {
       StringWriter r = new StringWriter();
 

Modified: lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/util/RestTestBase.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/util/RestTestBase.java?rev=1487777&r1=1487776&r2=1487777&view=diff
==============================================================================
--- lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/util/RestTestBase.java (original)
+++ lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/util/RestTestBase.java Thu May 30 07:53:18 2013
@@ -108,7 +108,9 @@ abstract public class RestTestBase exten
         query = request.substring(queryStartPos + 1);
         path = request.substring(0, queryStartPos);
       }
-      query = setParam(query, "wt", "xml");
+      if ( ! query.matches(".*wt=schema\\.xml.*")) { // don't overwrite wt=schema.xml
+        query = setParam(query, "wt", "xml");
+      }
       request = path + '?' + setParam(query, "indent", "on");
 
       String response = restTestHarness.query(request);
@@ -227,7 +229,170 @@ abstract public class RestTestBase exten
 
     for (String test : tests) {
       if (null == test || 0 == test.length()) continue;
-      String testJSON = test.replace('\'', '"');
+      String testJSON = test.replaceAll("(?<!\\\\)\'", "\"");
+      testJSON = testJSON.replaceAll("\\\\\'", "'");
+
+      try {
+        failed = true;
+        String err = JSONTestUtil.match(response, testJSON, delta);
+        failed = false;
+        if (err != null) {
+          log.error("query failed JSON validation. error=" + err +
+              "\n expected =" + testJSON +
+              "\n response = " + response +
+              "\n request = " + request + "\n"
+          );
+          throw new RuntimeException(err);
+        }
+      } finally {
+        if (failed) {
+          log.error("JSON query validation threw an exception." +
+              "\n expected =" + testJSON +
+              "\n response = " + response +
+              "\n request = " + request + "\n"
+          );
+        }
+      }
+    }
+  }
+
+  
+  
+  /**
+   * Validates the response from a PUT request matches some JSON test expressions
+   * 
+   * @see org.apache.solr.JSONTestUtil#DEFAULT_DELTA
+   * @see #assertJQ(String,double,String...)
+   */
+  public static void assertJPut(String request, String content, String... tests) throws Exception {
+    assertJPut(request, content, JSONTestUtil.DEFAULT_DELTA, tests);
+  }
+
+
+  /**
+   * Validates the response from a PUT request matches some JSON test expressions
+   * and closes the query. The text expression is of the form path==JSON.
+   * To facilitate easy embedding in Java strings, the JSON can have double
+   * quotes replaced with single quotes.
+   * <p>
+   * Please use this with care: this makes it easy to match complete
+   * structures, but doing so can result in fragile tests if you are
+   * matching more than what you want to test.
+   * </p>
+   * @param request a URL path with optional query params, e.g. "/schema/fields?fl=id,_version_"
+   * @param content The content to include with the PUT request
+   * @param delta tolerance allowed in comparing float/double values
+   * @param tests JSON path expression + '==' + expected value
+   */
+  public static void assertJPut(String request, String content, double delta, String... tests) throws Exception {
+    int queryStartPos = request.indexOf('?');
+    String query;
+    String path;
+    if (-1 == queryStartPos) {
+      query = "";
+      path = request;
+    } else {
+      query = request.substring(queryStartPos + 1);
+      path = request.substring(0, queryStartPos);
+    }
+    query = setParam(query, "wt", "json");
+    request = path + '?' + setParam(query, "indent", "on");
+
+    String response;
+    boolean failed = true;
+    try {
+      response = restTestHarness.put(request, content);
+      failed = false;
+    } finally {
+      if (failed) {
+        log.error("REQUEST FAILED: " + request);
+      }
+    }
+
+    for (String test : tests) {
+      if (null == test || 0 == test.length()) continue;
+      String testJSON = test.replaceAll("(?<!\\\\)\'", "\"");
+      testJSON = testJSON.replaceAll("\\\\\'", "'");
+
+      try {
+        failed = true;
+        String err = JSONTestUtil.match(response, testJSON, delta);
+        failed = false;
+        if (err != null) {
+          log.error("query failed JSON validation. error=" + err +
+              "\n expected =" + testJSON +
+              "\n response = " + response +
+              "\n request = " + request + "\n"
+          );
+          throw new RuntimeException(err);
+        }
+      } finally {
+        if (failed) {
+          log.error("JSON query validation threw an exception." +
+              "\n expected =" + testJSON +
+              "\n response = " + response +
+              "\n request = " + request + "\n"
+          );
+        }
+      }
+    }
+  }
+
+  /**
+   * Validates the response from a POST request matches some JSON test expressions
+   *
+   * @see org.apache.solr.JSONTestUtil#DEFAULT_DELTA
+   * @see #assertJQ(String,double,String...)
+   */
+  public static void assertJPost(String request, String content, String... tests) throws Exception {
+    assertJPost(request, content, JSONTestUtil.DEFAULT_DELTA, tests);
+  }
+
+
+  /**
+   * Validates the response from a PUT request matches some JSON test expressions
+   * and closes the query. The text expression is of the form path==JSON.
+   * To facilitate easy embedding in Java strings, the JSON can have double
+   * quotes replaced with single quotes.
+   * <p>
+   * Please use this with care: this makes it easy to match complete
+   * structures, but doing so can result in fragile tests if you are
+   * matching more than what you want to test.
+   * </p>
+   * @param request a URL path with optional query params, e.g. "/schema/fields?fl=id,_version_"
+   * @param content The content to include with the PUT request
+   * @param delta tolerance allowed in comparing float/double values
+   * @param tests JSON path expression + '==' + expected value
+   */
+  public static void assertJPost(String request, String content, double delta, String... tests) throws Exception {
+    int queryStartPos = request.indexOf('?');
+    String query;
+    String path;
+    if (-1 == queryStartPos) {
+      query = "";
+      path = request;
+    } else {
+      query = request.substring(queryStartPos + 1);
+      path = request.substring(0, queryStartPos);
+    }
+    query = setParam(query, "wt", "json");
+    request = path + '?' + setParam(query, "indent", "on");
+
+    String response;
+    boolean failed = true;
+    try {
+      response = restTestHarness.post(request, content);
+      failed = false;
+    } finally {
+      if (failed) {
+        log.error("REQUEST FAILED: " + request);
+      }
+    }
+
+    for (String test : tests) {
+      if (null == test || 0 == test.length()) continue;
+      String testJSON = test.replaceAll("(?<!\\\\)\'", "\"");
+      testJSON = testJSON.replaceAll("\\\\\'", "'");
 
       try {
         failed = true;
@@ -253,6 +418,8 @@ abstract public class RestTestBase exten
     }
   }
 
+
+
   /**
    * Insures that the given param is included in the query with the given value.
    *

Modified: lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/util/RestTestHarness.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/util/RestTestHarness.java?rev=1487777&r1=1487776&r2=1487777&view=diff
==============================================================================
--- lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/util/RestTestHarness.java (original)
+++ lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/util/RestTestHarness.java Thu May 30 07:53:18 2013
@@ -17,12 +17,12 @@ package org.apache.solr.util;
  */
 
 import org.apache.commons.io.IOUtils;
-import org.eclipse.jetty.util.IO;
 
 import javax.xml.xpath.XPathExpressionException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
 import java.io.StringWriter;
 import java.net.HttpURLConnection;
 import java.net.URL;
@@ -43,20 +43,37 @@ public class RestTestHarness extends Bas
   }
   
   /**
-   * Validates a "query" response against an array of XPath test strings
+   * Validates an XML "query" response against an array of XPath test strings
    *
    * @param request the Query to process
    * @return null if all good, otherwise the first test that fails.
    * @exception Exception any exception in the response.
    * @exception java.io.IOException if there is a problem writing the XML
    */
-  public String validateQuery(String request, String... tests)
-      throws Exception {
+  public String validateQuery(String request, String... tests) throws Exception {
 
     String res = query(request);
     return validateXPath(res, tests);
   }
 
+
+  /**
+   * Validates an XML PUT response against an array of XPath test strings
+   *
+   * @param request the PUT request to process
+   * @param content the content to send with the PUT request
+   * @param tests the validating XPath tests
+   * @return null if all good, otherwise the first test that fails.
+   * @exception Exception any exception in the response.
+   * @exception java.io.IOException if there is a problem writing the XML
+   */
+  public String validatePut(String request, String content, String... tests) throws Exception {
+
+    String res = put(request, content);
+    return validateXPath(res, tests);
+  }
+
+
   /**
    * Processes a "query" using a URL path (with no context path) + optional query params,
    * e.g. "/schema/fields?indent=on"
@@ -84,7 +101,74 @@ public class RestTestHarness extends Bas
     return strWriter.toString();
   }
 
-  public String checkQueryStatus(String xml, String code) throws Exception {
+  /**
+   * Processes a PUT request using a URL path (with no context path) + optional query params,
+   * e.g. "/schema/fields/newfield", PUTs the given content, and returns the response content.
+   * 
+   * @param request The URL path and optional query params
+   * @param content The content to include with the PUT request
+   * @return The response to the PUT request
+   */
+  public String put(String request, String content) throws IOException {
+    URL url = new URL(getBaseURL() + request);
+    HttpURLConnection connection = (HttpURLConnection)url.openConnection();
+    connection.setDoOutput(true);
+    connection.setRequestMethod("PUT");
+    OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream(), "UTF-8");
+    out.write(content);
+    out.close();
+    InputStream inputStream = null;
+    StringWriter stringWriter;
+    try {
+      try {
+        inputStream = connection.getInputStream();
+      } catch (IOException e) {
+        inputStream = connection.getErrorStream();
+      }
+      stringWriter = new StringWriter();
+      IOUtils.copy(new InputStreamReader(inputStream, "UTF-8"), stringWriter);
+    } finally {
+      IOUtils.closeQuietly(inputStream);
+    }
+    return stringWriter.toString();
+  }
+
+  /**
+   * Processes a POST request using a URL path (with no context path) + optional query params,
+   * e.g. "/schema/fields/newfield", PUTs the given content, and returns the response content.
+   *
+   * @param request The URL path and optional query params
+   * @param content The content to include with the POST request
+   * @return The response to the PUT request
+   */
+  public String post(String request, String content) throws IOException {
+    URL url = new URL(getBaseURL() + request);
+    HttpURLConnection connection = (HttpURLConnection)url.openConnection();
+    connection.setDoOutput(true);
+    connection.setRequestMethod("POST");
+    connection.setRequestProperty("Content-Type", "application/json; charset=utf-8");
+
+    OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream(), "UTF-8");
+    out.write(content);
+    out.close();
+    InputStream inputStream = null;
+    StringWriter stringWriter;
+    try {
+      try {
+        inputStream = connection.getInputStream();
+      } catch (IOException e) {
+        inputStream = connection.getErrorStream();
+      }
+      stringWriter = new StringWriter();
+      IOUtils.copy(new InputStreamReader(inputStream, "UTF-8"), stringWriter);
+    } finally {
+      IOUtils.closeQuietly(inputStream);
+    }
+    return stringWriter.toString();
+  }
+
+
+  public String checkResponseStatus(String xml, String code) throws Exception {
     try {
       String response = query(xml);
       String valid = validateXPath(response, "//int[@name='status']="+code );
@@ -94,9 +178,10 @@ public class RestTestHarness extends Bas
     }
   }
 
+  
   @Override
   public void reload() throws Exception {
-    String xml = checkQueryStatus("/admin/cores?action=RELOAD", "0");
+    String xml = checkResponseStatus("/admin/cores?action=RELOAD", "0");
     if (null != xml) {
       throw new RuntimeException("RELOAD failed:\n" + xml);
     }
@@ -112,7 +197,7 @@ public class RestTestHarness extends Bas
   @Override
   public String update(String xml) {
     try {
-      return query("/update?stream.base=" + URLEncoder.encode(xml, "UTF-8"));
+      return query("/update?stream.body=" + URLEncoder.encode(xml, "UTF-8"));
     } catch (Exception e) {
       throw new RuntimeException(e);
     }

Modified: lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/util/TestHarness.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/util/TestHarness.java?rev=1487777&r1=1487776&r2=1487777&view=diff
==============================================================================
--- lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/util/TestHarness.java (original)
+++ lucene/dev/branches/security/solr/test-framework/src/java/org/apache/solr/util/TestHarness.java Thu May 30 07:53:18 2013
@@ -20,6 +20,8 @@ package org.apache.solr.util;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.params.CommonParams;
 import org.apache.solr.common.util.NamedList;
+import org.apache.solr.core.Config;
+import org.apache.solr.core.ConfigSolrXmlOld;
 import org.apache.solr.core.SolrConfig;
 import org.apache.solr.core.SolrCore;
 import org.apache.solr.core.CoreContainer;
@@ -36,15 +38,21 @@ import org.apache.solr.request.SolrReque
 import org.apache.solr.response.QueryResponseWriter;
 import org.apache.solr.response.SolrQueryResponse;
 import org.apache.solr.schema.IndexSchema;
+import org.apache.solr.schema.IndexSchemaFactory;
 import org.apache.solr.servlet.DirectSolrConnection;
 import org.apache.solr.common.util.NamedList.NamedListEntry;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
 
+import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.IOException;
 import java.io.StringWriter;
 import java.util.HashMap;
 import java.util.Map;
 
+import javax.xml.parsers.ParserConfigurationException;
+
 
 /**
  * This class provides a simple harness that may be useful when
@@ -87,6 +95,30 @@ public class TestHarness extends BaseTes
     return createConfig(solrHome, CoreContainer.DEFAULT_DEFAULT_CORE_NAME, confFile);
   }
 
+  /**
+   * @param coreName to initialize
+   * @param dataDirectory path for index data, will not be cleaned up
+   * @param solrConfig solronfig instance
+   * @param schemaFile schema filename
+   */
+     public TestHarness( String coreName,
+                         String dataDirectory,
+                         SolrConfig solrConfig,
+                         String schemaFile) {
+    this( coreName, dataDirectory, solrConfig, IndexSchemaFactory.buildIndexSchema(schemaFile, solrConfig));
+  } 
+  /**
+   * @param coreName to initialize
+   * @param dataDirectory path for index data, will not be cleaned up
+   * @param solrConfig solrconfig instance
+   * @param indexSchema schema instance
+   */
+    public TestHarness( String coreName,
+                        String dataDirectory,
+                        SolrConfig solrConfig,
+                        IndexSchema indexSchema) {
+        this(coreName, new Initializer(coreName, dataDirectory, solrConfig, indexSchema));
+    }
    /**
     * @param dataDirectory path for index data, will not be cleaned up
     * @param solrConfig solronfig instance
@@ -95,7 +127,7 @@ public class TestHarness extends BaseTes
       public TestHarness( String dataDirectory,
                           SolrConfig solrConfig,
                           String schemaFile) {
-     this( dataDirectory, solrConfig, new IndexSchema(solrConfig, schemaFile, null));
+     this( dataDirectory, solrConfig, IndexSchemaFactory.buildIndexSchema(schemaFile, solrConfig));
    }
    /**
     * @param dataDirectory path for index data, will not be cleaned up
@@ -146,17 +178,30 @@ public class TestHarness extends BaseTes
     }
     @Override
     public CoreContainer initialize() {
-      CoreContainer container = new CoreContainer(new SolrResourceLoader(SolrResourceLoader.locateSolrHome())) {
-        {
-          hostPort = System.getProperty("hostPort");
-          hostContext = "solr";
-          defaultCoreName = CoreContainer.DEFAULT_DEFAULT_CORE_NAME;
-          initShardHandler();
-          initZooKeeper(System.getProperty("zkHost"), 10000);
-        }
-      };
+      CoreContainer container;
+      try {
+        String solrHome = SolrResourceLoader.locateSolrHome();
+        container = new CoreContainer(new SolrResourceLoader(solrHome)) {
+          {
+            String hostPort = System.getProperty("hostPort", "8983");
+            String hostContext = System.getProperty("hostContext", "solr");
+            defaultCoreName = CoreContainer.DEFAULT_DEFAULT_CORE_NAME;
+            initShardHandler();
+            zkSys.initZooKeeper(this, solrHome, System.getProperty("zkHost"), 30000, hostPort, hostContext, null, "30000", 30000, 30000);
+            ByteArrayInputStream is = new ByteArrayInputStream(ConfigSolrXmlOld.DEF_SOLR_XML.getBytes("UTF-8"));
+            Config config = new Config(loader, null, new InputSource(is), null, false);
+            cfg = new ConfigSolrXmlOld(config, this);
+          }
+        };
+      } catch (ParserConfigurationException e) {
+        throw new RuntimeException(e);
+      } catch (IOException e) {
+        throw new RuntimeException(e);
+      } catch (SAXException e) {
+        throw new RuntimeException(e);
+      }
       LogWatcher<?> logging = new JulWatcher("test");
-      logging.registerListener(new ListenerConfig(), container);
+      logging.registerListener(new ListenerConfig());
       container.setLogging(logging);
       
       CoreDescriptor dcore = new CoreDescriptor(container, coreName, solrConfig.getResourceLoader().getInstanceDir());

Modified: lucene/dev/branches/security/solr/webapp/build.xml
URL: http://svn.apache.org/viewvc/lucene/dev/branches/security/solr/webapp/build.xml?rev=1487777&r1=1487776&r2=1487777&view=diff
==============================================================================
--- lucene/dev/branches/security/solr/webapp/build.xml (original)
+++ lucene/dev/branches/security/solr/webapp/build.xml Thu May 30 07:53:18 2013
@@ -20,7 +20,7 @@
 
   <import file="../common-build.xml"/>
 
-	<property name="exclude.from.war" value="" />
+  <property name="exclude.from.war" value="*slf4j*,log4j-*" />
   <property name="solr.war.suffix" value="" />
 	
   <target name="test" depends="compile-test-solr-core">
@@ -42,7 +42,7 @@
           depends="test, init-dist, dist-core, dist-solrj, lucene-jars-to-solr">
     <build-manifest title="Apache Solr Search Server"
                     implementation.title="org.apache.solr"
-                    spec.version="${solr.spec.version}"/>
+                    spec.version="${spec.version}"/>
     <ant dir="${common-solr.dir}" inheritall="false" target="contribs-add-to-war"/>
     <war destfile="${dist}/solr-${version}${solr.war.suffix}.war"
          webxml="web/WEB-INF/web.xml"
@@ -50,6 +50,8 @@
       <lib dir="${common-solr.dir}/core/lib" excludes="${exclude.from.war},${common.classpath.excludes}">
         <exclude name="*servlet-api*.jar" />
         <exclude name="easymock-*.jar" />
+        <exclude name="cglib-nodep-*.jar" />
+        <exclude name="objenesis-*.jar" />
       </lib>
       <lib dir="${common-solr.dir}/solrj/lib" excludes="${exclude.from.war},${common.classpath.excludes}"/>
       <lib dir="${lucene-libs}" excludes="${exclude.from.war},${common.classpath.excludes}" />

Modified: lucene/dev/branches/security/solr/webapp/web/WEB-INF/web.xml
URL: http://svn.apache.org/viewvc/lucene/dev/branches/security/solr/webapp/web/WEB-INF/web.xml?rev=1487777&r1=1487776&r2=1487777&view=diff
==============================================================================
--- lucene/dev/branches/security/solr/webapp/web/WEB-INF/web.xml (original)
+++ lucene/dev/branches/security/solr/webapp/web/WEB-INF/web.xml Thu May 30 07:53:18 2013
@@ -127,11 +127,11 @@
   </servlet>
 
   <servlet>
-    <servlet-name>SchemaRestApi</servlet-name>
+    <servlet-name>SolrRestApi</servlet-name>
     <servlet-class>org.restlet.ext.servlet.ServerServlet</servlet-class>
     <init-param>
       <param-name>org.restlet.application</param-name>
-      <param-value>org.apache.solr.rest.SchemaRestApi</param-value>
+      <param-value>org.apache.solr.rest.SolrRestApi</param-value>
     </init-param>
   </servlet>
   
@@ -164,7 +164,7 @@
   </servlet-mapping>
 
   <servlet-mapping>
-    <servlet-name>SchemaRestApi</servlet-name>
+    <servlet-name>SolrRestApi</servlet-name>
     <url-pattern>/schema/*</url-pattern>
   </servlet-mapping>
   

Modified: lucene/dev/branches/security/solr/webapp/web/admin.html
URL: http://svn.apache.org/viewvc/lucene/dev/branches/security/solr/webapp/web/admin.html?rev=1487777&r1=1487776&r2=1487777&view=diff
==============================================================================
--- lucene/dev/branches/security/solr/webapp/web/admin.html (original)
+++ lucene/dev/branches/security/solr/webapp/web/admin.html Thu May 30 07:53:18 2013
@@ -22,24 +22,24 @@ limitations under the License.
     
   <title>Solr Admin</title>
     
-  <link rel="icon" type="image/ico" href="img/favicon.ico">
+  <link rel="icon" type="image/ico" href="img/favicon.ico?_=${version}">
 
-  <link rel="stylesheet" type="text/css" href="css/styles/common.css">
-  <link rel="stylesheet" type="text/css" href="css/styles/analysis.css">
-  <link rel="stylesheet" type="text/css" href="css/styles/cloud.css">
-  <link rel="stylesheet" type="text/css" href="css/styles/cores.css">
-  <link rel="stylesheet" type="text/css" href="css/styles/dashboard.css">
-  <link rel="stylesheet" type="text/css" href="css/styles/dataimport.css">
-  <link rel="stylesheet" type="text/css" href="css/styles/index.css">
-  <link rel="stylesheet" type="text/css" href="css/styles/java-properties.css">
-  <link rel="stylesheet" type="text/css" href="css/styles/logging.css">
-  <link rel="stylesheet" type="text/css" href="css/styles/menu.css">
-  <link rel="stylesheet" type="text/css" href="css/styles/plugins.css">
-  <link rel="stylesheet" type="text/css" href="css/styles/query.css">
-  <link rel="stylesheet" type="text/css" href="css/styles/replication.css">
-  <link rel="stylesheet" type="text/css" href="css/styles/schema-browser.css">
-  <link rel="stylesheet" type="text/css" href="css/styles/threads.css">
-  <link rel="stylesheet" type="text/css" href="css/chosen.css">
+  <link rel="stylesheet" type="text/css" href="css/styles/common.css?_=${version}">
+  <link rel="stylesheet" type="text/css" href="css/styles/analysis.css?_=${version}">
+  <link rel="stylesheet" type="text/css" href="css/styles/cloud.css?_=${version}">
+  <link rel="stylesheet" type="text/css" href="css/styles/cores.css?_=${version}">
+  <link rel="stylesheet" type="text/css" href="css/styles/dashboard.css?_=${version}">
+  <link rel="stylesheet" type="text/css" href="css/styles/dataimport.css?_=${version}">
+  <link rel="stylesheet" type="text/css" href="css/styles/index.css?_=${version}">
+  <link rel="stylesheet" type="text/css" href="css/styles/java-properties.css?_=${version}">
+  <link rel="stylesheet" type="text/css" href="css/styles/logging.css?_=${version}">
+  <link rel="stylesheet" type="text/css" href="css/styles/menu.css?_=${version}">
+  <link rel="stylesheet" type="text/css" href="css/styles/plugins.css?_=${version}">
+  <link rel="stylesheet" type="text/css" href="css/styles/query.css?_=${version}">
+  <link rel="stylesheet" type="text/css" href="css/styles/replication.css?_=${version}">
+  <link rel="stylesheet" type="text/css" href="css/styles/schema-browser.css?_=${version}">
+  <link rel="stylesheet" type="text/css" href="css/styles/threads.css?_=${version}">
+  <link rel="stylesheet" type="text/css" href="css/chosen.css?_=${version}">
     
   <script type="text/javascript">
     
@@ -138,8 +138,9 @@ limitations under the License.
     </div>
     
   </div>
-    
-  <script src="js/require.js" data-main="js/main"></script>
+  
+  <script type="text/javascript"> var require = { urlArgs: '_=${version}' }; </script>
+  <script src="js/require.js?_=${version}" data-main="js/main"></script>
 
 </body>
 </html>

Modified: lucene/dev/branches/security/solr/webapp/web/css/styles/logging.css
URL: http://svn.apache.org/viewvc/lucene/dev/branches/security/solr/webapp/web/css/styles/logging.css?rev=1487777&r1=1487776&r2=1487777&view=diff
==============================================================================
--- lucene/dev/branches/security/solr/webapp/web/css/styles/logging.css (original)
+++ lucene/dev/branches/security/solr/webapp/web/css/styles/logging.css Thu May 30 07:53:18 2013
@@ -84,8 +84,13 @@
 }
 
 #content #logging #viewer tbody .level-info .level span { background-color: #ebf5eb; }
-#content #logging #viewer tbody .level-warning span { background-color: #d5dd00; }
-#content #logging #viewer tbody .level-severe span  { background-color: #c43c35; color: #fff; }
+#content #logging #viewer tbody .level-warning     span { background-color: #FFD930; }
+#content #logging #viewer tbody .level-severe     span  { background-color: #c43c35; color: #fff; }
+
+#content #logging #viewer tbody .level-debug  span  { background-color: #ebf5eb; }
+#content #logging #viewer tbody .level-warn   span  { background-color: #FFD930; }
+#content #logging #viewer tbody .level-error  span  { background-color: #FF6130; }
+#content #logging #viewer tbody .level-fatal  span  { background-color: #c43c35; }
 
 #content #logging #viewer tbody .has-trace a
 {
@@ -188,6 +193,13 @@
   padding-right: 21px;
 }
 
+#content #logging .jstree a span.name em
+{
+  color: #f00;
+  font-style: normal;
+  text-transform: uppercase;
+}
+
 #content #logging .jstree a.trigger.set
 {
   font-weight: bold;
@@ -219,6 +231,7 @@
 #content #logging .jstree li li li li li .selector-holder { left: 368px; }
 #content #logging .jstree li li li li li li .selector-holder { left: 350px; }
 #content #logging .jstree li li li li li li li .selector-holder { left: 332px; }
+#content #logging .jstree li li li li li li li li .selector-holder { left: 314px; }
 
 #content #logging .jstree .selector
 {

Modified: lucene/dev/branches/security/solr/webapp/web/css/styles/replication.css
URL: http://svn.apache.org/viewvc/lucene/dev/branches/security/solr/webapp/web/css/styles/replication.css?rev=1487777&r1=1487776&r2=1487777&view=diff
==============================================================================
--- lucene/dev/branches/security/solr/webapp/web/css/styles/replication.css (original)
+++ lucene/dev/branches/security/solr/webapp/web/css/styles/replication.css Thu May 30 07:53:18 2013
@@ -283,6 +283,7 @@
 
 #content #replication #details table
 {
+  margin-left: 20px;
   border-collapse: collapse;
 }
 
@@ -339,6 +340,7 @@
 {
   padding-right: 10px;
   text-align: right;
+  white-space: nowrap;
 }
 
 #content #replication #details table tbody .size

Modified: lucene/dev/branches/security/solr/webapp/web/js/scripts/analysis.js
URL: http://svn.apache.org/viewvc/lucene/dev/branches/security/solr/webapp/web/js/scripts/analysis.js?rev=1487777&r1=1487776&r2=1487777&view=diff
==============================================================================
--- lucene/dev/branches/security/solr/webapp/web/js/scripts/analysis.js (original)
+++ lucene/dev/branches/security/solr/webapp/web/js/scripts/analysis.js Thu May 30 07:53:18 2013
@@ -247,9 +247,12 @@ sammy.get
             'submit',
             function( event )
             {
-              var params = compute_analysis_params();
+              var params = $.param( compute_analysis_params() )
+                            .replace( /[\w\.]+=\+*(&)/g, '$1' ) // remove empty parameters
+                            .replace( /(&)+/, '$1' )            // reduce multiple ampersands
+                            .replace( /^&/, '' );               // remove leading ampersand
 
-              context.redirect( context.path.split( '?' ).shift() + '?' + $.param( params ) );
+              context.redirect( context.path.split( '?' ).shift() + '?' + params );
               return false;
             }
           )

Modified: lucene/dev/branches/security/solr/webapp/web/js/scripts/app.js
URL: http://svn.apache.org/viewvc/lucene/dev/branches/security/solr/webapp/web/js/scripts/app.js?rev=1487777&r1=1487776&r2=1487777&view=diff
==============================================================================
--- lucene/dev/branches/security/solr/webapp/web/js/scripts/app.js (original)
+++ lucene/dev/branches/security/solr/webapp/web/js/scripts/app.js Thu May 30 07:53:18 2013
@@ -585,4 +585,5 @@ var solr_admin = function( app_config )
 
 };
 
+$.ajaxSetup( { cache: false } );
 var app = new solr_admin( app_config );

Modified: lucene/dev/branches/security/solr/webapp/web/js/scripts/dashboard.js
URL: http://svn.apache.org/viewvc/lucene/dev/branches/security/solr/webapp/web/js/scripts/dashboard.js?rev=1487777&r1=1487776&r2=1487777&view=diff
==============================================================================
--- lucene/dev/branches/security/solr/webapp/web/js/scripts/dashboard.js (original)
+++ lucene/dev/branches/security/solr/webapp/web/js/scripts/dashboard.js Thu May 30 07:53:18 2013
@@ -48,27 +48,26 @@ sammy.get
     content_element
       .removeClass( 'single' );
     
-    var core_menu = this.active_core.closest( 'ul' );
-    if( !core_menu.data( 'admin-extra-loaded' ) )
+    if( !app.core_menu.data( 'admin-extra-loaded' ) )
     {
-      core_menu.data( 'admin-extra-loaded', new Date() );
+      app.core_menu.data( 'admin-extra-loaded', new Date() );
 
       $.get
       (
-        core_basepath + '/admin/file/?file=admin-extra.menu-top.html',
+        core_basepath + '/admin/file/?file=admin-extra.menu-top.html&contentType=text/html;charset=utf-8',
         function( menu_extra )
         {
-          core_menu
+          app.core_menu
             .prepend( menu_extra );
         }
       );
-            
+      
       $.get
       (
-        core_basepath + '/admin/file/?file=admin-extra.menu-bottom.html',
+        core_basepath + '/admin/file/?file=admin-extra.menu-bottom.html&contentType=text/html;charset=utf-8',
         function( menu_extra )
         {
-          core_menu
+          app.core_menu
             .append( menu_extra );
         }
       );
@@ -257,7 +256,8 @@ sammy.get
               var is_slave = 'undefined' !== typeof( data.slave );
               var headline = $( 'h2 span', this );
               var details_element = $( '#details', this );
-              var current_type_element = $( ( is_slave ? '.slave' : '.master' ), this );
+              var current_type_element = $( ( is_slave ? '.slave' : '.masterSearch' ), this );
+              var master_data = is_slave ? data.slave.masterDetails : data;
 
               if( is_slave )
               {
@@ -276,6 +276,7 @@ sammy.get
                   .html( headline.html() + ' (Master)' );
               }
 
+              // the currently searchable commit regardless of type
               $( '.version div', current_type_element )
                 .html( data.indexVersion );
               $( '.generation div', current_type_element )
@@ -283,9 +284,18 @@ sammy.get
               $( '.size div', current_type_element )
                 .html( data.indexSize );
                             
+              // what's replicable on the master
+              var master_element = $( '.master', details_element );
+              $( '.version div', master_element )
+                .html( master_data.master.replicableVersion || '-' );
+              $( '.generation div', master_element )
+                .html( master_data.master.replicableGeneration || '-' );
+              $( '.size div', master_element )
+                .html( "-" );
+
               if( is_slave )
               {
-                var master_element = $( '.master', details_element );
+                var master_element = $( '.masterSearch', details_element );
                 $( '.version div', master_element )
                   .html( data.slave.masterDetails.indexVersion );
                 $( '.generation div', master_element )
@@ -293,7 +303,8 @@ sammy.get
                 $( '.size div', master_element )
                   .html( data.slave.masterDetails.indexSize );
                                 
-                if( data.indexVersion !== data.slave.masterDetails.indexVersion )
+                // warnings if slave version|gen doesn't match what's replicable
+                if( data.indexVersion !== master_data.master.replicableVersion )
                 {
                   $( '.version', details_element )
                     .addClass( 'diff' );
@@ -304,7 +315,7 @@ sammy.get
                     .removeClass( 'diff' );
                 }
                                 
-                if( data.generation !== data.slave.masterDetails.generation )
+                if( data.generation !== master_data.master.replicableGeneration )
                 {
                   $( '.generation', details_element )
                     .addClass( 'diff' );

Modified: lucene/dev/branches/security/solr/webapp/web/js/scripts/logging.js
URL: http://svn.apache.org/viewvc/lucene/dev/branches/security/solr/webapp/web/js/scripts/logging.js?rev=1487777&r1=1487776&r2=1487777&view=diff
==============================================================================
--- lucene/dev/branches/security/solr/webapp/web/js/scripts/logging.js (original)
+++ lucene/dev/branches/security/solr/webapp/web/js/scripts/logging.js Thu May 30 07:53:18 2013
@@ -99,17 +99,20 @@ var logging_handler = function( response
         logger_content += '<span class="ns">' + logger_name.join( '.' ).esc() + '.</span>';
       }
 
-      logger_content += '<span class="name">' + display_name.esc() + '</span>' + "\n";
+      logger_content += '<span class="name">' + ( display_name ? display_name.esc() : '<em>empty</em>' ) + '</span>' + "\n";
       logger_content += '</a>';
 
       logger_content += levels;
 
-      var child_logger_content = logger_tree( logger.name );
-      if( child_logger_content )
+      if( !!logger.name )
       {
-        logger_content += '<ul>';
-        logger_content += child_logger_content;
-        logger_content += '</ul>';
+        var child_logger_content = logger_tree( logger.name );
+        if( child_logger_content )
+        {
+          logger_content += '<ul>';
+          logger_content += child_logger_content;
+          logger_content += '</ul>';
+        }
       }
 
       logger_content += '</li>';

Modified: lucene/dev/branches/security/solr/webapp/web/js/scripts/replication.js
URL: http://svn.apache.org/viewvc/lucene/dev/branches/security/solr/webapp/web/js/scripts/replication.js?rev=1487777&r1=1487776&r2=1487777&view=diff
==============================================================================
--- lucene/dev/branches/security/solr/webapp/web/js/scripts/replication.js (original)
+++ lucene/dev/branches/security/solr/webapp/web/js/scripts/replication.js Thu May 30 07:53:18 2013
@@ -246,26 +246,39 @@ var replication_fetch_status = function(
         }
 
         var details_element = $( '#details', replication_element );
-        var current_type_element = $( ( is_slave ? '.slave' : '.master' ), details_element );
+        var current_type_element = $( ( is_slave ? '.slave' : '.masterSearch' ), details_element );
+        var master_data = is_slave ? data.slave.masterDetails : data;
 
+        // the currently searchable commit regardless of type
         $( '.version div', current_type_element )
           .html( data.indexVersion );
         $( '.generation div', current_type_element )
           .html( data.generation );
         $( '.size div', current_type_element )
           .html( data.indexSize );
+
+        // what's replicable on the master
+        var master_element = $( '.master', details_element );
+        $( '.version div', master_element )
+          .html( master_data.master.replicableVersion || '-' );
+        $( '.generation div', master_element )
+          .html( master_data.master.replicableGeneration || '-' );
+        $( '.size div', master_element )
+          .html( "-" );
                 
         if( is_slave )
         {
-          var master_element = $( '.master', details_element );
-          $( '.version div', master_element )
-            .html( data.slave.masterDetails.indexVersion );
-          $( '.generation div', master_element )
-            .html( data.slave.masterDetails.generation );
-          $( '.size div', master_element )
-            .html( data.slave.masterDetails.indexSize );
-                    
-          if( data.indexVersion !== data.slave.masterDetails.indexVersion )
+          // what's searchable on the master
+          var master_searchable = $( '.masterSearch', details_element );
+          $( '.version div', master_searchable )
+            .html( master_data.indexVersion );
+          $( '.generation div', master_searchable )
+            .html( master_data.generation );
+          $( '.size div', master_searchable )
+            .html( master_data.indexSize );
+ 
+          // warnings if slave version|gen doesn't match what's replicable
+          if( data.indexVersion !== master_data.master.replicableVersion )
           {
             $( '.version', details_element )
               .addClass( 'diff' );
@@ -276,7 +289,7 @@ var replication_fetch_status = function(
               .removeClass( 'diff' );
           }
                     
-          if( data.generation !== data.slave.masterDetails.generation )
+          if( data.generation !== master_data.master.replicableGeneration )
           {
             $( '.generation', details_element )
               .addClass( 'diff' );
@@ -511,4 +524,4 @@ sammy.get
       }
     );
   }
-);
\ No newline at end of file
+);

Modified: lucene/dev/branches/security/solr/webapp/web/tpl/dashboard.html
URL: http://svn.apache.org/viewvc/lucene/dev/branches/security/solr/webapp/web/tpl/dashboard.html?rev=1487777&r1=1487776&r2=1487777&view=diff
==============================================================================
--- lucene/dev/branches/security/solr/webapp/web/tpl/dashboard.html (original)
+++ lucene/dev/branches/security/solr/webapp/web/tpl/dashboard.html Thu May 30 07:53:18 2013
@@ -103,9 +103,18 @@ limitations under the License.
           </thead>
           <tbody>
 
+            <tr class="masterSearch">
+
+              <th>Master (Searching)</th>
+              <td class="version"><div>x</div></td>
+              <td class="generation"><div>y</div></td>
+              <td class="size"><div>z</div></td>
+
+            </tr>
+
             <tr class="master">
 
-              <th>Master:</th>
+              <th>Master (Replicable)</th>
               <td class="version"><div>x</div></td>
               <td class="generation"><div>y</div></td>
               <td class="size"><div>z</div></td>
@@ -114,7 +123,7 @@ limitations under the License.
 
             <tr class="slave slaveOnly">
 
-              <th>Slave:</th>
+              <th>Slave (Searching)</th>
               <td class="version"><div>a</div></td>
               <td class="generation"><div>c</div></td>
               <td class="size"><div>c</div></td>

Modified: lucene/dev/branches/security/solr/webapp/web/tpl/replication.html
URL: http://svn.apache.org/viewvc/lucene/dev/branches/security/solr/webapp/web/tpl/replication.html?rev=1487777&r1=1487776&r2=1487777&view=diff
==============================================================================
--- lucene/dev/branches/security/solr/webapp/web/tpl/replication.html (original)
+++ lucene/dev/branches/security/solr/webapp/web/tpl/replication.html Thu May 30 07:53:18 2013
@@ -116,9 +116,18 @@ limitations under the License.
         </thead>
         <tbody>
 
+          <tr class="masterSearch">
+
+            <th>Master (Searching)</th>
+            <td class="version"><div></div></td>
+            <td class="generation"><div></div></td>
+            <td class="size"><div></div></td>
+
+          </tr>
+
           <tr class="master">
 
-            <th>Master:</th>
+            <th>Master (Replicable)</th>
             <td class="version"><div></div></td>
             <td class="generation"><div></div></td>
             <td class="size"><div></div></td>
@@ -127,7 +136,7 @@ limitations under the License.
 
           <tr class="slave slaveOnly">
 
-            <th>Slave:</th>
+            <th>Slave (Searching)</th>
             <td class="version"><div></div></td>
             <td class="generation"><div></div></td>
             <td class="size"><div></div></td>
@@ -204,4 +213,4 @@ limitations under the License.
     
   </div>
 
-</div>
\ No newline at end of file
+</div>