mirror of https://github.com/apache/lucene.git
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:
parent
5f002f0f66
commit
9a9444a642
|
@ -24,29 +24,22 @@ import org.apache.solr.core.CoreDescriptor;
|
|||
import org.apache.solr.core.SolrCore;
|
||||
import org.apache.solr.request.SolrQueryRequest;
|
||||
import org.apache.solr.rest.BaseSolrResource;
|
||||
import org.noggit.JSONParser;
|
||||
import org.noggit.ObjectBuilder;
|
||||
import org.apache.solr.util.CommandOperation;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static java.util.Collections.EMPTY_LIST;
|
||||
import static java.util.Collections.EMPTY_MAP;
|
||||
import static java.util.Collections.emptyMap;
|
||||
import static java.util.Collections.singletonList;
|
||||
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.IndexSchema.DESTINATION;
|
||||
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.
|
||||
*/
|
||||
public List performOperations(Reader rdr) {
|
||||
List<Operation> ops = null;
|
||||
List<CommandOperation> ops = null;
|
||||
try {
|
||||
ops = SchemaManager.parse(rdr);
|
||||
ops = CommandOperation.parse(rdr);
|
||||
} catch (Exception e) {
|
||||
String msg= "Error parsing schema operations ";
|
||||
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;
|
||||
|
||||
IndexSchema schema = req.getCore().getLatestSchema();
|
||||
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()) {
|
||||
|
@ -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);
|
||||
long startTime = System.nanoTime();
|
||||
long endTime = timeout >0 ? System.nanoTime()+ (timeout * 1000*1000) : Long.MAX_VALUE;
|
||||
SolrCore core = req.getCore();
|
||||
for(;System.nanoTime() < endTime ;) {
|
||||
managedIndexSchema = (ManagedIndexSchema) core.getLatestSchema();
|
||||
for (Operation op : operations) {
|
||||
for (CommandOperation op : operations) {
|
||||
if (ADD_FIELD.equals(op.name) || ADD_DYNAMIC_FIELD.equals(op.name)) {
|
||||
applyAddField(op);
|
||||
} else if(ADD_COPY_FIELD.equals(op.name)) {
|
||||
|
@ -128,7 +121,7 @@ public class SchemaManager {
|
|||
op.addError("No such operation : " + op.name);
|
||||
}
|
||||
}
|
||||
List errs = captureErrors(operations);
|
||||
List errs = CommandOperation.captureErrors(operations);
|
||||
if (!errs.isEmpty()) return errs;
|
||||
|
||||
try {
|
||||
|
@ -169,13 +162,13 @@ public class SchemaManager {
|
|||
}
|
||||
}
|
||||
|
||||
private boolean applyAddType(Operation op) {
|
||||
private boolean applyAddType(CommandOperation op) {
|
||||
String name = op.getStr(NAME);
|
||||
String clz = op.getStr(CLASS_NAME);
|
||||
if(op.hasError())
|
||||
return false;
|
||||
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);
|
||||
return true;
|
||||
} 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();
|
||||
Throwable cause= e;
|
||||
for(int i =0;i<5;i++) {
|
||||
|
@ -195,7 +188,7 @@ public class SchemaManager {
|
|||
return sb.toString();
|
||||
}
|
||||
|
||||
private boolean applyAddCopyField(Operation op) {
|
||||
private boolean applyAddCopyField(CommandOperation op) {
|
||||
String src = op.getStr(SOURCE);
|
||||
List<String> dest = op.getStrs(DESTINATION);
|
||||
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 type = op.getStr(TYPE);
|
||||
if(op.hasError())
|
||||
|
@ -238,142 +231,4 @@ public class SchemaManager {
|
|||
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";
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -19,7 +19,6 @@ package org.apache.solr.rest.schema;
|
|||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.solr.SolrTestCaseJ4;
|
||||
import org.apache.solr.schema.SchemaManager;
|
||||
import org.apache.solr.util.RestTestBase;
|
||||
import org.apache.solr.util.RestTestHarness;
|
||||
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)));
|
||||
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());
|
||||
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());
|
||||
assertTrue (((String)errorList.get(0)).contains("is a required field"));
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.apache.solr.schema;
|
|||
*/
|
||||
|
||||
import org.apache.solr.SolrTestCaseJ4;
|
||||
import org.apache.solr.util.CommandOperation;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -49,9 +50,9 @@ public class TestSchemaManager extends SolrTestCaseJ4 {
|
|||
"\n" +
|
||||
"}";
|
||||
|
||||
List<SchemaManager.Operation> ops = SchemaManager.parse(new StringReader(x));
|
||||
List<CommandOperation> ops = CommandOperation.parse(new StringReader(x));
|
||||
assertEquals(2,ops.size());
|
||||
assertTrue( SchemaManager.captureErrors(ops).isEmpty());
|
||||
assertTrue( CommandOperation.captureErrors(ops).isEmpty());
|
||||
|
||||
x = " {\"add-field\" : [{\n" +
|
||||
" \"name\":\"a1\",\n" +
|
||||
|
@ -66,9 +67,9 @@ public class TestSchemaManager extends SolrTestCaseJ4 {
|
|||
" \"indexed\":true\n" +
|
||||
" }]\n" +
|
||||
" }";
|
||||
ops = SchemaManager.parse(new StringReader(x));
|
||||
ops = CommandOperation.parse(new StringReader(x));
|
||||
assertEquals(2,ops.size());
|
||||
assertTrue( SchemaManager.captureErrors(ops).isEmpty());
|
||||
assertTrue( CommandOperation.captureErrors(ops).isEmpty());
|
||||
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue