SOLR-6476 refactored bulk schema APIs and other read REST APIs to use standard RequestHandler mechanism

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1642641 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Noble Paul 2014-12-01 08:02:05 +00:00
parent a371f353f5
commit f317f12f87
19 changed files with 324 additions and 660 deletions

View File

@ -2123,7 +2123,7 @@ public final class ZkController {
final ZkController zkController = zkLoader.getZkController();
final SolrZkClient zkClient = zkController.getZkClient();
final String resourceLocation = zkLoader.getConfigSetZkPath() + "/" + resourceName;
String errMsg = "Failed to persist resource at {0} - version mismatch {1}";
String errMsg = "Failed to persist resource at {0} - old {1}";
try {
try {
zkClient.setData(resourceLocation , content,znodeVersion, true);
@ -2136,7 +2136,7 @@ public final class ZkController {
} catch (KeeperException.NodeExistsException nee) {
try {
Stat stat = zkClient.exists(resourceLocation, null, true);
log.info("failed to set data version in zk is {} and expected version is {} ", stat.getVersion(),znodeVersion);
log.info("failed to set data version in zk is {0} and expected version is {1} ", stat.getVersion(),znodeVersion);
} catch (Exception e1) {
log.warn("could not get stat");
}
@ -2148,7 +2148,15 @@ public final class ZkController {
}
} catch (KeeperException.BadVersionException bve){
log.info(MessageFormat.format(errMsg,resourceLocation));
int v = -1;
try {
Stat stat = zkClient.exists(resourceLocation, null, true);
v = stat.getVersion();
} catch (Exception e) {
log.error(e.getMessage());
}
log.info(MessageFormat.format(errMsg+ " zkVersion= "+v,resourceLocation,znodeVersion));
throw new ResourceModifiedInZkException(ErrorCode.CONFLICT, MessageFormat.format(errMsg,resourceLocation,znodeVersion) + ", retry.");
}catch (ResourceModifiedInZkException e){
throw e;

View File

@ -27,6 +27,7 @@ import org.apache.solr.common.util.NamedList;
import org.apache.solr.handler.PingRequestHandler;
import org.apache.solr.handler.RealTimeGetHandler;
import org.apache.solr.handler.ReplicationHandler;
import org.apache.solr.handler.SchemaHandler;
import org.apache.solr.handler.SolrConfigHandler;
import org.apache.solr.handler.UpdateRequestHandler;
import org.apache.solr.handler.admin.LoggingHandler;
@ -57,7 +58,8 @@ public class PluginsRegistry {
//solrconfighandler
implicits.add(getReqHandlerInfo("/config", SolrConfigHandler.class, null));
//schemahandler
implicits.add(getReqHandlerInfo("/schema", SchemaHandler.class, null));
//register replicationhandler always for SolrCloud
implicits.add(getReqHandlerInfo("/replication", ReplicationHandler.class,null));

View File

@ -0,0 +1,169 @@
package org.apache.solr.handler;
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.solr.cloud.ZkSolrResourceLoader;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.util.ContentStream;
import org.apache.solr.common.util.SimpleOrderedMap;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.request.SolrRequestHandler;
import org.apache.solr.response.SolrQueryResponse;
import org.apache.solr.schema.IndexSchema;
import org.apache.solr.schema.ManagedIndexSchema;
import org.apache.solr.schema.SchemaManager;
import org.apache.solr.schema.ZkIndexSchemaReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SchemaHandler extends RequestHandlerBase {
private static final Logger log = LoggerFactory.getLogger(SchemaHandler.class);
@Override
public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
SolrConfigHandler.setWt(req, "json");
String httpMethod = (String) req.getContext().get("httpMethod");
if("POST".equals(httpMethod)){
if (req.getContentStreams() == null) {
rsp.add("errors", "no stream");
return;
}
for (ContentStream stream : req.getContentStreams()) {
try {
List errs = new SchemaManager(req).performOperations(stream.getReader());
if(!errs.isEmpty()) rsp.add("errors", errs);
} catch (IOException e) {
rsp.add("errors", Collections.singletonList("Error reading input String " + e.getMessage()));
rsp.setException(e);
}
break;
}
} else {
handleGET(req, rsp);
}
}
private void handleGET(SolrQueryRequest req, SolrQueryResponse rsp) {
try {
String path = (String) req.getContext().get("path");
switch (path){
case "/schema":
rsp.add(IndexSchema.SCHEMA, req.getSchema().getNamedPropertyValues());
break;
case "/schema/version" :
rsp.add(IndexSchema.VERSION, req.getSchema().getVersion());
break;
case "/schema/uniquekey" :
rsp.add(IndexSchema.UNIQUE_KEY, req.getSchema().getUniqueKeyField().getName());
break;
case "/schema/similarity" :
rsp.add(IndexSchema.SIMILARITY, req.getSchema().getSimilarityFactory().getNamedPropertyValues());
break;
case "/schema/name" : {
final String schemaName = req.getSchema().getSchemaName();
if (null == schemaName) {
String message = "Schema has no name";
throw new SolrException(SolrException.ErrorCode.NOT_FOUND, message);
}
rsp.add(IndexSchema.NAME, schemaName);
break;
}
case "/schema/defaultsearchfield" : {
final String defaultSearchFieldName = req.getSchema().getDefaultSearchFieldName();
if (null == defaultSearchFieldName) {
final String message = "undefined " + IndexSchema.DEFAULT_SEARCH_FIELD;
throw new SolrException(SolrException.ErrorCode.NOT_FOUND, message);
}
rsp.add(IndexSchema.DEFAULT_SEARCH_FIELD, defaultSearchFieldName);
break;
}
case "/schema/solrqueryparser":{
SimpleOrderedMap<Object> props = new SimpleOrderedMap<>();
props.add(IndexSchema.DEFAULT_OPERATOR, req.getSchema().getQueryParserDefaultOperator());
rsp.add(IndexSchema.SOLR_QUERY_PARSER, props);
break;
}
case "/schema/zkversion" : {
int refreshIfBelowVersion = -1;
Object refreshParam = req.getParams().get("refreshIfBelowVersion");
if (refreshParam != null)
refreshIfBelowVersion = (refreshParam instanceof Number) ? ((Number)refreshParam).intValue()
: Integer.parseInt(refreshParam.toString());
int zkVersion = -1;
IndexSchema schema = req.getSchema();
if (schema instanceof ManagedIndexSchema) {
ManagedIndexSchema managed = (ManagedIndexSchema)schema;
zkVersion = managed.getSchemaZkVersion();
if (refreshIfBelowVersion != -1 && zkVersion < refreshIfBelowVersion) {
log.info("REFRESHING SCHEMA (refreshIfBelowVersion="+refreshIfBelowVersion+
", currentVersion="+zkVersion+") before returning version!");
ZkSolrResourceLoader zkSolrResourceLoader = (ZkSolrResourceLoader)req.getCore().getResourceLoader();
ZkIndexSchemaReader zkIndexSchemaReader = zkSolrResourceLoader.getZkIndexSchemaReader();
managed = zkIndexSchemaReader.refreshSchemaFromZk(refreshIfBelowVersion);
zkVersion = managed.getSchemaZkVersion();
}
}
rsp.add("zkversion", zkVersion);
break;
}
case "/schema/solrqueryparser/defaultoperator" : {
rsp.add(IndexSchema.DEFAULT_OPERATOR, req.getSchema().getQueryParserDefaultOperator());
break;
}
default : {
throw new SolrException(SolrException.ErrorCode.NOT_FOUND,"No such path "+path);
}
}
} catch (Exception e) {
rsp.setException(e);
}
}
private static Set<String> subPaths = new HashSet<>(Arrays.asList(
"/version",
"/uniquekey",
"/name",
"/similarity" ,
"/defaultsearchfield",
"/solrqueryparser",
"/zkversion",
"/solrqueryparser/defaultoperator"
));
@Override
public SolrRequestHandler getSubHandler(String subPath) {
if(subPaths.contains(subPath)) return this;
return null;
}
@Override
public String getDescription() {
return "Edit schema.xml";
}
}

View File

@ -52,6 +52,7 @@ import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.request.SolrRequestHandler;
import org.apache.solr.response.SolrQueryResponse;
import org.apache.solr.schema.FieldType;
import org.apache.solr.schema.ManagedIndexSchema;
import org.apache.solr.schema.SchemaManager;
import org.apache.solr.util.CommandOperation;
import org.apache.solr.util.plugin.SolrCoreAware;
@ -104,22 +105,33 @@ public class SolrConfigHandler extends RequestHandlerBase implements SolrCoreAwa
private static Runnable getListener(SolrCore core, ZkSolrResourceLoader zkSolrResourceLoader) {
final String coreName = core.getName();
final CoreContainer cc = core.getCoreDescriptor().getCoreContainer();
final String overlayPath = (zkSolrResourceLoader).getConfigSetZkPath() + "/" + ConfigOverlay.RESOURCE_NAME;
final String solrConfigPath = (zkSolrResourceLoader).getConfigSetZkPath() + "/" + core.getSolrConfig().getName();
final String overlayPath = zkSolrResourceLoader.getConfigSetZkPath() + "/" + ConfigOverlay.RESOURCE_NAME;
final String solrConfigPath = zkSolrResourceLoader.getConfigSetZkPath() + "/" + core.getSolrConfig().getName();
String schemaRes = null;
if(core.getLatestSchema().isMutable() && core.getLatestSchema() instanceof ManagedIndexSchema){
ManagedIndexSchema mis = (ManagedIndexSchema) core.getLatestSchema();
schemaRes = mis.getResourceName();
}
final String managedSchmaResourcePath = schemaRes ==null ? null: zkSolrResourceLoader.getConfigSetZkPath() + "/" + schemaRes;
return new Runnable() {
@Override
public void run() {
log.info("config update listener called for core {}", coreName);
SolrZkClient zkClient = cc.getZkController().getZkClient();
int solrConfigversion,overlayVersion;
int solrConfigversion,overlayVersion, managedSchemaVersion=0;
try (SolrCore core = cc.getCore(coreName)) {
if (core.isClosed()) return;
solrConfigversion = core.getSolrConfig().getOverlay().getZnodeVersion();
overlayVersion = core.getSolrConfig().getZnodeVersion();
if(managedSchmaResourcePath != null){
managedSchemaVersion = ((ManagedIndexSchema)core.getLatestSchema()).getSchemaZkVersion();
}
}
if (checkStale(zkClient, overlayPath, solrConfigversion) ||
checkStale(zkClient, solrConfigPath, overlayVersion)) {
checkStale(zkClient, solrConfigPath, overlayVersion) ||
checkStale(zkClient, managedSchmaResourcePath,managedSchemaVersion)) {
log.info("core reload {}",coreName);
cc.reload(coreName);
}
@ -128,6 +140,7 @@ public class SolrConfigHandler extends RequestHandlerBase implements SolrCoreAwa
}
private static boolean checkStale(SolrZkClient zkClient, String zkPath, int currentVersion) {
if(zkPath == null) return false;
try {
Stat stat = zkClient.exists(zkPath, null, true);
if(stat == null){

View File

@ -18,21 +18,12 @@ package org.apache.solr.rest;
import org.apache.solr.request.SolrRequestInfo;
import org.apache.solr.rest.schema.CopyFieldCollectionResource;
import org.apache.solr.rest.schema.SchemaResource;
import org.apache.solr.rest.schema.DefaultSearchFieldResource;
import org.apache.solr.rest.schema.DynamicFieldCollectionResource;
import org.apache.solr.rest.schema.DynamicFieldResource;
import org.apache.solr.rest.schema.FieldCollectionResource;
import org.apache.solr.rest.schema.FieldResource;
import org.apache.solr.rest.schema.FieldTypeCollectionResource;
import org.apache.solr.rest.schema.FieldTypeResource;
import org.apache.solr.rest.schema.SchemaNameResource;
import org.apache.solr.rest.schema.SchemaSimilarityResource;
import org.apache.solr.rest.schema.SchemaVersionResource;
import org.apache.solr.rest.schema.SchemaZkVersionResource;
import org.apache.solr.rest.schema.SolrQueryParserDefaultOperatorResource;
import org.apache.solr.rest.schema.SolrQueryParserResource;
import org.apache.solr.rest.schema.UniqueKeyFieldResource;
import org.apache.solr.schema.IndexSchema;
import org.restlet.Application;
import org.restlet.Restlet;
@ -58,30 +49,11 @@ public class SolrSchemaRestApi extends Application {
public static final String FIELDTYPES = IndexSchema.FIELD_TYPES.toLowerCase(Locale.ROOT);
public static final String FIELDTYPES_PATH = "/" + FIELDTYPES;
public static final String NAME_PATH = "/" + IndexSchema.NAME.toLowerCase(Locale.ROOT);
public static final String NAME_SEGMENT = "/{" + IndexSchema.NAME.toLowerCase(Locale.ROOT) + "}";
public static final String COPY_FIELDS = IndexSchema.COPY_FIELDS.toLowerCase(Locale.ROOT);
public static final String COPY_FIELDS_PATH = "/" + COPY_FIELDS;
public static final String VERSION_PATH = "/" + IndexSchema.VERSION.toLowerCase(Locale.ROOT);
public static final String DEFAULT_SEARCH_FIELD = IndexSchema.DEFAULT_SEARCH_FIELD.toLowerCase(Locale.ROOT);
public static final String DEFAULT_SEARCH_FIELD_PATH = "/" + DEFAULT_SEARCH_FIELD;
public static final String SIMILARITY_PATH = "/" + IndexSchema.SIMILARITY.toLowerCase(Locale.ROOT);
public static final String SOLR_QUERY_PARSER = IndexSchema.SOLR_QUERY_PARSER.toLowerCase(Locale.ROOT);
public static final String SOLR_QUERY_PARSER_PATH = "/" + SOLR_QUERY_PARSER;
public static final String DEFAULT_OPERATOR = IndexSchema.DEFAULT_OPERATOR.toLowerCase(Locale.ROOT);
public static final String DEFAULT_OPERATOR_PATH = SOLR_QUERY_PARSER_PATH + "/" + DEFAULT_OPERATOR;
public static final String UNIQUE_KEY_FIELD = IndexSchema.UNIQUE_KEY.toLowerCase(Locale.ROOT);
public static final String UNIQUE_KEY_FIELD_PATH = "/" + UNIQUE_KEY_FIELD;
public static final String ZK_VERSION_PATH = "/zkversion";
/**
* Returns reserved endpoints under /schema
*/
@ -90,15 +62,7 @@ public class SolrSchemaRestApi extends Application {
reservedEndpoints.add(RestManager.SCHEMA_BASE_PATH + FIELDS_PATH);
reservedEndpoints.add(RestManager.SCHEMA_BASE_PATH + DYNAMIC_FIELDS_PATH);
reservedEndpoints.add(RestManager.SCHEMA_BASE_PATH + FIELDTYPES_PATH);
reservedEndpoints.add(RestManager.SCHEMA_BASE_PATH + NAME_PATH);
reservedEndpoints.add(RestManager.SCHEMA_BASE_PATH + COPY_FIELDS_PATH);
reservedEndpoints.add(RestManager.SCHEMA_BASE_PATH + VERSION_PATH);
reservedEndpoints.add(RestManager.SCHEMA_BASE_PATH + DEFAULT_SEARCH_FIELD_PATH);
reservedEndpoints.add(RestManager.SCHEMA_BASE_PATH + SIMILARITY_PATH);
reservedEndpoints.add(RestManager.SCHEMA_BASE_PATH + SOLR_QUERY_PARSER_PATH);
reservedEndpoints.add(RestManager.SCHEMA_BASE_PATH + DEFAULT_OPERATOR_PATH);
reservedEndpoints.add(RestManager.SCHEMA_BASE_PATH + UNIQUE_KEY_FIELD_PATH);
reservedEndpoints.add(RestManager.SCHEMA_BASE_PATH + ZK_VERSION_PATH);
return Collections.unmodifiableSet(reservedEndpoints);
}
@ -123,10 +87,7 @@ public class SolrSchemaRestApi extends Application {
log.info("createInboundRoot started for /schema");
router.attach("", SchemaResource.class);
// Allow a trailing slash on full-schema requests
router.attach("/", SchemaResource.class);
router.attach(FIELDS_PATH, FieldCollectionResource.class);
// Allow a trailing slash on collection requests
router.attach(FIELDS_PATH + "/", FieldCollectionResource.class);
@ -145,23 +106,7 @@ public class SolrSchemaRestApi extends Application {
router.attach(COPY_FIELDS_PATH, CopyFieldCollectionResource.class);
// Allow a trailing slash on collection requests
router.attach(COPY_FIELDS_PATH + "/", CopyFieldCollectionResource.class);
router.attach(NAME_PATH, SchemaNameResource.class);
router.attach(VERSION_PATH, SchemaVersionResource.class);
router.attach(UNIQUE_KEY_FIELD_PATH, UniqueKeyFieldResource.class);
router.attach(DEFAULT_SEARCH_FIELD_PATH, DefaultSearchFieldResource.class);
router.attach(SIMILARITY_PATH, SchemaSimilarityResource.class);
// At present solrQueryParser only contains defaultOperator, but there may be more children in the future
router.attach(SOLR_QUERY_PARSER_PATH, SolrQueryParserResource.class);
router.attach(DEFAULT_OPERATOR_PATH, SolrQueryParserDefaultOperatorResource.class);
router.attach(ZK_VERSION_PATH, SchemaZkVersionResource.class);
router.attachDefault(RestManager.ManagedEndpoint.class);
// attach all the dynamically registered schema resources

View File

@ -1,61 +0,0 @@
package org.apache.solr.rest.schema;
/*
* 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 org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrException.ErrorCode;
import org.apache.solr.rest.BaseSolrResource;
import org.apache.solr.rest.GETable;
import org.apache.solr.schema.IndexSchema;
import org.restlet.representation.Representation;
import org.restlet.resource.ResourceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class responds to requests at /solr/(corename)/schema/defaultsearchfield
*/
public class DefaultSearchFieldResource extends BaseSolrResource implements GETable {
private static final Logger log = LoggerFactory.getLogger(DefaultSearchFieldResource.class);
public DefaultSearchFieldResource() {
super();
}
@Override
public void doInit() throws ResourceException {
super.doInit();
}
@Override
public Representation get() {
try {
final String defaultSearchFieldName = getSchema().getDefaultSearchFieldName();
if (null == defaultSearchFieldName) {
final String message = "undefined " + IndexSchema.DEFAULT_SEARCH_FIELD;
throw new SolrException(ErrorCode.NOT_FOUND, message);
}
getSolrResponse().add(IndexSchema.DEFAULT_SEARCH_FIELD, defaultSearchFieldName);
} catch (Exception e) {
getSolrResponse().setException(e);
}
handlePostExecution(log);
return new SolrOutputRepresentation();
}
}

View File

@ -1,60 +0,0 @@
package org.apache.solr.rest.schema;
/*
* 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 org.apache.solr.common.SolrException;
import org.apache.solr.rest.BaseSolrResource;
import org.apache.solr.rest.GETable;
import org.apache.solr.schema.IndexSchema;
import org.restlet.representation.Representation;
import org.restlet.resource.ResourceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class responds to requests at /solr/(corename)/schema/name
*/
public class SchemaNameResource extends BaseSolrResource implements GETable {
private static final Logger log = LoggerFactory.getLogger(SchemaNameResource.class);
public SchemaNameResource() {
super();
}
@Override
public void doInit() throws ResourceException {
super.doInit();
}
@Override
public Representation get() {
try {
final String schemaName = getSchema().getSchemaName();
if (null == schemaName) {
final String message = "Schema has no name";
throw new SolrException(SolrException.ErrorCode.NOT_FOUND, message);
}
getSolrResponse().add(IndexSchema.NAME, schemaName);
} catch (Exception e) {
getSolrResponse().setException(e);
}
handlePostExecution(log);
return new SolrOutputRepresentation();
}
}

View File

@ -1,82 +0,0 @@
package org.apache.solr.rest.schema;
/*
* 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 org.apache.solr.request.SolrRequestInfo;
import org.apache.solr.rest.BaseSolrResource;
import org.apache.solr.rest.GETable;
import org.apache.solr.rest.POSTable;
import org.apache.solr.schema.IndexSchema;
import org.apache.solr.schema.SchemaManager;
import org.restlet.representation.Representation;
import org.restlet.resource.ResourceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.StringReader;
import java.util.Collections;
import java.util.List;
/**
* This class responds to requests at /solr/(corename)/schema
*/
public class SchemaResource extends BaseSolrResource implements GETable,POSTable {
private static final Logger log = LoggerFactory.getLogger(SchemaResource.class);
public SchemaResource() {
super();
}
@Override
public void doInit() throws ResourceException {
super.doInit();
}
@Override
public Representation get() {
try {
getSolrResponse().add(IndexSchema.SCHEMA, getSchema().getNamedPropertyValues());
} catch (Exception e) {
getSolrResponse().setException(e);
}
handlePostExecution(log);
return new SolrOutputRepresentation();
}
@Override
public Representation post(Representation representation) {
SolrRequestInfo requestInfo = SolrRequestInfo.getRequestInfo();
List<String> errs = null;
try {
String text = representation.getText();
errs = new SchemaManager(requestInfo.getReq()).performOperations(new StringReader(text));
} catch (IOException e) {
requestInfo.getRsp().add("errors", Collections.singletonList("Error reading input String " + e.getMessage()));
requestInfo.getRsp().setException(e);
}
if(!errs.isEmpty()){
requestInfo.getRsp().add("errors", errs);
}
return new BaseSolrResource.SolrOutputRepresentation();
}
}

View File

@ -1,55 +0,0 @@
package org.apache.solr.rest.schema;
/*
* 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 org.apache.solr.rest.BaseSolrResource;
import org.apache.solr.rest.GETable;
import org.apache.solr.schema.IndexSchema;
import org.restlet.representation.Representation;
import org.restlet.resource.ResourceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class responds to requests at /solr/(corename)/schema/similarity
*/
public class SchemaSimilarityResource extends BaseSolrResource implements GETable {
private static final Logger log = LoggerFactory.getLogger(SchemaSimilarityResource.class);
public SchemaSimilarityResource() {
super();
}
@Override
public void doInit() throws ResourceException {
super.doInit();
}
@Override
public Representation get() {
try {
getSolrResponse().add(IndexSchema.SIMILARITY, getSchema().getSimilarityFactory().getNamedPropertyValues());
} catch (Exception e) {
getSolrResponse().setException(e);
}
handlePostExecution(log);
return new SolrOutputRepresentation();
}
}

View File

@ -1,54 +0,0 @@
package org.apache.solr.rest.schema;
/*
* 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 org.apache.solr.rest.BaseSolrResource;
import org.apache.solr.rest.GETable;
import org.apache.solr.schema.IndexSchema;
import org.restlet.representation.Representation;
import org.restlet.resource.ResourceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class responds to requests at /solr/(corename)/schema/version
*/
public class SchemaVersionResource extends BaseSolrResource implements GETable {
private static final Logger log = LoggerFactory.getLogger(SchemaVersionResource.class);
public SchemaVersionResource() {
super();
}
@Override
public void doInit() throws ResourceException {
super.doInit();
}
@Override
public Representation get() {
try {
getSolrResponse().add(IndexSchema.VERSION, getSchema().getVersion());
} catch (Exception e) {
getSolrResponse().setException(e);
}
handlePostExecution(log);
return new SolrOutputRepresentation();
}
}

View File

@ -1,79 +0,0 @@
package org.apache.solr.rest.schema;
/*
* 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 org.apache.solr.cloud.ZkSolrResourceLoader;
import org.apache.solr.rest.BaseSolrResource;
import org.apache.solr.rest.GETable;
import org.apache.solr.schema.IndexSchema;
import org.apache.solr.schema.ManagedIndexSchema;
import org.apache.solr.schema.ZkIndexSchemaReader;
import org.restlet.representation.Representation;
import org.restlet.resource.ResourceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class responds to requests at /solr/(corename)/schema/zkversion
*/
public class SchemaZkVersionResource extends BaseSolrResource implements GETable {
private static final Logger log = LoggerFactory.getLogger(SchemaZkVersionResource.class);
protected int refreshIfBelowVersion = -1;
public SchemaZkVersionResource() {
super();
}
@Override
public void doInit() throws ResourceException {
super.doInit();
// sometimes the client knows which version it expects
Object refreshParam = getSolrRequest().getParams().get("refreshIfBelowVersion");
if (refreshParam != null)
refreshIfBelowVersion = (refreshParam instanceof Number) ? ((Number)refreshParam).intValue()
: Integer.parseInt(refreshParam.toString());
}
@Override
public Representation get() {
try {
int zkVersion = -1;
IndexSchema schema = getSchema();
if (schema instanceof ManagedIndexSchema) {
ManagedIndexSchema managed = (ManagedIndexSchema)schema;
zkVersion = managed.getSchemaZkVersion();
if (refreshIfBelowVersion != -1 && zkVersion < refreshIfBelowVersion) {
log.info("REFRESHING SCHEMA (refreshIfBelowVersion="+refreshIfBelowVersion+
", currentVersion="+zkVersion+") before returning version!");
ZkSolrResourceLoader zkSolrResourceLoader = (ZkSolrResourceLoader)getSolrCore().getResourceLoader();
ZkIndexSchemaReader zkIndexSchemaReader = zkSolrResourceLoader.getZkIndexSchemaReader();
managed = zkIndexSchemaReader.refreshSchemaFromZk(refreshIfBelowVersion);
zkVersion = managed.getSchemaZkVersion();
}
}
getSolrResponse().add("zkversion", zkVersion);
} catch (Exception e) {
getSolrResponse().setException(e);
}
handlePostExecution(log);
return new SolrOutputRepresentation();
}
}

View File

@ -1,54 +0,0 @@
package org.apache.solr.rest.schema;
/*
* 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 org.apache.solr.rest.BaseSolrResource;
import org.apache.solr.rest.GETable;
import org.apache.solr.schema.IndexSchema;
import org.restlet.representation.Representation;
import org.restlet.resource.ResourceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class responds to requests at /solr/(corename)/schema/solrqueryparser/defaultoperator
*/
public class SolrQueryParserDefaultOperatorResource extends BaseSolrResource implements GETable {
private static final Logger log = LoggerFactory.getLogger(SolrQueryParserDefaultOperatorResource.class);
public SolrQueryParserDefaultOperatorResource() {
super();
}
@Override
public void doInit() throws ResourceException {
super.doInit();
}
@Override
public Representation get() {
try {
getSolrResponse().add(IndexSchema.DEFAULT_OPERATOR, getSchema().getQueryParserDefaultOperator());
} catch (Exception e) {
getSolrResponse().setException(e);
}
handlePostExecution(log);
return new SolrOutputRepresentation();
}
}

View File

@ -1,57 +0,0 @@
package org.apache.solr.rest.schema;
/*
* 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 org.apache.solr.common.util.SimpleOrderedMap;
import org.apache.solr.rest.BaseSolrResource;
import org.apache.solr.rest.GETable;
import org.apache.solr.schema.IndexSchema;
import org.restlet.representation.Representation;
import org.restlet.resource.ResourceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class responds to requests at /solr/(corename)/schema/solrqueryparser
*/
public class SolrQueryParserResource extends BaseSolrResource implements GETable {
private static final Logger log = LoggerFactory.getLogger(SolrQueryParserResource.class);
public SolrQueryParserResource() {
super();
}
@Override
public void doInit() throws ResourceException {
super.doInit();
}
@Override
public Representation get() {
try {
SimpleOrderedMap<Object> props = new SimpleOrderedMap<>();
props.add(IndexSchema.DEFAULT_OPERATOR, getSchema().getQueryParserDefaultOperator());
getSolrResponse().add(IndexSchema.SOLR_QUERY_PARSER, props);
} catch (Exception e) {
getSolrResponse().setException(e);
}
handlePostExecution(log);
return new SolrOutputRepresentation();
}
}

View File

@ -1,54 +0,0 @@
package org.apache.solr.rest.schema;
/*
* 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 org.apache.solr.rest.BaseSolrResource;
import org.apache.solr.rest.GETable;
import org.apache.solr.schema.IndexSchema;
import org.restlet.representation.Representation;
import org.restlet.resource.ResourceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class responds to requests at /solr/(corename)/schema/uniquekey
*/
public class UniqueKeyFieldResource extends BaseSolrResource implements GETable {
private static final Logger log = LoggerFactory.getLogger(UniqueKeyFieldResource.class);
public UniqueKeyFieldResource() {
super();
}
@Override
public void doInit() throws ResourceException {
super.doInit();
}
@Override
public Representation get() {
try {
getSolrResponse().add(IndexSchema.UNIQUE_KEY, getSchema().getUniqueKeyField().getName());
} catch (Exception e) {
getSolrResponse().setException(e);
}
handlePostExecution(log);
return new SolrOutputRepresentation();
}
}

View File

@ -18,17 +18,26 @@ package org.apache.solr.schema;
*/
import org.apache.solr.cloud.ZkController;
import org.apache.solr.cloud.ZkSolrResourceLoader;
import org.apache.solr.common.SolrException;
import org.apache.solr.core.ConfigOverlay;
import org.apache.solr.core.CoreDescriptor;
import org.apache.solr.core.SolrCore;
import org.apache.solr.core.SolrResourceLoader;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.rest.BaseSolrResource;
import org.apache.solr.util.CommandOperation;
import org.apache.zookeeper.KeeperException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.InputSource;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
@ -79,7 +88,7 @@ public class SchemaManager {
* @param rdr The input as a Reader
* @return Lis of errors . If the List is empty then the operation is successful.
*/
public List performOperations(Reader rdr) {
public List performOperations(Reader rdr) throws Exception {
List<CommandOperation> ops = null;
try {
ops = CommandOperation.parse(rdr);
@ -102,13 +111,13 @@ public class SchemaManager {
}
private List doOperations(List<CommandOperation> operations){
private List doOperations(List<CommandOperation> operations) throws InterruptedException, IOException, KeeperException {
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;
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 (; System.nanoTime() < endTime; ) {
managedIndexSchema = getFreshManagedSchema();
for (CommandOperation op : operations) {
if (ADD_FIELD.equals(op.name) || ADD_DYNAMIC_FIELD.equals(op.name)) {
applyAddField(op);
@ -123,20 +132,51 @@ public class SchemaManager {
}
List errs = CommandOperation.captureErrors(operations);
if (!errs.isEmpty()) return errs;
SolrResourceLoader loader = req.getCore().getResourceLoader();
if (loader instanceof ZkSolrResourceLoader) {
try {
managedIndexSchema.persistManagedSchema(false);
core.setLatestSchema(managedIndexSchema);
waitForOtherReplicasToUpdate(timeout, startTime);
return EMPTY_LIST;
} catch (ManagedIndexSchema.SchemaChangedInZkException e) {
String s = "Failed to update schema because schema is modified";
log.warn(s, e);
continue;
} catch (Exception e){
String s = "Exception persisting schema";
log.warn(s, e);
return singletonList(s + e.getMessage());
StringWriter sw = new StringWriter();
try {
managedIndexSchema.persist(sw);
} catch (IOException e) {
log.info("race condition ");
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "unable to serialize schema");
//unlikely
}
try {
ZkController.persistConfigResourceToZooKeeper(loader,
managedIndexSchema.getSchemaZkVersion(),
managedIndexSchema.getResourceName(),
sw.toString().getBytes(StandardCharsets.UTF_8),
true);
return EMPTY_LIST;
} catch (ZkController.ResourceModifiedInZkException e) {
log.info("Race condition schema modified by another node");
continue;
} catch (Exception e) {
String s = "Exception persisting schema";
log.warn(s, e);
return singletonList(s + e.getMessage());
}
}else {
try {
//only for non cloud stuff
managedIndexSchema.persistManagedSchema(false);
core.setLatestSchema(managedIndexSchema);
waitForOtherReplicasToUpdate(timeout, startTime);
return EMPTY_LIST;
} catch (ManagedIndexSchema.SchemaChangedInZkException e) {
String s = "Failed to update schema because schema is modified";
log.warn(s, e);
continue;
} catch (Exception e) {
String s = "Exception persisting schema";
log.warn(s, e);
return singletonList(s + e.getMessage());
}
}
}
@ -231,4 +271,28 @@ public class SchemaManager {
return true;
}
public ManagedIndexSchema getFreshManagedSchema() throws IOException, KeeperException, InterruptedException {
SolrResourceLoader resourceLoader = req.getCore().getResourceLoader();
if (resourceLoader instanceof ZkSolrResourceLoader) {
ZkSolrResourceLoader loader = (ZkSolrResourceLoader) resourceLoader;
InputStream in = resourceLoader.openResource(req.getSchema().getResourceName());
if (in instanceof ZkSolrResourceLoader.ZkByteArrayInputStream) {
int version = ((ZkSolrResourceLoader.ZkByteArrayInputStream) in).getStat().getVersion();
log.info("managed schema loaded . version : {} ", version);
return new ManagedIndexSchema(req.getCore().getSolrConfig(),
req.getSchema().getResourceName() ,new InputSource(in),
true,
req.getSchema().getResourceName(),
version,new Object());
}else {
return (ManagedIndexSchema) req.getCore().getLatestSchema();
}
} else {
return (ManagedIndexSchema) req.getCore().getLatestSchema();
}
}
}

View File

@ -343,26 +343,31 @@ public class SolrDispatchFilter extends BaseSolrFilter {
// get or create/cache the parser for the core
SolrRequestParsers parser = config.getRequestParsers();
// Handle /schema/* and /config/* paths via Restlet
if( path.equals("/schema") || path.startsWith("/schema/")
/*|| path.equals("/config") || path.startsWith("/config/")*/) {
solrReq = parser.parse(core, path, req);
SolrRequestInfo.setRequestInfo(new SolrRequestInfo(solrReq, new SolrQueryResponse()));
if( path.equals(req.getServletPath()) ) {
// avoid endless loop - pass through to Restlet via webapp
chain.doFilter(request, response);
} else {
// forward rewritten URI (without path prefix and core/collection name) to Restlet
req.getRequestDispatcher(path).forward(request, response);
}
return;
}
// Determine the handler from the url path if not set
// (we might already have selected the cores handler)
if( handler == null && path.length() > 1 ) { // don't match "" or "/" as valid path
handler = core.getRequestHandler( path );
if(handler == null){
//may be a restlet path
// Handle /schema/* paths via Restlet
if( path.equals("/schema") || path.startsWith("/schema/")) {
solrReq = parser.parse(core, path, req);
SolrRequestInfo.setRequestInfo(new SolrRequestInfo(solrReq, new SolrQueryResponse()));
if( path.equals(req.getServletPath()) ) {
// avoid endless loop - pass through to Restlet via webapp
chain.doFilter(request, response);
} else {
// forward rewritten URI (without path prefix and core/collection name) to Restlet
req.getRequestDispatcher(path).forward(request, response);
}
return;
}
}
// no handler yet but allowed to handle select; let's check
if( handler == null && parser.isHandleSelect() ) {
if( "/select".equals( path ) || "/select/".equals( path ) ) {
solrReq = parser.parse( core, path, req );

View File

@ -53,6 +53,7 @@ import org.apache.solr.common.util.ContentStream;
import org.apache.solr.common.util.ContentStreamBase;
import org.apache.solr.common.util.FastInputStream;
import org.apache.solr.core.CoreContainer;
import org.apache.solr.core.RequestHandlers;
import org.apache.solr.core.SolrConfig;
import org.apache.solr.core.SolrCore;
import org.apache.solr.request.SolrQueryRequest;
@ -149,7 +150,7 @@ public class SolrRequestParsers
// Handlers and login will want to know the path. If it contains a ':'
// the handler could use it for RESTful URLs
sreq.getContext().put( "path", path );
sreq.getContext().put( "path", RequestHandlers.normalize(path) );
sreq.getContext().put("httpMethod", req.getMethod());
if(addHttpRequestToContext) {

View File

@ -106,13 +106,15 @@ public class MinimalSchemaTest extends SolrTestCaseJ4 {
for (String handler : handlerNames) {
try {
if (handler.startsWith("/update")) {
if (handler.startsWith("/update") ||
handler.startsWith("/admin") ||
handler.startsWith("/schema") ||
handler.startsWith("/config") ||
handler.startsWith("/mlt")
) {
continue;
}
if (handler.startsWith("/mlt")) {
continue;
}
if(handler.equals("/admin/ping")) continue;
assertQ("failure w/handler: '" + handler + "'",
req("qt", handler,

View File

@ -25,6 +25,7 @@ import org.apache.solr.cloud.AbstractFullDistribZkTestBase;
import org.apache.solr.common.cloud.ZkStateReader;
import org.apache.solr.util.RESTfulServerProvider;
import org.apache.solr.util.RestTestHarness;
import org.junit.BeforeClass;
import org.noggit.JSONParser;
import org.noggit.ObjectBuilder;
import org.slf4j.Logger;
@ -48,6 +49,16 @@ public class TestBulkSchemaConcurrent extends AbstractFullDistribZkTestBase {
static final Logger log = LoggerFactory.getLogger(TestBulkSchemaConcurrent.class);
private List<RestTestHarness> restTestHarnesses = new ArrayList<>();
@BeforeClass
public static void initSysProperties() {
System.setProperty("managed.schema.mutable", "true");
System.setProperty("enable.update.log", "true");
}
protected String getCloudSolrConfig() {
return "solrconfig-managed-schema.xml";
}
private void setupHarnesses() {
for (final SolrServer client : clients) {
RestTestHarness harness = new RestTestHarness(new RESTfulServerProvider() {
@ -101,7 +112,7 @@ public class TestBulkSchemaConcurrent extends AbstractFullDistribZkTestBase {
}
assertTrue(success);
assertTrue(collectErrors.toString(), success);
}
@ -138,7 +149,7 @@ public class TestBulkSchemaConcurrent extends AbstractFullDistribZkTestBase {
RestTestHarness publisher = restTestHarnesses.get(r.nextInt(restTestHarnesses.size()));
payload = payload.replace("replaceFieldA1", aField);
payload = payload.replace("replaceFieldA", aField);
payload = payload.replace("replaceDynamicField", dynamicFldName);
payload = payload.replace("dynamicFieldLol","lol"+seed);