diff --git a/src/main/java/org/elasticsearch/license/core/ESLicense.java b/src/main/java/org/elasticsearch/license/core/ESLicense.java index e6520967986..c80fe4bb067 100644 --- a/src/main/java/org/elasticsearch/license/core/ESLicense.java +++ b/src/main/java/org/elasticsearch/license/core/ESLicense.java @@ -5,15 +5,17 @@ */ package org.elasticsearch.license.core; +import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentBuilderString; +import org.elasticsearch.common.xcontent.XContentParser; import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -public class ESLicense implements Comparable { +public class ESLicense implements Comparable, ToXContent { private final String uid; private final String issuer; @@ -117,6 +119,129 @@ public class ESLicense implements Comparable { return Long.compare(expiryDate, o.expiryDate); } + static ESLicense readESLicense(StreamInput in) throws IOException { + in.readVInt(); // Version for future extensibility + Builder builder = builder(); + builder.uid(in.readString()); + builder.type(Type.fromString(in.readString())); + builder.subscriptionType(SubscriptionType.fromString(in.readString())); + builder.issueDate(in.readLong()); + builder.feature(in.readString()); + builder.expiryDate(in.readLong()); + builder.maxNodes(in.readInt()); + builder.issuedTo(in.readString()); + builder.issuer(in.readString()); + builder.signature(in.readOptionalString()); + return builder.verifyAndBuild(); + } + + public void writeTo(StreamOutput out) throws IOException { + out.writeVInt(VERSION); + out.writeString(uid); + out.writeString(type.string()); + out.writeString(subscriptionType.string()); + out.writeLong(issueDate); + out.writeString(feature); + out.writeLong(expiryDate); + out.writeInt(maxNodes); + out.writeString(issuedTo); + out.writeString(issuer); + out.writeOptionalString(signature); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + builder.field(XFields.UID, uid); + builder.field(XFields.TYPE, type.string()); + builder.field(XFields.SUBSCRIPTION_TYPE, subscriptionType.string()); + builder.field(XFields.ISSUE_DATE, issueDate); + builder.field(XFields.FEATURE, feature); + builder.field(XFields.EXPIRY_DATE, expiryDate); + builder.field(XFields.MAX_NODES, maxNodes); + builder.field(XFields.ISSUED_TO, issuedTo); + builder.field(XFields.ISSUER, issuer); + if (signature != null) { + builder.field(XFields.SIGNATURE, signature); + } + builder.endObject(); + return builder; + } + + private final static int VERSION = 1; + + final static class Fields { + static final String UID = "uid"; + static final String TYPE = "type"; + static final String SUBSCRIPTION_TYPE = "subscription_type"; + static final String ISSUE_DATE = "issue_date"; + static final String FEATURE = "feature"; + static final String EXPIRY_DATE = "expiry_date"; + static final String MAX_NODES = "max_nodes"; + static final String ISSUED_TO = "issued_to"; + static final String ISSUER = "issuer"; + static final String SIGNATURE = "signature"; + } + + private final static class XFields { + static final XContentBuilderString UID = new XContentBuilderString(Fields.UID); + static final XContentBuilderString TYPE = new XContentBuilderString(Fields.TYPE); + static final XContentBuilderString SUBSCRIPTION_TYPE = new XContentBuilderString(Fields.SUBSCRIPTION_TYPE); + static final XContentBuilderString ISSUE_DATE = new XContentBuilderString(Fields.ISSUE_DATE); + static final XContentBuilderString FEATURE = new XContentBuilderString(Fields.FEATURE); + static final XContentBuilderString EXPIRY_DATE = new XContentBuilderString(Fields.EXPIRY_DATE); + static final XContentBuilderString MAX_NODES = new XContentBuilderString(Fields.MAX_NODES); + static final XContentBuilderString ISSUED_TO = new XContentBuilderString(Fields.ISSUED_TO); + static final XContentBuilderString ISSUER = new XContentBuilderString(Fields.ISSUER); + static final XContentBuilderString SIGNATURE = new XContentBuilderString(Fields.SIGNATURE); + } + + public static ESLicense fromXContent(XContentParser parser) throws IOException { + Builder builder = new Builder(); + XContentParser.Token token = parser.currentToken(); + if (token == XContentParser.Token.START_OBJECT) { + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + String currentFieldName = parser.currentName(); + token = parser.nextToken(); + if (token.isValue()) { + if (Fields.UID.equals(currentFieldName)) { + builder.uid(parser.text()); + } else if (Fields.TYPE.equals(currentFieldName)) { + builder.type(Type.fromString(parser.text())); + } else if (Fields.SUBSCRIPTION_TYPE.equals(currentFieldName)) { + builder.subscriptionType(SubscriptionType.fromString(parser.text())); + } else if (Fields.ISSUE_DATE.equals(currentFieldName)) { + builder.issueDate(parser.longValue()); + } else if (Fields.FEATURE.equals(currentFieldName)) { + builder.feature(parser.text()); + } else if (Fields.EXPIRY_DATE.equals(currentFieldName)) { + builder.expiryDate(parser.longValue()); + } else if (Fields.MAX_NODES.equals(currentFieldName)) { + builder.maxNodes(parser.intValue()); + } else if (Fields.ISSUED_TO.equals(currentFieldName)) { + builder.issuedTo(parser.text()); + } else if (Fields.ISSUER.equals(currentFieldName)) { + builder.issuer(parser.text()); + } else if (Fields.SIGNATURE.equals(currentFieldName)) { + builder.signature(parser.text()); + } + // Ignore unknown elements - might be new version of license + } else if (token == XContentParser.Token.START_ARRAY) { + // 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("failed to parse licenses expected a license object"); + } + return builder.verifyAndBuild(); + } + /** * Enum for License Type */ @@ -306,81 +431,4 @@ public class ESLicense implements Comparable { } } - - final static class Fields { - static final String UID = "uid"; - static final String TYPE = "type"; - static final String SUBSCRIPTION_TYPE = "subscription_type"; - static final String ISSUE_DATE = "issue_date"; - static final String FEATURE = "feature"; - static final String EXPIRY_DATE = "expiry_date"; - static final String MAX_NODES = "max_nodes"; - static final String ISSUED_TO = "issued_to"; - static final String ISSUER = "issuer"; - static final String SIGNATURE = "signature"; - } - - - static void toXContent(ESLicense license, XContentBuilder builder) throws IOException { - builder.startObject(); - builder.field(Fields.UID, license.uid); - builder.field(Fields.TYPE, license.type.string()); - builder.field(Fields.SUBSCRIPTION_TYPE, license.subscriptionType.string()); - builder.field(Fields.ISSUE_DATE, license.issueDate); - builder.field(Fields.FEATURE, license.feature); - builder.field(Fields.EXPIRY_DATE, license.expiryDate); - builder.field(Fields.MAX_NODES, license.maxNodes); - builder.field(Fields.ISSUED_TO, license.issuedTo); - builder.field(Fields.ISSUER, license.issuer); - builder.field(Fields.SIGNATURE, license.signature); - builder.endObject(); - } - - - static ESLicense fromXContent(Map map) throws IOException { - return new Builder() - .uid((String) map.get(Fields.UID)) - .type(Type.fromString((String) map.get(Fields.TYPE))) - .subscriptionType(SubscriptionType.fromString((String) map.get(Fields.SUBSCRIPTION_TYPE))) - .feature((String) map.get(Fields.FEATURE)) - .maxNodes((int) map.get(Fields.MAX_NODES)) - .issuedTo((String) map.get(Fields.ISSUED_TO)) - .signature((String) map.get(Fields.SIGNATURE)) - .issueDate((long) map.get(Fields.ISSUE_DATE)) - .expiryDate((long) map.get(Fields.EXPIRY_DATE)) - .issuer((String) map.get(Fields.ISSUER)) - .verifyAndBuild(); - } - - static ESLicense readFrom(StreamInput in) throws IOException { - Map licenseMap = in.readMap(); - return builder() - .uid((String) licenseMap.get(Fields.UID)) - .type(Type.fromString((String) licenseMap.get(Fields.TYPE))) - .subscriptionType(SubscriptionType.fromString((String) licenseMap.get(Fields.SUBSCRIPTION_TYPE))) - .issueDate((long) licenseMap.get(Fields.ISSUE_DATE)) - .feature((String) licenseMap.get(Fields.FEATURE)) - .expiryDate((long) licenseMap.get(Fields.EXPIRY_DATE)) - .maxNodes((int) licenseMap.get(Fields.MAX_NODES)) - .issuedTo((String) licenseMap.get(Fields.ISSUED_TO)) - .signature((String) licenseMap.get(Fields.SIGNATURE)) - .issuer((String) licenseMap.get(Fields.ISSUER)) - .verifyAndBuild(); - } - - static void writeTo(ESLicense esLicense, StreamOutput out) throws IOException { - Map licenseMap = new HashMap<>(); - licenseMap.put(Fields.UID, esLicense.uid); - licenseMap.put(Fields.TYPE, esLicense.type.string()); - licenseMap.put(Fields.SUBSCRIPTION_TYPE, esLicense.subscriptionType.string()); - licenseMap.put(Fields.ISSUE_DATE, esLicense.issueDate); - licenseMap.put(Fields.FEATURE, esLicense.feature); - licenseMap.put(Fields.EXPIRY_DATE, esLicense.expiryDate); - licenseMap.put(Fields.MAX_NODES, esLicense.maxNodes); - licenseMap.put(Fields.ISSUED_TO, esLicense.issuedTo); - licenseMap.put(Fields.ISSUER, esLicense.issuer); - licenseMap.put(Fields.SIGNATURE, esLicense.signature); - out.writeMap(licenseMap); - } - } diff --git a/src/main/java/org/elasticsearch/license/core/ESLicenses.java b/src/main/java/org/elasticsearch/license/core/ESLicenses.java index 2b5ae37c208..81d07396462 100644 --- a/src/main/java/org/elasticsearch/license/core/ESLicenses.java +++ b/src/main/java/org/elasticsearch/license/core/ESLicenses.java @@ -5,23 +5,30 @@ */ package org.elasticsearch.license.core; +import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.common.collect.ImmutableMap; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; -import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.common.xcontent.XContentFactory; -import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.*; import java.io.IOException; import java.util.*; public class ESLicenses { - public static void toXContent(Collection licenses, XContentBuilder builder) throws IOException { + final static class Fields { + static final String LICENSES = "licenses"; + } + + private final static class XFields { + static final XContentBuilderString LICENSES = new XContentBuilderString(Fields.LICENSES); + } + + public static void toXContent(Collection licenses, XContentBuilder builder, ToXContent.Params params) throws IOException { builder.startObject(); - builder.startArray("licenses"); + builder.startArray(XFields.LICENSES); for (ESLicense license : licenses) { - ESLicense.toXContent(license, builder); + license.toXContent(builder, params); } builder.endArray(); builder.endObject(); @@ -35,23 +42,35 @@ public class ESLicenses { return fromXContent(XContentFactory.xContent(bytes).createParser(bytes)); } - private static List fromXContent(XContentParser parser) throws IOException { - Set esLicenses = new HashSet<>(); - final Map licensesMap = parser.mapAndClose(); - @SuppressWarnings("unchecked") - final List> licenseMaps = (ArrayList>)licensesMap.get("licenses"); - for (Map licenseMap : licenseMaps) { - final ESLicense esLicense = ESLicense.fromXContent(licenseMap); - esLicenses.add(esLicense); + public static List fromXContent(XContentParser parser) throws IOException { + List esLicenses = new ArrayList<>(); + if (parser.nextToken() == XContentParser.Token.START_OBJECT) { + if (parser.nextToken() == XContentParser.Token.FIELD_NAME) { + String currentFieldName = parser.currentName(); + if (Fields.LICENSES.equals(currentFieldName)) { + if (parser.nextToken() == XContentParser.Token.START_ARRAY) { + while (parser.nextToken() != XContentParser.Token.END_ARRAY) { + esLicenses.add(ESLicense.fromXContent(parser)); + } + } else { + throw new ElasticsearchParseException("failed to parse licenses expected an array of licenses"); + } + } + // Ignore all other fields - might be created with new version + } else { + throw new ElasticsearchParseException("failed to parse licenses expected field"); + } + } else { + throw new ElasticsearchParseException("failed to parse licenses expected start object"); } - return new ArrayList<>(esLicenses); + return esLicenses; } public static List readFrom(StreamInput in) throws IOException { int size = in.readVInt(); List esLicenses = new ArrayList<>(size); for (int i = 0; i < size; i++) { - esLicenses.add(ESLicense.readFrom(in)); + esLicenses.add(ESLicense.readESLicense(in)); } return esLicenses; } @@ -59,9 +78,8 @@ public class ESLicenses { public static void writeTo(List esLicenses, StreamOutput out) throws IOException { out.writeVInt(esLicenses.size()); for (ESLicense license : esLicenses) { - ESLicense.writeTo(license, out); + license.writeTo(out); } - } public static ImmutableMap reduceAndMap(Set esLicensesSet) { diff --git a/src/main/java/org/elasticsearch/license/licensor/tools/LicenseGeneratorTool.java b/src/main/java/org/elasticsearch/license/licensor/tools/LicenseGeneratorTool.java index 07e9b572d8a..e6b66bf19a4 100644 --- a/src/main/java/org/elasticsearch/license/licensor/tools/LicenseGeneratorTool.java +++ b/src/main/java/org/elasticsearch/license/licensor/tools/LicenseGeneratorTool.java @@ -6,6 +6,7 @@ package org.elasticsearch.license.licensor.tools; import org.elasticsearch.common.collect.ImmutableSet; +import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentType; @@ -93,7 +94,7 @@ public class LicenseGeneratorTool { XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON, out); - ESLicenses.toXContent(signedLicences, builder); + ESLicenses.toXContent(signedLicences, builder, ToXContent.EMPTY_PARAMS); builder.flush(); } diff --git a/src/main/java/org/elasticsearch/license/licensor/tools/LicenseVerificationTool.java b/src/main/java/org/elasticsearch/license/licensor/tools/LicenseVerificationTool.java index 7da3acba810..3aec8d4811b 100644 --- a/src/main/java/org/elasticsearch/license/licensor/tools/LicenseVerificationTool.java +++ b/src/main/java/org/elasticsearch/license/licensor/tools/LicenseVerificationTool.java @@ -5,6 +5,7 @@ */ package org.elasticsearch.license.licensor.tools; +import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentType; @@ -80,7 +81,7 @@ public class LicenseVerificationTool { // dump effective licences XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON, out); - ESLicenses.toXContent(licenseProvider.getEffectiveLicenses().values(), builder); + ESLicenses.toXContent(licenseProvider.getEffectiveLicenses().values(), builder, ToXContent.EMPTY_PARAMS); builder.flush(); }