diff --git a/src/main/java/org/elasticsearch/action/admin/cluster/state/TransportClusterStateAction.java b/src/main/java/org/elasticsearch/action/admin/cluster/state/TransportClusterStateAction.java index c5aa944d9d3..e3dedfe6252 100644 --- a/src/main/java/org/elasticsearch/action/admin/cluster/state/TransportClusterStateAction.java +++ b/src/main/java/org/elasticsearch/action/admin/cluster/state/TransportClusterStateAction.java @@ -19,6 +19,8 @@ package org.elasticsearch.action.admin.cluster.state; +import com.carrotsearch.hppc.cursors.ObjectCursor; +import com.carrotsearch.hppc.cursors.ObjectObjectCursor; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.support.ActionFilters; @@ -30,12 +32,18 @@ import org.elasticsearch.cluster.block.ClusterBlockException; import org.elasticsearch.cluster.block.ClusterBlockLevel; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.metadata.MetaData; +import org.elasticsearch.cluster.metadata.MetaData.Custom; import org.elasticsearch.cluster.routing.RoutingTable; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; +import java.util.List; + +import static com.google.common.collect.Lists.newArrayList; +import static org.elasticsearch.cluster.metadata.MetaData.lookupFactorySafe; + /** * */ @@ -118,8 +126,18 @@ public class TransportClusterStateAction extends TransportMasterNodeReadOperatio } } + // Filter our metadata that shouldn't be returned by API + for(ObjectCursor type : currentState.metaData().customs().keys()) { + Custom.Factory factory = lookupFactorySafe(type.value); + if(!factory.context().contains(MetaData.XContentContext.API)) { + mdBuilder.removeCustom(type.value); + } + } + builder.metaData(mdBuilder); } listener.onResponse(new ClusterStateResponse(clusterName, builder.build())); } + + } diff --git a/src/main/java/org/elasticsearch/cluster/metadata/BenchmarkMetaData.java b/src/main/java/org/elasticsearch/cluster/metadata/BenchmarkMetaData.java index bbb06ee364e..6638efda76f 100644 --- a/src/main/java/org/elasticsearch/cluster/metadata/BenchmarkMetaData.java +++ b/src/main/java/org/elasticsearch/cluster/metadata/BenchmarkMetaData.java @@ -155,7 +155,7 @@ public class BenchmarkMetaData implements MetaData.Custom { } - public static class Factory implements MetaData.Custom.Factory { + public static class Factory extends MetaData.Custom.Factory { @Override public String type() { @@ -209,10 +209,6 @@ public class BenchmarkMetaData implements MetaData.Custom { builder.endArray(); builder.endObject(); } - - public boolean isPersistent() { - return false; - } } public boolean contains(String benchmarkId) { diff --git a/src/main/java/org/elasticsearch/cluster/metadata/MetaData.java b/src/main/java/org/elasticsearch/cluster/metadata/MetaData.java index b4f0b8cb7f5..44ca787c20e 100644 --- a/src/main/java/org/elasticsearch/cluster/metadata/MetaData.java +++ b/src/main/java/org/elasticsearch/cluster/metadata/MetaData.java @@ -60,24 +60,38 @@ public class MetaData implements Iterable { public static final String ALL = "_all"; + public enum XContentContext { + /* Custom metadata should be returns as part of API call */ + API, + + /* Custom metadata should be stored as part of the persistent cluster state */ + GATEWAY, + + /* Custom metadata should be stored as part of a snapshot */ + SNAPSHOT; + } + + public static EnumSet API_ONLY = EnumSet.of(XContentContext.API); + public static EnumSet API_AND_GATEWAY = EnumSet.of(XContentContext.API, XContentContext.GATEWAY); + public static EnumSet API_AND_SNAPSHOT = EnumSet.of(XContentContext.API, XContentContext.SNAPSHOT); + public interface Custom { - interface Factory { + abstract class Factory { - String type(); + public abstract String type(); - T readFrom(StreamInput in) throws IOException; + public abstract T readFrom(StreamInput in) throws IOException; - void writeTo(T customIndexMetaData, StreamOutput out) throws IOException; + public abstract void writeTo(T customIndexMetaData, StreamOutput out) throws IOException; - T fromXContent(XContentParser parser) throws IOException; + public abstract T fromXContent(XContentParser parser) throws IOException; - void toXContent(T customIndexMetaData, XContentBuilder builder, ToXContent.Params params) throws IOException; + public abstract void toXContent(T customIndexMetaData, XContentBuilder builder, ToXContent.Params params) throws IOException; - /** - * Returns true if this custom metadata should be persisted as part of global cluster state - */ - boolean isPersistent(); + public EnumSet context() { + return API_ONLY; + } } } @@ -118,9 +132,11 @@ public class MetaData implements Iterable { public static final MetaData EMPTY_META_DATA = builder().build(); - public static final String GLOBAL_ONLY_PARAM = "global_only"; + public static final String CONTEXT_MODE_PARAM = "context_mode"; - public static final String PERSISTENT_ONLY_PARAM = "persistent_only"; + public static final String CONTEXT_MODE_SNAPSHOT = XContentContext.SNAPSHOT.toString(); + + public static final String CONTEXT_MODE_GATEWAY = XContentContext.GATEWAY.toString(); private final String uuid; private final long version; @@ -1076,14 +1092,14 @@ public class MetaData implements Iterable { // Check if any persistent metadata needs to be saved int customCount1 = 0; for (ObjectObjectCursor cursor : metaData1.customs) { - if (customFactories.get(cursor.key).isPersistent()) { + if (customFactories.get(cursor.key).context().contains(XContentContext.GATEWAY)) { if (!cursor.value.equals(metaData2.custom(cursor.key))) return false; customCount1++; } } int customCount2 = 0; for (ObjectObjectCursor cursor : metaData2.customs) { - if (customFactories.get(cursor.key).isPersistent()) { + if (customFactories.get(cursor.key).context().contains(XContentContext.GATEWAY)) { customCount2++; } } @@ -1262,8 +1278,8 @@ public class MetaData implements Iterable { } public static void toXContent(MetaData metaData, XContentBuilder builder, ToXContent.Params params) throws IOException { - boolean globalOnly = params.paramAsBoolean(GLOBAL_ONLY_PARAM, false); - boolean persistentOnly = params.paramAsBoolean(PERSISTENT_ONLY_PARAM, false); + XContentContext context = XContentContext.valueOf(params.param(CONTEXT_MODE_PARAM, "API")); + builder.startObject("meta-data"); builder.field("version", metaData.version()); @@ -1277,7 +1293,7 @@ public class MetaData implements Iterable { builder.endObject(); } - if (!persistentOnly && !metaData.transientSettings().getAsMap().isEmpty()) { + if (context == XContentContext.API && !metaData.transientSettings().getAsMap().isEmpty()) { builder.startObject("transient_settings"); for (Map.Entry entry : metaData.transientSettings().getAsMap().entrySet()) { builder.field(entry.getKey(), entry.getValue()); @@ -1291,7 +1307,7 @@ public class MetaData implements Iterable { } builder.endObject(); - if (!globalOnly && !metaData.indices().isEmpty()) { + if (context == XContentContext.API && !metaData.indices().isEmpty()) { builder.startObject("indices"); for (IndexMetaData indexMetaData : metaData) { IndexMetaData.Builder.toXContent(indexMetaData, builder, params); @@ -1301,13 +1317,12 @@ public class MetaData implements Iterable { for (ObjectObjectCursor cursor : metaData.customs()) { Custom.Factory factory = lookupFactorySafe(cursor.key); - if (!persistentOnly || factory.isPersistent()) { + if(factory.context().contains(context)) { builder.startObject(cursor.key); factory.toXContent(cursor.value, builder, params); builder.endObject(); } } - builder.endObject(); } diff --git a/src/main/java/org/elasticsearch/cluster/metadata/RepositoriesMetaData.java b/src/main/java/org/elasticsearch/cluster/metadata/RepositoriesMetaData.java index af515d68c2b..81b11fc14b1 100644 --- a/src/main/java/org/elasticsearch/cluster/metadata/RepositoriesMetaData.java +++ b/src/main/java/org/elasticsearch/cluster/metadata/RepositoriesMetaData.java @@ -32,6 +32,7 @@ import org.elasticsearch.common.xcontent.XContentParser; import java.io.IOException; import java.util.ArrayList; +import java.util.EnumSet; import java.util.List; import java.util.Map; @@ -82,7 +83,7 @@ public class RepositoriesMetaData implements MetaData.Custom { /** * Repository metadata factory */ - public static class Factory implements MetaData.Custom.Factory { + public static class Factory extends MetaData.Custom.Factory { /** * {@inheritDoc} @@ -171,6 +172,11 @@ public class RepositoriesMetaData implements MetaData.Custom { } } + @Override + public EnumSet context() { + return MetaData.API_AND_GATEWAY; + } + /** * Serializes information about a single repository * @@ -190,15 +196,6 @@ public class RepositoriesMetaData implements MetaData.Custom { builder.endObject(); } - - /** - * {@inheritDoc} - */ - @Override - public boolean isPersistent() { - return true; - } - } } diff --git a/src/main/java/org/elasticsearch/cluster/metadata/RestoreMetaData.java b/src/main/java/org/elasticsearch/cluster/metadata/RestoreMetaData.java index bfcb91bc25b..373d5ff858c 100644 --- a/src/main/java/org/elasticsearch/cluster/metadata/RestoreMetaData.java +++ b/src/main/java/org/elasticsearch/cluster/metadata/RestoreMetaData.java @@ -397,7 +397,7 @@ public class RestoreMetaData implements MetaData.Custom { /** * Restore metadata factory */ - public static class Factory implements MetaData.Custom.Factory { + public static class Factory extends MetaData.Custom.Factory { /** * {@inheritDoc} @@ -512,15 +512,6 @@ public class RestoreMetaData implements MetaData.Custom { builder.endArray(); builder.endObject(); } - - /** - * {@inheritDoc} - */ - @Override - public boolean isPersistent() { - return false; - } - } diff --git a/src/main/java/org/elasticsearch/cluster/metadata/SnapshotMetaData.java b/src/main/java/org/elasticsearch/cluster/metadata/SnapshotMetaData.java index 7a488fc2fbc..edf55d40884 100644 --- a/src/main/java/org/elasticsearch/cluster/metadata/SnapshotMetaData.java +++ b/src/main/java/org/elasticsearch/cluster/metadata/SnapshotMetaData.java @@ -312,7 +312,7 @@ public class SnapshotMetaData implements MetaData.Custom { } - public static class Factory implements MetaData.Custom.Factory { + public static class Factory extends MetaData.Custom.Factory { @Override public String type() { @@ -410,11 +410,6 @@ public class SnapshotMetaData implements MetaData.Custom { builder.endArray(); builder.endObject(); } - - public boolean isPersistent() { - return false; - } - } diff --git a/src/main/java/org/elasticsearch/gateway/local/state/meta/LocalGatewayMetaState.java b/src/main/java/org/elasticsearch/gateway/local/state/meta/LocalGatewayMetaState.java index fb744530206..70b5af147a3 100644 --- a/src/main/java/org/elasticsearch/gateway/local/state/meta/LocalGatewayMetaState.java +++ b/src/main/java/org/elasticsearch/gateway/local/state/meta/LocalGatewayMetaState.java @@ -106,7 +106,7 @@ public class LocalGatewayMetaState extends AbstractComponent implements ClusterS private final XContentType format; private final ToXContent.Params formatParams; - private final ToXContent.Params globalOnlyFormatParams; + private final ToXContent.Params gatewayModeFormatParams; private final AutoImportDangledState autoImportDangled; @@ -130,17 +130,15 @@ public class LocalGatewayMetaState extends AbstractComponent implements ClusterS Map params = Maps.newHashMap(); params.put("binary", "true"); formatParams = new ToXContent.MapParams(params); - Map globalOnlyParams = Maps.newHashMap(); - globalOnlyParams.put("binary", "true"); - globalOnlyParams.put(MetaData.PERSISTENT_ONLY_PARAM, "true"); - globalOnlyParams.put(MetaData.GLOBAL_ONLY_PARAM, "true"); - globalOnlyFormatParams = new ToXContent.MapParams(globalOnlyParams); + Map gatewayModeParams = Maps.newHashMap(); + gatewayModeParams.put("binary", "true"); + gatewayModeParams.put(MetaData.CONTEXT_MODE_PARAM, MetaData.CONTEXT_MODE_GATEWAY); + gatewayModeFormatParams = new ToXContent.MapParams(gatewayModeParams); } else { formatParams = ToXContent.EMPTY_PARAMS; - Map globalOnlyParams = Maps.newHashMap(); - globalOnlyParams.put(MetaData.PERSISTENT_ONLY_PARAM, "true"); - globalOnlyParams.put(MetaData.GLOBAL_ONLY_PARAM, "true"); - globalOnlyFormatParams = new ToXContent.MapParams(globalOnlyParams); + Map gatewayModeParams = Maps.newHashMap(); + gatewayModeParams.put(MetaData.CONTEXT_MODE_PARAM, MetaData.CONTEXT_MODE_GATEWAY); + gatewayModeFormatParams = new ToXContent.MapParams(gatewayModeParams); } this.autoImportDangled = AutoImportDangledState.fromString(settings.get("gateway.local.auto_import_dangled", AutoImportDangledState.YES.toString())); @@ -399,7 +397,7 @@ public class LocalGatewayMetaState extends AbstractComponent implements ClusterS XContentBuilder builder = XContentFactory.contentBuilder(format); builder.startObject(); - MetaData.Builder.toXContent(metaData, builder, globalOnlyFormatParams); + MetaData.Builder.toXContent(metaData, builder, gatewayModeFormatParams); builder.endObject(); builder.flush(); String globalFileName = "global-" + metaData.version(); diff --git a/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java b/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java index 9dddb8efbc4..65fc3ad1a74 100644 --- a/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java +++ b/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java @@ -120,7 +120,7 @@ public abstract class BlobStoreRepository extends AbstractLifecycleComponent globalOnlyParams = Maps.newHashMap(); - globalOnlyParams.put(MetaData.PERSISTENT_ONLY_PARAM, "true"); - globalOnlyParams.put(MetaData.GLOBAL_ONLY_PARAM, "true"); - globalOnlyFormatParams = new ToXContent.MapParams(globalOnlyParams); + Map snpashotOnlyParams = Maps.newHashMap(); + snpashotOnlyParams.put(MetaData.CONTEXT_MODE_PARAM, MetaData.CONTEXT_MODE_SNAPSHOT); + snapshotOnlyFormatParams = new ToXContent.MapParams(snpashotOnlyParams); snapshotRateLimiter = getRateLimiter(repositorySettings, "max_snapshot_bytes_per_sec", new ByteSizeValue(20, ByteSizeUnit.MB)); restoreRateLimiter = getRateLimiter(repositorySettings, "max_restore_bytes_per_sec", new ByteSizeValue(20, ByteSizeUnit.MB)); } @@ -555,7 +554,7 @@ public abstract class BlobStoreRepository extends AbstractLifecycleComponent start node"); + internalCluster().startNode(settingsBuilder().put("gateway.type", "local")); + Client client = client(); + createIndex("test-idx"); + ensureYellow(); + logger.info("--> add custom persistent metadata"); + updateClusterState(new ClusterStateUpdater() { + @Override + public ClusterState execute(ClusterState currentState) throws Exception { + ClusterState.Builder builder = ClusterState.builder(currentState); + MetaData.Builder metadataBuilder = MetaData.builder(currentState.metaData()); + metadataBuilder.putCustom(SnapshottableMetadata.TYPE, new SnapshottableMetadata("before_snapshot_s")); + metadataBuilder.putCustom(NonSnapshottableMetadata.TYPE, new NonSnapshottableMetadata("before_snapshot_ns")); + metadataBuilder.putCustom(SnapshottableGatewayMetadata.TYPE, new SnapshottableGatewayMetadata("before_snapshot_s_gw")); + metadataBuilder.putCustom(NonSnapshottableGatewayMetadata.TYPE, new NonSnapshottableGatewayMetadata("before_snapshot_ns_gw")); + metadataBuilder.putCustom(SnapshotableGatewayNoApiMetadata.TYPE, new SnapshotableGatewayNoApiMetadata("before_snapshot_s_gw_noapi")); + builder.metaData(metadataBuilder); + return builder.build(); + } + }); + + logger.info("--> create repository"); + PutRepositoryResponse putRepositoryResponse = client.admin().cluster().preparePutRepository("test-repo") + .setType("fs").setSettings(ImmutableSettings.settingsBuilder().put("location", tempDir)).execute().actionGet(); + assertThat(putRepositoryResponse.isAcknowledged(), equalTo(true)); + + logger.info("--> start snapshot"); + CreateSnapshotResponse createSnapshotResponse = client.admin().cluster().prepareCreateSnapshot("test-repo", "test-snap").setWaitForCompletion(true).execute().actionGet(); + assertThat(createSnapshotResponse.getSnapshotInfo().totalShards(), greaterThan(0)); + assertThat(createSnapshotResponse.getSnapshotInfo().successfulShards(), equalTo(createSnapshotResponse.getSnapshotInfo().successfulShards())); + assertThat(client.admin().cluster().prepareGetSnapshots("test-repo").setSnapshots("test-snap").execute().actionGet().getSnapshots().get(0).state(), equalTo(SnapshotState.SUCCESS)); + + logger.info("--> change custom persistent metadata"); + updateClusterState(new ClusterStateUpdater() { + @Override + public ClusterState execute(ClusterState currentState) throws Exception { + ClusterState.Builder builder = ClusterState.builder(currentState); + MetaData.Builder metadataBuilder = MetaData.builder(currentState.metaData()); + if (randomBoolean()) { + metadataBuilder.putCustom(SnapshottableMetadata.TYPE, new SnapshottableMetadata("after_snapshot_s")); + } else { + metadataBuilder.removeCustom(SnapshottableMetadata.TYPE); + } + metadataBuilder.putCustom(NonSnapshottableMetadata.TYPE, new NonSnapshottableMetadata("after_snapshot_ns")); + if (randomBoolean()) { + metadataBuilder.putCustom(SnapshottableGatewayMetadata.TYPE, new SnapshottableGatewayMetadata("after_snapshot_s_gw")); + } else { + metadataBuilder.removeCustom(SnapshottableGatewayMetadata.TYPE); + } + metadataBuilder.putCustom(NonSnapshottableGatewayMetadata.TYPE, new NonSnapshottableGatewayMetadata("after_snapshot_ns_gw")); + metadataBuilder.removeCustom(SnapshotableGatewayNoApiMetadata.TYPE); + builder.metaData(metadataBuilder); + return builder.build(); + } + }); + + logger.info("--> delete repository"); + assertAcked(client.admin().cluster().prepareDeleteRepository("test-repo")); + + logger.info("--> create repository"); + putRepositoryResponse = client.admin().cluster().preparePutRepository("test-repo-2") + .setType("fs").setSettings(ImmutableSettings.settingsBuilder().put("location", tempDir)).execute().actionGet(); + assertThat(putRepositoryResponse.isAcknowledged(), equalTo(true)); + + logger.info("--> restore snapshot"); + client.admin().cluster().prepareRestoreSnapshot("test-repo-2", "test-snap").setRestoreGlobalState(true).setIndices("-*").setWaitForCompletion(true).execute().actionGet(); + + logger.info("--> make sure old repository wasn't restored"); + assertThrows(client.admin().cluster().prepareGetRepositories("test-repo"), RepositoryMissingException.class); + assertThat(client.admin().cluster().prepareGetRepositories("test-repo-2").get().repositories().size(), equalTo(1)); + + logger.info("--> check that custom persistent metadata was restored"); + ClusterState clusterState = client.admin().cluster().prepareState().get().getState(); + logger.info("Cluster state: {}", clusterState); + MetaData metaData = clusterState.getMetaData(); + assertThat(((SnapshottableMetadata)metaData.custom(SnapshottableMetadata.TYPE)).getData(), equalTo("before_snapshot_s")); + assertThat(((NonSnapshottableMetadata)metaData.custom(NonSnapshottableMetadata.TYPE)).getData(), equalTo("after_snapshot_ns")); + assertThat(((SnapshottableGatewayMetadata)metaData.custom(SnapshottableGatewayMetadata.TYPE)).getData(), equalTo("before_snapshot_s_gw")); + assertThat(((NonSnapshottableGatewayMetadata)metaData.custom(NonSnapshottableGatewayMetadata.TYPE)).getData(), equalTo("after_snapshot_ns_gw")); + + logger.info("--> restart all nodes"); + internalCluster().fullRestart(); + ensureYellow(); + + logger.info("--> check that gateway-persistent custom metadata survived full cluster restart"); + clusterState = client().admin().cluster().prepareState().get().getState(); + logger.info("Cluster state: {}", clusterState); + metaData = clusterState.getMetaData(); + assertThat(metaData.custom(SnapshottableMetadata.TYPE), nullValue()); + assertThat(metaData.custom(NonSnapshottableMetadata.TYPE), nullValue()); + assertThat(((SnapshottableGatewayMetadata)metaData.custom(SnapshottableGatewayMetadata.TYPE)).getData(), equalTo("before_snapshot_s_gw")); + assertThat(((NonSnapshottableGatewayMetadata)metaData.custom(NonSnapshottableGatewayMetadata.TYPE)).getData(), equalTo("after_snapshot_ns_gw")); + // Shouldn't be returned as part of API response + assertThat(metaData.custom(SnapshotableGatewayNoApiMetadata.TYPE), nullValue()); + // But should still be in state + metaData = internalCluster().getInstance(ClusterService.class).state().metaData(); + assertThat(((SnapshotableGatewayNoApiMetadata)metaData.custom(SnapshotableGatewayNoApiMetadata.TYPE)).getData(), equalTo("before_snapshot_s_gw_noapi")); + } + + private void updateClusterState(final ClusterStateUpdater updater) throws InterruptedException { + final CountDownLatch countDownLatch = new CountDownLatch(1); + final ClusterService clusterService = internalCluster().getInstance(ClusterService.class); + clusterService.submitStateUpdateTask("test", new ProcessedClusterStateUpdateTask() { + @Override + public ClusterState execute(ClusterState currentState) throws Exception { + return updater.execute(currentState); + } + + @Override + public void onFailure(String source, @Nullable Throwable t) { + countDownLatch.countDown(); + } + + @Override + public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { + countDownLatch.countDown(); + } + }); + countDownLatch.await(); + } + + private static interface ClusterStateUpdater { + public ClusterState execute(ClusterState currentState) throws Exception; + } + @Test public void snapshotDuringNodeShutdownTest() throws Exception { logger.info("--> start 2 nodes"); @@ -484,4 +627,224 @@ public class DedicatedClusterSnapshotRestoreTests extends AbstractSnapshotTests .put(AbstractIndexStore.INDEX_STORE_THROTTLE_MAX_BYTES_PER_SEC, between(100, 50000)) )); } + + public static abstract class TestCustomMetaData implements MetaData.Custom { + private final String data; + + protected TestCustomMetaData(String data) { + this.data = data; + } + + public String getData() { + return data; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + TestCustomMetaData that = (TestCustomMetaData) o; + + if (!data.equals(that.data)) return false; + + return true; + } + + @Override + public int hashCode() { + return data.hashCode(); + } + + public static abstract class TestCustomMetaDataFactory extends MetaData.Custom.Factory { + + protected abstract TestCustomMetaData newTestCustomMetaData(String data); + + @Override + public T readFrom(StreamInput in) throws IOException { + return (T)newTestCustomMetaData(in.readString()); + } + + @Override + public void writeTo(T metadata, StreamOutput out) throws IOException { + out.writeString(metadata.getData()); + } + + @Override + public T fromXContent(XContentParser parser) throws IOException { + XContentParser.Token token; + String data = null; + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + String currentFieldName = parser.currentName(); + if ("data".equals(currentFieldName)) { + if (parser.nextToken() != XContentParser.Token.VALUE_STRING) { + throw new ElasticsearchParseException("failed to parse snapshottable metadata, invalid data type"); + } + data = parser.text(); + } else { + throw new ElasticsearchParseException("failed to parse snapshottable metadata, unknown field [" + currentFieldName + "]"); + } + } else { + throw new ElasticsearchParseException("failed to parse snapshottable metadata"); + } + } + if (data == null) { + throw new ElasticsearchParseException("failed to parse snapshottable metadata, data not found"); + } + return (T)newTestCustomMetaData(data); + } + + @Override + public void toXContent(T metadata, XContentBuilder builder, ToXContent.Params params) throws IOException { + builder.field("data", metadata.getData()); + } + } + } + + static { + MetaData.registerFactory(SnapshottableMetadata.TYPE, SnapshottableMetadata.FACTORY); + MetaData.registerFactory(NonSnapshottableMetadata.TYPE, NonSnapshottableMetadata.FACTORY); + MetaData.registerFactory(SnapshottableGatewayMetadata.TYPE, SnapshottableGatewayMetadata.FACTORY); + MetaData.registerFactory(NonSnapshottableGatewayMetadata.TYPE, NonSnapshottableGatewayMetadata.FACTORY); + MetaData.registerFactory(SnapshotableGatewayNoApiMetadata.TYPE, SnapshotableGatewayNoApiMetadata.FACTORY); + } + + public static class SnapshottableMetadata extends TestCustomMetaData { + public static final String TYPE = "test_snapshottable"; + + public static final Factory FACTORY = new Factory(); + + public SnapshottableMetadata(String data) { + super(data); + } + + private static class Factory extends TestCustomMetaDataFactory { + + @Override + public String type() { + return TYPE; + } + + @Override + protected TestCustomMetaData newTestCustomMetaData(String data) { + return new SnapshottableMetadata(data); + } + + @Override + public EnumSet context() { + return MetaData.API_AND_SNAPSHOT; + } + } + } + + public static class NonSnapshottableMetadata extends TestCustomMetaData { + public static final String TYPE = "test_non_snapshottable"; + + public static final Factory FACTORY = new Factory(); + + public NonSnapshottableMetadata(String data) { + super(data); + } + + private static class Factory extends TestCustomMetaDataFactory { + + @Override + public String type() { + return TYPE; + } + + @Override + protected NonSnapshottableMetadata newTestCustomMetaData(String data) { + return new NonSnapshottableMetadata(data); + } + } + } + + public static class SnapshottableGatewayMetadata extends TestCustomMetaData { + public static final String TYPE = "test_snapshottable_gateway"; + + public static final Factory FACTORY = new Factory(); + + public SnapshottableGatewayMetadata(String data) { + super(data); + } + + private static class Factory extends TestCustomMetaDataFactory { + + @Override + public String type() { + return TYPE; + } + + @Override + protected TestCustomMetaData newTestCustomMetaData(String data) { + return new SnapshottableGatewayMetadata(data); + } + + @Override + public EnumSet context() { + return EnumSet.of(MetaData.XContentContext.API, MetaData.XContentContext.SNAPSHOT, MetaData.XContentContext.GATEWAY); + } + } + } + + public static class NonSnapshottableGatewayMetadata extends TestCustomMetaData { + public static final String TYPE = "test_non_snapshottable_gateway"; + + public static final Factory FACTORY = new Factory(); + + public NonSnapshottableGatewayMetadata(String data) { + super(data); + } + + private static class Factory extends TestCustomMetaDataFactory { + + @Override + public String type() { + return TYPE; + } + + @Override + protected NonSnapshottableGatewayMetadata newTestCustomMetaData(String data) { + return new NonSnapshottableGatewayMetadata(data); + } + + @Override + public EnumSet context() { + return MetaData.API_AND_GATEWAY; + } + + } + } + + public static class SnapshotableGatewayNoApiMetadata extends TestCustomMetaData { + public static final String TYPE = "test_snapshottable_gateway_no_api"; + + public static final Factory FACTORY = new Factory(); + + public SnapshotableGatewayNoApiMetadata(String data) { + super(data); + } + + private static class Factory extends TestCustomMetaDataFactory { + + @Override + public String type() { + return TYPE; + } + + @Override + protected SnapshotableGatewayNoApiMetadata newTestCustomMetaData(String data) { + return new SnapshotableGatewayNoApiMetadata(data); + } + + @Override + public EnumSet context() { + return EnumSet.of(MetaData.XContentContext.GATEWAY, MetaData.XContentContext.SNAPSHOT); + } + + } + } + }