diff --git a/buildSrc/src/main/resources/checkstyle_suppressions.xml b/buildSrc/src/main/resources/checkstyle_suppressions.xml index 152405fcffc..0473f58cf73 100644 --- a/buildSrc/src/main/resources/checkstyle_suppressions.xml +++ b/buildSrc/src/main/resources/checkstyle_suppressions.xml @@ -778,7 +778,6 @@ - diff --git a/core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/create/CreateSnapshotResponse.java b/core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/create/CreateSnapshotResponse.java index 0be07c703f1..0a7a8a9ce80 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/create/CreateSnapshotResponse.java +++ b/core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/create/CreateSnapshotResponse.java @@ -57,13 +57,13 @@ public class CreateSnapshotResponse extends ActionResponse implements ToXContent @Override public void readFrom(StreamInput in) throws IOException { super.readFrom(in); - snapshotInfo = SnapshotInfo.readOptionalSnapshotInfo(in); + snapshotInfo = in.readOptionalWriteable(SnapshotInfo::new); } @Override public void writeTo(StreamOutput out) throws IOException { super.writeTo(out); - out.writeOptionalStreamable(snapshotInfo); + out.writeOptionalWriteable(snapshotInfo); } /** @@ -90,7 +90,7 @@ public class CreateSnapshotResponse extends ActionResponse implements ToXContent public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { if (snapshotInfo != null) { builder.field(Fields.SNAPSHOT); - snapshotInfo.toXContent(builder, params); + snapshotInfo.toExternalXContent(builder, params); } else { builder.field(Fields.ACCEPTED, true); } diff --git a/core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/get/GetSnapshotsResponse.java b/core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/get/GetSnapshotsResponse.java index 65b0e4faa4a..a5db19684b2 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/get/GetSnapshotsResponse.java +++ b/core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/get/GetSnapshotsResponse.java @@ -60,7 +60,7 @@ public class GetSnapshotsResponse extends ActionResponse implements ToXContent { int size = in.readVInt(); List builder = new ArrayList<>(); for (int i = 0; i < size; i++) { - builder.add(SnapshotInfo.readSnapshotInfo(in)); + builder.add(new SnapshotInfo(in)); } snapshots = Collections.unmodifiableList(builder); } @@ -82,7 +82,7 @@ public class GetSnapshotsResponse extends ActionResponse implements ToXContent { public XContentBuilder toXContent(XContentBuilder builder, ToXContent.Params params) throws IOException { builder.startArray(Fields.SNAPSHOTS); for (SnapshotInfo snapshotInfo : snapshots) { - snapshotInfo.toXContent(builder, params); + snapshotInfo.toExternalXContent(builder, params); } builder.endArray(); return builder; diff --git a/core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/get/TransportGetSnapshotsAction.java b/core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/get/TransportGetSnapshotsAction.java index 0198102a200..833b1a62289 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/get/TransportGetSnapshotsAction.java +++ b/core/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/get/TransportGetSnapshotsAction.java @@ -31,7 +31,6 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.regex.Regex; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.snapshots.Snapshot; import org.elasticsearch.snapshots.SnapshotInfo; import org.elasticsearch.snapshots.SnapshotsService; import org.elasticsearch.threadpool.ThreadPool; @@ -77,18 +76,12 @@ public class TransportGetSnapshotsAction extends TransportMasterNodeAction snapshotInfoBuilder = new ArrayList<>(); if (isAllSnapshots(request.snapshots())) { - List snapshots = snapshotsService.snapshots(request.repository(), request.ignoreUnavailable()); - for (Snapshot snapshot : snapshots) { - snapshotInfoBuilder.add(new SnapshotInfo(snapshot)); - } + snapshotInfoBuilder.addAll(snapshotsService.snapshots(request.repository(), request.ignoreUnavailable())); } else if (isCurrentSnapshots(request.snapshots())) { - List snapshots = snapshotsService.currentSnapshots(request.repository()); - for (Snapshot snapshot : snapshots) { - snapshotInfoBuilder.add(new SnapshotInfo(snapshot)); - } + snapshotInfoBuilder.addAll(snapshotsService.currentSnapshots(request.repository())); } else { Set snapshotsToGet = new LinkedHashSet<>(); // to keep insertion order - List snapshots = null; + List snapshots = null; for (String snapshotOrPattern : request.snapshots()) { if (Regex.isSimpleMatchPattern(snapshotOrPattern) == false) { snapshotsToGet.add(snapshotOrPattern); @@ -96,7 +89,7 @@ public class TransportGetSnapshotsAction extends TransportMasterNodeAction shardStatusBuilder = new ArrayList<>(); if (snapshot.state().completed()) { Map shardStatues = snapshotsService.snapshotShards(snapshotId); diff --git a/core/src/main/java/org/elasticsearch/repositories/Repository.java b/core/src/main/java/org/elasticsearch/repositories/Repository.java index 294b36df491..f3c44f29226 100644 --- a/core/src/main/java/org/elasticsearch/repositories/Repository.java +++ b/core/src/main/java/org/elasticsearch/repositories/Repository.java @@ -24,7 +24,7 @@ import org.elasticsearch.cluster.metadata.SnapshotId; import org.elasticsearch.common.component.LifecycleComponent; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.index.snapshots.IndexShardSnapshotStatus; -import org.elasticsearch.snapshots.Snapshot; +import org.elasticsearch.snapshots.SnapshotInfo; import org.elasticsearch.snapshots.SnapshotShardFailure; import java.io.IOException; @@ -54,7 +54,7 @@ public interface Repository extends LifecycleComponent { * @param snapshotId snapshot ID * @return information about snapshot */ - Snapshot readSnapshot(SnapshotId snapshotId); + SnapshotInfo readSnapshot(SnapshotId snapshotId); /** * Returns global metadata associate with the snapshot. @@ -65,7 +65,7 @@ public interface Repository extends LifecycleComponent { * @param indices list of indices * @return information about snapshot */ - MetaData readSnapshotMetaData(SnapshotId snapshotId, Snapshot snapshot, List indices) throws IOException; + MetaData readSnapshotMetaData(SnapshotId snapshotId, SnapshotInfo snapshot, List indices) throws IOException; /** * Returns the list of snapshots currently stored in the repository @@ -94,7 +94,7 @@ public interface Repository extends LifecycleComponent { * @param shardFailures list of shard failures * @return snapshot description */ - Snapshot finalizeSnapshot(SnapshotId snapshotId, List indices, long startTime, String failure, int totalShards, List shardFailures); + SnapshotInfo finalizeSnapshot(SnapshotId snapshotId, List indices, long startTime, String failure, int totalShards, List shardFailures); /** * Deletes snapshot diff --git a/core/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java b/core/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java index ef0cab5c156..121df3e5832 100644 --- a/core/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java +++ b/core/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java @@ -57,9 +57,9 @@ import org.elasticsearch.repositories.RepositoryException; import org.elasticsearch.repositories.RepositorySettings; import org.elasticsearch.repositories.RepositoryVerificationException; import org.elasticsearch.snapshots.InvalidSnapshotNameException; -import org.elasticsearch.snapshots.Snapshot; import org.elasticsearch.snapshots.SnapshotCreationException; import org.elasticsearch.snapshots.SnapshotException; +import org.elasticsearch.snapshots.SnapshotInfo; import org.elasticsearch.snapshots.SnapshotMissingException; import org.elasticsearch.snapshots.SnapshotShardFailure; @@ -165,9 +165,9 @@ public abstract class BlobStoreRepository extends AbstractLifecycleComponent indexMetaDataLegacyFormat; - private ChecksumBlobStoreFormat snapshotFormat; + private ChecksumBlobStoreFormat snapshotFormat; - private LegacyBlobStoreFormat snapshotLegacyFormat; + private LegacyBlobStoreFormat snapshotLegacyFormat; private final boolean readOnly; @@ -202,8 +202,8 @@ public abstract class BlobStoreRepository extends AbstractLifecycleComponent(INDEX_METADATA_CODEC, METADATA_NAME_FORMAT, IndexMetaData.PROTO, parseFieldMatcher, isCompress()); indexMetaDataLegacyFormat = new LegacyBlobStoreFormat<>(LEGACY_SNAPSHOT_NAME_FORMAT, IndexMetaData.PROTO, parseFieldMatcher); - snapshotFormat = new ChecksumBlobStoreFormat<>(SNAPSHOT_CODEC, SNAPSHOT_NAME_FORMAT, Snapshot.PROTO, parseFieldMatcher, isCompress()); - snapshotLegacyFormat = new LegacyBlobStoreFormat<>(LEGACY_SNAPSHOT_NAME_FORMAT, Snapshot.PROTO, parseFieldMatcher); + snapshotFormat = new ChecksumBlobStoreFormat<>(SNAPSHOT_CODEC, SNAPSHOT_NAME_FORMAT, SnapshotInfo.PROTO, parseFieldMatcher, isCompress()); + snapshotLegacyFormat = new LegacyBlobStoreFormat<>(LEGACY_SNAPSHOT_NAME_FORMAT, SnapshotInfo.PROTO, parseFieldMatcher); } /** @@ -294,7 +294,7 @@ public abstract class BlobStoreRepository extends AbstractLifecycleComponent indices = Collections.emptyList(); - Snapshot snapshot = null; + SnapshotInfo snapshot = null; try { snapshot = readSnapshot(snapshotId); indices = snapshot.indices(); @@ -368,9 +368,9 @@ public abstract class BlobStoreRepository extends AbstractLifecycleComponent indices, long startTime, String failure, int totalShards, List shardFailures) { + public SnapshotInfo finalizeSnapshot(SnapshotId snapshotId, List indices, long startTime, String failure, int totalShards, List shardFailures) { try { - Snapshot blobStoreSnapshot = new Snapshot(snapshotId.getSnapshot(), indices, startTime, failure, System.currentTimeMillis(), totalShards, shardFailures); + SnapshotInfo blobStoreSnapshot = new SnapshotInfo(snapshotId.getSnapshot(), indices, startTime, failure, System.currentTimeMillis(), totalShards, shardFailures); snapshotFormat.write(blobStoreSnapshot, snapshotsBlobContainer, snapshotId.getSnapshot()); List snapshotIds = snapshots(); if (!snapshotIds.contains(snapshotId)) { @@ -425,7 +425,7 @@ public abstract class BlobStoreRepository extends AbstractLifecycleComponent indices) throws IOException { + public MetaData readSnapshotMetaData(SnapshotId snapshotId, SnapshotInfo snapshot, List indices) throws IOException { return readSnapshotMetaData(snapshotId, snapshot.version(), indices, false); } @@ -433,7 +433,7 @@ public abstract class BlobStoreRepository extends AbstractLifecycleComponent snapshotFormat(Version version) { + private BlobStoreFormat snapshotFormat(Version version) { if(legacyMetaData(version)) { return snapshotLegacyFormat; } else { diff --git a/core/src/main/java/org/elasticsearch/snapshots/RestoreService.java b/core/src/main/java/org/elasticsearch/snapshots/RestoreService.java index 8419b7c2667..5338f927005 100644 --- a/core/src/main/java/org/elasticsearch/snapshots/RestoreService.java +++ b/core/src/main/java/org/elasticsearch/snapshots/RestoreService.java @@ -190,7 +190,7 @@ public class RestoreService extends AbstractComponent implements ClusterStateLis // Read snapshot info and metadata from the repository Repository repository = repositoriesService.repository(request.repository()); final SnapshotId snapshotId = new SnapshotId(request.repository(), request.name()); - final Snapshot snapshot = repository.readSnapshot(snapshotId); + final SnapshotInfo snapshot = repository.readSnapshot(snapshotId); List filteredIndices = SnapshotUtils.filterIndices(snapshot.indices(), request.indices(), request.indicesOptions()); MetaData metaDataIn = repository.readSnapshotMetaData(snapshotId, snapshot, filteredIndices); @@ -708,7 +708,7 @@ public class RestoreService extends AbstractComponent implements ClusterStateLis * @param snapshotId snapshot id * @param snapshot snapshot metadata */ - private void validateSnapshotRestorable(SnapshotId snapshotId, Snapshot snapshot) { + private void validateSnapshotRestorable(SnapshotId snapshotId, SnapshotInfo snapshot) { if (!snapshot.state().restorable()) { throw new SnapshotRestoreException(snapshotId, "unsupported snapshot state [" + snapshot.state() + "]"); } @@ -765,7 +765,7 @@ public class RestoreService extends AbstractComponent implements ClusterStateLis UPDATE_RESTORE_ACTION_NAME, request, EmptyTransportResponseHandler.INSTANCE_SAME); } - private boolean failed(Snapshot snapshot, String index) { + private boolean failed(SnapshotInfo snapshot, String index) { for (SnapshotShardFailure failure : snapshot.shardFailures()) { if (index.equals(failure.index())) { return true; diff --git a/core/src/main/java/org/elasticsearch/snapshots/Snapshot.java b/core/src/main/java/org/elasticsearch/snapshots/Snapshot.java deleted file mode 100644 index 13ec659b629..00000000000 --- a/core/src/main/java/org/elasticsearch/snapshots/Snapshot.java +++ /dev/null @@ -1,358 +0,0 @@ -/* - * 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.snapshots; - -import org.elasticsearch.ElasticsearchParseException; -import org.elasticsearch.Version; -import org.elasticsearch.common.ParseFieldMatcher; -import org.elasticsearch.common.xcontent.FromXContentBuilder; -import org.elasticsearch.common.xcontent.ToXContent; -import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.common.xcontent.XContentParser; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * Represent information about snapshot - */ -public class Snapshot implements Comparable, ToXContent, FromXContentBuilder { - - private final String name; - - private final Version version; - - private final SnapshotState state; - - private final String reason; - - private final List indices; - - private final long startTime; - - private final long endTime; - - private final int totalShard; - - private final int successfulShards; - - private final List shardFailures; - - private final static List NO_FAILURES = Collections.emptyList(); - - public final static Snapshot PROTO = new Snapshot(); - - private Snapshot(String name, List indices, SnapshotState state, String reason, Version version, long startTime, long endTime, - int totalShard, int successfulShards, List shardFailures) { - assert name != null; - assert indices != null; - assert state != null; - assert shardFailures != null; - this.name = name; - this.indices = indices; - this.state = state; - this.reason = reason; - this.version = version; - this.startTime = startTime; - this.endTime = endTime; - this.totalShard = totalShard; - this.successfulShards = successfulShards; - this.shardFailures = shardFailures; - } - - - public Snapshot(String name, List indices, long startTime) { - this(name, indices, SnapshotState.IN_PROGRESS, null, Version.CURRENT, startTime, 0L, 0, 0, NO_FAILURES); - } - - public Snapshot(String name, List indices, long startTime, String reason, long endTime, - int totalShard, List shardFailures) { - this(name, indices, snapshotState(reason, shardFailures), reason, Version.CURRENT, - startTime, endTime, totalShard, totalShard - shardFailures.size(), shardFailures); - } - - /** - * Special constructor for the prototype object - */ - private Snapshot() { - this("", Collections.emptyList(), 0); - } - - private static SnapshotState snapshotState(String reason, List shardFailures) { - if (reason == null) { - if (shardFailures.isEmpty()) { - return SnapshotState.SUCCESS; - } else { - return SnapshotState.PARTIAL; - } - } else { - return SnapshotState.FAILED; - } - } - - /** - * Returns snapshot name - * - * @return snapshot name - */ - public String name() { - return name; - } - - /** - * Returns current snapshot state - * - * @return snapshot state - */ - public SnapshotState state() { - return state; - } - - /** - * Returns reason for complete snapshot failure - * - * @return snapshot failure reason - */ - public String reason() { - return reason; - } - - /** - * Returns version of Elasticsearch that was used to create this snapshot - * - * @return Elasticsearch version - */ - public Version version() { - return version; - } - - /** - * Returns indices that were included into this snapshot - * - * @return list of indices - */ - public List indices() { - return indices; - } - - /** - * Returns time when snapshot started - * - * @return snapshot start time - */ - public long startTime() { - return startTime; - } - - /** - * Returns time when snapshot ended - *

- * Can be 0L if snapshot is still running - * - * @return snapshot end time - */ - public long endTime() { - return endTime; - } - - /** - * Returns total number of shards that were snapshotted - * - * @return number of shards - */ - public int totalShard() { - return totalShard; - } - - /** - * Returns total number of shards that were successfully snapshotted - * - * @return number of successful shards - */ - public int successfulShards() { - return successfulShards; - } - - /** - * Returns shard failures - */ - public List shardFailures() { - return shardFailures; - } - - /** - * Compares two snapshots by their start time - * - * @param o other snapshot - * @return the value {@code 0} if snapshots were created at the same time; - * a value less than {@code 0} if this snapshot was created before snapshot {@code o}; and - * a value greater than {@code 0} if this snapshot was created after snapshot {@code o}; - */ - @Override - public int compareTo(Snapshot o) { - return Long.compare(startTime, o.startTime); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - Snapshot that = (Snapshot) o; - - if (startTime != that.startTime) return false; - if (!name.equals(that.name)) return false; - - return true; - } - - @Override - public int hashCode() { - int result = name.hashCode(); - result = 31 * result + Long.hashCode(startTime); - return result; - } - - @Override - public Snapshot fromXContent(XContentParser parser, ParseFieldMatcher parseFieldMatcher) throws IOException { - return fromXContent(parser); - } - - static final class Fields { - static final String SNAPSHOT = "snapshot"; - static final String NAME = "name"; - static final String VERSION_ID = "version_id"; - static final String INDICES = "indices"; - static final String STATE = "state"; - static final String REASON = "reason"; - static final String START_TIME = "start_time"; - static final String END_TIME = "end_time"; - static final String TOTAL_SHARDS = "total_shards"; - static final String SUCCESSFUL_SHARDS = "successful_shards"; - static final String FAILURES = "failures"; - } - - - @Override - public XContentBuilder toXContent(XContentBuilder builder, ToXContent.Params params) throws IOException { - builder.startObject(Fields.SNAPSHOT); - builder.field(Fields.NAME, name); - builder.field(Fields.VERSION_ID, version.id); - builder.startArray(Fields.INDICES); - for (String index : indices) { - builder.value(index); - } - builder.endArray(); - builder.field(Fields.STATE, state); - if (reason != null) { - builder.field(Fields.REASON, reason); - } - builder.field(Fields.START_TIME, startTime); - builder.field(Fields.END_TIME, endTime); - builder.field(Fields.TOTAL_SHARDS, totalShard); - builder.field(Fields.SUCCESSFUL_SHARDS, successfulShards); - builder.startArray(Fields.FAILURES); - for (SnapshotShardFailure shardFailure : shardFailures) { - builder.startObject(); - shardFailure.toXContent(builder, params); - builder.endObject(); - } - builder.endArray(); - builder.endObject(); - return builder; - } - - - public static Snapshot fromXContent(XContentParser parser) throws IOException { - String name = null; - Version version = Version.CURRENT; - SnapshotState state = SnapshotState.IN_PROGRESS; - String reason = null; - List indices = Collections.emptyList(); - long startTime = 0; - long endTime = 0; - int totalShard = 0; - int successfulShards = 0; - List shardFailures = NO_FAILURES; - if (parser.currentToken() == null) { // fresh parser? move to the first token - parser.nextToken(); - } - if (parser.currentToken() == XContentParser.Token.START_OBJECT) { // on a start object move to next token - parser.nextToken(); - } - XContentParser.Token token; - if ((token = parser.nextToken()) == XContentParser.Token.START_OBJECT) { - String currentFieldName = parser.currentName(); - if ("snapshot".equals(currentFieldName)) { - while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { - if (token == XContentParser.Token.FIELD_NAME) { - currentFieldName = parser.currentName(); - token = parser.nextToken(); - if (token.isValue()) { - if ("name".equals(currentFieldName)) { - name = parser.text(); - } else if ("state".equals(currentFieldName)) { - state = SnapshotState.valueOf(parser.text()); - } else if ("reason".equals(currentFieldName)) { - reason = parser.text(); - } else if ("start_time".equals(currentFieldName)) { - startTime = parser.longValue(); - } else if ("end_time".equals(currentFieldName)) { - endTime = parser.longValue(); - } else if ("total_shards".equals(currentFieldName)) { - totalShard = parser.intValue(); - } else if ("successful_shards".equals(currentFieldName)) { - successfulShards = parser.intValue(); - } else if ("version_id".equals(currentFieldName)) { - version = Version.fromId(parser.intValue()); - } - } else if (token == XContentParser.Token.START_ARRAY) { - if ("indices".equals(currentFieldName)) { - ArrayList indicesArray = new ArrayList<>(); - while (parser.nextToken() != XContentParser.Token.END_ARRAY) { - indicesArray.add(parser.text()); - } - indices = Collections.unmodifiableList(indicesArray); - } else if ("failures".equals(currentFieldName)) { - ArrayList shardFailureArrayList = new ArrayList<>(); - while (parser.nextToken() != XContentParser.Token.END_ARRAY) { - shardFailureArrayList.add(SnapshotShardFailure.fromXContent(parser)); - } - shardFailures = Collections.unmodifiableList(shardFailureArrayList); - } else { - // It was probably created by newer version - ignoring - parser.skipChildren(); - } - } else if (token == XContentParser.Token.START_OBJECT) { - // It was probably created by newer version - ignoring - parser.skipChildren(); - } - } - } - } - } else { - throw new ElasticsearchParseException("unexpected token [" + token + "]"); - } - return new Snapshot(name, indices, state, reason, version, startTime, endTime, totalShard, successfulShards, shardFailures); - } - -} diff --git a/core/src/main/java/org/elasticsearch/snapshots/SnapshotInfo.java b/core/src/main/java/org/elasticsearch/snapshots/SnapshotInfo.java index 354094404ae..871e765cfd0 100644 --- a/core/src/main/java/org/elasticsearch/snapshots/SnapshotInfo.java +++ b/core/src/main/java/org/elasticsearch/snapshots/SnapshotInfo.java @@ -18,15 +18,19 @@ */ package org.elasticsearch.snapshots; +import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.Version; import org.elasticsearch.action.ShardOperationFailedException; +import org.elasticsearch.common.ParseFieldMatcher; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; -import org.elasticsearch.common.io.stream.Streamable; +import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.common.joda.FormatDateTimeFormatter; import org.elasticsearch.common.joda.Joda; +import org.elasticsearch.common.xcontent.FromXContentBuilder; import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.rest.RestStatus; import java.io.IOException; @@ -35,52 +39,109 @@ import java.util.Collections; import java.util.List; /** - * Information about snapshot + * Information about a snapshot */ -public class SnapshotInfo implements ToXContent, Streamable { +public final class SnapshotInfo implements Comparable, ToXContent, FromXContentBuilder, Writeable { + public static final SnapshotInfo PROTO = new SnapshotInfo("", Collections.emptyList(), 0); private static final FormatDateTimeFormatter DATE_TIME_FORMATTER = Joda.forPattern("strictDateOptionalTime"); + private static final String SNAPSHOT = "snapshot"; + private static final String INDICES = "indices"; + private static final String STATE = "state"; + private static final String REASON = "reason"; + private static final String START_TIME = "start_time"; + private static final String START_TIME_IN_MILLIS = "start_time_in_millis"; + private static final String END_TIME = "end_time"; + private static final String END_TIME_IN_MILLIS = "end_time_in_millis"; + private static final String DURATION = "duration"; + private static final String DURATION_IN_MILLIS = "duration_in_millis"; + private static final String FAILURES = "failures"; + private static final String SHARDS = "shards"; + private static final String TOTAL = "total"; + private static final String FAILED = "failed"; + private static final String SUCCESSFUL = "successful"; + private static final String VERSION_ID = "version_id"; + private static final String VERSION = "version"; + private static final String NAME = "name"; + private static final String TOTAL_SHARDS = "total_shards"; + private static final String SUCCESSFUL_SHARDS = "successful_shards"; - private String name; + private final String name; - private SnapshotState state; + private final SnapshotState state; - private String reason; + private final String reason; - private List indices; + private final List indices; - private long startTime; + private final long startTime; - private long endTime; + private final long endTime; - private int totalShards; + private final int totalShards; - private int successfulShards; + private final int successfulShards; - private Version version; + private final Version version; - private List shardFailures; + private final List shardFailures; - SnapshotInfo() { + public SnapshotInfo(String name, List indices, long startTime) { + this(name, indices, SnapshotState.IN_PROGRESS, null, Version.CURRENT, startTime, 0L, 0, 0, Collections.emptyList()); + } + public SnapshotInfo(String name, List indices, long startTime, String reason, long endTime, + int totalShards, List shardFailures) { + this(name, indices, snapshotState(reason, shardFailures), reason, Version.CURRENT, + startTime, endTime, totalShards, totalShards - shardFailures.size(), shardFailures); + } + + private SnapshotInfo(String name, List indices, SnapshotState state, String reason, Version version, long startTime, + long endTime, int totalShards, int successfulShards, List shardFailures) { + assert name != null; + assert indices != null; + assert state != null; + assert shardFailures != null; + this.name = name; + this.indices = indices; + this.state = state; + this.reason = reason; + this.version = version; + this.startTime = startTime; + this.endTime = endTime; + this.totalShards = totalShards; + this.successfulShards = successfulShards; + this.shardFailures = shardFailures; } /** - * Creates a new snapshot information from a {@link Snapshot} - * - * @param snapshot snapshot information returned by repository + * Constructs snapshot information from stream input */ - public SnapshotInfo(Snapshot snapshot) { - name = snapshot.name(); - state = snapshot.state(); - reason = snapshot.reason(); - indices = snapshot.indices(); - startTime = snapshot.startTime(); - endTime = snapshot.endTime(); - totalShards = snapshot.totalShard(); - successfulShards = snapshot.successfulShards(); - shardFailures = snapshot.shardFailures(); - version = snapshot.version(); + public SnapshotInfo(final StreamInput in) throws IOException { + name = in.readString(); + int size = in.readVInt(); + List indicesListBuilder = new ArrayList<>(); + for (int i = 0; i < size; i++) { + indicesListBuilder.add(in.readString()); + } + indices = Collections.unmodifiableList(indicesListBuilder); + state = SnapshotState.fromValue(in.readByte()); + reason = in.readOptionalString(); + startTime = in.readVLong(); + endTime = in.readVLong(); + totalShards = in.readVInt(); + successfulShards = in.readVInt(); + size = in.readVInt(); + if (size > 0) { + List failureBuilder = new ArrayList<>(); + for (int i = 0; i < size; i++) { + failureBuilder.add(SnapshotShardFailure.readSnapshotShardFailure(in)); + } + shardFailures = Collections.unmodifiableList(failureBuilder); + } else { + shardFailures = Collections.emptyList(); + } + version = Version.readVersion(in); } /** @@ -184,6 +245,39 @@ public class SnapshotInfo implements ToXContent, Streamable { return version; } + /** + * Compares two snapshots by their start time + * + * @param o other snapshot + * @return the value {@code 0} if snapshots were created at the same time; + * a value less than {@code 0} if this snapshot was created before snapshot {@code o}; and + * a value greater than {@code 0} if this snapshot was created after snapshot {@code o}; + */ + @Override + public int compareTo(final SnapshotInfo o) { + return Long.compare(startTime, o.startTime); + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + final SnapshotInfo that = (SnapshotInfo) o; + return startTime == that.startTime && name.equals(that.name); + } + + @Override + public int hashCode() { + int result = name.hashCode(); + result = 31 * result + Long.hashCode(startTime); + return result; + } + /** * Returns snapshot REST status */ @@ -194,98 +288,166 @@ public class SnapshotInfo implements ToXContent, Streamable { if (shardFailures.size() == 0) { return RestStatus.OK; } - return RestStatus.status(successfulShards, totalShards, shardFailures.toArray(new ShardOperationFailedException[shardFailures.size()])); - } - - static final class Fields { - static final String INDICES = "indices"; - static final String STATE = "state"; - static final String REASON = "reason"; - static final String START_TIME = "start_time"; - static final String START_TIME_IN_MILLIS = "start_time_in_millis"; - static final String END_TIME = "end_time"; - static final String END_TIME_IN_MILLIS = "end_time_in_millis"; - static final String DURATION = "duration"; - static final String DURATION_IN_MILLIS = "duration_in_millis"; - static final String FAILURES = "failures"; - static final String SHARDS = "shards"; - static final String TOTAL = "total"; - static final String FAILED = "failed"; - static final String SUCCESSFUL = "successful"; - static final String VERSION_ID = "version_id"; - static final String VERSION = "version"; + return RestStatus.status(successfulShards, totalShards, + shardFailures.toArray(new ShardOperationFailedException[shardFailures.size()])); } @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - builder.startObject(); - builder.field("snapshot", name); - builder.field(Fields.VERSION_ID, version.id); - builder.field(Fields.VERSION, version.toString()); - builder.startArray(Fields.INDICES); + public XContentBuilder toXContent(final XContentBuilder builder, final Params params) throws IOException { + builder.startObject(SNAPSHOT); + builder.field(NAME, name); + builder.field(VERSION_ID, version.id); + builder.startArray(INDICES); for (String index : indices) { builder.value(index); } builder.endArray(); - builder.field(Fields.STATE, state); + builder.field(STATE, state); if (reason != null) { - builder.field(Fields.REASON, reason); + builder.field(REASON, reason); } - if (startTime != 0) { - builder.field(Fields.START_TIME, DATE_TIME_FORMATTER.printer().print(startTime)); - builder.field(Fields.START_TIME_IN_MILLIS, startTime); - } - if (endTime != 0) { - builder.field(Fields.END_TIME, DATE_TIME_FORMATTER.printer().print(endTime)); - builder.field(Fields.END_TIME_IN_MILLIS, endTime); - builder.timeValueField(Fields.DURATION_IN_MILLIS, Fields.DURATION, endTime - startTime); - } - builder.startArray(Fields.FAILURES); + builder.field(START_TIME, startTime); + builder.field(END_TIME, endTime); + builder.field(TOTAL_SHARDS, totalShards); + builder.field(SUCCESSFUL_SHARDS, successfulShards); + builder.startArray(FAILURES); for (SnapshotShardFailure shardFailure : shardFailures) { builder.startObject(); shardFailure.toXContent(builder, params); builder.endObject(); } builder.endArray(); - builder.startObject(Fields.SHARDS); - builder.field(Fields.TOTAL, totalShards); - builder.field(Fields.FAILED, failedShards()); - builder.field(Fields.SUCCESSFUL, successfulShards); + builder.endObject(); + return builder; + } + + /** + * Produces the external X-content that is delivered through the REST layer. + */ + public XContentBuilder toExternalXContent(final XContentBuilder builder, final ToXContent.Params params) throws IOException { + builder.startObject(); + builder.field(SNAPSHOT, name); + builder.field(VERSION_ID, version.id); + builder.field(VERSION, version.toString()); + builder.startArray(INDICES); + for (String index : indices) { + builder.value(index); + } + builder.endArray(); + builder.field(STATE, state); + if (reason != null) { + builder.field(REASON, reason); + } + if (startTime != 0) { + builder.field(START_TIME, DATE_TIME_FORMATTER.printer().print(startTime)); + builder.field(START_TIME_IN_MILLIS, startTime); + } + if (endTime != 0) { + builder.field(END_TIME, DATE_TIME_FORMATTER.printer().print(endTime)); + builder.field(END_TIME_IN_MILLIS, endTime); + builder.timeValueField(DURATION_IN_MILLIS, DURATION, endTime - startTime); + } + builder.startArray(FAILURES); + for (SnapshotShardFailure shardFailure : shardFailures) { + builder.startObject(); + shardFailure.toXContent(builder, params); + builder.endObject(); + } + builder.endArray(); + builder.startObject(SHARDS); + builder.field(TOTAL, totalShards); + builder.field(FAILED, failedShards()); + builder.field(SUCCESSFUL, successfulShards); builder.endObject(); builder.endObject(); return builder; } @Override - public void readFrom(StreamInput in) throws IOException { - name = in.readString(); - int size = in.readVInt(); - List indicesListBuilder = new ArrayList<>(); - for (int i = 0; i < size; i++) { - indicesListBuilder.add(in.readString()); + public SnapshotInfo fromXContent(final XContentParser parser, final ParseFieldMatcher matcher) throws IOException { + return fromXContent(parser); + } + + /** + * This method creates a SnapshotInfo from internal x-content. It does not + * handle x-content written with the external version as external x-content + * is only for display purposes and does not need to be parsed. + */ + public static SnapshotInfo fromXContent(final XContentParser parser) throws IOException { + String name = null; + Version version = Version.CURRENT; + SnapshotState state = SnapshotState.IN_PROGRESS; + String reason = null; + List indices = Collections.emptyList(); + long startTime = 0; + long endTime = 0; + int totalShard = 0; + int successfulShards = 0; + List shardFailures = Collections.emptyList(); + if (parser.currentToken() == null) { // fresh parser? move to the first token + parser.nextToken(); } - indices = Collections.unmodifiableList(indicesListBuilder); - state = SnapshotState.fromValue(in.readByte()); - reason = in.readOptionalString(); - startTime = in.readVLong(); - endTime = in.readVLong(); - totalShards = in.readVInt(); - successfulShards = in.readVInt(); - size = in.readVInt(); - if (size > 0) { - List failureBuilder = new ArrayList<>(); - for (int i = 0; i < size; i++) { - failureBuilder.add(SnapshotShardFailure.readSnapshotShardFailure(in)); + if (parser.currentToken() == XContentParser.Token.START_OBJECT) { // on a start object move to next token + parser.nextToken(); + } + XContentParser.Token token; + if ((token = parser.nextToken()) == XContentParser.Token.START_OBJECT) { + String currentFieldName = parser.currentName(); + if (SNAPSHOT.equals(currentFieldName)) { + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + token = parser.nextToken(); + if (token.isValue()) { + if (NAME.equals(currentFieldName)) { + name = parser.text(); + } else if (STATE.equals(currentFieldName)) { + state = SnapshotState.valueOf(parser.text()); + } else if (REASON.equals(currentFieldName)) { + reason = parser.text(); + } else if (START_TIME.equals(currentFieldName)) { + startTime = parser.longValue(); + } else if (END_TIME.equals(currentFieldName)) { + endTime = parser.longValue(); + } else if (TOTAL_SHARDS.equals(currentFieldName)) { + totalShard = parser.intValue(); + } else if (SUCCESSFUL_SHARDS.equals(currentFieldName)) { + successfulShards = parser.intValue(); + } else if (VERSION_ID.equals(currentFieldName)) { + version = Version.fromId(parser.intValue()); + } + } else if (token == XContentParser.Token.START_ARRAY) { + if (INDICES.equals(currentFieldName)) { + ArrayList indicesArray = new ArrayList<>(); + while (parser.nextToken() != XContentParser.Token.END_ARRAY) { + indicesArray.add(parser.text()); + } + indices = Collections.unmodifiableList(indicesArray); + } else if (FAILURES.equals(currentFieldName)) { + ArrayList shardFailureArrayList = new ArrayList<>(); + while (parser.nextToken() != XContentParser.Token.END_ARRAY) { + shardFailureArrayList.add(SnapshotShardFailure.fromXContent(parser)); + } + shardFailures = Collections.unmodifiableList(shardFailureArrayList); + } else { + // It was probably created by newer version - ignoring + parser.skipChildren(); + } + } else if (token == XContentParser.Token.START_OBJECT) { + // It was probably created by newer version - ignoring + parser.skipChildren(); + } + } + } } - shardFailures = Collections.unmodifiableList(failureBuilder); } else { - shardFailures = Collections.emptyList(); + throw new ElasticsearchParseException("unexpected token [" + token + "]"); } - version = Version.readVersion(in); + return new SnapshotInfo(name, indices, state, reason, version, startTime, endTime, totalShard, successfulShards, shardFailures); } @Override - public void writeTo(StreamOutput out) throws IOException { + public void writeTo(final StreamOutput out) throws IOException { out.writeString(name); out.writeVInt(indices.size()); for (String index : indices) { @@ -304,26 +466,16 @@ public class SnapshotInfo implements ToXContent, Streamable { Version.writeVersion(version, out); } - /** - * Reads snapshot information from stream input - * - * @param in stream input - * @return deserialized snapshot info - */ - public static SnapshotInfo readSnapshotInfo(StreamInput in) throws IOException { - SnapshotInfo snapshotInfo = new SnapshotInfo(); - snapshotInfo.readFrom(in); - return snapshotInfo; - } - - /** - * Reads optional snapshot information from stream input - * - * @param in stream input - * @return deserialized snapshot info or null - */ - public static SnapshotInfo readOptionalSnapshotInfo(StreamInput in) throws IOException { - return in.readOptionalStreamable(SnapshotInfo::new); + private static SnapshotState snapshotState(final String reason, final List shardFailures) { + if (reason == null) { + if (shardFailures.isEmpty()) { + return SnapshotState.SUCCESS; + } else { + return SnapshotState.PARTIAL; + } + } else { + return SnapshotState.FAILED; + } } } diff --git a/core/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java b/core/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java index 186a1965a96..8e6681893c9 100644 --- a/core/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java +++ b/core/src/main/java/org/elasticsearch/snapshots/SnapshotsService.java @@ -126,7 +126,7 @@ public class SnapshotsService extends AbstractLifecycleComponent entries = currentSnapshots(snapshotId.getRepository(), new String[]{snapshotId.getSnapshot()}); if (!entries.isEmpty()) { @@ -141,8 +141,8 @@ public class SnapshotsService extends AbstractLifecycleComponent snapshots(String repositoryName, boolean ignoreUnavailable) { - Set snapshotSet = new HashSet<>(); + public List snapshots(String repositoryName, boolean ignoreUnavailable) { + Set snapshotSet = new HashSet<>(); List entries = currentSnapshots(repositoryName, null); for (SnapshotsInProgress.Entry entry : entries) { snapshotSet.add(inProgressSnapshot(entry)); @@ -161,7 +161,7 @@ public class SnapshotsService extends AbstractLifecycleComponent snapshotList = new ArrayList<>(snapshotSet); + ArrayList snapshotList = new ArrayList<>(snapshotSet); CollectionUtil.timSort(snapshotList); return Collections.unmodifiableList(snapshotList); } @@ -172,8 +172,8 @@ public class SnapshotsService extends AbstractLifecycleComponent currentSnapshots(String repositoryName) { - List snapshotList = new ArrayList<>(); + public List currentSnapshots(String repositoryName) { + List snapshotList = new ArrayList<>(); List entries = currentSnapshots(repositoryName, null); for (SnapshotsInProgress.Entry entry : entries) { snapshotList.add(inProgressSnapshot(entry)); @@ -408,8 +408,8 @@ public class SnapshotsService extends AbstractLifecycleComponent shardStatus = new HashMap<>(); Repository repository = repositoriesService.repository(snapshotId.getRepository()); IndexShardRepository indexShardRepository = repositoriesService.indexShardRepository(snapshotId.getRepository()); - Snapshot snapshot = repository.readSnapshot(snapshotId); + SnapshotInfo snapshot = repository.readSnapshot(snapshotId); MetaData metaData = repository.readSnapshotMetaData(snapshotId, snapshot, snapshot.indices()); for (String index : snapshot.indices()) { IndexMetaData indexMetaData = metaData.indices().get(index); @@ -800,8 +800,8 @@ public class SnapshotsService extends AbstractLifecycleComponent