package de.is24.infrastructure.gridfs.http.web.controller;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.ImmutableSet;
import com.mongodb.CommandResult;
import com.mongodb.DBCollection;
import com.mongodb.Mongo;
import com.mongodb.ReplicaSetStatus;
import com.mongodb.util.JSON;
import de.is24.infrastructure.gridfs.http.AppVersion;
import de.is24.infrastructure.gridfs.http.mongo.MongoStringToJsonConverter;
import de.is24.util.monitoring.InApplicationMonitor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Iterator;
import java.util.Set;
import static com.fasterxml.jackson.core.JsonParser.Feature.ALLOW_SINGLE_QUOTES;
import static com.fasterxml.jackson.core.JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES;
import static javax.servlet.http.HttpServletResponse.SC_SERVICE_UNAVAILABLE;
import static org.springframework.web.bind.annotation.RequestMethod.GET;
@Controller
public class StatusController {
private static final Logger LOGGER = LoggerFactory.getLogger(StatusController.class);
public static final String OK_STATUS = "ok";
public static final String NOT_RESPONDING_STATUS = "not responding";
private final MongoTemplate mongoTemplate;
private final Mongo mongo;
private final AppVersion appVersion;
private final MongoStringToJsonConverter jsonConverter = new MongoStringToJsonConverter();
private static final Set<String> EXPECTED_COLLECTION_NAMES = ImmutableSet.of(
"fs.chunks",
"fs.files",
"system.indexes",
"yum.entries",
"yum.repos");
@Autowired
public StatusController(final MongoTemplate mongoTemplate, Mongo mongo, AppVersion appVersion) {
this.mongoTemplate = mongoTemplate;
this.mongo = mongo;
this.appVersion = appVersion;
}
@RequestMapping(value = "/status", method = GET, produces = {"application/json"})
@ResponseBody
public String getStatus(HttpServletResponse response) {
return getStatusJson(response, false);
}
@RequestMapping(value = "/status-full", method = GET, produces = {"application/json"})
@ResponseBody
public String getStatusFull(HttpServletResponse response) {
return prettyJson(getStatusJson(response, true));
}
private String prettyJson(final String rawJson) {
try {
final ObjectMapper mapper = new ObjectMapper().disableDefaultTyping()
.configure(ALLOW_UNQUOTED_FIELD_NAMES, true)
.configure(ALLOW_SINGLE_QUOTES, true);
final Object o = mapper.readValue(rawJson, Object.class);
return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(o);
} catch (IOException ioEx) {
LOGGER.debug("can't pretty json for rawJson {}\n{}", rawJson, ioEx);
return rawJson;
}
}
private String getStatusJson(HttpServletResponse response, final boolean showExtendedInformation) {
boolean isOK;
final StringBuilder detailedInfo = new StringBuilder();
try {
Set<String> collectionNames = mongoTemplate.getCollectionNames();
isOK = collectionNames.containsAll(EXPECTED_COLLECTION_NAMES);
if (showExtendedInformation) {
appendVersionInfo(detailedInfo);
appendInfoOnReplicaSet(detailedInfo);
appendCollectionInfo(collectionNames, detailedInfo);
}
} catch (Exception e) {
LOGGER.warn("status not ok because", e);
isOK = false;
}
if (!isOK) {
response.setStatus(SC_SERVICE_UNAVAILABLE);
}
InApplicationMonitor.getInstance().incrementCounter(getClass().getName() + ".status");
return createJSONContent(isOK, detailedInfo);
}
private void appendVersionInfo(final StringBuilder detailedInfo) {
detailedInfo.append("version:\"");
detailedInfo.append(appVersion.getVersion());
detailedInfo.append("\"");
}
private String createJSONContent(final boolean ok, final StringBuilder detailedInfo) {
return "{mainInfo:{mongoDBStatus: \"" + (ok ? OK_STATUS : NOT_RESPONDING_STATUS) + "\"},"
+ "detailedInfo: {" + detailedInfo + "}}";
}
private void appendCollectionInfo(final Iterable<String> collectionNames, final StringBuilder content) {
content.append(",collections: [");
final Iterator<String> it = collectionNames.iterator();
while (it.hasNext()) {
final String collectionName = it.next();
content.append("{name: \"");
content.append(collectionName);
content.append("\"");
final CommandResult stats = getCollectionStats(collectionName);
if (stats == null) {
content.append(",info: \"unavialable\"");
} else {
content.append(",info: ");
JSON.serialize(stats, content);
}
if (it.hasNext()) {
content.append("},");
} else {
content.append("}");
}
}
content.append("]");
}
private CommandResult getCollectionStats(final String collectionName) {
final DBCollection dbCollection = mongoTemplate.getCollection(collectionName);
if (dbCollection != null) {
return dbCollection.getStats();
}
return null;
}
private void appendInfoOnReplicaSet(final StringBuilder content) {
content.append(",replicaSetInfo: ");
ReplicaSetStatus replicaSetStatus = mongo.getReplicaSetStatus();
if (replicaSetStatus == null) {
content.append("\"unavailable\"");
} else {
content.append(jsonConverter.convert(replicaSetStatus.toString()));
}
}
}