diff --git a/src/main/java/org/elasticsearch/action/WriteFailureException.java b/src/main/java/org/elasticsearch/action/WriteFailureException.java new file mode 100644 index 0000000000000..f04d1c61bda15 --- /dev/null +++ b/src/main/java/org/elasticsearch/action/WriteFailureException.java @@ -0,0 +1,40 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.action; + +import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.ElasticsearchWrapperException; +import org.elasticsearch.common.Nullable; + + +public class WriteFailureException extends ElasticsearchException implements ElasticsearchWrapperException { + @Nullable + private final String mappingTypeToUpdate; + + public WriteFailureException(Throwable cause, String mappingTypeToUpdate) { + super(null, cause); + assert cause != null; + this.mappingTypeToUpdate = mappingTypeToUpdate; + } + + public String getMappingTypeToUpdate() { + return mappingTypeToUpdate; + } +} diff --git a/src/main/java/org/elasticsearch/action/bulk/TransportShardBulkAction.java b/src/main/java/org/elasticsearch/action/bulk/TransportShardBulkAction.java index 963866e85fce6..710b5a118086f 100644 --- a/src/main/java/org/elasticsearch/action/bulk/TransportShardBulkAction.java +++ b/src/main/java/org/elasticsearch/action/bulk/TransportShardBulkAction.java @@ -22,10 +22,10 @@ import com.google.common.collect.Sets; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.ElasticsearchIllegalStateException; -import org.elasticsearch.ElasticsearchWrapperException; import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.RoutingMissingException; +import org.elasticsearch.action.WriteFailureException; import org.elasticsearch.action.delete.DeleteRequest; import org.elasticsearch.action.delete.DeleteResponse; import org.elasticsearch.action.index.IndexRequest; @@ -41,7 +41,6 @@ import org.elasticsearch.cluster.action.shard.ShardStateAction; import org.elasticsearch.cluster.metadata.MappingMetaData; import org.elasticsearch.cluster.routing.ShardIterator; -import org.elasticsearch.common.Nullable; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.inject.Inject; @@ -160,9 +159,9 @@ protected PrimaryResponse shardOperationOnP } ops[requestIndex] = result.op; } - } catch (WriteFailure e) { - if (e.mappingTypeToUpdate != null) { - mappingTypesToUpdate.add(e.mappingTypeToUpdate); + } catch (WriteFailureException e) { + if (e.getMappingTypeToUpdate() != null) { + mappingTypesToUpdate.add(e.getMappingTypeToUpdate()); } throw e.getCause(); } @@ -395,17 +394,6 @@ T response() { } - static class WriteFailure extends ElasticsearchException implements ElasticsearchWrapperException { - @Nullable - final String mappingTypeToUpdate; - - WriteFailure(Throwable cause, String mappingTypeToUpdate) { - super(null, cause); - assert cause != null; - this.mappingTypeToUpdate = mappingTypeToUpdate; - } - } - private WriteResult shardIndexOperation(BulkShardRequest request, IndexRequest indexRequest, ClusterState clusterState, IndexShard indexShard, boolean processed) { @@ -455,7 +443,7 @@ private WriteResult shardIndexOperation(BulkShardRequest request, IndexRequest i indexRequest.versionType(indexRequest.versionType().versionTypeForReplicationAndRecovery()); indexRequest.version(version); } catch (Throwable t) { - throw new WriteFailure(t, mappingTypeToUpdate); + throw new WriteFailureException(t, mappingTypeToUpdate); } assert indexRequest.versionType().validateVersionForWrites(indexRequest.version()); diff --git a/src/main/java/org/elasticsearch/action/index/TransportIndexAction.java b/src/main/java/org/elasticsearch/action/index/TransportIndexAction.java index a00d116c0132f..cd5882c1cc28f 100644 --- a/src/main/java/org/elasticsearch/action/index/TransportIndexAction.java +++ b/src/main/java/org/elasticsearch/action/index/TransportIndexAction.java @@ -22,6 +22,7 @@ import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.RoutingMissingException; +import org.elasticsearch.action.WriteFailureException; import org.elasticsearch.action.admin.indices.create.CreateIndexRequest; import org.elasticsearch.action.admin.indices.create.CreateIndexResponse; import org.elasticsearch.action.admin.indices.create.TransportCreateIndexAction; @@ -39,6 +40,7 @@ import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.engine.Engine; +import org.elasticsearch.index.mapper.DocumentMapper; import org.elasticsearch.index.mapper.SourceToParse; import org.elasticsearch.index.service.IndexService; import org.elasticsearch.index.shard.service.IndexShard; @@ -166,7 +168,7 @@ protected ShardIterator shards(ClusterState clusterState, InternalRequest reques } @Override - protected PrimaryResponse shardOperationOnPrimary(ClusterState clusterState, PrimaryOperationRequest shardRequest) { + protected PrimaryResponse shardOperationOnPrimary(ClusterState clusterState, PrimaryOperationRequest shardRequest) throws Throwable { final IndexRequest request = shardRequest.request; // validate, if routing is required, that we got routing @@ -184,43 +186,52 @@ protected PrimaryResponse shardOperationOnPrimary(C .routing(request.routing()).parent(request.parent()).timestamp(request.timestamp()).ttl(request.ttl()); long version; boolean created; - Engine.IndexingOperation op; - if (request.opType() == IndexRequest.OpType.INDEX) { - Engine.Index index = indexShard.prepareIndex(sourceToParse, request.version(), request.versionType(), Engine.Operation.Origin.PRIMARY, request.canHaveDuplicates()); - if (index.parsedDoc().mappingsModified()) { - mappingUpdatedAction.updateMappingOnMaster(shardRequest.shardId.getIndex(), index.docMapper(), indexService.indexUUID()); - } - indexShard.index(index); - version = index.version(); - op = index; - created = index.created(); - } else { - Engine.Create create = indexShard.prepareCreate(sourceToParse, - request.version(), request.versionType(), Engine.Operation.Origin.PRIMARY, request.canHaveDuplicates(), request.autoGeneratedId()); - if (create.parsedDoc().mappingsModified()) { - mappingUpdatedAction.updateMappingOnMaster(shardRequest.shardId.getIndex(), create.docMapper(), indexService.indexUUID()); + try { + Engine.IndexingOperation op; + if (request.opType() == IndexRequest.OpType.INDEX) { + Engine.Index index = indexShard.prepareIndex(sourceToParse, request.version(), request.versionType(), Engine.Operation.Origin.PRIMARY, request.canHaveDuplicates()); + if (index.parsedDoc().mappingsModified()) { + mappingUpdatedAction.updateMappingOnMaster(shardRequest.shardId.getIndex(), index.docMapper(), indexService.indexUUID()); + } + indexShard.index(index); + version = index.version(); + op = index; + created = index.created(); + } else { + Engine.Create create = indexShard.prepareCreate(sourceToParse, + request.version(), request.versionType(), Engine.Operation.Origin.PRIMARY, request.canHaveDuplicates(), request.autoGeneratedId()); + if (create.parsedDoc().mappingsModified()) { + mappingUpdatedAction.updateMappingOnMaster(shardRequest.shardId.getIndex(), create.docMapper(), indexService.indexUUID()); + } + indexShard.create(create); + version = create.version(); + op = create; + created = true; } - indexShard.create(create); - version = create.version(); - op = create; - created = true; - } - if (request.refresh()) { - try { - indexShard.refresh(new Engine.Refresh("refresh_flag_index").force(false)); - } catch (Throwable e) { - // ignore + if (request.refresh()) { + try { + indexShard.refresh(new Engine.Refresh("refresh_flag_index").force(false)); + } catch (Throwable e) { + // ignore + } } - } + // update the version on the request, so it will be used for the replicas + request.version(version); + request.versionType(request.versionType().versionTypeForReplicationAndRecovery()); + assert request.versionType().validateVersionForWrites(request.version()); - // update the version on the request, so it will be used for the replicas - request.version(version); - request.versionType(request.versionType().versionTypeForReplicationAndRecovery()); + IndexResponse response = new IndexResponse(shardRequest.shardId.getIndex(), request.type(), request.id(), version, created); + return new PrimaryResponse<>(shardRequest.request, response, op); - assert request.versionType().validateVersionForWrites(request.version()); - - IndexResponse response = new IndexResponse(shardRequest.shardId.getIndex(), request.type(), request.id(), version, created); - return new PrimaryResponse<>(shardRequest.request, response, op); + } catch (WriteFailureException e) { + if (e.getMappingTypeToUpdate() != null) { + DocumentMapper docMapper = indexService.mapperService().documentMapper(e.getMappingTypeToUpdate()); + if (docMapper != null) { + mappingUpdatedAction.updateMappingOnMaster(indexService.index().name(), docMapper, indexService.indexUUID()); + } + } + throw e.getCause(); + } } @Override diff --git a/src/main/java/org/elasticsearch/action/support/replication/TransportShardReplicationOperationAction.java b/src/main/java/org/elasticsearch/action/support/replication/TransportShardReplicationOperationAction.java index a9682c9ebf6f6..f1bfc1971381e 100644 --- a/src/main/java/org/elasticsearch/action/support/replication/TransportShardReplicationOperationAction.java +++ b/src/main/java/org/elasticsearch/action/support/replication/TransportShardReplicationOperationAction.java @@ -105,7 +105,7 @@ protected void doExecute(Request request, ActionListener listener) { protected abstract String executor(); - protected abstract PrimaryResponse shardOperationOnPrimary(ClusterState clusterState, PrimaryOperationRequest shardRequest); + protected abstract PrimaryResponse shardOperationOnPrimary(ClusterState clusterState, PrimaryOperationRequest shardRequest) throws Throwable; protected abstract void shardOperationOnReplica(ReplicaOperationRequest shardRequest); diff --git a/src/main/java/org/elasticsearch/index/shard/service/InternalIndexShard.java b/src/main/java/org/elasticsearch/index/shard/service/InternalIndexShard.java index cf4d687dcddb7..eff2b52ca22f2 100644 --- a/src/main/java/org/elasticsearch/index/shard/service/InternalIndexShard.java +++ b/src/main/java/org/elasticsearch/index/shard/service/InternalIndexShard.java @@ -29,6 +29,7 @@ import org.elasticsearch.ElasticsearchException; import org.elasticsearch.ElasticsearchIllegalArgumentException; import org.elasticsearch.ElasticsearchIllegalStateException; +import org.elasticsearch.action.WriteFailureException; import org.elasticsearch.cluster.routing.ShardRouting; import org.elasticsearch.cluster.routing.ShardRoutingState; import org.elasticsearch.common.Booleans; @@ -55,6 +56,12 @@ import org.elasticsearch.index.codec.CodecService; import org.elasticsearch.index.deletionpolicy.SnapshotIndexCommit; import org.elasticsearch.index.engine.*; +import org.elasticsearch.index.engine.Engine; +import org.elasticsearch.index.engine.EngineClosedException; +import org.elasticsearch.index.engine.EngineException; +import org.elasticsearch.index.engine.IgnoreOnRecoveryEngineException; +import org.elasticsearch.index.engine.RefreshFailedEngineException; +import org.elasticsearch.index.engine.SegmentsStats; import org.elasticsearch.index.fielddata.FieldDataStats; import org.elasticsearch.index.fielddata.IndexFieldDataService; import org.elasticsearch.index.fielddata.ShardFieldData; @@ -389,8 +396,16 @@ private IndexShardState changeState(IndexShardState newState, String reason) { public Engine.Create prepareCreate(SourceToParse source, long version, VersionType versionType, Engine.Operation.Origin origin, boolean canHaveDuplicates, boolean autoGeneratedId) throws ElasticsearchException { long startTime = System.nanoTime(); Tuple docMapper = mapperService.documentMapperWithAutoCreate(source.type()); - ParsedDocument doc = docMapper.v1().parse(source).setMappingsModified(docMapper); - return new Engine.Create(docMapper.v1(), docMapper.v1().uidMapper().term(doc.uid().stringValue()), doc, version, versionType, origin, startTime, state != IndexShardState.STARTED || canHaveDuplicates, autoGeneratedId); + try { + ParsedDocument doc = docMapper.v1().parse(source).setMappingsModified(docMapper); + return new Engine.Create(docMapper.v1(), docMapper.v1().uidMapper().term(doc.uid().stringValue()), doc, version, versionType, origin, startTime, state != IndexShardState.STARTED || canHaveDuplicates, autoGeneratedId); + } catch (Throwable t) { + if (docMapper.v2()) { + throw new WriteFailureException(t, docMapper.v1().type()); + } else { + throw t; + } + } } @Override @@ -410,8 +425,16 @@ public ParsedDocument create(Engine.Create create) throws ElasticsearchException public Engine.Index prepareIndex(SourceToParse source, long version, VersionType versionType, Engine.Operation.Origin origin, boolean canHaveDuplicates) throws ElasticsearchException { long startTime = System.nanoTime(); Tuple docMapper = mapperService.documentMapperWithAutoCreate(source.type()); - ParsedDocument doc = docMapper.v1().parse(source).setMappingsModified(docMapper); - return new Engine.Index(docMapper.v1(), docMapper.v1().uidMapper().term(doc.uid().stringValue()), doc, version, versionType, origin, startTime, state != IndexShardState.STARTED || canHaveDuplicates); + try { + ParsedDocument doc = docMapper.v1().parse(source).setMappingsModified(docMapper); + return new Engine.Index(docMapper.v1(), docMapper.v1().uidMapper().term(doc.uid().stringValue()), doc, version, versionType, origin, startTime, state != IndexShardState.STARTED || canHaveDuplicates); + } catch (Throwable t) { + if (docMapper.v2()) { + throw new WriteFailureException(t, docMapper.v1().type()); + } else { + throw t; + } + } } @Override diff --git a/src/test/java/org/elasticsearch/index/mapper/dynamic/DynamicMappingIntegrationTests.java b/src/test/java/org/elasticsearch/index/mapper/dynamic/DynamicMappingIntegrationTests.java new file mode 100644 index 0000000000000..b9c9727f402f8 --- /dev/null +++ b/src/test/java/org/elasticsearch/index/mapper/dynamic/DynamicMappingIntegrationTests.java @@ -0,0 +1,76 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ +package org.elasticsearch.index.mapper.dynamic; + +import com.google.common.base.Predicate; +import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsResponse; +import org.elasticsearch.client.Client; +import org.elasticsearch.index.mapper.StrictDynamicMappingException; +import org.elasticsearch.test.ElasticsearchIntegrationTest; +import org.junit.Test; + +import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; + + +public class DynamicMappingIntegrationTests extends ElasticsearchIntegrationTest { + + // https://github.com/elasticsearch/elasticsearch/issues/8423#issuecomment-64229717 + @Test + public void testStrictAllMapping() throws Exception { + String defaultMapping = jsonBuilder().startObject().startObject("_default_") + .field("dynamic", "strict") + .endObject().endObject().string(); + client().admin().indices().prepareCreate("index").addMapping("_default_", defaultMapping).get(); + + try { + client().prepareIndex("index", "type", "id").setSource("test", "test").get(); + fail(); + } catch (StrictDynamicMappingException ex) { + // this should not be created dynamically so we expect this exception + } + awaitBusy(new Predicate() { + @Override + public boolean apply(java.lang.Object input) { + GetMappingsResponse currentMapping = client().admin().indices().prepareGetMappings("index").get(); + return currentMapping.getMappings().get("index").get("type") != null; + } + }); + + String docMapping = jsonBuilder().startObject().startObject("type") + .startObject("_all") + .field("enabled", false) + .endObject() + .endObject().endObject().string(); + try { + client().admin().indices() + .preparePutMapping("index") + .setType("type") + .setSource(docMapping).get(); + fail(); + } catch (Exception e) { + // the mapping was created anyway with _all enabled: true, although the index request fails so we expect the update to fail + } + + // make sure type was created + for (Client client : cluster()) { + GetMappingsResponse mapping = client.admin().indices().prepareGetMappings("index").setLocal(true).get(); + assertNotNull(mapping.getMappings().get("index").get("type")); + } + } +} diff --git a/src/test/java/org/elasticsearch/index/mapper/dynamic/DynamicMappingTests.java b/src/test/java/org/elasticsearch/index/mapper/dynamic/DynamicMappingTests.java index 19e9a01e3c49c..1129abfe32af6 100644 --- a/src/test/java/org/elasticsearch/index/mapper/dynamic/DynamicMappingTests.java +++ b/src/test/java/org/elasticsearch/index/mapper/dynamic/DynamicMappingTests.java @@ -18,15 +18,22 @@ */ package org.elasticsearch.index.mapper.dynamic; +import com.google.common.base.Predicate; +import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsResponse; +import org.elasticsearch.common.settings.ImmutableSettings; +import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.index.mapper.DocumentMapper; +import org.elasticsearch.index.mapper.FieldMappers; import org.elasticsearch.index.mapper.ParsedDocument; import org.elasticsearch.index.mapper.StrictDynamicMappingException; +import org.elasticsearch.index.service.IndexService; import org.elasticsearch.test.ElasticsearchSingleNodeTest; import org.junit.Test; import java.io.IOException; +import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.nullValue; @@ -34,7 +41,7 @@ public class DynamicMappingTests extends ElasticsearchSingleNodeTest { @Test public void testDynamicTrue() throws IOException { - String mapping = XContentFactory.jsonBuilder().startObject().startObject("type") + String mapping = jsonBuilder().startObject().startObject("type") .field("dynamic", "true") .startObject("properties") .startObject("field1").field("type", "string").endObject() @@ -43,7 +50,7 @@ public void testDynamicTrue() throws IOException { DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser().parse(mapping); - ParsedDocument doc = defaultMapper.parse("type", "1", XContentFactory.jsonBuilder() + ParsedDocument doc = defaultMapper.parse("type", "1", jsonBuilder() .startObject() .field("field1", "value1") .field("field2", "value2") @@ -55,7 +62,7 @@ public void testDynamicTrue() throws IOException { @Test public void testDynamicFalse() throws IOException { - String mapping = XContentFactory.jsonBuilder().startObject().startObject("type") + String mapping = jsonBuilder().startObject().startObject("type") .field("dynamic", "false") .startObject("properties") .startObject("field1").field("type", "string").endObject() @@ -64,7 +71,7 @@ public void testDynamicFalse() throws IOException { DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser().parse(mapping); - ParsedDocument doc = defaultMapper.parse("type", "1", XContentFactory.jsonBuilder() + ParsedDocument doc = defaultMapper.parse("type", "1", jsonBuilder() .startObject() .field("field1", "value1") .field("field2", "value2") @@ -77,7 +84,7 @@ public void testDynamicFalse() throws IOException { @Test public void testDynamicStrict() throws IOException { - String mapping = XContentFactory.jsonBuilder().startObject().startObject("type") + String mapping = jsonBuilder().startObject().startObject("type") .field("dynamic", "strict") .startObject("properties") .startObject("field1").field("type", "string").endObject() @@ -87,7 +94,7 @@ public void testDynamicStrict() throws IOException { DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser().parse(mapping); try { - defaultMapper.parse("type", "1", XContentFactory.jsonBuilder() + defaultMapper.parse("type", "1", jsonBuilder() .startObject() .field("field1", "value1") .field("field2", "value2") @@ -111,7 +118,7 @@ public void testDynamicStrict() throws IOException { @Test public void testDynamicFalseWithInnerObjectButDynamicSetOnRoot() throws IOException { - String mapping = XContentFactory.jsonBuilder().startObject().startObject("type") + String mapping = jsonBuilder().startObject().startObject("type") .field("dynamic", "false") .startObject("properties") .startObject("obj1").startObject("properties") @@ -122,7 +129,7 @@ public void testDynamicFalseWithInnerObjectButDynamicSetOnRoot() throws IOExcept DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser().parse(mapping); - ParsedDocument doc = defaultMapper.parse("type", "1", XContentFactory.jsonBuilder() + ParsedDocument doc = defaultMapper.parse("type", "1", jsonBuilder() .startObject().startObject("obj1") .field("field1", "value1") .field("field2", "value2") @@ -135,7 +142,7 @@ public void testDynamicFalseWithInnerObjectButDynamicSetOnRoot() throws IOExcept @Test public void testDynamicStrictWithInnerObjectButDynamicSetOnRoot() throws IOException { - String mapping = XContentFactory.jsonBuilder().startObject().startObject("type") + String mapping = jsonBuilder().startObject().startObject("type") .field("dynamic", "strict") .startObject("properties") .startObject("obj1").startObject("properties") @@ -147,7 +154,7 @@ public void testDynamicStrictWithInnerObjectButDynamicSetOnRoot() throws IOExcep DocumentMapper defaultMapper = createIndex("test").mapperService().documentMapperParser().parse(mapping); try { - defaultMapper.parse("type", "1", XContentFactory.jsonBuilder() + defaultMapper.parse("type", "1", jsonBuilder() .startObject().startObject("obj1") .field("field1", "value1") .field("field2", "value2") @@ -158,4 +165,74 @@ public void testDynamicStrictWithInnerObjectButDynamicSetOnRoot() throws IOExcep // all is well } } + + @Test + public void testIndexingFailureDoesStillCreateType() throws IOException, InterruptedException { + XContentBuilder mapping = jsonBuilder().startObject().startObject("_default_") + .field("dynamic", "strict") + .endObject().endObject(); + + IndexService indexService = createIndex("test", ImmutableSettings.EMPTY, "_default_", mapping); + + try { + client().prepareIndex().setIndex("test").setType("type").setSource(jsonBuilder().startObject().field("test", "test").endObject()).get(); + fail(); + } catch (StrictDynamicMappingException e) { + + } + awaitBusy(new Predicate() { + @Override + public boolean apply(java.lang.Object input) { + GetMappingsResponse currentMapping = client().admin().indices().prepareGetMappings("test").get(); + return currentMapping.getMappings().get("test").get("type") != null; + } + }); + + GetMappingsResponse getMappingsResponse = client().admin().indices().prepareGetMappings("test").get(); + assertNotNull(getMappingsResponse.getMappings().get("test").get("type")); + DocumentMapper mapper = indexService.mapperService().documentMapper("type"); + assertNotNull(mapper); + + } + + @Test + public void testTypeCreatedProperly() throws IOException, InterruptedException { + XContentBuilder mapping = jsonBuilder().startObject().startObject("_default_") + .field("dynamic", "strict") + .startObject("properties") + .startObject("test_string") + .field("type", "string") + .endObject() + .endObject() + .endObject().endObject(); + + IndexService indexService = createIndex("test", ImmutableSettings.EMPTY, "_default_", mapping); + + try { + client().prepareIndex().setIndex("test").setType("type").setSource(jsonBuilder().startObject().field("test", "test").endObject()).get(); + fail(); + } catch (StrictDynamicMappingException e) { + + } + awaitBusy(new Predicate() { + @Override + public boolean apply(java.lang.Object input) { + GetMappingsResponse currentMapping = client().admin().indices().prepareGetMappings("test").get(); + return currentMapping.getMappings().get("test").get("type") != null; + } + }); + //type should be in mapping + GetMappingsResponse getMappingsResponse = client().admin().indices().prepareGetMappings("test").get(); + assertNotNull(getMappingsResponse.getMappings().get("test").get("type")); + + client().prepareIndex().setIndex("test").setType("type").setSource(jsonBuilder().startObject().field("test_string", "test").endObject()).get(); + client().admin().indices().prepareRefresh("test").get(); + assertThat(client().prepareSearch("test").get().getHits().getTotalHits(), equalTo(1l)); + + DocumentMapper mapper = indexService.mapperService().documentMapper("type"); + assertNotNull(mapper); + + getMappingsResponse = client().admin().indices().prepareGetMappings("test").get(); + assertNotNull(getMappingsResponse.getMappings().get("test").get("type")); + } } \ No newline at end of file