com.netflix.exhibitor.core.rest.ConfigResource.java Source code

Java tutorial

Introduction

Here is the source code for com.netflix.exhibitor.core.rest.ConfigResource.java

Source

/*
 * Copyright 2012 Netflix, Inc.
 *
 *    Licensed 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 com.netflix.exhibitor.core.rest;

import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.hash.Hashing;
import com.netflix.exhibitor.core.backup.BackupConfigSpec;
import com.netflix.exhibitor.core.config.ConfigManager;
import com.netflix.exhibitor.core.config.EncodedConfigParser;
import com.netflix.exhibitor.core.config.InstanceConfig;
import com.netflix.exhibitor.core.config.IntConfigs;
import com.netflix.exhibitor.core.config.PseudoLock;
import com.netflix.exhibitor.core.config.StringConfigs;
import com.netflix.exhibitor.core.entities.Result;
import com.netflix.exhibitor.core.state.FourLetterWord;
import com.netflix.exhibitor.core.state.ServerList;
import com.netflix.exhibitor.core.state.ServerSpec;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.node.JsonNodeFactory;
import org.codehaus.jackson.node.ObjectNode;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.EntityTag;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ContextResolver;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Path("exhibitor/v1/config")
public class ConfigResource {
    private final UIContext context;

    private static final String CANT_UPDATE_CONFIG_MESSAGE = "It appears that another process has updated the config. Your change was not committed.";

    public ConfigResource(@Context ContextResolver<UIContext> resolver) {
        context = resolver.getContext(UIContext.class);
    }

    @Path("get-state")
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response getSystemState(@Context Request request) throws Exception {
        InstanceConfig config = context.getExhibitor().getConfigManager().getConfig();

        String response = new FourLetterWord(FourLetterWord.Word.RUOK, config,
                context.getExhibitor().getConnectionTimeOutMs()).getResponse();
        ServerList serverList = new ServerList(config.getString(StringConfigs.SERVERS_SPEC));
        ServerSpec us = Iterables.find(serverList.getSpecs(),
                ServerList.isUs(context.getExhibitor().getThisJVMHostname()), null);

        ObjectNode mainNode = JsonNodeFactory.instance.objectNode();
        ObjectNode configNode = JsonNodeFactory.instance.objectNode();
        ObjectNode controlPanelNode = JsonNodeFactory.instance.objectNode();

        mainNode.put("version", context.getExhibitor().getVersion());
        mainNode.put("running", "imok".equals(response));
        mainNode.put("backupActive", context.getExhibitor().getBackupManager().isActive());
        mainNode.put("standaloneMode", context.getExhibitor().getConfigManager().isStandaloneMode());
        mainNode.put("extraHeadingText", context.getExhibitor().getExtraHeadingText());
        mainNode.put("nodeMutationsAllowed", context.getExhibitor().nodeMutationsAllowed());

        configNode.put("rollInProgress", context.getExhibitor().getConfigManager().isRolling());
        configNode.put("rollStatus",
                context.getExhibitor().getConfigManager().getRollingConfigState().getRollingStatus());
        configNode.put("rollPercentDone",
                context.getExhibitor().getConfigManager().getRollingConfigState().getRollingPercentDone());

        configNode.put("hostname", context.getExhibitor().getThisJVMHostname());
        configNode.put("serverId", (us != null) ? us.getServerId() : -1);
        for (StringConfigs c : StringConfigs.values()) {
            configNode.put(fixName(c), config.getString(c));
        }
        for (IntConfigs c : IntConfigs.values()) {
            String fixedName = fixName(c);
            int value = config.getInt(c);

            configNode.put(fixedName, value);
        }

        EncodedConfigParser zooCfgParser = new EncodedConfigParser(config.getString(StringConfigs.ZOO_CFG_EXTRA));
        ObjectNode zooCfgNode = JsonNodeFactory.instance.objectNode();
        for (EncodedConfigParser.FieldValue fv : zooCfgParser.getFieldValues()) {
            zooCfgNode.put(fv.getField(), fv.getValue());
        }
        configNode.put("zooCfgExtra", zooCfgNode);

        if (context.getExhibitor().getBackupManager().isActive()) {
            ObjectNode backupExtraNode = JsonNodeFactory.instance.objectNode();
            EncodedConfigParser parser = context.getExhibitor().getBackupManager().getBackupConfigParser();
            List<BackupConfigSpec> configs = context.getExhibitor().getBackupManager().getConfigSpecs();
            for (BackupConfigSpec c : configs) {
                String value = parser.getValue(c.getKey());
                backupExtraNode.put(c.getKey(), (value != null) ? value : "");
            }
            configNode.put("backupExtra", backupExtraNode);
        }

        configNode.put("controlPanel", controlPanelNode);
        mainNode.put("config", configNode);

        String json = JsonUtil.writeValueAsString(mainNode);
        EntityTag tag = new EntityTag(Hashing.sha1().hashString(json).toString());

        Response.ResponseBuilder builder = request.evaluatePreconditions(tag);
        if (builder == null) {
            builder = Response.ok(json).tag(tag);
        }

        return builder.build();
    }

    @Path("rollback-rolling")
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response rollbackRolling() throws Exception {
        context.getExhibitor().getConfigManager().cancelRollingConfig(ConfigManager.CancelMode.ROLLBACK);
        return Response.ok(new Result("OK", true)).build();
    }

    @Path("force-commit-rolling")
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response forceCommitRolling() throws Exception {
        context.getExhibitor().getConfigManager().cancelRollingConfig(ConfigManager.CancelMode.FORCE_COMMIT);
        return Response.ok(new Result("OK", true)).build();
    }

    @Path("set-rolling")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public Response setConfigRolling(String newConfigJson) throws Exception {
        InstanceConfig wrapped = parseToConfig(newConfigJson);

        Result result = null;
        try {
            PseudoLock lock = context.getExhibitor().getConfigManager().newConfigBasedLock();
            try {
                if (lock.lock(context.getExhibitor().getLog(), 10, TimeUnit.SECONDS)) // TODO consider making configurable in the future
                {
                    if (context.getExhibitor().getConfigManager().startRollingConfig(wrapped, null)) {
                        result = new Result("OK", true);
                    }
                }
            } finally {
                lock.unlock();
            }

            if (result == null) {
                result = new Result("Another process has updated the config.", false);
            }
            context.getExhibitor().resetLocalConnection();
        } catch (Exception e) {
            result = new Result(e);
        }

        return Response.ok(result).build();
    }

    @Path("set")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public Response setConfig(String newConfigJson) throws Exception {
        InstanceConfig wrapped = parseToConfig(newConfigJson);

        Result result = null;
        try {
            PseudoLock lock = context.getExhibitor().getConfigManager().newConfigBasedLock();
            try {
                if (lock.lock(context.getExhibitor().getLog(), 10, TimeUnit.SECONDS)) // TODO consider making configurable in the future
                {
                    if (context.getExhibitor().getConfigManager().updateConfig(wrapped)) {
                        result = new Result("OK", true);
                    }
                }
            } finally {
                lock.unlock();
            }

            if (result == null) {
                result = new Result(CANT_UPDATE_CONFIG_MESSAGE, false);
            }
            context.getExhibitor().resetLocalConnection();
        } catch (Exception e) {
            result = new Result(e);
        }

        return Response.ok(result).build();
    }

    private InstanceConfig parseToConfig(String newConfigJson) throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        final JsonNode tree = mapper.readTree(mapper.getJsonFactory().createJsonParser(newConfigJson));

        String backupExtraValue = "";
        if (tree.get("backupExtra") != null) {
            List<EncodedConfigParser.FieldValue> values = Lists.newArrayList();
            JsonNode backupExtra = tree.get("backupExtra");
            Iterator<String> fieldNames = backupExtra.getFieldNames();
            while (fieldNames.hasNext()) {
                String name = fieldNames.next();
                String value = backupExtra.get(name).getTextValue();
                values.add(new EncodedConfigParser.FieldValue(name, value));
            }
            backupExtraValue = new EncodedConfigParser(values).toEncoded();
        }

        List<EncodedConfigParser.FieldValue> zooCfgValues = Lists.newArrayList();
        JsonNode zooCfgExtra = tree.get("zooCfgExtra");
        Iterator<String> fieldNames = zooCfgExtra.getFieldNames();
        while (fieldNames.hasNext()) {
            String name = fieldNames.next();
            String value = zooCfgExtra.get(name).getTextValue();
            zooCfgValues.add(new EncodedConfigParser.FieldValue(name, value));
        }
        final String zooCfgExtraValue = new EncodedConfigParser(zooCfgValues).toEncoded();

        final String finalBackupExtraValue = backupExtraValue;
        return new InstanceConfig() {
            @Override
            public String getString(StringConfigs config) {
                switch (config) {
                case BACKUP_EXTRA: {
                    return finalBackupExtraValue;
                }

                case ZOO_CFG_EXTRA: {
                    return zooCfgExtraValue;
                }

                default: {
                    // NOP
                    break;
                }
                }

                JsonNode node = tree.get(fixName(config));
                if (node == null) {
                    return "";
                }
                return node.getTextValue();
            }

            @Override
            public int getInt(IntConfigs config) {
                JsonNode node = tree.get(fixName(config));
                if (node == null) {
                    return 0;
                }
                try {
                    return Integer.parseInt(node.getTextValue());
                } catch (NumberFormatException e) {
                    // ignore
                }
                return 0;
            }
        };
    }

    static String fixName(Enum c) {
        StringBuilder str = new StringBuilder();
        String[] parts = c.name().toLowerCase().split("_");
        for (String p : parts) {
            if (p.length() > 0) {
                if (str.length() > 0) {
                    str.append(p.substring(0, 1).toUpperCase());
                    if (p.length() > 1) {
                        str.append(p.substring(1));
                    }
                } else {
                    str.append(p);
                }
            }
        }
        return str.toString();
    }
}