You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by cm...@apache.org on 2013/08/11 14:19:39 UTC

svn commit: r1512909 [27/38] - in /lucene/dev/branches/lucene4956: ./ dev-tools/ dev-tools/eclipse/ dev-tools/idea/.idea/libraries/ dev-tools/idea/lucene/suggest/ dev-tools/idea/solr/contrib/dataimporthandler/ dev-tools/idea/solr/core/src/test/ dev-too...

Modified: lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/admin/CoreAdminHandler.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/admin/CoreAdminHandler.java?rev=1512909&r1=1512908&r2=1512909&view=diff
==============================================================================
--- lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/admin/CoreAdminHandler.java (original)
+++ lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/admin/CoreAdminHandler.java Sun Aug 11 12:19:13 2013
@@ -17,19 +17,8 @@
 
 package org.apache.solr.handler.admin;
 
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-import java.util.concurrent.Future;
-
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.lang.StringUtils;
 import org.apache.lucene.index.DirectoryReader;
@@ -57,8 +46,8 @@ import org.apache.solr.core.CloseHook;
 import org.apache.solr.core.CoreContainer;
 import org.apache.solr.core.CoreDescriptor;
 import org.apache.solr.core.DirectoryFactory;
-import org.apache.solr.core.SolrCore;
 import org.apache.solr.core.DirectoryFactory.DirContext;
+import org.apache.solr.core.SolrCore;
 import org.apache.solr.handler.RequestHandlerBase;
 import org.apache.solr.request.LocalSolrQueryRequest;
 import org.apache.solr.request.SolrQueryRequest;
@@ -75,6 +64,19 @@ import org.apache.zookeeper.KeeperExcept
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.Future;
+
 /**
  *
  * @since solr 1.3
@@ -126,7 +128,7 @@ public class CoreAdminHandler extends Re
       throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
               "Core container instance missing");
     }
-    boolean doPersist = false;
+    //boolean doPersist = false;
 
     // Pick the action
     SolrParams params = req.getParams();
@@ -135,54 +137,54 @@ public class CoreAdminHandler extends Re
     if (a != null) {
       action = CoreAdminAction.get(a);
       if (action == null) {
-        doPersist = this.handleCustomAction(req, rsp);
+        this.handleCustomAction(req, rsp);
       }
     }
     if (action != null) {
       switch (action) {
         case CREATE: {
-          doPersist = this.handleCreateAction(req, rsp);
+          this.handleCreateAction(req, rsp);
           break;
         }
 
         case RENAME: {
-          doPersist = this.handleRenameAction(req, rsp);
+          this.handleRenameAction(req, rsp);
           break;
         }
 
         case UNLOAD: {
-          doPersist = this.handleUnloadAction(req, rsp);
+          this.handleUnloadAction(req, rsp);
           break;
         }
 
         case STATUS: {
-          doPersist = this.handleStatusAction(req, rsp);
+          this.handleStatusAction(req, rsp);
           break;
 
         }
 
         case PERSIST: {
-          doPersist = this.handlePersistAction(req, rsp);
+          this.handlePersistAction(req, rsp);
           break;
         }
 
         case RELOAD: {
-          doPersist = this.handleReloadAction(req, rsp);
+          this.handleReloadAction(req, rsp);
           break;
         }
 
         case SWAP: {
-          doPersist = this.handleSwapAction(req, rsp);
+          this.handleSwapAction(req, rsp);
           break;
         }
 
         case MERGEINDEXES: {
-          doPersist = this.handleMergeAction(req, rsp);
+          this.handleMergeAction(req, rsp);
           break;
         }
 
         case SPLIT: {
-          doPersist = this.handleSplitAction(req, rsp);
+          this.handleSplitAction(req, rsp);
           break;
         }
 
@@ -208,28 +210,21 @@ public class CoreAdminHandler extends Re
         }
         
         default: {
-          doPersist = this.handleCustomAction(req, rsp);
+          this.handleCustomAction(req, rsp);
           break;
         }
         case LOAD:
           break;
       }
     }
-    // Should we persist the changes?
-    if (doPersist) {
-      cores.persist();
-      rsp.add("saved", cores.getConfigFile().getAbsolutePath());
-    }
     rsp.setHttpCaching(false);
   }
 
   
   /**
    * Handle the core admin SPLIT action.
-   * @return true if a modification has resulted that requires persistence 
-   *         of the CoreContainer configuration.
    */
-  protected boolean handleSplitAction(SolrQueryRequest adminReq, SolrQueryResponse rsp) throws IOException {
+  protected void handleSplitAction(SolrQueryRequest adminReq, SolrQueryResponse rsp) throws IOException {
     SolrParams params = adminReq.getParams();
     List<DocRouter.Range> ranges = null;
 
@@ -298,21 +293,20 @@ public class CoreAdminHandler extends Re
       }
     }
 
-    return false;
   }
 
 
-  protected boolean handleMergeAction(SolrQueryRequest req, SolrQueryResponse rsp) throws IOException {
+  protected void handleMergeAction(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
     SolrParams params = req.getParams();
     String cname = params.required().get(CoreAdminParams.CORE);
     SolrCore core = coreContainer.getCore(cname);
     SolrQueryRequest wrappedReq = null;
 
-    SolrCore[] sourceCores = null;
-    RefCounted<SolrIndexSearcher>[] searchers = null;
+    List<SolrCore> sourceCores = Lists.newArrayList();
+    List<RefCounted<SolrIndexSearcher>> searchers = Lists.newArrayList();
     // stores readers created from indexDir param values
-    DirectoryReader[] readersToBeClosed = null;
-    Directory[] dirsToBeReleased = null;
+    List<DirectoryReader> readersToBeClosed = Lists.newArrayList();
+    List<Directory> dirsToBeReleased = Lists.newArrayList();
     if (core != null) {
       try {
         String[] dirNames = params.getParams(CoreAdminParams.INDEX_DIR);
@@ -322,38 +316,34 @@ public class CoreAdminHandler extends Re
             throw new SolrException( SolrException.ErrorCode.BAD_REQUEST,
                 "At least one indexDir or srcCore must be specified");
 
-          sourceCores = new SolrCore[sources.length];
           for (int i = 0; i < sources.length; i++) {
             String source = sources[i];
             SolrCore srcCore = coreContainer.getCore(source);
             if (srcCore == null)
               throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
                   "Core: " + source + " does not exist");
-            sourceCores[i] = srcCore;
+            sourceCores.add(srcCore);
           }
         } else  {
-          readersToBeClosed = new DirectoryReader[dirNames.length];
-          dirsToBeReleased = new Directory[dirNames.length];
           DirectoryFactory dirFactory = core.getDirectoryFactory();
           for (int i = 0; i < dirNames.length; i++) {
             Directory dir = dirFactory.get(dirNames[i], DirContext.DEFAULT, core.getSolrConfig().indexConfig.lockType);
-            dirsToBeReleased[i] = dir;
+            dirsToBeReleased.add(dir);
             // TODO: why doesn't this use the IR factory? what is going on here?
-            readersToBeClosed[i] = DirectoryReader.open(dir);
+            readersToBeClosed.add(DirectoryReader.open(dir));
           }
         }
 
-        DirectoryReader[] readers = null;
-        if (readersToBeClosed != null)  {
+        List<DirectoryReader> readers = null;
+        if (readersToBeClosed.size() > 0)  {
           readers = readersToBeClosed;
         } else {
-          readers = new DirectoryReader[sourceCores.length];
-          searchers = new RefCounted[sourceCores.length];
-          for (int i = 0; i < sourceCores.length; i++) {
-            SolrCore solrCore = sourceCores[i];
+          readers = Lists.newArrayList();
+          for (SolrCore solrCore: sourceCores) {
             // record the searchers so that we can decref
-            searchers[i] = solrCore.getSearcher();
-            readers[i] = searchers[i].get().getIndexReader();
+            RefCounted<SolrIndexSearcher> searcher = solrCore.getSearcher();
+            searchers.add(searcher);
+            readers.add(searcher.get().getIndexReader());
           }
         }
 
@@ -363,29 +353,26 @@ public class CoreAdminHandler extends Re
         UpdateRequestProcessor processor =
                 processorChain.createProcessor(wrappedReq, rsp);
         processor.processMergeIndexes(new MergeIndexesCommand(readers, req));
+      } catch (Exception e) {
+        // log and rethrow so that if the finally fails we don't lose the original problem
+        log.error("ERROR executing merge:", e);
+        throw e;
       } finally {
-        if (searchers != null) {
-          for (RefCounted<SolrIndexSearcher> searcher : searchers) {
-            if (searcher != null) searcher.decref();
-          }
+        for (RefCounted<SolrIndexSearcher> searcher : searchers) {
+          if (searcher != null) searcher.decref();
         }
-        if (sourceCores != null) {
-          for (SolrCore solrCore : sourceCores) {
-            if (solrCore != null) solrCore.close();
-          }
+        for (SolrCore solrCore : sourceCores) {
+          if (solrCore != null) solrCore.close();
         }
-        if (readersToBeClosed != null) IOUtils.closeWhileHandlingException(readersToBeClosed);
-        if (dirsToBeReleased != null) {
-          for (Directory dir : dirsToBeReleased) {
-            DirectoryFactory dirFactory = core.getDirectoryFactory();
-            dirFactory.release(dir);
-          }
+        IOUtils.closeWhileHandlingException(readersToBeClosed);
+        for (Directory dir : dirsToBeReleased) {
+          DirectoryFactory dirFactory = core.getDirectoryFactory();
+          dirFactory.release(dir);
         }
         if (wrappedReq != null) wrappedReq.close();
         core.close();
       }
     }
-    return coreContainer.isPersistent();
   }
 
   /**
@@ -394,164 +381,147 @@ public class CoreAdminHandler extends Re
    * This method could be overridden by derived classes to handle custom actions. <br> By default - this method throws a
    * solr exception. Derived classes are free to write their derivation if necessary.
    */
-  protected boolean handleCustomAction(SolrQueryRequest req, SolrQueryResponse rsp) {
+  protected void handleCustomAction(SolrQueryRequest req, SolrQueryResponse rsp) {
     throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Unsupported operation: " +
             req.getParams().get(CoreAdminParams.ACTION));
   }
 
+  public static ImmutableMap<String, String> paramToProp = ImmutableMap.<String, String>builder()
+      .put(CoreAdminParams.CONFIG, CoreDescriptor.CORE_CONFIG)
+      .put(CoreAdminParams.SCHEMA, CoreDescriptor.CORE_SCHEMA)
+      .put(CoreAdminParams.DATA_DIR, CoreDescriptor.CORE_DATADIR)
+      .put(CoreAdminParams.ULOG_DIR, CoreDescriptor.CORE_ULOGDIR)
+      .put(CoreAdminParams.LOAD_ON_STARTUP, CoreDescriptor.CORE_LOADONSTARTUP)
+      .put(CoreAdminParams.TRANSIENT, CoreDescriptor.CORE_TRANSIENT)
+      .put(CoreAdminParams.SHARD, CoreDescriptor.CORE_SHARD)
+      .put(CoreAdminParams.COLLECTION, CoreDescriptor.CORE_COLLECTION)
+      .put(CoreAdminParams.ROLES, CoreDescriptor.CORE_ROLES)
+      .put(CoreAdminParams.CORE_NODE_NAME, CoreDescriptor.CORE_NODE_NAME)
+      .put(CoreAdminParams.SHARD_STATE, CloudDescriptor.SHARD_STATE)
+      .put(CoreAdminParams.SHARD_RANGE, CloudDescriptor.SHARD_RANGE)
+      .put(ZkStateReader.NUM_SHARDS_PROP, CloudDescriptor.NUM_SHARDS)
+      .build();
+
+  public static ImmutableMap<String, String> cloudParamToProp;
+
+  protected static CoreDescriptor buildCoreDescriptor(SolrParams params, CoreContainer container) {
+
+    String name = checkNotEmpty(params.get(CoreAdminParams.NAME),
+        "Missing parameter [" + CoreAdminParams.NAME + "]");
+    String instancedir = params.get(CoreAdminParams.INSTANCE_DIR);
+    if (StringUtils.isEmpty(instancedir))
+      instancedir = container.getSolrHome() + File.separator + name;
+
+    Properties coreProps = new Properties();
+    for (String param : paramToProp.keySet()) {
+      String value = params.get(param, null);
+      if (StringUtils.isNotEmpty(value)) {
+        coreProps.setProperty(paramToProp.get(param), value);
+      }
+    }
+    Iterator<String> paramsIt = params.getParameterNamesIterator();
+    while (paramsIt.hasNext()) {
+      String param = paramsIt.next();
+      if (!param.startsWith(CoreAdminParams.PROPERTY_PREFIX))
+        continue;
+      String propName = param.substring(CoreAdminParams.PROPERTY_PREFIX.length());
+      String propValue = params.get(param);
+      coreProps.setProperty(propName, propValue);
+    }
+
+    return new CoreDescriptor(container, name, instancedir, coreProps);
+  }
+
+  private static String checkNotEmpty(String value, String message) {
+    if (StringUtils.isEmpty(value))
+      throw new SolrException(ErrorCode.BAD_REQUEST, message);
+    return value;
+  }
+
   /**
    * Handle 'CREATE' action.
    *
-   * @return true if a modification has resulted that requires persistance 
-   *         of the CoreContainer configuration.
-   *
    * @throws SolrException in case of a configuration error.
    */
-  protected boolean handleCreateAction(SolrQueryRequest req, SolrQueryResponse rsp) throws SolrException {
-    SolrParams params = req.getParams();
-    String name = params.get(CoreAdminParams.NAME);
-    if (null == name || "".equals(name)) {
-      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
-                              "Core name is mandatory to CREATE a SolrCore");
-    }
-    try {
-      
-      if (coreContainer.getAllCoreNames().contains(name)) {
-        log.warn("Creating a core with existing name is not allowed");
-        throw new SolrException(ErrorCode.SERVER_ERROR,
-            "Core with name '" + name + "' already exists.");
-      }
-
-      String instanceDir = params.get(CoreAdminParams.INSTANCE_DIR);
-      if (instanceDir == null) {
-        // instanceDir = coreContainer.getSolrHome() + "/" + name;
-        instanceDir = name; // bare name is already relative to solr home
-      }
-
-      CoreDescriptor dcore = new CoreDescriptor(coreContainer, name, instanceDir);
-
-      //  fillup optional parameters
-      String opts = params.get(CoreAdminParams.CONFIG);
-      if (opts != null)
-        dcore.setConfigName(opts);
-
-      opts = params.get(CoreAdminParams.SCHEMA);
-      if (opts != null)
-        dcore.setSchemaName(opts);
+  protected void handleCreateAction(SolrQueryRequest req, SolrQueryResponse rsp) throws SolrException {
 
-      opts = params.get(CoreAdminParams.DATA_DIR);
-      if (opts != null)
-        dcore.setDataDir(opts);
+    SolrParams params = req.getParams();
+    CoreDescriptor dcore = buildCoreDescriptor(params, coreContainer);
 
-      opts = params.get(CoreAdminParams.ULOG_DIR);
-      if (opts != null)
-        dcore.setUlogDir(opts);
+    if (coreContainer.getAllCoreNames().contains(dcore.getName())) {
+      log.warn("Creating a core with existing name is not allowed");
+      throw new SolrException(ErrorCode.SERVER_ERROR,
+          "Core with name '" + dcore.getName() + "' already exists.");
+    }
 
-      opts = params.get(CoreAdminParams.LOAD_ON_STARTUP);
-      if (opts != null){
-        Boolean value = Boolean.valueOf(opts);
-        dcore.setLoadOnStartup(value);
+    // TODO this should be moved into CoreContainer, really...
+    try {
+      if (coreContainer.getZkController() != null) {
+        coreContainer.preRegisterInZk(dcore);
       }
-      
-      opts = params.get(CoreAdminParams.TRANSIENT);
-      if (opts != null){
-        Boolean value = Boolean.valueOf(opts);
-        dcore.setTransient(value);
+      coreContainer.getCoresLocator().create(coreContainer, dcore);
+      SolrCore core = coreContainer.create(dcore);
+      coreContainer.register(dcore.getName(), core, false);
+      rsp.add("core", core.getName());
+    }
+    catch (Exception ex) {
+      if (coreContainer.isZooKeeperAware() && dcore != null) {
+        try {
+          coreContainer.getZkController().unregister(dcore.getName(), dcore);
+        } catch (InterruptedException e) {
+          Thread.currentThread().interrupt();
+          SolrException.log(log, null, e);
+        } catch (KeeperException e) {
+          SolrException.log(log, null, e);
+        }
       }
       
-      CloudDescriptor cd = dcore.getCloudDescriptor();
-      if (cd != null) {
-        cd.setParams(req.getParams());
-
-        opts = params.get(CoreAdminParams.COLLECTION);
-        if (opts != null)
-          cd.setCollectionName(opts);
-        
-        opts = params.get(CoreAdminParams.SHARD);
-        if (opts != null)
-          cd.setShardId(opts);
-        
-        opts = params.get(CoreAdminParams.SHARD_RANGE);
-        if (opts != null)
-          cd.setShardRange(opts);
-
-        opts = params.get(CoreAdminParams.SHARD_STATE);
-        if (opts != null)
-          cd.setShardState(opts);
-        
-        opts = params.get(CoreAdminParams.ROLES);
-        if (opts != null)
-          cd.setRoles(opts);
-        
-        opts = params.get(CoreAdminParams.CORE_NODE_NAME);
-        if (opts != null)
-          cd.setCoreNodeName(opts);
-                        
-        Integer numShards = params.getInt(ZkStateReader.NUM_SHARDS_PROP);
-        if (numShards != null)
-          cd.setNumShards(numShards);
-      }
+      Throwable tc = ex;
+      Throwable c = null;
+      do {
+        tc = tc.getCause();
+        if (tc != null) {
+          c = tc;
+        }
+      } while (tc != null);
       
-      // Process all property.name=value parameters and set them as name=value core properties
-      Properties coreProperties = new Properties();
-      Iterator<String> parameterNamesIterator = params.getParameterNamesIterator();
-      while (parameterNamesIterator.hasNext()) {
-          String parameterName = parameterNamesIterator.next();
-          if(parameterName.startsWith(CoreAdminParams.PROPERTY_PREFIX)) {
-              String parameterValue = params.get(parameterName);
-              String propertyName = parameterName.substring(CoreAdminParams.PROPERTY_PREFIX.length()); // skip prefix
-              coreProperties.put(propertyName, parameterValue);
-          }
+      String rootMsg = "";
+      if (c != null) {
+        rootMsg = " Caused by: " + c.getMessage();
       }
-      dcore.setCoreProperties(coreProperties);
       
-      SolrCore core = coreContainer.create(dcore);
-
-      coreContainer.register(name, core, false);
-      rsp.add("core", core.getName());
-      return coreContainer.isPersistent();
-    } catch (Exception ex) {
       throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
-                              "Error CREATEing SolrCore '" + name + "': " +
+                              "Error CREATEing SolrCore '" + dcore.getName() + "': " +
                               ex.getMessage(), ex);
     }
   }
 
   /**
    * Handle "RENAME" Action
-   *
-   * @return true if a modification has resulted that requires persistence 
-   *         of the CoreContainer configuration.
    */
-  protected boolean handleRenameAction(SolrQueryRequest req, SolrQueryResponse rsp) throws SolrException {
+  protected void handleRenameAction(SolrQueryRequest req, SolrQueryResponse rsp) throws SolrException {
     SolrParams params = req.getParams();
 
     String name = params.get(CoreAdminParams.OTHER);
     String cname = params.get(CoreAdminParams.CORE);
-    boolean doPersist = false;
 
-    if (cname.equals(name)) return doPersist;
-    
-    doPersist = coreContainer.isPersistent();
+    if (cname.equals(name)) return;
+
     coreContainer.rename(cname, name);
-    
-    return doPersist;
+
   }
 
   /**
    * Handle "ALIAS" action
-   *
-   * @return true if a modification has resulted that requires persistance 
-   *         of the CoreContainer configuration.
    */
   @Deprecated
-  protected boolean handleAliasAction(SolrQueryRequest req, SolrQueryResponse rsp) {
+  protected void handleAliasAction(SolrQueryRequest req, SolrQueryResponse rsp) {
     SolrParams params = req.getParams();
 
     String name = params.get(CoreAdminParams.OTHER);
     String cname = params.get(CoreAdminParams.CORE);
     boolean doPersist = false;
-    if (cname.equals(name)) return doPersist;
+    if (cname.equals(name)) return;
 
     SolrCore core = coreContainer.getCore(cname);
     if (core != null) {
@@ -559,17 +529,14 @@ public class CoreAdminHandler extends Re
       coreContainer.register(name, core, false);
       // no core.close() since each entry in the cores map should increase the ref
     }
-    return doPersist;
+    return;
   }
 
 
   /**
    * Handle "UNLOAD" Action
-   *
-   * @return true if a modification has resulted that requires persistance 
-   *         of the CoreContainer configuration.
    */
-  protected boolean handleUnloadAction(SolrQueryRequest req,
+  protected void handleUnloadAction(SolrQueryRequest req,
       SolrQueryResponse rsp) throws SolrException {
     SolrParams params = req.getParams();
     String cname = params.get(CoreAdminParams.CORE);
@@ -647,17 +614,13 @@ public class CoreAdminHandler extends Re
         core.close();
       }
     }
-    return coreContainer.isPersistent();
     
   }
 
   /**
    * Handle "STATUS" action
-   *
-   * @return true if a modification has resulted that requires persistance 
-   *         of the CoreContainer configuration.
    */
-  protected boolean handleStatusAction(SolrQueryRequest req, SolrQueryResponse rsp)
+  protected void handleStatusAction(SolrQueryRequest req, SolrQueryResponse rsp)
           throws SolrException {
     SolrParams params = req.getParams();
 
@@ -682,8 +645,6 @@ public class CoreAdminHandler extends Re
         status.add(cname, getCoreStatus(coreContainer, cname, isIndexInfoNeeded));
       }
       rsp.add("status", status);
-      doPersist = false; // no state change
-      return doPersist;
     } catch (Exception ex) {
       throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
               "Error handling 'status' action ", ex);
@@ -692,40 +653,20 @@ public class CoreAdminHandler extends Re
 
   /**
    * Handler "PERSIST" action
-   *
-   * @return true if a modification has resulted that requires persistence 
-   *         of the CoreContainer configuration.
    */
-  protected boolean handlePersistAction(SolrQueryRequest req, SolrQueryResponse rsp)
+  protected void handlePersistAction(SolrQueryRequest req, SolrQueryResponse rsp)
           throws SolrException {
-    SolrParams params = req.getParams();
-    boolean doPersist = false;
-    String fileName = params.get(CoreAdminParams.FILE);
-    if (fileName != null) {
-      File file = new File(coreContainer.getConfigFile().getParentFile(), fileName);
-      coreContainer.persistFile(file);
-      rsp.add("saved", file.getAbsolutePath());
-      doPersist = false;
-    } else if (!coreContainer.isPersistent()) {
-      throw new SolrException(SolrException.ErrorCode.FORBIDDEN, "Persistence is not enabled");
-    } else
-      doPersist = true;
-
-    return doPersist;
+    rsp.add("message", "The PERSIST action has been deprecated");
   }
 
   /**
    * Handler "RELOAD" action
-   *
-   * @return true if a modification has resulted that requires persistence 
-   *         of the CoreContainer configuration.
    */
-  protected boolean handleReloadAction(SolrQueryRequest req, SolrQueryResponse rsp) {
+  protected void handleReloadAction(SolrQueryRequest req, SolrQueryResponse rsp) {
     SolrParams params = req.getParams();
     String cname = params.get(CoreAdminParams.CORE);
     try {
       coreContainer.reload(cname);
-      return false; // no change on reload
     } catch (Exception ex) {
       throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Error handling 'reload' action", ex);
     }
@@ -733,19 +674,14 @@ public class CoreAdminHandler extends Re
 
   /**
    * Handle "SWAP" action
-   *
-   * @return true if a modification has resulted that requires persistence 
-   *         of the CoreContainer configuration.
    */
-  protected boolean handleSwapAction(SolrQueryRequest req, SolrQueryResponse rsp) {
+  protected void handleSwapAction(SolrQueryRequest req, SolrQueryResponse rsp) {
     final SolrParams params = req.getParams();
     final SolrParams required = params.required();
 
     final String cname = params.get(CoreAdminParams.CORE);
-    boolean doPersist = params.getBool(CoreAdminParams.PERSISTENT, coreContainer.isPersistent());
     String other = required.get(CoreAdminParams.OTHER);
     coreContainer.swap(cname, other);
-    return doPersist;
 
   }
   

Modified: lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/admin/LoggingHandler.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/admin/LoggingHandler.java?rev=1512909&r1=1512908&r2=1512909&view=diff
==============================================================================
--- lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/admin/LoggingHandler.java (original)
+++ lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/admin/LoggingHandler.java Sun Aug 11 12:19:13 2013
@@ -27,6 +27,7 @@ import org.apache.solr.common.SolrExcept
 import org.apache.solr.common.SolrException.ErrorCode;
 import org.apache.solr.common.params.SolrParams;
 import org.apache.solr.common.util.SimpleOrderedMap;
+import org.apache.solr.core.CoreContainer;
 import org.apache.solr.core.SolrCore;
 import org.apache.solr.handler.RequestHandlerBase;
 import org.apache.solr.logging.LogWatcher;
@@ -44,12 +45,22 @@ import org.slf4j.LoggerFactory;
  */
 public class LoggingHandler extends RequestHandlerBase implements SolrCoreAware {
   static final org.slf4j.Logger log = LoggerFactory.getLogger(LoggingHandler.class);
+
+  private LogWatcher watcher;
+  
+  public LoggingHandler(CoreContainer cc) {
+    this.watcher = cc.getLogging();
+  }
   
-  LogWatcher watcher = null;
+  public LoggingHandler() {
+    
+  }
   
   @Override
   public void inform(SolrCore core) {
-    watcher = core.getCoreDescriptor().getCoreContainer().getLogging();
+    if (watcher == null) {
+      watcher = core.getCoreDescriptor().getCoreContainer().getLogging();
+    }
   }
 
   @Override

Modified: lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/admin/LukeRequestHandler.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/admin/LukeRequestHandler.java?rev=1512909&r1=1512908&r2=1512909&view=diff
==============================================================================
--- lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/admin/LukeRequestHandler.java (original)
+++ lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/admin/LukeRequestHandler.java Sun Aug 11 12:19:13 2013
@@ -576,7 +576,7 @@ public class LukeRequestHandler extends 
       throws IOException {
 
     SolrParams params = req.getParams();
-    int numTerms = params.getInt( NUMTERMS, DEFAULT_COUNT );
+    final int numTerms = params.getInt( NUMTERMS, DEFAULT_COUNT );
 
     TopTermQueue tiq = new TopTermQueue(numTerms + 1);  // Something to collect the top N terms in.
 
@@ -596,13 +596,13 @@ public class LukeRequestHandler extends 
     BytesRef text;
     int[] buckets = new int[HIST_ARRAY_SIZE];
     while ((text = termsEnum.next()) != null) {
+      ++tiq.distinctTerms;
       int freq = termsEnum.docFreq();  // This calculation seems odd, but it gives the same results as it used to.
       int slot = 32 - Integer.numberOfLeadingZeros(Math.max(0, freq - 1));
       buckets[slot] = buckets[slot] + 1;
-      if (freq > tiq.minFreq) {
+      if (numTerms > 0 && freq > tiq.minFreq) {
         UnicodeUtil.UTF8toUTF16(text, spare);
         String t = spare.toString();
-        tiq.distinctTerms = new Long(terms.size()).intValue();
 
         tiq.add(new TopTermQueue.TermInfo(new Term(field, t), termsEnum.docFreq()));
         if (tiq.size() > numTerms) { // if tiq full

Modified: lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/admin/SystemInfoHandler.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/admin/SystemInfoHandler.java?rev=1512909&r1=1512908&r2=1512909&view=diff
==============================================================================
--- lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/admin/SystemInfoHandler.java (original)
+++ lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/admin/SystemInfoHandler.java Sun Aug 11 12:19:13 2013
@@ -37,6 +37,7 @@ import org.apache.commons.io.IOUtils;
 import org.apache.lucene.LucenePackage;
 import org.apache.solr.common.util.NamedList;
 import org.apache.solr.common.util.SimpleOrderedMap;
+import org.apache.solr.core.CoreContainer;
 import org.apache.solr.core.SolrCore;
 import org.apache.solr.handler.RequestHandlerBase;
 import org.apache.solr.request.SolrQueryRequest;
@@ -62,8 +63,20 @@ public class SystemInfoHandler extends R
   //(ie: not static, so core reload will refresh)
   private String hostname = null;
 
+  private CoreContainer cc;
+
   public SystemInfoHandler() {
     super();
+    init();
+  }
+
+  public SystemInfoHandler(CoreContainer cc) {
+    super();
+    this.cc = cc;
+    init();
+  }
+  
+  private void init() {
     try {
       InetAddress addr = InetAddress.getLocalHost();
       hostname = addr.getCanonicalHostName();
@@ -75,14 +88,25 @@ public class SystemInfoHandler extends R
   @Override
   public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception
   {
-    rsp.add( "core", getCoreInfo( req.getCore(), req.getSchema() ) );
-    boolean solrCloudMode = req.getCore().getCoreDescriptor().getCoreContainer().isZooKeeperAware();
+    SolrCore core = req.getCore();
+    if (core != null) rsp.add( "core", getCoreInfo( core, req.getSchema() ) );
+    boolean solrCloudMode =  getCoreContainer(req, core).isZooKeeperAware();
     rsp.add( "mode", solrCloudMode ? "solrcloud" : "std");
     rsp.add( "lucene", getLuceneInfo() );
     rsp.add( "jvm", getJvmInfo() );
     rsp.add( "system", getSystemInfo() );
     rsp.setHttpCaching(false);
   }
+
+  private CoreContainer getCoreContainer(SolrQueryRequest req, SolrCore core) {
+    CoreContainer coreContainer;
+    if (core != null) {
+       coreContainer = req.getCore().getCoreDescriptor().getCoreContainer();
+    } else {
+      coreContainer = cc;
+    }
+    return coreContainer;
+  }
   
   /**
    * Get system info

Modified: lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/component/HttpShardHandler.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/component/HttpShardHandler.java?rev=1512909&r1=1512908&r2=1512909&view=diff
==============================================================================
--- lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/component/HttpShardHandler.java (original)
+++ lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/component/HttpShardHandler.java Sun Aug 11 12:19:13 2013
@@ -16,18 +16,6 @@ package org.apache.solr.handler.componen
  * limitations under the License.
  */
 
-import java.net.ConnectException;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.Callable;
-import java.util.concurrent.CompletionService;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Future;
-
 import org.apache.http.client.HttpClient;
 import org.apache.solr.client.solrj.SolrRequest;
 import org.apache.solr.client.solrj.SolrResponse;
@@ -44,7 +32,6 @@ import org.apache.solr.common.cloud.DocC
 import org.apache.solr.common.cloud.Replica;
 import org.apache.solr.common.cloud.Slice;
 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.CommonParams;
 import org.apache.solr.common.params.ModifiableSolrParams;
@@ -55,6 +42,18 @@ import org.apache.solr.common.util.StrUt
 import org.apache.solr.core.CoreDescriptor;
 import org.apache.solr.request.SolrQueryRequest;
 
+import java.net.ConnectException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CompletionService;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
 public class HttpShardHandler extends ShardHandler {
 
   private HttpShardHandlerFactory httpShardHandlerFactory;
@@ -277,7 +276,8 @@ public class HttpShardHandler extends Sh
         // we weren't provided with an explicit list of slices to query via "shards", so use the cluster state
 
         clusterState =  zkController.getClusterState();
-        String shardKeys = params.get(ShardParams.SHARD_KEYS);
+        String shardKeys =  params.get(ShardParams._ROUTE_);
+        if(shardKeys == null) shardKeys = params.get(ShardParams.SHARD_KEYS);//eprecated
 
         // This will be the complete list of slices we need to query for this request.
         slices = new HashMap<String,Slice>();
@@ -362,20 +362,18 @@ public class HttpShardHandler extends Sh
             Map<String, Replica> sliceShards = slice.getReplicasMap();
 
             // For now, recreate the | delimited list of equivalent servers
-            Set<String> liveNodes = clusterState.getLiveNodes();
             StringBuilder sliceShardsStr = new StringBuilder();
             boolean first = true;
-            for (ZkNodeProps nodeProps : sliceShards.values()) {
-              ZkCoreNodeProps coreNodeProps = new ZkCoreNodeProps(nodeProps);
-              if (!liveNodes.contains(coreNodeProps.getNodeName())
-                  || !coreNodeProps.getState().equals(
+            for (Replica replica : sliceShards.values()) {
+              if (!clusterState.liveNodesContain(replica.getNodeName())
+                  || !replica.getStr(ZkStateReader.STATE_PROP).equals(
                       ZkStateReader.ACTIVE)) continue;
               if (first) {
                 first = false;
               } else {
                 sliceShardsStr.append('|');
               }
-              String url = coreNodeProps.getCoreUrl();
+              String url = ZkCoreNodeProps.getCoreUrl(replica);
               if (url.startsWith("http://"))
                 url = url.substring(7);
               sliceShardsStr.append(url);

Modified: lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/component/QueryComponent.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/component/QueryComponent.java?rev=1512909&r1=1512908&r2=1512909&view=diff
==============================================================================
--- lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/component/QueryComponent.java (original)
+++ lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/component/QueryComponent.java Sun Aug 11 12:19:13 2013
@@ -152,9 +152,8 @@ public class QueryComponent extends Sear
       String[] fqs = req.getParams().getParams(CommonParams.FQ);
       if (fqs!=null && fqs.length!=0) {
         List<Query> filters = rb.getFilters();
-        if (filters==null) {
-          filters = new ArrayList<Query>(fqs.length);
-        }
+        // if filters already exists, make a copy instead of modifying the original
+        filters = filters == null ? new ArrayList<Query>(fqs.length) : new ArrayList<Query>(filters);
         for (String fq : fqs) {
           if (fq != null && fq.trim().length()!=0) {
             QParser fqp = QParser.getParser(fq, null, req);

Modified: lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/component/QueryElevationComponent.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/component/QueryElevationComponent.java?rev=1512909&r1=1512908&r2=1512909&view=diff
==============================================================================
--- lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/component/QueryElevationComponent.java (original)
+++ lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/component/QueryElevationComponent.java Sun Aug 11 12:19:13 2013
@@ -72,7 +72,6 @@ import javax.xml.xpath.XPathFactory;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.StringReader;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.ArrayList;
@@ -344,7 +343,7 @@ public class QueryElevationComponent ext
       return query;
     }
     StringBuilder norm = new StringBuilder();
-    TokenStream tokens = analyzer.tokenStream("", new StringReader(query));
+    TokenStream tokens = analyzer.tokenStream("", query);
     tokens.reset();
 
     CharTermAttribute termAtt = tokens.addAttribute(CharTermAttribute.class);
@@ -563,7 +562,7 @@ public class QueryElevationComponent ext
 
         for (String id : elevations.ids) {
           term.copyChars(id);
-          if (seen.contains(id) == false  && termsEnum.seekExact(term, false)) {
+          if (seen.contains(id) == false  && termsEnum.seekExact(term)) {
             docsEnum = termsEnum.docs(liveDocs, docsEnum, DocsEnum.FLAG_NONE);
             if (docsEnum != null) {
               int docId = docsEnum.nextDoc();

Modified: lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/component/ShardHandlerFactory.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/component/ShardHandlerFactory.java?rev=1512909&r1=1512908&r2=1512909&view=diff
==============================================================================
--- lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/component/ShardHandlerFactory.java (original)
+++ lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/component/ShardHandlerFactory.java Sun Aug 11 12:19:13 2013
@@ -17,9 +17,48 @@ package org.apache.solr.handler.componen
  */
 
 
+import com.google.common.collect.ImmutableMap;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.core.PluginInfo;
+import org.apache.solr.core.SolrResourceLoader;
+import org.apache.solr.util.plugin.PluginInfoInitialized;
+
+import java.util.Collections;
+import java.util.Locale;
+
 public abstract class ShardHandlerFactory {
 
   public abstract ShardHandler getShardHandler();
 
   public abstract void close();
+
+  /**
+   * Create a new ShardHandlerFactory instance
+   * @param info    a PluginInfo object defining which type to create.  If null,
+   *                the default {@link HttpShardHandlerFactory} will be used
+   * @param loader  a SolrResourceLoader used to find the ShardHandlerFactory classes
+   * @return a new, initialized ShardHandlerFactory instance
+   */
+  public static ShardHandlerFactory newInstance(PluginInfo info, SolrResourceLoader loader) {
+
+    if (info == null)
+      info = DEFAULT_SHARDHANDLER_INFO;
+
+    try {
+      ShardHandlerFactory shf = loader.findClass(info.className, ShardHandlerFactory.class).newInstance();
+      if (PluginInfoInitialized.class.isAssignableFrom(shf.getClass()))
+        PluginInfoInitialized.class.cast(shf).init(info);
+      return shf;
+    }
+    catch (Exception e) {
+      throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
+          String.format(Locale.ROOT, "Error instantiating shardHandlerFactory class [%s]: %s",
+              info.className, e.getMessage()));
+    }
+
+  }
+
+  public static final PluginInfo DEFAULT_SHARDHANDLER_INFO =
+      new PluginInfo("shardHandlerFactory", ImmutableMap.of("class", HttpShardHandlerFactory.class.getName()),
+          null, Collections.<PluginInfo>emptyList());
 }

Modified: lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/component/SpellCheckComponent.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/component/SpellCheckComponent.java?rev=1512909&r1=1512908&r2=1512909&view=diff
==============================================================================
--- lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/component/SpellCheckComponent.java (original)
+++ lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/component/SpellCheckComponent.java Sun Aug 11 12:19:13 2013
@@ -18,7 +18,6 @@
 package org.apache.solr.handler.component;
 
 import java.io.IOException;
-import java.io.StringReader;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -464,7 +463,7 @@ public class SpellCheckComponent extends
   private Collection<Token> getTokens(String q, Analyzer analyzer) throws IOException {
     Collection<Token> result = new ArrayList<Token>();
     assert analyzer != null;
-    TokenStream ts = analyzer.tokenStream("", new StringReader(q));
+    TokenStream ts = analyzer.tokenStream("", q);
     ts.reset();
     // TODO: support custom attributes
     CharTermAttribute termAtt = ts.addAttribute(CharTermAttribute.class);

Modified: lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/component/StatsValuesFactory.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/component/StatsValuesFactory.java?rev=1512909&r1=1512908&r2=1512909&view=diff
==============================================================================
--- lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/component/StatsValuesFactory.java (original)
+++ lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/component/StatsValuesFactory.java Sun Aug 11 12:19:13 2013
@@ -49,9 +49,7 @@ public class StatsValuesFactory {
     if (DoubleField.class.isInstance(fieldType) ||
         IntField.class.isInstance(fieldType) ||
         LongField.class.isInstance(fieldType) ||
-        ShortField.class.isInstance(fieldType) ||
         FloatField.class.isInstance(fieldType) ||
-        ByteField.class.isInstance(fieldType) ||
         TrieField.class.isInstance(fieldType) ||
         SortableDoubleField.class.isInstance(fieldType) ||
         SortableIntField.class.isInstance(fieldType) ||

Modified: lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/component/TermsComponent.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/component/TermsComponent.java?rev=1512909&r1=1512908&r2=1512909&view=diff
==============================================================================
--- lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/component/TermsComponent.java (original)
+++ lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/component/TermsComponent.java Sun Aug 11 12:19:13 2013
@@ -163,7 +163,7 @@ public class TermsComponent extends Sear
      BytesRef term = null;
 
       if (lowerBytes != null) {
-        if (termsEnum.seekCeil(lowerBytes, true) == TermsEnum.SeekStatus.END) {
+        if (termsEnum.seekCeil(lowerBytes) == TermsEnum.SeekStatus.END) {
           termsEnum = null;
         } else {
           term = termsEnum.term();

Modified: lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/loader/CSVLoaderBase.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/loader/CSVLoaderBase.java?rev=1512909&r1=1512908&r2=1512909&view=diff
==============================================================================
--- lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/loader/CSVLoaderBase.java (original)
+++ lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/loader/CSVLoaderBase.java Sun Aug 11 12:19:13 2013
@@ -25,8 +25,6 @@ import org.apache.solr.common.params.Sol
 import org.apache.solr.common.params.UpdateParams;
 import org.apache.solr.common.util.StrUtils;
 import org.apache.solr.common.util.ContentStream;
-import org.apache.solr.schema.IndexSchema;
-import org.apache.solr.schema.SchemaField;
 import org.apache.solr.update.*;
 import org.apache.solr.update.processor.UpdateRequestProcessor;
 import org.apache.solr.internal.csv.CSVStrategy;
@@ -55,23 +53,26 @@ abstract class CSVLoaderBase extends Con
   public static final String ESCAPE="escape";
   public static final String OVERWRITE="overwrite";
   public static final String LITERALS_PREFIX = "literal.";
+  public static final String ROW_ID = "rowid";
+  public static final String ROW_ID_OFFSET = "rowidOffset";
 
   private static Pattern colonSplit = Pattern.compile(":");
   private static Pattern commaSplit = Pattern.compile(",");
   
   public static Logger log = LoggerFactory.getLogger(CSVLoaderBase.class);
 
-  final IndexSchema schema;
   final SolrParams params;
   final CSVStrategy strategy;
   final UpdateRequestProcessor processor;
-
   // hashmap to save any literal fields and their values
-  HashMap <SchemaField, String> literals;
+  HashMap <String, String> literals;
+
   String[] fieldnames;
-  SchemaField[] fields;
   CSVLoaderBase.FieldAdder[] adders;
 
+  String rowId = null;// if not null, add a special field by the name given with the line number/row id as the value
+  int rowIdOffset = 0; //add to line/rowid before creating the field
+
   int skipLines;    // number of lines to skip at start of file
 
   final AddUpdateCommand templateAdd;
@@ -87,7 +88,7 @@ abstract class CSVLoaderBase extends Con
   private class FieldAdder {
     void add(SolrInputDocument doc, int line, int column, String val) {
       if (val.length() > 0) {
-        doc.addField(fields[column].getName(),val,1.0f);
+        doc.addField(fieldnames[column],val,1.0f);
       }
     }
   }
@@ -96,7 +97,7 @@ abstract class CSVLoaderBase extends Con
   private class FieldAdderEmpty extends CSVLoaderBase.FieldAdder {
     @Override
     void add(SolrInputDocument doc, int line, int column, String val) {
-      doc.addField(fields[column].getName(),val,1.0f);
+      doc.addField(fieldnames[column],val,1.0f);
     }
   }
 
@@ -163,8 +164,7 @@ abstract class CSVLoaderBase extends Con
   CSVLoaderBase(SolrQueryRequest req, UpdateRequestProcessor processor) {
     this.processor = processor;
     this.params = req.getParams();
-    schema = req.getSchema();
-    this.literals = new HashMap<SchemaField, String>();
+    this.literals = new HashMap<String, String>();
 
     templateAdd = new AddUpdateCommand(req);
     templateAdd.overwrite=params.getBool(OVERWRITE,true);
@@ -186,6 +186,8 @@ abstract class CSVLoaderBase extends Con
     if (escape!=null) {
       if (escape.length()!=1) throw new SolrException( SolrException.ErrorCode.BAD_REQUEST,"Invalid escape:'"+escape+"'");
     }
+    rowId = params.get(ROW_ID);
+    rowIdOffset = params.getInt(ROW_ID_OFFSET, 0);
 
     // if only encapsulator or escape is set, disable the other escaping mechanism
     if (encapsulator == null && escape != null) {
@@ -236,7 +238,6 @@ abstract class CSVLoaderBase extends Con
     // from a POST, one could cache all of this setup info based on the params.
     // The link from FieldAdder to this would need to be severed for that to happen.
 
-    fields = new SchemaField[fieldnames.length];
     adders = new CSVLoaderBase.FieldAdder[fieldnames.length];
     String skipStr = params.get(SKIP);
     List<String> skipFields = skipStr==null ? null : StrUtils.splitSmart(skipStr,',');
@@ -244,12 +245,11 @@ abstract class CSVLoaderBase extends Con
     CSVLoaderBase.FieldAdder adder = new CSVLoaderBase.FieldAdder();
     CSVLoaderBase.FieldAdder adderKeepEmpty = new CSVLoaderBase.FieldAdderEmpty();
 
-    for (int i=0; i<fields.length; i++) {
+    for (int i=0; i<fieldnames.length; i++) {
       String fname = fieldnames[i];
       // to skip a field, leave the entries in fields and addrs null
       if (fname.length()==0 || (skipFields!=null && skipFields.contains(fname))) continue;
 
-      fields[i] = schema.getField(fname);
       boolean keepEmpty = params.getFieldBool(fname,EMPTY,false);
       adders[i] = keepEmpty ? adderKeepEmpty : adder;
 
@@ -290,10 +290,7 @@ abstract class CSVLoaderBase extends Con
       if (!pname.startsWith(LITERALS_PREFIX)) continue;
 
       String name = pname.substring(LITERALS_PREFIX.length());
-      SchemaField sf = schema.getFieldOrNull(name);
-      if(sf == null)
-        throw new SolrException( SolrException.ErrorCode.BAD_REQUEST,"Invalid field name for literal:'"+ name +"'");
-      literals.put(sf, params.get(pname));
+      literals.put(name, params.get(pname));
     }
   }
 
@@ -360,8 +357,8 @@ abstract class CSVLoaderBase extends Con
         }
         if (vals==null) break;
 
-        if (vals.length != fields.length) {
-          input_err("expected "+fields.length+" values but got "+vals.length, vals, line);
+        if (vals.length != fieldnames.length) {
+          input_err("expected "+fieldnames.length+" values but got "+vals.length, vals, line);
         }
 
         addDoc(line,vals);
@@ -378,21 +375,22 @@ abstract class CSVLoaderBase extends Con
 
   /** this must be MT safe... may be called concurrently from multiple threads. */
   void doAdd(int line, String[] vals, SolrInputDocument doc, AddUpdateCommand template) throws IOException {
-    // the line number is passed simply for error reporting in MT mode.
+    // the line number is passed for error reporting in MT mode as well as for optional rowId.
     // first, create the lucene document
     for (int i=0; i<vals.length; i++) {
-      if (fields[i]==null) continue;  // ignore this field
+      if (adders[i]==null) continue;  // skip this field
       String val = vals[i];
       adders[i].add(doc, line, i, val);
     }
 
     // add any literals
-    for (SchemaField sf : literals.keySet()) {
-      String fn = sf.getName();
-      String val = literals.get(sf);
-      doc.addField(fn, val);
+    for (String fname : literals.keySet()) {
+      String val = literals.get(fname);
+      doc.addField(fname, val);
+    }
+    if (rowId != null){
+      doc.addField(rowId, line + rowIdOffset);
     }
-   
     template.solrDoc = doc;
     processor.processAdd(template);
   }

Modified: lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/loader/JsonLoader.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/loader/JsonLoader.java?rev=1512909&r1=1512908&r2=1512909&view=diff
==============================================================================
--- lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/loader/JsonLoader.java (original)
+++ lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/handler/loader/JsonLoader.java Sun Aug 11 12:19:13 2013
@@ -19,15 +19,20 @@ package org.apache.solr.handler.loader;
 import java.io.IOException;
 import java.io.Reader;
 import java.io.StringReader;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
 
 import org.apache.commons.io.IOUtils;
+import org.apache.solr.common.params.SolrParams;
+import org.apache.solr.common.params.UpdateParams;
 import org.noggit.JSONParser;
 import org.noggit.ObjectBuilder;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.SolrInputDocument;
 import org.apache.solr.common.SolrInputField;
-import org.apache.solr.common.params.*;
 import org.apache.solr.common.util.ContentStream;
 import org.apache.solr.handler.RequestHandlerUtils;
 import org.apache.solr.handler.UpdateRequestHandler;
@@ -512,11 +517,13 @@ public class JsonLoader extends ContentS
         case JSONParser.STRING:
           return parser.getString();
         case JSONParser.LONG:
+          return parser.getLong();
         case JSONParser.NUMBER:
+          return parser.getDouble();
         case JSONParser.BIGNUMBER:
           return parser.getNumberChars().toString();
         case JSONParser.BOOLEAN:
-          return Boolean.toString(parser.getBoolean()); // for legacy reasons, single values s are expected to be strings
+          return parser.getBoolean();
         case JSONParser.NULL:
           parser.getNull();
           return null;

Modified: lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/highlight/DefaultSolrHighlighter.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/highlight/DefaultSolrHighlighter.java?rev=1512909&r1=1512908&r2=1512909&view=diff
==============================================================================
--- lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/highlight/DefaultSolrHighlighter.java (original)
+++ lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/highlight/DefaultSolrHighlighter.java Sun Aug 11 12:19:13 2013
@@ -48,7 +48,6 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
-import java.io.StringReader;
 import java.util.*;
 
 /**
@@ -636,7 +635,7 @@ public class DefaultSolrHighlighter exte
   private TokenStream createAnalyzerTStream(IndexSchema schema, String fieldName, String docText) throws IOException {
 
     TokenStream tstream;
-    TokenStream ts = schema.getAnalyzer().tokenStream(fieldName, new StringReader(docText));
+    TokenStream ts = schema.getAnalyzer().tokenStream(fieldName, docText);
     ts.reset();
     tstream = new TokenOrderingFilter(ts, 10);
     return tstream;

Modified: lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/highlight/PostingsSolrHighlighter.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/highlight/PostingsSolrHighlighter.java?rev=1512909&r1=1512908&r2=1512909&view=diff
==============================================================================
--- lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/highlight/PostingsSolrHighlighter.java (original)
+++ lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/highlight/PostingsSolrHighlighter.java Sun Aug 11 12:19:13 2013
@@ -67,6 +67,7 @@ import org.apache.solr.util.plugin.Plugi
  *       &lt;str name="hl.bs.variant"&gt;&lt;/str&gt;
  *       &lt;str name="hl.bs.type"&gt;SENTENCE&lt;/str&gt;
  *       &lt;int name="hl.maxAnalyzedChars"&gt;10000&lt;/int&gt;
+ *       &lt;str name="hl.multiValuedSeparatorChar"&gt; &lt;/str&gt;
  *     &lt;/lst&gt;
  *   &lt;/requestHandler&gt;
  * </pre>
@@ -96,6 +97,7 @@ import org.apache.solr.util.plugin.Plugi
  *    <li>hl.bs.country (string) specifies country code for BreakIterator. default is empty string (root locale)
  *    <li>hl.bs.variant (string) specifies country code for BreakIterator. default is empty string (root locale)
  *    <li>hl.maxAnalyzedChars specifies how many characters at most will be processed in a document.
+ *    <li>hl.multiValuedSeparatorChar specifies the logical separator between values for multi-valued fields.
  *        NOTE: currently hl.maxAnalyzedChars cannot yet be specified per-field
  *  </ul>
  *  
@@ -167,6 +169,15 @@ public class PostingsSolrHighlighter ext
           String type = params.getFieldParam(field, HighlightParams.BS_TYPE);
           return parseBreakIterator(type, locale);
         }
+
+        @Override
+        protected char getMultiValuedSeparator(String field) {
+          String sep = params.getFieldParam(field, HighlightParams.MULTI_VALUED_SEPARATOR, " ");
+          if (sep.length() != 1) {
+            throw new IllegalArgumentException(HighlightParams.MULTI_VALUED_SEPARATOR + " must be exactly one character.");
+          }
+          return sep.charAt(0);
+        }
       };
       
       Map<String,String[]> snippets = highlighter.highlightFields(fieldNames, query, searcher, docIDs, maxPassages);

Modified: lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/logging/ListenerConfig.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/logging/ListenerConfig.java?rev=1512909&r1=1512908&r2=1512909&view=diff
==============================================================================
--- lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/logging/ListenerConfig.java (original)
+++ lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/logging/ListenerConfig.java Sun Aug 11 12:19:13 2013
@@ -19,8 +19,18 @@ package org.apache.solr.logging;
  */
 
 public class ListenerConfig {
-  public int size = 50;
-  public String threshold = null;
+
+  public final int size;
+  public final String threshold;
+
+  public ListenerConfig(int size, String threshold) {
+    this.size = size;
+    this.threshold = threshold;
+  }
+
+  public ListenerConfig() {
+    this(50, null);
+  }
   
   // Down the line, settings for solr URL/core to store logging
 }

Modified: lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/logging/LogWatcher.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/logging/LogWatcher.java?rev=1512909&r1=1512908&r2=1512909&view=diff
==============================================================================
--- lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/logging/LogWatcher.java (original)
+++ lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/logging/LogWatcher.java Sun Aug 11 12:19:13 2013
@@ -19,7 +19,6 @@ package org.apache.solr.logging;
 
 import org.apache.solr.common.SolrDocument;
 import org.apache.solr.common.SolrDocumentList;
-import org.apache.solr.core.ConfigSolr;
 import org.apache.solr.core.SolrResourceLoader;
 import org.apache.solr.logging.jul.JulWatcher;
 import org.apache.solr.logging.log4j.Log4jWatcher;
@@ -119,35 +118,32 @@ public abstract class LogWatcher<E> {
    * JUL and Log4j watchers are supported out-of-the-box.  You can register your own
    * LogWatcher implementation via the plugins architecture
    *
-   * @param config the CoreContainer's config, with logging configuration details
+   * @param config a LogWatcherConfig object, containing the configuration for this LogWatcher.
    * @param loader a SolrResourceLoader, to be used to load plugin LogWatcher implementations.
    *               Can be null if
    *
    * @return a LogWatcher configured for the container's logging framework
    */
-  public static LogWatcher newRegisteredLogWatcher(ConfigSolr config, SolrResourceLoader loader) {
+  public static LogWatcher newRegisteredLogWatcher(LogWatcherConfig config, SolrResourceLoader loader) {
 
-    if (!config.getBool(ConfigSolr.CfgProp.SOLR_LOGGING_ENABLED, true))
+    if (!config.isEnabled())
       return null;
 
     LogWatcher logWatcher = createWatcher(config, loader);
 
     if (logWatcher != null) {
-      ListenerConfig v = new ListenerConfig();
-      v.size = config.getInt(ConfigSolr.CfgProp.SOLR_LOGGING_WATCHER_SIZE, 50);
-      v.threshold = config.get(ConfigSolr.CfgProp.SOLR_LOGGING_WATCHER_THRESHOLD, null);
-      if (v.size > 0) {
-        log.info("Registering Log Listener");
-        logWatcher.registerListener(v);
+      if (config.getWatcherSize() > 0) {
+        log.info("Registering Log Listener [{}]", logWatcher.getName());
+        logWatcher.registerListener(config.asListenerConfig());
       }
     }
 
     return logWatcher;
   }
 
-  private static LogWatcher createWatcher(ConfigSolr config, SolrResourceLoader loader) {
+  private static LogWatcher createWatcher(LogWatcherConfig config, SolrResourceLoader loader) {
 
-    String fname = config.get(ConfigSolr.CfgProp.SOLR_LOGGING_CLASS, null);
+    String fname = config.getLoggingClass();
     String slf4jImpl;
 
     try {

Modified: lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/logging/log4j/Log4jWatcher.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/logging/log4j/Log4jWatcher.java?rev=1512909&r1=1512908&r2=1512909&view=diff
==============================================================================
--- lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/logging/log4j/Log4jWatcher.java (original)
+++ lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/logging/log4j/Log4jWatcher.java Sun Aug 11 12:19:13 2013
@@ -66,10 +66,12 @@ public class Log4jWatcher extends LogWat
 
   @Override
   public void setLogLevel(String category, String level) {
+    org.apache.log4j.Logger log;
     if(LoggerInfo.ROOT_NAME.equals(category)) {
-      category = "";
+      log = org.apache.log4j.LogManager.getRootLogger();
+    } else {
+      log = org.apache.log4j.Logger.getLogger(category);
     }
-    org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(category);
     if(level==null||"unset".equals(level)||"null".equals(level)) {
       log.setLevel(null);
     }

Modified: lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/parser/SolrQueryParserBase.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/parser/SolrQueryParserBase.java?rev=1512909&r1=1512908&r2=1512909&view=diff
==============================================================================
--- lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/parser/SolrQueryParserBase.java (original)
+++ lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/parser/SolrQueryParserBase.java Sun Aug 11 12:19:13 2013
@@ -405,7 +405,7 @@ public abstract class SolrQueryParserBas
 
     TokenStream source;
     try {
-      source = analyzer.tokenStream(field, new StringReader(queryText));
+      source = analyzer.tokenStream(field, queryText);
       source.reset();
     } catch (IOException e) {
       throw new SyntaxError("Unable to initialize TokenStream to analyze query text", e);

Modified: lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/request/SimpleFacets.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/request/SimpleFacets.java?rev=1512909&r1=1512908&r2=1512909&view=diff
==============================================================================
--- lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/request/SimpleFacets.java (original)
+++ lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/request/SimpleFacets.java Sun Aug 11 12:19:13 2013
@@ -295,7 +295,9 @@ public class SimpleFacets {
         // TODO: slight optimization would prevent double-parsing of any localParams
         Query qobj = QParser.getParser(q, null, req).getQuery();
 
-        if (params.getBool(GroupParams.GROUP_FACET, false)) {
+        if (qobj == null) {
+          res.add(key, 0);
+        } else if (params.getBool(GroupParams.GROUP_FACET, false)) {
           res.add(key, getGroupedFacetQueryCount(qobj));
         } else {
           res.add(key, searcher.numDocs(qobj, docs));
@@ -766,7 +768,7 @@ public class SimpleFacets {
       // facet.offset when sorting by index order.
 
       if (startTermBytes != null) {
-        if (termsEnum.seekCeil(startTermBytes, true) == TermsEnum.SeekStatus.END) {
+        if (termsEnum.seekCeil(startTermBytes) == TermsEnum.SeekStatus.END) {
           termsEnum = null;
         } else {
           term = termsEnum.term();

Modified: lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/request/SolrQueryRequest.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/request/SolrQueryRequest.java?rev=1512909&r1=1512908&r2=1512909&view=diff
==============================================================================
--- lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/request/SolrQueryRequest.java (original)
+++ lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/request/SolrQueryRequest.java Sun Aug 11 12:19:13 2013
@@ -73,6 +73,9 @@ public interface SolrQueryRequest {
 
   /** The schema snapshot from core.getLatestSchema() at request creation. */
   public IndexSchema getSchema();
+  
+  /** Replaces the current schema snapshot with the latest from the core. */
+  public void updateSchemaToLatest();
 
   /**
    * Returns a string representing all the important parameters.

Modified: lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/request/SolrQueryRequestBase.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/request/SolrQueryRequestBase.java?rev=1512909&r1=1512908&r2=1512909&view=diff
==============================================================================
--- lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/request/SolrQueryRequestBase.java (original)
+++ lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/request/SolrQueryRequestBase.java Sun Aug 11 12:19:13 2013
@@ -42,8 +42,8 @@ import java.util.HashMap;
  */
 public abstract class SolrQueryRequestBase implements SolrQueryRequest {
   protected final SolrCore core;
-  protected final IndexSchema schema;
   protected final SolrParams origParams;
+  protected volatile IndexSchema schema;
   protected SolrParams params;
   protected Map<Object,Object> context;
   protected Iterable<ContentStream> streams;
@@ -112,6 +112,11 @@ public abstract class SolrQueryRequestBa
     return schema;
   }
 
+  @Override
+  public void updateSchemaToLatest() {
+    schema = core.getLatestSchema();
+  }
+
   /**
    * Frees resources associated with this request, this method <b>must</b>
    * be called when the object is no longer in use.

Modified: lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/request/UnInvertedField.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/request/UnInvertedField.java?rev=1512909&r1=1512908&r2=1512909&view=diff
==============================================================================
--- lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/request/UnInvertedField.java (original)
+++ lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/request/UnInvertedField.java Sun Aug 11 12:19:13 2013
@@ -231,13 +231,13 @@ public class UnInvertedField extends Doc
       TermsEnum te = getOrdTermsEnum(searcher.getAtomicReader());
       if (te != null && prefix != null && prefix.length() > 0) {
         final BytesRef prefixBr = new BytesRef(prefix);
-        if (te.seekCeil(prefixBr, true) == TermsEnum.SeekStatus.END) {
+        if (te.seekCeil(prefixBr) == TermsEnum.SeekStatus.END) {
           startTerm = numTermsInField;
         } else {
           startTerm = (int) te.ord();
         }
         prefixBr.append(UnicodeUtil.BIG_TERM);
-        if (te.seekCeil(prefixBr, true) == TermsEnum.SeekStatus.END) {
+        if (te.seekCeil(prefixBr) == TermsEnum.SeekStatus.END) {
           endTerm = numTermsInField;
         } else {
           endTerm = (int) te.ord();

Modified: lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/response/BinaryResponseWriter.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/response/BinaryResponseWriter.java?rev=1512909&r1=1512908&r2=1512909&view=diff
==============================================================================
--- lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/response/BinaryResponseWriter.java (original)
+++ lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/response/BinaryResponseWriter.java Sun Aug 11 12:19:13 2013
@@ -260,11 +260,9 @@ public class BinaryResponseWriter implem
     KNOWN_TYPES.add(BCDIntField.class);
     KNOWN_TYPES.add(BCDLongField.class);
     KNOWN_TYPES.add(BCDStrField.class);
-    KNOWN_TYPES.add(ByteField.class);
     KNOWN_TYPES.add(DateField.class);
     KNOWN_TYPES.add(DoubleField.class);
     KNOWN_TYPES.add(FloatField.class);
-    KNOWN_TYPES.add(ShortField.class);
     KNOWN_TYPES.add(IntField.class);
     KNOWN_TYPES.add(LongField.class);
     KNOWN_TYPES.add(SortableLongField.class);

Modified: lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/rest/schema/CopyFieldCollectionResource.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/rest/schema/CopyFieldCollectionResource.java?rev=1512909&r1=1512908&r2=1512909&view=diff
==============================================================================
--- lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/rest/schema/CopyFieldCollectionResource.java (original)
+++ lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/rest/schema/CopyFieldCollectionResource.java Sun Aug 11 12:19:13 2013
@@ -17,37 +17,47 @@ package org.apache.solr.rest.schema;
  */
 
 
+import org.apache.solr.common.SolrException;
 import org.apache.solr.common.params.CommonParams;
 import org.apache.solr.rest.GETable;
+import org.apache.solr.rest.POSTable;
 import org.apache.solr.schema.IndexSchema;
+import org.apache.solr.schema.ManagedIndexSchema;
+import org.noggit.ObjectBuilder;
+import org.restlet.data.MediaType;
 import org.restlet.representation.Representation;
 import org.restlet.resource.ResourceException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.io.IOException;
 import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 /**
  * This class responds to requests at /solr/(corename)/schema/copyfields
- * 
  * <p/>
- * 
+ *
  * To restrict the set of copyFields in the response, specify one or both
  * of the following as query parameters, with values as space and/or comma
  * separated dynamic or explicit field names:
- * 
+ *
  * <ul>
  *   <li>dest.fl: include copyFields that have one of these as a destination</li>
  *   <li>source.fl: include copyFields that have one of these as a source</li>
  * </ul>
- * 
+ *
  * If both dest.fl and source.fl are given as query parameters, the copyfields
  * in the response will be restricted to those that match any of the destinations
  * in dest.fl and also match any of the sources in source.fl.
  */
-public class CopyFieldCollectionResource extends BaseFieldResource implements GETable {
+public class CopyFieldCollectionResource extends BaseFieldResource implements GETable, POSTable {
   private static final Logger log = LoggerFactory.getLogger(CopyFieldCollectionResource.class);
   private static final String SOURCE_FIELD_LIST = IndexSchema.SOURCE + "." + CommonParams.FL;
   private static final String DESTINATION_FIELD_LIST = IndexSchema.DESTINATION + "." + CommonParams.FL;
@@ -94,4 +104,80 @@ public class CopyFieldCollectionResource
 
     return new SolrOutputRepresentation();
   }
+
+  @Override
+  public Representation post(Representation entity) throws ResourceException {
+    try {
+      if (!getSchema().isMutable()) {
+        final String message = "This IndexSchema is not mutable.";
+        throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, message);
+      } else {
+        if (!entity.getMediaType().equals(MediaType.APPLICATION_JSON, true)) {
+          String message = "Only media type " + MediaType.APPLICATION_JSON.toString() + " is accepted."
+              + "  Request has media type " + entity.getMediaType().toString() + ".";
+          log.error(message);
+          throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, message);
+        } else {
+          Object object = ObjectBuilder.fromJSON(entity.getText());
+
+          if (!(object instanceof List)) {
+            String message = "Invalid JSON type " + object.getClass().getName() + ", expected List of the form"
+                + " (ignore the backslashes): [{\"source\":\"foo\",\"dest\":\"comma-separated list of targets\"}, {...}, ...]";
+            log.error(message);
+            throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, message);
+          } else {
+            List<Map<String, Object>> list = (List<Map<String, Object>>) object;
+            Map<String, Collection<String>> fieldsToCopy = new HashMap<>();
+            ManagedIndexSchema oldSchema = (ManagedIndexSchema) getSchema();
+            Set<String> malformed = new HashSet<>();
+            for (Map<String,Object> map : list) {
+              String fieldName = (String)map.get(IndexSchema.SOURCE);
+              if (null == fieldName) {
+                String message = "Missing '" + IndexSchema.SOURCE + "' mapping.";
+                log.error(message);
+                throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, message);
+              }
+              Object dest = map.get(IndexSchema.DESTINATION);
+              List<String> destinations = null;
+              if (dest != null) {
+                if (dest instanceof List){
+                  destinations = (List<String>)dest;
+                } else if (dest instanceof String){
+                  destinations = Collections.singletonList(dest.toString());
+                } else {
+                  String message = "Invalid '" + IndexSchema.DESTINATION + "' type.";
+                  log.error(message);
+                  throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, message);
+                }
+              }
+              if (destinations == null) {
+                malformed.add(fieldName);
+              } else {
+                fieldsToCopy.put(fieldName, destinations);
+              }
+            }
+            if (malformed.size() > 0){
+              StringBuilder message = new StringBuilder("Malformed destination(s) for: ");
+              for (String s : malformed) {
+                message.append(s).append(", ");
+              }
+              if (message.length() > 2) {
+                message.setLength(message.length() - 2);//drop the last ,
+              }
+              log.error(message.toString().trim());
+              throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, message.toString().trim());
+            }
+            IndexSchema newSchema = oldSchema.addCopyFields(fieldsToCopy);
+            if (newSchema != null) {
+              getSolrCore().setLatestSchema(newSchema);
+            }
+          }
+        }
+      }
+    } catch (Exception e) {
+      getSolrResponse().setException(e);
+    }
+    handlePostExecution(log);
+    return new SolrOutputRepresentation();
+  }
 }

Modified: lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/rest/schema/FieldCollectionResource.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/rest/schema/FieldCollectionResource.java?rev=1512909&r1=1512908&r2=1512909&view=diff
==============================================================================
--- lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/rest/schema/FieldCollectionResource.java (original)
+++ lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/rest/schema/FieldCollectionResource.java Sun Aug 11 12:19:13 2013
@@ -33,8 +33,13 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.SortedSet;
 import java.util.TreeSet;
 
@@ -43,22 +48,22 @@ import java.util.TreeSet;
  * <p/>
  * Two query parameters are supported:
  * <ul>
- *   <li>
- *     "fl": a comma- and/or space-separated list of fields to send properties
- *     for in the response, rather than the default: all of them.
- *   </li>
- *   <li>
- *     "includeDynamic": if the "fl" parameter is specified, matching dynamic
- *     fields are included in the response and identified with the "dynamicBase"
- *     property.  If the "fl" parameter is not specified, the "includeDynamic"
- *     query parameter is ignored.
- *   </li>
+ * <li>
+ * "fl": a comma- and/or space-separated list of fields to send properties
+ * for in the response, rather than the default: all of them.
+ * </li>
+ * <li>
+ * "includeDynamic": if the "fl" parameter is specified, matching dynamic
+ * fields are included in the response and identified with the "dynamicBase"
+ * property.  If the "fl" parameter is not specified, the "includeDynamic"
+ * query parameter is ignored.
+ * </li>
  * </ul>
  */
-public class FieldCollectionResource extends BaseFieldResource implements GETable,POSTable {
+public class FieldCollectionResource extends BaseFieldResource implements GETable, POSTable {
   private static final Logger log = LoggerFactory.getLogger(FieldCollectionResource.class);
   private boolean includeDynamic;
-  
+
   public FieldCollectionResource() {
     super();
   }
@@ -108,49 +113,70 @@ public class FieldCollectionResource ext
 
     return new SolrOutputRepresentation();
   }
-  
+
   @Override
   public Representation post(Representation entity) {
     try {
-      if ( ! getSchema().isMutable()) {
+      if (!getSchema().isMutable()) {
         final String message = "This IndexSchema is not mutable.";
         throw new SolrException(ErrorCode.BAD_REQUEST, message);
       } else {
         if (null == entity.getMediaType()) {
           entity.setMediaType(MediaType.APPLICATION_JSON);
         }
-        if ( ! entity.getMediaType().equals(MediaType.APPLICATION_JSON, true)) {
+        if (!entity.getMediaType().equals(MediaType.APPLICATION_JSON, true)) {
           String message = "Only media type " + MediaType.APPLICATION_JSON.toString() + " is accepted."
               + "  Request has media type " + entity.getMediaType().toString() + ".";
           log.error(message);
           throw new SolrException(ErrorCode.BAD_REQUEST, message);
         } else {
           Object object = ObjectBuilder.fromJSON(entity.getText());
-          if ( ! (object instanceof List)) {
+          if (!(object instanceof List)) {
             String message = "Invalid JSON type " + object.getClass().getName() + ", expected List of the form"
                 + " (ignore the backslashes): [{\"name\":\"foo\",\"type\":\"text_general\", ...}, {...}, ...]";
             log.error(message);
             throw new SolrException(ErrorCode.BAD_REQUEST, message);
           } else {
-            List<Map<String,Object>> list = (List<Map<String,Object>>)object;
+            List<Map<String, Object>> list = (List<Map<String, Object>>) object;
             List<SchemaField> newFields = new ArrayList<SchemaField>();
             IndexSchema oldSchema = getSchema();
-            for (Map<String,Object> map : list) {
-              String fieldName = (String)map.remove(IndexSchema.NAME);
+            Map<String, Collection<String>> copyFields = new HashMap<>();
+            Set<String> malformed = new HashSet<>();
+            for (Map<String, Object> map : list) {
+              String fieldName = (String) map.remove(IndexSchema.NAME);
               if (null == fieldName) {
                 String message = "Missing '" + IndexSchema.NAME + "' mapping.";
                 log.error(message);
                 throw new SolrException(ErrorCode.BAD_REQUEST, message);
               }
-              String fieldType = (String)map.remove(IndexSchema.TYPE);
+              String fieldType = (String) map.remove(IndexSchema.TYPE);
               if (null == fieldType) {
                 String message = "Missing '" + IndexSchema.TYPE + "' mapping.";
                 log.error(message);
                 throw new SolrException(ErrorCode.BAD_REQUEST, message);
               }
+              // copyFields:"comma separated list of destination fields"
+              Object copies = map.get(IndexSchema.COPY_FIELDS);
+              List<String> copyTo = null;
+              if (copies != null) {
+                if (copies instanceof List){
+                  copyTo = (List<String>) copies;
+                } else if (copies instanceof String){
+                  copyTo = Collections.singletonList(copies.toString());
+                } else {
+                  String message = "Invalid '" + IndexSchema.COPY_FIELDS + "' type.";
+                  log.error(message);
+                  throw new SolrException(ErrorCode.BAD_REQUEST, message);
+                }
+              }
+              if (copyTo != null) {
+                map.remove(IndexSchema.COPY_FIELDS);
+                copyFields.put(fieldName, copyTo);
+              }
               newFields.add(oldSchema.newField(fieldName, fieldType, map));
             }
-            IndexSchema newSchema = oldSchema.addFields(newFields);
+            IndexSchema newSchema = oldSchema.addFields(newFields, copyFields);
+
             getSolrCore().setLatestSchema(newSchema);
           }
         }

Modified: lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/rest/schema/FieldResource.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/rest/schema/FieldResource.java?rev=1512909&r1=1512908&r2=1512909&view=diff
==============================================================================
--- lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/rest/schema/FieldResource.java (original)
+++ lucene/dev/branches/lucene4956/solr/core/src/java/org/apache/solr/rest/schema/FieldResource.java Sun Aug 11 12:19:13 2013
@@ -31,6 +31,10 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.io.UnsupportedEncodingException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -44,9 +48,9 @@ import java.util.Map;
  * <p/>
  * The PUT method accepts field addition requests in JSON format.
  */
-public class FieldResource extends BaseFieldResource implements GETable,PUTable {
+public class FieldResource extends BaseFieldResource implements GETable, PUTable {
   private static final Logger log = LoggerFactory.getLogger(FieldResource.class);
-  
+
   private boolean includeDynamic;
   private String fieldName;
 
@@ -59,7 +63,7 @@ public class FieldResource extends BaseF
     super.doInit();
     if (isExisting()) {
       includeDynamic = getSolrRequest().getParams().getBool(INCLUDE_DYNAMIC_PARAM, false);
-      fieldName = (String)getRequestAttributes().get(IndexSchema.NAME);
+      fieldName = (String) getRequestAttributes().get(IndexSchema.NAME);
       try {
         fieldName = null == fieldName ? "" : urlDecode(fieldName.trim()).trim();
       } catch (UnsupportedEncodingException e) {
@@ -97,53 +101,69 @@ public class FieldResource extends BaseF
   }
 
   /**
-   * Accepts JSON add field request, to URL  
+   * Accepts JSON add field request, to URL
    */
   @Override
   public Representation put(Representation entity) {
     try {
-      if ( ! getSchema().isMutable()) {
+      if (!getSchema().isMutable()) {
         final String message = "This IndexSchema is not mutable.";
         throw new SolrException(ErrorCode.BAD_REQUEST, message);
       } else {
         if (null == entity.getMediaType()) {
           entity.setMediaType(MediaType.APPLICATION_JSON);
         }
-        if ( ! entity.getMediaType().equals(MediaType.APPLICATION_JSON, true)) {
+        if (!entity.getMediaType().equals(MediaType.APPLICATION_JSON, true)) {
           String message = "Only media type " + MediaType.APPLICATION_JSON.toString() + " is accepted."
-                         + "  Request has media type " + entity.getMediaType().toString() + ".";
+              + "  Request has media type " + entity.getMediaType().toString() + ".";
           log.error(message);
           throw new SolrException(ErrorCode.BAD_REQUEST, message);
         } else {
           Object object = ObjectBuilder.fromJSON(entity.getText());
-          if ( ! (object instanceof Map)) {
+          if (!(object instanceof Map)) {
             String message = "Invalid JSON type " + object.getClass().getName() + ", expected Map of the form"
-                           + " (ignore the backslashes): {\"type\":\"text_general\", ...}, either with or"
-                           + " without a \"name\" mapping.  If the \"name\" is specified, it must match the"
-                           + " name given in the request URL: /schema/fields/(name)";
+                + " (ignore the backslashes): {\"type\":\"text_general\", ...}, either with or"
+                + " without a \"name\" mapping.  If the \"name\" is specified, it must match the"
+                + " name given in the request URL: /schema/fields/(name)";
             log.error(message);
             throw new SolrException(ErrorCode.BAD_REQUEST, message);
           } else {
-            Map<String,Object> map = (Map<String,Object>)object;
+            Map<String, Object> map = (Map<String, Object>) object;
             if (1 == map.size() && map.containsKey(IndexSchema.FIELD)) {
-              map = (Map<String,Object>)map.get(IndexSchema.FIELD);
+              map = (Map<String, Object>) map.get(IndexSchema.FIELD);
             }
             String bodyFieldName;
-            if (null != (bodyFieldName = (String)map.remove(IndexSchema.NAME)) && ! fieldName.equals(bodyFieldName)) {
-              String message = "Field name in the request body '" + bodyFieldName 
-                             + "' doesn't match field name in the request URL '" + fieldName + "'";
+            if (null != (bodyFieldName = (String) map.remove(IndexSchema.NAME)) && !fieldName.equals(bodyFieldName)) {
+              String message = "Field name in the request body '" + bodyFieldName
+                  + "' doesn't match field name in the request URL '" + fieldName + "'";
               log.error(message);
               throw new SolrException(ErrorCode.BAD_REQUEST, message);
             } else {
               String fieldType;
-              if (null == (fieldType = (String)map.remove(IndexSchema.TYPE))) {
+              if (null == (fieldType = (String) map.remove(IndexSchema.TYPE))) {
                 String message = "Missing '" + IndexSchema.TYPE + "' mapping.";
                 log.error(message);
                 throw new SolrException(ErrorCode.BAD_REQUEST, message);
               } else {
-                ManagedIndexSchema oldSchema = (ManagedIndexSchema)getSchema();
+                ManagedIndexSchema oldSchema = (ManagedIndexSchema) getSchema();
+                Object copies = map.get(IndexSchema.COPY_FIELDS);
+                List<String> copyFieldNames = null;
+                if (copies != null) {
+                  if (copies instanceof List) {
+                    copyFieldNames = (List<String>) copies;
+                  } else if (copies instanceof String) {
+                    copyFieldNames = Collections.singletonList(copies.toString());
+                  } else {
+                    String message = "Invalid '" + IndexSchema.COPY_FIELDS + "' type.";
+                    log.error(message);
+                    throw new SolrException(ErrorCode.BAD_REQUEST, message);
+                  }
+                }
+                if (copyFieldNames != null) {
+                  map.remove(IndexSchema.COPY_FIELDS);
+                }
                 SchemaField newField = oldSchema.newField(fieldName, fieldType, map);
-                ManagedIndexSchema newSchema = oldSchema.addField(newField);
+                IndexSchema newSchema = oldSchema.addField(newField, copyFieldNames);
                 getSolrCore().setLatestSchema(newSchema);
               }
             }