/*
 * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
 * or more contributor license agreements. Licensed under the Elastic License;
 * you may not use this file except in compliance with the Elastic License.
 */
package org.elasticsearch.marvel.agent.renderer.cluster;

import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.admin.cluster.stats.ClusterStatsResponse;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentBuilderString;
import org.elasticsearch.license.core.License;
import org.elasticsearch.marvel.agent.collector.cluster.ClusterInfoMarvelDoc;
import org.elasticsearch.marvel.agent.renderer.AbstractRenderer;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.List;

public class ClusterInfoRenderer extends AbstractRenderer<ClusterInfoMarvelDoc> {

    static final class MessageDigestSHA256Provider {
        private static final MessageDigest digest;

        static {
            try {
                digest = MessageDigest.getInstance("SHA-256");
            } catch (NoSuchAlgorithmException e) {
                throw new ElasticsearchException("Unexpected exception creating MessageDigest instance", e);
            }
        }

        private static MessageDigest sha256() {
            try {
                MessageDigest sha = (MessageDigest) digest.clone();
                sha.reset();
                return sha;
            } catch (CloneNotSupportedException e) {
                throw new ElasticsearchException("Unexpected exception creating MessageDigest instance", e);
            }
        }
    }

    public ClusterInfoRenderer() {
        super(null, false);
    }

    @Override
    protected void doRender(ClusterInfoMarvelDoc marvelDoc, XContentBuilder builder, ToXContent.Params params) throws IOException {
        builder.field(Fields.CLUSTER_NAME, marvelDoc.getClusterName());
        builder.field(Fields.VERSION, marvelDoc.getVersion());

        builder.startArray(Fields.LICENSES);
        List<License> licenses = marvelDoc.getLicenses();
        if (licenses != null) {
            for (License license : licenses) {
                builder.startObject();
                builder.field(Fields.STATUS, license.status().label());
                builder.field(Fields.UID, license.uid());
                builder.field(Fields.TYPE, license.type());
                builder.dateValueField(Fields.ISSUE_DATE_IN_MILLIS, Fields.ISSUE_DATE, license.issueDate());
                builder.field(Fields.FEATURE, license.feature());
                builder.dateValueField(Fields.EXPIRY_DATE_IN_MILLIS, 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.HKEY, hash(license, marvelDoc.clusterUUID()));
                builder.endObject();

            }
        }
        builder.endArray();

        builder.startObject(Fields.CLUSTER_STATS);
        ClusterStatsResponse clusterStats = marvelDoc.getClusterStats();
        if (clusterStats != null) {
            clusterStats.toXContent(builder, params);
        }
        builder.endObject();
    }

    public static String hash(License license, String clusterName) {
        return hash(license.status().label(), license.uid(), license.type(), String.valueOf(license.expiryDate()), clusterName);
    }

    public static String hash(String licenseStatus, String licenseUid, String licenseType, String licenseExpiryDate, String clusterUUID) {
        String toHash = licenseStatus + licenseUid + licenseType + licenseExpiryDate + clusterUUID;
        try {
            byte[] sha256Bytes = MessageDigestSHA256Provider.sha256().digest(toHash.getBytes("UTF-8"));
            StringBuffer hash = new StringBuffer();
            for (int i = 0; i < sha256Bytes.length; i++) {
                hash.append(Integer.toHexString(0xff & sha256Bytes[i]));
            }
            return hash.toString();
        } catch (UnsupportedEncodingException e) {
            throw new ElasticsearchException("Unexpected error calculating SHA-256 MessageDigest", e);
        }
    }

    static final class Fields {
        static final XContentBuilderString CLUSTER_NAME = new XContentBuilderString("cluster_name");
        static final XContentBuilderString LICENSES = new XContentBuilderString("licenses");
        static final XContentBuilderString VERSION = new XContentBuilderString("version");
        static final XContentBuilderString CLUSTER_STATS = new XContentBuilderString("cluster_stats");

        static final XContentBuilderString HKEY = new XContentBuilderString("hkey");

        static final XContentBuilderString STATUS = new XContentBuilderString("status");
        static final XContentBuilderString UID = new XContentBuilderString("uid");
        static final XContentBuilderString TYPE = new XContentBuilderString("type");
        static final XContentBuilderString FEATURE = new XContentBuilderString("feature");
        static final XContentBuilderString ISSUE_DATE_IN_MILLIS = new XContentBuilderString("issue_date_in_millis");
        static final XContentBuilderString ISSUE_DATE = new XContentBuilderString("issue_date");
        static final XContentBuilderString EXPIRY_DATE_IN_MILLIS = new XContentBuilderString("expiry_date_in_millis");
        static final XContentBuilderString EXPIRY_DATE = new XContentBuilderString("expiry_date");
        static final XContentBuilderString MAX_NODES = new XContentBuilderString("max_nodes");
        static final XContentBuilderString ISSUED_TO = new XContentBuilderString("issued_to");
        static final XContentBuilderString ISSUER = new XContentBuilderString("issuer");
    }
}

@@ -412,24 +412,77 @@
 tsResponse;%0A
+import org.elasticsearch.common.hash.MessageDigests;%0A
 import org.e
@@ -522,16 +522,16 @@
 ontent;%0A
-
 import o
@@ -900,32 +900,74 @@
 odingException;%0A
+import java.nio.charset.StandardCharsets;%0A
 import java.secu
@@ -1057,16 +1057,16 @@
 .List;%0A%0A
+
 public c
@@ -1143,781 +1143,8 @@
 %3E %7B%0A
-%0A    static final class MessageDigestSHA256Provider %7B%0A        private static final MessageDigest digest;%0A%0A        static %7B%0A            try %7B%0A                digest = MessageDigest.getInstance(%22SHA-256%22);%0A            %7D catch (NoSuchAlgorithmException e) %7B%0A                throw new ElasticsearchException(%22Unexpected exception creating MessageDigest instance%22, e);%0A            %7D%0A        %7D%0A%0A        private static MessageDigest sha256() %7B%0A            try %7B%0A                MessageDigest sha = (MessageDigest) digest.clone();%0A                sha.reset();%0A                return sha;%0A            %7D catch (CloneNotSupportedException e) %7B%0A                throw new ElasticsearchException(%22Unexpected exception creating MessageDigest instance%22, e);%0A            %7D%0A        %7D%0A    %7D%0A%0A
     
@@ -3282,515 +3282,115 @@
     
-try %7B%0A            byte%5B%5D sha256Bytes = MessageDigestSHA256Provider.sha256().digest(toHash.getBytes(%22UTF-8%22));%0A            StringBuffer hash = new StringBuffer();%0A            for (int i = 0; i %3C sha256Bytes.length; i++) %7B%0A                hash.append(Integer.toHexString(0xff & sha256Bytes%5Bi%5D));%0A            %7D%0A            return hash.toString();%0A        %7D catch (UnsupportedEncodingException e) %7B%0A            throw new ElasticsearchException(%22Unexpected error calculating SHA-256 MessageDigest%22, e);%0A        %7D
+return MessageDigests.toHexString(MessageDigests.sha256().digest(toHash.getBytes(StandardCharsets.UTF_8)));
 %0A   

Use abstraction from core