SOLR-6476 refactored to get the Operation class outside

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1631397 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Noble Paul 2014-10-13 14:01:25 +00:00
parent 5f002f0f66
commit 9a9444a642
4 changed files with 230 additions and 166 deletions

View File

@ -24,29 +24,22 @@ import org.apache.solr.core.CoreDescriptor;
import org.apache.solr.core.SolrCore; import org.apache.solr.core.SolrCore;
import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.rest.BaseSolrResource; import org.apache.solr.rest.BaseSolrResource;
import org.noggit.JSONParser; import org.apache.solr.util.CommandOperation;
import org.noggit.ObjectBuilder;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.Reader; import java.io.Reader;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static java.util.Collections.EMPTY_LIST; import static java.util.Collections.EMPTY_LIST;
import static java.util.Collections.EMPTY_MAP; import static java.util.Collections.EMPTY_MAP;
import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap; import static java.util.Collections.singletonMap;
import static org.apache.solr.common.cloud.ZkNodeProps.makeMap;
import static org.apache.solr.schema.FieldType.CLASS_NAME; import static org.apache.solr.schema.FieldType.CLASS_NAME;
import static org.apache.solr.schema.IndexSchema.DESTINATION; import static org.apache.solr.schema.IndexSchema.DESTINATION;
import static org.apache.solr.schema.IndexSchema.NAME; import static org.apache.solr.schema.IndexSchema.NAME;
@ -87,20 +80,20 @@ public class SchemaManager {
* @return Lis of errors . If the List is empty then the operation is successful. * @return Lis of errors . If the List is empty then the operation is successful.
*/ */
public List performOperations(Reader rdr) { public List performOperations(Reader rdr) {
List<Operation> ops = null; List<CommandOperation> ops = null;
try { try {
ops = SchemaManager.parse(rdr); ops = CommandOperation.parse(rdr);
} catch (Exception e) { } catch (Exception e) {
String msg= "Error parsing schema operations "; String msg= "Error parsing schema operations ";
log.warn(msg ,e ); log.warn(msg ,e );
return Collections.singletonList(singletonMap(ERR_MSGS, msg + ":" + e.getMessage())); return Collections.singletonList(singletonMap(CommandOperation.ERR_MSGS, msg + ":" + e.getMessage()));
} }
List errs = captureErrors(ops); List errs = CommandOperation.captureErrors(ops);
if(!errs.isEmpty()) return errs; if(!errs.isEmpty()) return errs;
IndexSchema schema = req.getCore().getLatestSchema(); IndexSchema schema = req.getCore().getLatestSchema();
if (!(schema instanceof ManagedIndexSchema)) { if (!(schema instanceof ManagedIndexSchema)) {
return singletonList( singletonMap(ERR_MSGS,"schema is not editable")); return singletonList( singletonMap(CommandOperation.ERR_MSGS,"schema is not editable"));
} }
synchronized (schema.getSchemaUpdateLock()) { synchronized (schema.getSchemaUpdateLock()) {
@ -109,14 +102,14 @@ public class SchemaManager {
} }
private List<String> doOperations(List<Operation> operations){ private List doOperations(List<CommandOperation> operations){
int timeout = req.getParams().getInt(BaseSolrResource.UPDATE_TIMEOUT_SECS, -1); int timeout = req.getParams().getInt(BaseSolrResource.UPDATE_TIMEOUT_SECS, -1);
long startTime = System.nanoTime(); long startTime = System.nanoTime();
long endTime = timeout >0 ? System.nanoTime()+ (timeout * 1000*1000) : Long.MAX_VALUE; long endTime = timeout >0 ? System.nanoTime()+ (timeout * 1000*1000) : Long.MAX_VALUE;
SolrCore core = req.getCore(); SolrCore core = req.getCore();
for(;System.nanoTime() < endTime ;) { for(;System.nanoTime() < endTime ;) {
managedIndexSchema = (ManagedIndexSchema) core.getLatestSchema(); managedIndexSchema = (ManagedIndexSchema) core.getLatestSchema();
for (Operation op : operations) { for (CommandOperation op : operations) {
if (ADD_FIELD.equals(op.name) || ADD_DYNAMIC_FIELD.equals(op.name)) { if (ADD_FIELD.equals(op.name) || ADD_DYNAMIC_FIELD.equals(op.name)) {
applyAddField(op); applyAddField(op);
} else if(ADD_COPY_FIELD.equals(op.name)) { } else if(ADD_COPY_FIELD.equals(op.name)) {
@ -128,7 +121,7 @@ public class SchemaManager {
op.addError("No such operation : " + op.name); op.addError("No such operation : " + op.name);
} }
} }
List errs = captureErrors(operations); List errs = CommandOperation.captureErrors(operations);
if (!errs.isEmpty()) return errs; if (!errs.isEmpty()) return errs;
try { try {
@ -169,13 +162,13 @@ public class SchemaManager {
} }
} }
private boolean applyAddType(Operation op) { private boolean applyAddType(CommandOperation op) {
String name = op.getStr(NAME); String name = op.getStr(NAME);
String clz = op.getStr(CLASS_NAME); String clz = op.getStr(CLASS_NAME);
if(op.hasError()) if(op.hasError())
return false; return false;
try { try {
FieldType fieldType = managedIndexSchema.newFieldType(name, clz, (Map<String, ?>) op.commandData); FieldType fieldType = managedIndexSchema.newFieldType(name, clz, op.getDataMap());
managedIndexSchema = managedIndexSchema.addFieldTypes(singletonList(fieldType), false); managedIndexSchema = managedIndexSchema.addFieldTypes(singletonList(fieldType), false);
return true; return true;
} catch (Exception e) { } catch (Exception e) {
@ -184,7 +177,7 @@ public class SchemaManager {
} }
} }
private String getErrorStr(Exception e) { public static String getErrorStr(Exception e) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
Throwable cause= e; Throwable cause= e;
for(int i =0;i<5;i++) { for(int i =0;i<5;i++) {
@ -195,7 +188,7 @@ public class SchemaManager {
return sb.toString(); return sb.toString();
} }
private boolean applyAddCopyField(Operation op) { private boolean applyAddCopyField(CommandOperation op) {
String src = op.getStr(SOURCE); String src = op.getStr(SOURCE);
List<String> dest = op.getStrs(DESTINATION); List<String> dest = op.getStrs(DESTINATION);
if(op.hasError()) if(op.hasError())
@ -210,7 +203,7 @@ public class SchemaManager {
} }
private boolean applyAddField( Operation op) { private boolean applyAddField( CommandOperation op) {
String name = op.getStr(NAME); String name = op.getStr(NAME);
String type = op.getStr(TYPE); String type = op.getStr(TYPE);
if(op.hasError()) if(op.hasError())
@ -238,142 +231,4 @@ public class SchemaManager {
return true; return true;
} }
public static class Operation {
public final String name;
private Object commandData;//this is most often a map
private List<String> errors = new ArrayList<>();
Operation(String operationName, Object metaData) {
commandData = metaData;
this.name = operationName;
if(!KNOWN_OPS.contains(this.name)) errors.add("Unknown Operation :"+this.name);
}
public String getStr(String key, String def){
String s = (String) getMapVal(key);
return s == null ? def : s;
}
private Object getMapVal(String key) {
if (commandData instanceof Map) {
Map metaData = (Map) commandData;
return metaData.get(key);
} else {
String msg= " value has to be an object for operation :"+name;
if(!errors.contains(msg)) errors.add(msg);
return null;
}
}
public List<String> getStrs(String key){
List<String> val = getStrs(key, null);
if(val == null) errors.add("'"+key + "' is a required field");
return val;
}
/**Get collection of values for a key. If only one val is present a
* single value collection is returned
*/
public List<String> getStrs(String key, List<String> def){
Object v = getMapVal(key);
if(v == null){
return def;
} else {
if (v instanceof List) {
ArrayList<String> l = new ArrayList<>();
for (Object o : (List)v) {
l.add(String.valueOf(o));
}
if(l.isEmpty()) return def;
return l;
} else {
return singletonList(String.valueOf(v));
}
}
}
/**Get a required field. If missing it adds to the errors
*/
public String getStr(String key){
String s = getStr(key,null);
if(s==null) errors.add("'"+key + "' is a required field");
return s;
}
private Map errorDetails(){
return makeMap(name, commandData, ERR_MSGS, errors);
}
public boolean hasError() {
return !errors.isEmpty();
}
public void addError(String s) {
errors.add(s);
}
/**Get all the values from the metadata for the command
* without the specified keys
*/
public Map getValuesExcluding(String... keys) {
getMapVal(null);
if(hasError()) return emptyMap();//just to verify the type is Map
LinkedHashMap<String, Object> cp = new LinkedHashMap<>((Map<String,?>) commandData);
if(keys == null) return cp;
for (String key : keys) {
cp.remove(key);
}
return cp;
}
public List<String> getErrors() {
return errors;
}
}
/**Parse the command operations into command objects
*/
static List<Operation> parse(Reader rdr ) throws IOException {
JSONParser parser = new JSONParser(rdr);
ObjectBuilder ob = new ObjectBuilder(parser);
if(parser.lastEvent() != JSONParser.OBJECT_START) {
throw new RuntimeException("The JSON must be an Object of the form {\"command\": {...},...");
}
List<Operation> operations = new ArrayList<>();
for(;;) {
int ev = parser.nextEvent();
if (ev==JSONParser.OBJECT_END) return operations;
Object key = ob.getKey();
ev = parser.nextEvent();
Object val = ob.getVal();
if (val instanceof List) {
List list = (List) val;
for (Object o : list) {
operations.add(new Operation(String.valueOf(key), o));
}
} else {
operations.add(new Operation(String.valueOf(key), val));
}
}
}
static List<Map> captureErrors(List<Operation> ops){
List<Map> errors = new ArrayList<>();
for (SchemaManager.Operation op : ops) {
if(op.hasError()) {
errors.add(op.errorDetails());
}
}
return errors;
}
public static final String ERR_MSGS = "errorMessages";
} }

View File

@ -0,0 +1,209 @@
package org.apache.solr.util;
/*
* 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.io.Reader;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.noggit.JSONParser;
import org.noggit.ObjectBuilder;
import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonList;
import static org.apache.solr.common.cloud.ZkNodeProps.makeMap;
public class CommandOperation {
public final String name;
private Object commandData;//this is most often a map
private List<String> errors = new ArrayList<>();
CommandOperation(String operationName, Object metaData) {
commandData = metaData;
this.name = operationName;
}
public String getStr(String key, String def){
if(ROOT_OBJ.equals(key)){
Object obj = getRootPrimitive();
return obj == def ? null: String.valueOf(obj);
}
String s = (String) getMapVal(key);
return s == null ? def : s;
}
public Map<String,Object> getDataMap(){
if (commandData instanceof Map) {
return (Map) commandData;
}
addError(MessageFormat.format("The command {0} should have the values as a json object {key:val} format", name));
return Collections.EMPTY_MAP;
}
private Object getRootPrimitive(){
if (commandData instanceof Map) {
errors.add(MessageFormat.format("The value has to be a string for command : {1}",name));
return null;
}
return commandData;
}
public Object getVal(String key){
return getMapVal(key);
}
private Object getMapVal(String key) {
if (commandData instanceof Map) {
Map metaData = (Map) commandData;
return metaData.get(key);
} else {
String msg= " value has to be an object for operation :"+name;
if(!errors.contains(msg)) errors.add(msg);
return null;
}
}
public List<String> getStrs(String key){
List<String> val = getStrs(key, null);
if(val == null) {
errors.add(MessageFormat.format(REQD, key));
}
return val;
}
static final String REQD = "'{1}' is a required field";
/**Get collection of values for a key. If only one val is present a
* single value collection is returned
*/
public List<String> getStrs(String key, List<String> def){
Object v = getMapVal(key);
if(v == null){
return def;
} else {
if (v instanceof List) {
ArrayList<String> l = new ArrayList<>();
for (Object o : (List)v) {
l.add(String.valueOf(o));
}
if(l.isEmpty()) return def;
return l;
} else {
return singletonList(String.valueOf(v));
}
}
}
/**Get a required field. If missing it adds to the errors
*/
public String getStr(String key){
if(ROOT_OBJ.equals(key)){
Object obj = getRootPrimitive();
if(obj == null) {
errors.add(MessageFormat.format(REQD,name));
}
return obj == null ? null: String.valueOf(obj);
}
String s = getStr(key,null);
if(s==null) errors.add(MessageFormat.format(REQD, key));
return s;
}
private Map errorDetails(){
return makeMap(name, commandData, ERR_MSGS, errors);
}
public boolean hasError() {
return !errors.isEmpty();
}
public void addError(String s) {
if(errors.contains(s)) return;
errors.add(s);
}
/**Get all the values from the metadata for the command
* without the specified keys
*/
public Map getValuesExcluding(String... keys) {
getMapVal(null);
if(hasError()) return emptyMap();//just to verify the type is Map
LinkedHashMap<String, Object> cp = new LinkedHashMap<>((Map<String,?>) commandData);
if(keys == null) return cp;
for (String key : keys) {
cp.remove(key);
}
return cp;
}
public List<String> getErrors() {
return errors;
}
public static final String ERR_MSGS = "errorMessages";
public static final String ROOT_OBJ = "";
public static List<Map> captureErrors(List<CommandOperation> ops){
List<Map> errors = new ArrayList<>();
for (CommandOperation op : ops) {
if(op.hasError()) {
errors.add(op.errorDetails());
}
}
return errors;
}
/**Parse the command operations into command objects
*/
public static List<CommandOperation> parse(Reader rdr ) throws IOException {
JSONParser parser = new JSONParser(rdr);
ObjectBuilder ob = new ObjectBuilder(parser);
if(parser.lastEvent() != JSONParser.OBJECT_START) {
throw new RuntimeException("The JSON must be an Object of the form {\"command\": {...},...");
}
List<CommandOperation> operations = new ArrayList<>();
for(;;) {
int ev = parser.nextEvent();
if (ev==JSONParser.OBJECT_END) return operations;
Object key = ob.getKey();
ev = parser.nextEvent();
Object val = ob.getVal();
if (val instanceof List) {
List list = (List) val;
for (Object o : list) {
operations.add(new CommandOperation(String.valueOf(key), o));
}
} else {
operations.add(new CommandOperation(String.valueOf(key), val));
}
}
}
}

View File

@ -19,7 +19,6 @@ package org.apache.solr.rest.schema;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.solr.SolrTestCaseJ4; import org.apache.solr.SolrTestCaseJ4;
import org.apache.solr.schema.SchemaManager;
import org.apache.solr.util.RestTestBase; import org.apache.solr.util.RestTestBase;
import org.apache.solr.util.RestTestHarness; import org.apache.solr.util.RestTestHarness;
import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.servlet.ServletHolder;
@ -98,10 +97,10 @@ public class TestBulkSchemaAPI extends RestTestBase {
Map map = (Map) ObjectBuilder.getVal(new JSONParser(new StringReader(response))); Map map = (Map) ObjectBuilder.getVal(new JSONParser(new StringReader(response)));
List l = (List) map.get("errors"); List l = (List) map.get("errors");
List errorList = (List) ((Map) l.get(0)).get(SchemaManager.ERR_MSGS); List errorList = (List) ((Map) l.get(0)).get("errorMessages");
assertEquals(1, errorList.size()); assertEquals(1, errorList.size());
assertTrue (((String)errorList.get(0)).contains("No such field type")); assertTrue (((String)errorList.get(0)).contains("No such field type"));
errorList = (List) ((Map) l.get(1)).get(SchemaManager.ERR_MSGS); errorList = (List) ((Map) l.get(1)).get("errorMessages");
assertEquals(1, errorList.size()); assertEquals(1, errorList.size());
assertTrue (((String)errorList.get(0)).contains("is a required field")); assertTrue (((String)errorList.get(0)).contains("is a required field"));

View File

@ -18,6 +18,7 @@ package org.apache.solr.schema;
*/ */
import org.apache.solr.SolrTestCaseJ4; import org.apache.solr.SolrTestCaseJ4;
import org.apache.solr.util.CommandOperation;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
@ -49,9 +50,9 @@ public class TestSchemaManager extends SolrTestCaseJ4 {
"\n" + "\n" +
"}"; "}";
List<SchemaManager.Operation> ops = SchemaManager.parse(new StringReader(x)); List<CommandOperation> ops = CommandOperation.parse(new StringReader(x));
assertEquals(2,ops.size()); assertEquals(2,ops.size());
assertTrue( SchemaManager.captureErrors(ops).isEmpty()); assertTrue( CommandOperation.captureErrors(ops).isEmpty());
x = " {\"add-field\" : [{\n" + x = " {\"add-field\" : [{\n" +
" \"name\":\"a1\",\n" + " \"name\":\"a1\",\n" +
@ -66,9 +67,9 @@ public class TestSchemaManager extends SolrTestCaseJ4 {
" \"indexed\":true\n" + " \"indexed\":true\n" +
" }]\n" + " }]\n" +
" }"; " }";
ops = SchemaManager.parse(new StringReader(x)); ops = CommandOperation.parse(new StringReader(x));
assertEquals(2,ops.size()); assertEquals(2,ops.size());
assertTrue( SchemaManager.captureErrors(ops).isEmpty()); assertTrue( CommandOperation.captureErrors(ops).isEmpty());
} }