Java tutorial
/* * 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(); } }