You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by sa...@apache.org on 2014/03/14 15:05:49 UTC

svn commit: r1577540 - in /lucene/dev/trunk/solr: ./ core/src/java/org/apache/solr/rest/schema/analysis/ core/src/test-files/solr/collection1/conf/ core/src/test/org/apache/solr/rest/ core/src/test/org/apache/solr/rest/schema/analysis/ example/solr/col...

Author: sarowe
Date: Fri Mar 14 14:05:49 2014
New Revision: 1577540

URL: http://svn.apache.org/r1577540
Log:
Create a stopword filter factory that is (re)configurable, and capable of reporting its configuration, via REST API

Added:
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/rest/schema/analysis/BaseManagedTokenFilterFactory.java   (with props)
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/rest/schema/analysis/ManagedStopFilterFactory.java   (with props)
    lucene/dev/trunk/solr/core/src/test/org/apache/solr/rest/schema/analysis/
    lucene/dev/trunk/solr/core/src/test/org/apache/solr/rest/schema/analysis/TestManagedStopFilterFactory.java   (with props)
    lucene/dev/trunk/solr/example/solr/collection1/conf/_schema_analysis_stopwords_english.json   (with props)
Modified:
    lucene/dev/trunk/solr/CHANGES.txt
    lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/schema-rest.xml
    lucene/dev/trunk/solr/core/src/test/org/apache/solr/rest/SolrRestletTestBase.java
    lucene/dev/trunk/solr/example/solr/collection1/conf/schema.xml
    lucene/dev/trunk/solr/test-framework/src/java/org/apache/solr/util/BaseTestHarness.java
    lucene/dev/trunk/solr/test-framework/src/java/org/apache/solr/util/RestTestHarness.java

Modified: lucene/dev/trunk/solr/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/CHANGES.txt?rev=1577540&r1=1577539&r2=1577540&view=diff
==============================================================================
--- lucene/dev/trunk/solr/CHANGES.txt (original)
+++ lucene/dev/trunk/solr/CHANGES.txt Fri Mar 14 14:05:49 2014
@@ -119,6 +119,10 @@ New Features
 * SOLR-5653: Create a RestManager to provide REST API endpoints for
   reconfigurable plugins. (Tim Potter, Steve Rowe)
 
+* SOLR-5655: Create a stopword filter factory that is (re)configurable,
+  and capable of reporting its configuration, via REST API.
+  (Tim Potter via Steve Rowe)
+
 * SOLR-5477: Async execution of OverseerCollectionProcessor(CollectionsAPI)
   tasks. (Anshum Gupta)
 

Added: lucene/dev/trunk/solr/core/src/java/org/apache/solr/rest/schema/analysis/BaseManagedTokenFilterFactory.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/rest/schema/analysis/BaseManagedTokenFilterFactory.java?rev=1577540&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/rest/schema/analysis/BaseManagedTokenFilterFactory.java (added)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/rest/schema/analysis/BaseManagedTokenFilterFactory.java Fri Mar 14 14:05:49 2014
@@ -0,0 +1,82 @@
+package org.apache.solr.rest.schema.analysis;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.lucene.analysis.util.ResourceLoader;
+import org.apache.lucene.analysis.util.ResourceLoaderAware;
+import org.apache.lucene.analysis.util.TokenFilterFactory;
+import org.apache.solr.core.SolrResourceLoader;
+import org.apache.solr.rest.ManagedResource;
+import org.apache.solr.rest.ManagedResourceObserver;
+
+/**
+ * Abstract based class for implementing TokenFilterFactory objects that
+ * are managed by the REST API. Specifically, this base class is useful
+ * for token filters that have configuration and data that needs to be
+ * updated programmatically, such as to support a UI for adding synonyms.  
+ */
+public abstract class BaseManagedTokenFilterFactory extends TokenFilterFactory 
+  implements ResourceLoaderAware, ManagedResourceObserver
+{
+  
+  protected String handle;
+  
+  protected BaseManagedTokenFilterFactory(Map<String,String> args) {
+    super(args);    
+    handle = require(args, "managed");
+    
+    if (!args.isEmpty()) {
+      throw new IllegalArgumentException("Unknown parameters: " + args);
+    }    
+  }
+  
+  /**
+   * Registers an endpoint with the RestManager so that this component can be
+   * managed using the REST API. This method can be invoked before all the
+   * resources the {@link org.apache.solr.rest.RestManager} needs to initialize
+   * a {@link ManagedResource} are available, so this simply registers the need
+   * to be managed at a specific endpoint and lets the RestManager deal with
+   * initialization when ready.
+   */
+  @Override
+  public void inform(ResourceLoader loader) throws IOException {
+    SolrResourceLoader solrResourceLoader = (SolrResourceLoader)loader;
+    
+    // here we want to register that we need to be managed
+    // at a specified path and the ManagedResource impl class
+    // that should be used to manage this component
+    solrResourceLoader.getManagedResourceRegistry().
+      registerManagedResource(getResourceId(), getManagedResourceImplClass(), this);
+  }
+  
+  /**
+   * Let the concrete analysis component decide the path it wishes to be managed at. 
+   */
+  protected abstract String getResourceId();
+  
+  /**
+   * Let the concrete analysis component determine the ManagedResource implementation.
+   * As there can be many instances of the same analysis component in a schema, this
+   * class should not presume to create ManagedResource. For instance, there may be
+   * 10 instances of the ManagedStopFilterFactory that use the same set of English 
+   * stop words and we don't want 10 copies of the ManagedWordSetResource in the same core. 
+   */
+  protected abstract Class<? extends ManagedResource> getManagedResourceImplClass();  
+}

Added: lucene/dev/trunk/solr/core/src/java/org/apache/solr/rest/schema/analysis/ManagedStopFilterFactory.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/rest/schema/analysis/ManagedStopFilterFactory.java?rev=1577540&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/rest/schema/analysis/ManagedStopFilterFactory.java (added)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/rest/schema/analysis/ManagedStopFilterFactory.java Fri Mar 14 14:05:49 2014
@@ -0,0 +1,99 @@
+package org.apache.solr.rest.schema.analysis;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.lucene.analysis.TokenStream;
+import org.apache.lucene.analysis.core.StopFilter;
+import org.apache.lucene.analysis.util.CharArraySet;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.util.NamedList;
+import org.apache.solr.rest.ManagedResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * TokenFilterFactory that uses the ManagedWordSetResource implementation
+ * for managing stop words using the REST API.
+ */
+public class ManagedStopFilterFactory extends BaseManagedTokenFilterFactory {
+
+  public static final Logger log = LoggerFactory.getLogger(ManagedStopFilterFactory.class);
+  
+  // this only gets changed once during core initialization and not every
+  // time an update is made to the underlying managed word set.
+  private CharArraySet stopWords = null;
+
+  /**
+   * Initialize the managed "handle"
+   */
+  public ManagedStopFilterFactory(Map<String,String> args) {
+    super(args);    
+  }
+  
+  /**
+   * This analysis component knows the most logical "path"
+   * for which to manage stop words from.
+   */
+  @Override
+  public String getResourceId() {
+    return "/schema/analysis/stopwords/" + handle;
+  }
+  
+  /**
+   * Returns the implementation class for managing stop words.
+   */
+  protected Class<? extends ManagedResource> getManagedResourceImplClass() {
+    return ManagedWordSetResource.class;
+  }
+  
+  /**
+   * Callback invoked by the {@link ManagedResource} instance to trigger this
+   * class to create the CharArraySet used to create the StopFilter using the
+   * wordset managed by {@link ManagedWordSetResource}. Keep in mind that
+   * a schema.xml may reuse the same {@link ManagedStopFilterFactory} many
+   * times for different field types; behind the scenes all instances of this
+   * class/handle combination share the same managed data, hence the need for
+   * a listener/callback scheme.
+   */
+  @Override
+  public void onManagedResourceInitialized(NamedList<?> args, ManagedResource res) 
+      throws SolrException {
+
+    Set<String> managedWords = ((ManagedWordSetResource)res).getWordSet(); 
+        
+    // first thing is to rebuild the Lucene CharArraySet from our managedWords set
+    // which is slightly inefficient to do for every instance of the managed filter
+    // but ManagedResource's don't have access to the luceneMatchVersion
+    boolean ignoreCase = args.getBooleanArg("ignoreCase");
+    stopWords = new CharArraySet(luceneMatchVersion, managedWords.size(), ignoreCase);
+    stopWords.addAll(managedWords);
+  }
+       
+  /**
+   * Returns a StopFilter based on our managed stop word set.
+   */
+  @Override
+  public TokenStream create(TokenStream input) {    
+    if (stopWords == null) {
+      throw new IllegalStateException("Managed stopwords not initialized correctly!");
+    }
+    return new StopFilter(luceneMatchVersion, input, stopWords);
+  }
+}

Modified: lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/schema-rest.xml
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/schema-rest.xml?rev=1577540&r1=1577539&r2=1577540&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/schema-rest.xml (original)
+++ lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/schema-rest.xml Fri Mar 14 14:05:49 2014
@@ -452,6 +452,14 @@
     <!-- A specialized field for geospatial search. If indexed, this fieldType must not be multivalued. -->
     <fieldType name="location" class="solr.LatLonType" subFieldSuffix="_coordinate"/>
 
+    <!-- Field type where stopwords are managed by the REST API -->
+    <fieldtype name="managed_en" class="solr.TextField">
+       <analyzer>
+        <tokenizer class="solr.StandardTokenizerFactory"/>
+        <filter class="solr.ManagedStopFilterFactory" managed="english" />
+      </analyzer>
+    </fieldtype>
+
  </types>
 
 

Modified: lucene/dev/trunk/solr/core/src/test/org/apache/solr/rest/SolrRestletTestBase.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/test/org/apache/solr/rest/SolrRestletTestBase.java?rev=1577540&r1=1577539&r2=1577540&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/test/org/apache/solr/rest/SolrRestletTestBase.java (original)
+++ lucene/dev/trunk/solr/core/src/test/org/apache/solr/rest/SolrRestletTestBase.java Fri Mar 14 14:05:49 2014
@@ -25,8 +25,14 @@ import java.util.SortedMap;
 import java.util.TreeMap;
 
 /**
- * Base class for Solr Restlet-based tests. Creates test harness,
- * including "extra" servlets for all Solr Restlet Application subclasses.
+ * Base class for Solr Restlet-based tests. Creates jetty and test harness
+ * with solrconfig.xml and schema-rest.xml, including "extra" servlets for
+ * all Solr Restlet Application subclasses.
+ *
+ * Use RestTestBase instead if you need to specialize the solrconfig,
+ * the schema, or jetty/test harness creation; otherwise you'll get
+ * imbalanced SolrIndexSearcher closes/opens and a suite-level failure
+ * for a zombie thread.
  */
 abstract public class SolrRestletTestBase extends RestTestBase {
 

Added: lucene/dev/trunk/solr/core/src/test/org/apache/solr/rest/schema/analysis/TestManagedStopFilterFactory.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/test/org/apache/solr/rest/schema/analysis/TestManagedStopFilterFactory.java?rev=1577540&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/test/org/apache/solr/rest/schema/analysis/TestManagedStopFilterFactory.java (added)
+++ lucene/dev/trunk/solr/core/src/test/org/apache/solr/rest/schema/analysis/TestManagedStopFilterFactory.java Fri Mar 14 14:05:49 2014
@@ -0,0 +1,191 @@
+package org.apache.solr.rest.schema.analysis;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.solr.util.RestTestBase;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.noggit.JSONUtil;
+import org.restlet.ext.servlet.ServerServlet;
+
+/**
+ * Test the REST API for managing stop words, which is pretty basic:
+ * GET: returns the list of stop words or a single word if it exists
+ * PUT: add some words to the current list
+ */
+public class TestManagedStopFilterFactory extends RestTestBase {
+  private static File tmpSolrHome;
+  private static File tmpConfDir;
+
+  private static final String collection = "collection1";
+  private static final String confDir = collection + "/conf";
+
+  @Before
+  public void before() throws Exception {
+    createTempDir();
+    tmpSolrHome = new File(TEMP_DIR + File.separator + TestManagedStopFilterFactory.class.getSimpleName()
+                          + System.currentTimeMillis());
+    tmpConfDir = new File(tmpSolrHome, confDir);
+    FileUtils.copyDirectory(new File(TEST_HOME()), tmpSolrHome.getAbsoluteFile());
+
+    final SortedMap<ServletHolder,String> extraServlets = new TreeMap<>();
+    final ServletHolder solrRestApi = new ServletHolder("SolrSchemaRestApi", ServerServlet.class);
+    solrRestApi.setInitParameter("org.restlet.application", "org.apache.solr.rest.SolrSchemaRestApi");
+    extraServlets.put(solrRestApi, "/schema/*");  // '/schema/*' matches '/schema', '/schema/', and '/schema/whatever...'
+
+    System.setProperty("managed.schema.mutable", "true");
+    System.setProperty("enable.update.log", "false");
+
+    createJettyAndHarness(tmpSolrHome.getAbsolutePath(), "solrconfig-managed-schema.xml", "schema-rest.xml",
+                          "/solr", true, extraServlets);
+  }
+
+  @After
+  private void after() throws Exception {
+    jetty.stop();
+    jetty = null;
+    FileUtils.deleteDirectory(tmpSolrHome);
+    System.clearProperty("managed.schema.mutable");
+    System.clearProperty("enable.update.log");
+  }
+
+
+  /**
+   * Test adding managed stopwords to an endpoint defined in the schema,
+   * then adding docs containing a stopword before and after removing
+   * the stopword from the managed stopwords set.
+   */
+  @Test
+  public void testManagedStopwords() throws Exception {
+    // invalid endpoint
+    //// TODO: This returns HTML vs JSON because the exception is thrown
+    ////       from the init method of ManagedEndpoint ... need a better solution
+    // assertJQ("/schema/analysis/stopwords/bogus", "/error/code==404");
+    
+    // this endpoint depends on at least one field type containing the following
+    // declaration in the schema-rest.xml:
+    // 
+    //   <filter class="solr.ManagedStopFilterFactory" managed="english" />
+    //
+    String endpoint = "/schema/analysis/stopwords/english";
+    
+    // test the initial GET request returns the default stopwords settings
+    assertJQ(endpoint, 
+             "/wordSet/initArgs/ignoreCase==false",
+             "/wordSet/managedList==[]");
+          
+    // add some stopwords and verify they were added
+    assertJPut(endpoint, 
+               JSONUtil.toJSON(Arrays.asList("a", "an", "the")),
+               "/responseHeader/status==0");
+          
+    // test requesting a specific stop word that exists / does not exist
+    assertJQ(endpoint + "/the", "/the=='the'");
+    // not exist - 404
+    assertJQ(endpoint + "/foo", "/error/code==404");
+    // wrong case - 404
+    assertJQ(endpoint + "/An", "/error/code==404");
+    
+    // update the ignoreCase initArg to true and make sure case is ignored
+    String updateIgnoreCase = 
+        "{ 'initArgs':{ 'ignoreCase':true }, "
+        + "'managedList':['A','a','AN','an','THE','the','of','OF'] }";
+    assertJPut(endpoint, json(updateIgnoreCase), "/responseHeader/status==0");
+    
+    assertJQ(endpoint, 
+             "/wordSet/initArgs/ignoreCase==true",
+             "/wordSet/managedList==['a','an','of','the']");
+    
+    // verify ignoreCase applies when requesting a word
+    assertJQ("/schema/analysis/stopwords/english/The", "/The=='the'");
+
+    // verify the resource supports XML writer type (wt) as well as JSON
+    assertQ(endpoint,
+            "count(/response/lst[@name='wordSet']/arr[@name='managedList']/*) = 4",
+            "(/response/lst[@name='wordSet']/arr[@name='managedList']/str)[1] = 'a'",     
+            "(/response/lst[@name='wordSet']/arr[@name='managedList']/str)[2] = 'an'",        
+            "(/response/lst[@name='wordSet']/arr[@name='managedList']/str)[3] = 'of'",        
+            "(/response/lst[@name='wordSet']/arr[@name='managedList']/str)[4] = 'the'");
+
+    restTestHarness.reload();  // make the word set available
+
+    String newFieldName = "managed_en_field";
+    // make sure the new field doesn't already exist
+    assertQ("/schema/fields/" + newFieldName + "?indent=on&wt=xml",
+            "count(/response/lst[@name='field']) = 0",
+            "/response/lst[@name='responseHeader']/int[@name='status'] = '404'",
+            "/response/lst[@name='error']/int[@name='code'] = '404'");
+
+    // add the new field
+    assertJPut("/schema/fields/" + newFieldName, json("{'type':'managed_en'}"),
+               "/responseHeader/status==0");
+
+    // make sure the new field exists now
+    assertQ("/schema/fields/" + newFieldName + "?indent=on&wt=xml",
+            "count(/response/lst[@name='field']) = 1",
+            "/response/lst[@name='responseHeader']/int[@name='status'] = '0'");
+
+    assertU(adoc(newFieldName, "This is the one", "id", "6"));
+    assertU(commit());
+
+    assertQ("/select?q=" + newFieldName + ":This",
+            "/response/lst[@name='responseHeader']/int[@name='status'] = '0'",
+            "/response/result[@name='response'][@numFound='1']",
+            "/response/result[@name='response']/doc/str[@name='id'][.='6']");
+
+    assertQ("/select?q=%7B%21raw%20f=" + newFieldName + "%7Dthe",
+            "/response/lst[@name='responseHeader']/int[@name='status'] = '0'",
+            "/response/result[@name='response'][@numFound='0']");
+
+    // verify delete works
+    assertJDelete(endpoint + "/the", "/responseHeader/status==0");
+
+    // verify that removing 'the' is not yet in effect
+    assertU(adoc(newFieldName, "This is the other one", "id", "7"));
+    assertU(commit());
+
+    assertQ("/select?q=%7B%21raw%20f=" + newFieldName + "%7Dthe",
+            "/response/lst[@name='responseHeader']/int[@name='status'] = '0'",
+            "/response/result[@name='response'][@numFound='0']");
+
+    restTestHarness.reload();
+
+    // verify that after reloading, removing 'the' has taken effect
+    assertU(adoc(newFieldName, "This is the other other one", "id", "8"));
+    assertU(commit());
+
+    assertQ("/select?q=%7B%21raw%20f=" + newFieldName + "%7Dthe",
+            "/response/lst[@name='responseHeader']/int[@name='status'] = '0'",
+            "/response/result[@name='response'][@numFound='1']",
+            "/response/result[@name='response']/doc/str[@name='id'][.='8']");
+
+    assertJQ(endpoint,
+             "/wordSet/initArgs/ignoreCase==true",
+             "/wordSet/managedList==['a','an','of']");
+    
+    // should fail with 404 as foo doesn't exist
+    assertJDelete(endpoint + "/foo", "/error/code==404");
+  }
+}

Added: lucene/dev/trunk/solr/example/solr/collection1/conf/_schema_analysis_stopwords_english.json
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/example/solr/collection1/conf/_schema_analysis_stopwords_english.json?rev=1577540&view=auto
==============================================================================
--- lucene/dev/trunk/solr/example/solr/collection1/conf/_schema_analysis_stopwords_english.json (added)
+++ lucene/dev/trunk/solr/example/solr/collection1/conf/_schema_analysis_stopwords_english.json Fri Mar 14 14:05:49 2014
@@ -0,0 +1,38 @@
+{
+  "initArgs":{"ignoreCase":true},
+  "managedList":[
+    "a",
+    "an",
+    "and",
+    "are",
+    "as",
+    "at",
+    "be",
+    "but",
+    "by",
+    "for",
+    "if",
+    "in",
+    "into",
+    "is",
+    "it",
+    "no",
+    "not",
+    "of",
+    "on",
+    "or",
+    "stopworda",
+    "stopwordb",
+    "such",
+    "that",
+    "the",
+    "their",
+    "then",
+    "there",
+    "these",
+    "they",
+    "this",
+    "to",
+    "was",
+    "will",
+    "with"]}

Modified: lucene/dev/trunk/solr/example/solr/collection1/conf/schema.xml
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/example/solr/collection1/conf/schema.xml?rev=1577540&r1=1577539&r2=1577540&view=diff
==============================================================================
--- lucene/dev/trunk/solr/example/solr/collection1/conf/schema.xml (original)
+++ lucene/dev/trunk/solr/example/solr/collection1/conf/schema.xml Fri Mar 14 14:05:49 2014
@@ -448,6 +448,14 @@
       </analyzer>
     </fieldType>
 
+    <!-- A text type for English text where stopwords and synonyms are managed using the REST API -->
+    <fieldType name="managed_en" class="solr.TextField" positionIncrementGap="100">
+      <analyzer>
+        <tokenizer class="solr.StandardTokenizerFactory"/>
+        <filter class="solr.ManagedStopFilterFactory" managed="english" />
+      </analyzer>
+    </fieldType>
+
     <!-- A general text field that has reasonable, generic
          cross-language defaults: it tokenizes with StandardTokenizer,
 	 removes stop words from case-insensitive "stopwords.txt"

Modified: lucene/dev/trunk/solr/test-framework/src/java/org/apache/solr/util/BaseTestHarness.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/test-framework/src/java/org/apache/solr/util/BaseTestHarness.java?rev=1577540&r1=1577539&r2=1577540&view=diff
==============================================================================
--- lucene/dev/trunk/solr/test-framework/src/java/org/apache/solr/util/BaseTestHarness.java (original)
+++ lucene/dev/trunk/solr/test-framework/src/java/org/apache/solr/util/BaseTestHarness.java Fri Mar 14 14:05:49 2014
@@ -21,6 +21,7 @@ import org.apache.solr.common.util.XML;
 import org.w3c.dom.Document;
 import org.xml.sax.SAXException;
 
+import javax.xml.namespace.QName;
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.xpath.XPath;
@@ -97,6 +98,24 @@ abstract public class BaseTestHarness {
     return null;
   }
 
+  public static Object evaluateXPath(String xml, String xpath, QName returnType)
+    throws XPathExpressionException, SAXException {
+    if (null == xpath) return null;
+
+    Document document = null;
+    try {
+      document = getXmlDocumentBuilder().parse(new ByteArrayInputStream
+          (xml.getBytes("UTF-8")));
+    } catch (UnsupportedEncodingException e1) {
+      throw new RuntimeException("Totally weird UTF-8 exception", e1);
+    } catch (IOException e2) {
+      throw new RuntimeException("Totally weird io exception", e2);
+    }
+
+    xpath = xpath.trim();
+    return getXpath().evaluate(xpath.trim(), document, returnType);
+  }
+
   /**
    * A helper that creates an xml &lt;doc&gt; containing all of the
    * fields and values specified

Modified: lucene/dev/trunk/solr/test-framework/src/java/org/apache/solr/util/RestTestHarness.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/test-framework/src/java/org/apache/solr/util/RestTestHarness.java?rev=1577540&r1=1577539&r2=1577540&view=diff
==============================================================================
--- lucene/dev/trunk/solr/test-framework/src/java/org/apache/solr/util/RestTestHarness.java (original)
+++ lucene/dev/trunk/solr/test-framework/src/java/org/apache/solr/util/RestTestHarness.java Fri Mar 14 14:05:49 2014
@@ -19,6 +19,7 @@ package org.apache.solr.util;
 import java.io.IOException;
 import java.net.URLEncoder;
 
+import javax.xml.xpath.XPathConstants;
 import javax.xml.xpath.XPathExpressionException;
 
 import org.apache.http.HttpEntity;
@@ -149,15 +150,22 @@ public class RestTestHarness extends Bas
     }
   }
 
-  
+
+  /**
+   * Reloads the first core listed in the response to the core admin handler STATUS command
+   */
   @Override
   public void reload() throws Exception {
-    String xml = checkResponseStatus("/admin/cores?action=RELOAD", "0");
+    String coreName = (String)evaluateXPath
+        (query("/admin/cores?action=STATUS"),
+         "//lst[@name='status']/lst[1]/str[@name='name']",
+         XPathConstants.STRING);
+    String xml = checkResponseStatus("/admin/cores?action=RELOAD&core=" + coreName, "0");
     if (null != xml) {
       throw new RuntimeException("RELOAD failed:\n" + xml);
     }
   }
-  
+
   /**
    * Processes an "update" (add, commit or optimize) and
    * returns the response as a String.