/*
* RHQ Management Platform
* Copyright (C) 2014 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
package org.rhq.modules.plugins.wildfly10.util;
import java.io.IOException;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.jackson.map.ObjectMapper;
import org.rhq.modules.plugins.wildfly10.json.Result;
/**
* @author Lukas Krejci
* @since 4.13
*/
public final class PatchDetails {
private static final Log LOG = LogFactory.getLog(PatchDetails.class);
private final String id;
private final Type type;
private final Date appliedAt;
public static List<PatchDetails> fromJSONArray(String patchHistoryList) {
ObjectMapper mapper = new ObjectMapper();
try {
@SuppressWarnings("unchecked")
List<Map<String, String>> lst = mapper.readValue(patchHistoryList, List.class);
List<PatchDetails> result = new ArrayList<PatchDetails>(lst.size());
for (Map<String, String> patchDescr : lst) {
result.add(processJSONParseResult(patchDescr));
}
return result;
} catch (IOException e) {
LOG.warn("Failed to parse the patch history JSON: " + e.getMessage());
return Collections.emptyList();
}
}
public static List<PatchDetails> fromHistory(String patchHistoryOutcome) {
ObjectMapper mapper = new ObjectMapper();
Result result;
try {
result = mapper.readValue(patchHistoryOutcome, Result.class);
} catch (IOException e) {
LOG.warn("Failed to parse the output of the 'patch history' command with message '" + e.getMessage() +
"'. The output was:\n" + patchHistoryOutcome);
return Collections.emptyList();
}
if (!result.isSuccess()) {
if (LOG.isDebugEnabled()) {
LOG.debug("'patch history' command didn't succeed: " + result);
}
return Collections.emptyList();
}
// expecting a list of maps of string->string
if (!(result.getResult() instanceof List)) {
if (LOG.isDebugEnabled()) {
LOG.debug("Unexpected patch history results. Expected list but found " +
(result.getResult() == null ? "null" : result.getResult().getClass().toString()));
}
return Collections.emptyList();
}
@SuppressWarnings("unchecked")
List<Map<String, String>> patches = (List<Map<String, String>>) result.getResult();
if (patches.isEmpty()) {
return Collections.emptyList();
}
List<PatchDetails> ret = new ArrayList<PatchDetails>();
for (Map<String, String> patchDescr : patches) {
ret.add(processJSONParseResult(patchDescr));
}
return ret;
}
private static PatchDetails processJSONParseResult(Map<String, String> jsonParseResult) {
String patchId = jsonParseResult.get("patch-id");
Date appliedAt = null;
Type type = Type.fromJsonValue(jsonParseResult.get("type"));
try {
//This seems to be the date format AS are using
DateFormat format = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
appliedAt = format.parse(jsonParseResult.get("applied-at"));
} catch (ParseException e) {
LOG.info("Failed to parse the installation date of the patch " + patchId + ": '" +
jsonParseResult.get("applied-at") + "' with error message: " + e.getMessage());
}
return new PatchDetails(patchId, type, appliedAt);
}
public static List<PatchDetails> fromInfo(String patchInfoOutcome, String patchHistoryOutcome) {
ObjectMapper mapper = new ObjectMapper();
Result result;
try {
result = mapper.readValue(patchInfoOutcome, Result.class);
} catch (IOException e) {
LOG.warn("Failed to parse the output of the 'patch info' command with message '" + e.getMessage() +
"'. The output was:\n" + patchInfoOutcome);
return Collections.emptyList();
}
if (!result.isSuccess()) {
if (LOG.isDebugEnabled()) {
LOG.debug("'patch info' command didn't succeed: " + result);
}
return Collections.emptyList();
}
if (!(result.getResult() instanceof Map)) {
if (LOG.isDebugEnabled()) {
LOG.debug("Unexpected patch history results. Expected map but found " +
(result.getResult() == null ? "null" : result.getResult().getClass().toString()));
}
return Collections.emptyList();
}
List<PatchDetails> history = fromHistory(patchHistoryOutcome);
List<PatchDetails> ret = new ArrayList<PatchDetails>();
@SuppressWarnings("unchecked")
Map<String, Object> resultMap = (Map<String, Object>) result.getResult();
@SuppressWarnings("unchecked")
List<String> oneOffs = (List<String>) resultMap.get("patches");
for (String id : oneOffs) {
PatchDetails p = findById(id, history);
if (p != null) {
ret.add(p);
}
}
String cumulativePatchId = (String) resultMap.get("cumulative-patch-id");
if (!"base".equals(cumulativePatchId)) {
PatchDetails p = findById(cumulativePatchId, history);
if (p != null) {
ret.add(p);
}
}
return ret;
}
private static PatchDetails findById(String patchId, List<PatchDetails> patchDetails) {
for (PatchDetails p : patchDetails) {
if (patchId.equals(p.getId())) {
return p;
}
}
return null;
}
public PatchDetails(String id, Type type, Date appliedAt) {
this.id = id;
this.type = type;
this.appliedAt = appliedAt;
}
public String getId() {
return id;
}
public Type getType() {
return type;
}
public Date getAppliedAt() {
return appliedAt;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof PatchDetails)) {
return false;
}
PatchDetails that = (PatchDetails) o;
if (!id.equals(that.id)) {
return false;
}
return true;
}
@Override
public int hashCode() {
return id.hashCode();
}
@Override
public String toString() {
DateFormat format = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
return "{\n \"patch-id\": \"" + id + "\",\n \"type\": \"" + type + "\",\n \"applied-at\": \"" +
format.format(appliedAt) + "\"\n}";
}
public enum Type {
CUMULATIVE, ONE_OFF;
public static Type fromJsonValue(String value) {
if ("one-off".equals(value)) {
return ONE_OFF;
} else if ("cumulative".equals(value)) {
return CUMULATIVE;
} else {
return null;
}
}
@Override
public String toString() {
switch (this) {
case CUMULATIVE:
return "cumulative";
case ONE_OFF:
return "one-off";
default:
throw new AssertionError("Non-exhaustive toString() implementation of PatchDetails.Type");
}
}
}
}