diff --git a/solr/core/src/java/org/apache/solr/schema/SchemaManager.java b/solr/core/src/java/org/apache/solr/schema/SchemaManager.java index 6ca05e624f5..2fd63f8a62a 100644 --- a/solr/core/src/java/org/apache/solr/schema/SchemaManager.java +++ b/solr/core/src/java/org/apache/solr/schema/SchemaManager.java @@ -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 ops = null; + List 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 doOperations(List operations){ + private List doOperations(List 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) 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 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 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 getStrs(String key){ - List 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 getStrs(String key, List def){ - Object v = getMapVal(key); - if(v == null){ - return def; - } else { - if (v instanceof List) { - ArrayList 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 cp = new LinkedHashMap<>((Map) commandData); - if(keys == null) return cp; - for (String key : keys) { - cp.remove(key); - } - return cp; - } - - - public List getErrors() { - return errors; - } - } - - /**Parse the command operations into command objects - */ - static List 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 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 captureErrors(List ops){ - List errors = new ArrayList<>(); - for (SchemaManager.Operation op : ops) { - if(op.hasError()) { - errors.add(op.errorDetails()); - } - } - return errors; - } - public static final String ERR_MSGS = "errorMessages"; - - } diff --git a/solr/core/src/java/org/apache/solr/util/CommandOperation.java b/solr/core/src/java/org/apache/solr/util/CommandOperation.java new file mode 100644 index 00000000000..9f5e114454a --- /dev/null +++ b/solr/core/src/java/org/apache/solr/util/CommandOperation.java @@ -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 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 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 getStrs(String key){ + List 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 getStrs(String key, List def){ + Object v = getMapVal(key); + if(v == null){ + return def; + } else { + if (v instanceof List) { + ArrayList 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 cp = new LinkedHashMap<>((Map) commandData); + if(keys == null) return cp; + for (String key : keys) { + cp.remove(key); + } + return cp; + } + + + public List getErrors() { + return errors; + } + public static final String ERR_MSGS = "errorMessages"; + public static final String ROOT_OBJ = ""; + public static List captureErrors(List ops){ + List 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 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 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)); + } + } + + } + +} diff --git a/solr/core/src/test/org/apache/solr/rest/schema/TestBulkSchemaAPI.java b/solr/core/src/test/org/apache/solr/rest/schema/TestBulkSchemaAPI.java index 2bb3a98d26a..0f8354cea35 100644 --- a/solr/core/src/test/org/apache/solr/rest/schema/TestBulkSchemaAPI.java +++ b/solr/core/src/test/org/apache/solr/rest/schema/TestBulkSchemaAPI.java @@ -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")); diff --git a/solr/core/src/test/org/apache/solr/schema/TestSchemaManager.java b/solr/core/src/test/org/apache/solr/schema/TestSchemaManager.java index a5df2096397..c4892c5f29e 100644 --- a/solr/core/src/test/org/apache/solr/schema/TestSchemaManager.java +++ b/solr/core/src/test/org/apache/solr/schema/TestSchemaManager.java @@ -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 ops = SchemaManager.parse(new StringReader(x)); + List 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()); }