/**
* Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.financial.depgraph.rest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.fudgemsg.FudgeField;
import org.fudgemsg.FudgeMsg;
import org.fudgemsg.MutableFudgeMsg;
import org.fudgemsg.mapping.FudgeBuilder;
import org.fudgemsg.mapping.FudgeBuilderFor;
import org.fudgemsg.mapping.FudgeDeserializer;
import org.fudgemsg.mapping.FudgeSerializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.engine.depgraph.DependencyGraph;
import com.opengamma.engine.depgraph.ResolutionFailure;
import com.opengamma.engine.value.ValueRequirement;
import com.opengamma.engine.value.ValueSpecification;
/**
* Fudge builder for {@link DependencyGraphBuildTrace} objects
*/
@FudgeBuilderFor(DependencyGraphBuildTrace.class)
public class DependencyGraphBuildTraceFudgeBuilder implements FudgeBuilder<DependencyGraphBuildTrace> {
private static final Logger s_logger = LoggerFactory.getLogger(DependencyGraphBuildTraceFudgeBuilder.class);
@Override
public MutableFudgeMsg buildMessage(FudgeSerializer serializer, DependencyGraphBuildTrace object) {
MutableFudgeMsg result = serializer.newMessage();
final DependencyGraph graph = object.getDependencyGraph();
serializer.addToMessage(result, "dependencyGraph", null, graph);
final Map<Throwable, Integer> exceptions = object.getExceptionsWithCounts();
for (final Map.Entry<Throwable, Integer> exception : exceptions.entrySet()) {
final MutableFudgeMsg submessage = serializer.newMessage();
submessage.add("class", exception.getKey().getClass().getName());
submessage.add("message", exception.getKey().getMessage());
if (exception.getValue() > 1) {
submessage.add("repeat", exception.getValue());
}
result.add("exception", submessage);
}
for (final ResolutionFailure failure : object.getFailures()) {
serializer.addToMessage(result, "failure", null, failure);
}
serializer.addToMessage(result, "mapping", null, object.getMappings());
return result;
}
@Override
public DependencyGraphBuildTrace buildObject(FudgeDeserializer deserializer, FudgeMsg message) {
DependencyGraph dependencyGraph = deserializer.fudgeMsgToObject(DependencyGraph.class, message.getMessage("dependencyGraph"));
Map<Throwable, Integer> exceptionsWithCounts = new LinkedHashMap<>();
List<FudgeField> exceptionSubMessages = message.getAllByName("exception");
for (FudgeField field : exceptionSubMessages) {
FudgeMsg subMessage = (FudgeMsg) field.getValue();
String clazzName = subMessage.getString("class");
String exceptionMessage = subMessage.getString("message");
Throwable throwable;
try {
throwable = new ThrowableWithClass(exceptionMessage, Class.forName(clazzName));
} catch (ClassNotFoundException ex) {
throwable = new ThrowableWithClass(exceptionMessage, null);
s_logger.error("Exception class not found, setting exception class to null");
}
if (subMessage.hasField("repeat")) {
Integer repeat = subMessage.getInt("repeat");
if (repeat == null) {
s_logger.error("repeat field was present, but not integer");
}
exceptionsWithCounts.put(throwable, repeat);
} else {
exceptionsWithCounts.put(throwable, 1);
}
}
List<ResolutionFailure> failures = new ArrayList<>();
List<FudgeField> failureMessages = message.getAllByName("failure");
for (FudgeField field : failureMessages) {
FudgeMsg subMessage = (FudgeMsg) field.getValue();
ResolutionFailure failure = deserializer.fudgeMsgToObject(ResolutionFailure.class, subMessage);
if (failure == null) {
s_logger.error("Couldn't deserialize failure " + subMessage.toString());
}
failures.add(failure);
}
Map<ValueRequirement, ValueSpecification> mappings = new HashMap<>();
FudgeField mapping = message.getByName("mapping");
FudgeMsg mappingMsg = (FudgeMsg) mapping.getValue();
List<FudgeField> keys = mappingMsg.getAllByOrdinal(1); // keys
List<FudgeField> values = mappingMsg.getAllByOrdinal(2); // values
if (keys.size() != values.size()) {
s_logger.error("keys and values list in map don't have the same number of elements: message = {}", message);
throw new OpenGammaRuntimeException("keys and values list in map don't have the same number of elements");
}
for (int i = 0; i < keys.size(); i++) { // better to use iterators?
FudgeField reqField = keys.get(i);
ValueRequirement valueReq = deserializer.fieldValueToObject(ValueRequirement.class, reqField);
FudgeField specField = values.get(i);
ValueSpecification valueSpec = deserializer.fieldValueToObject(ValueSpecification.class, specField);
if (valueReq == null || valueSpec == null) {
s_logger.error("valueReq or valueSpec was null during deserialize: message = {}", message);
throw new OpenGammaRuntimeException("valueReq or valueSpec was null during deserialize");
}
mappings.put(valueReq, valueSpec);
}
return DependencyGraphBuildTrace.of(dependencyGraph, exceptionsWithCounts, failures, mappings);
}
/**
* Minimal wrapper for throwables sent over network.
*/
public static class ThrowableWithClass extends Throwable {
private static final long serialVersionUID = 1L;
private Class<?> _sourceClass;
public ThrowableWithClass(String message, Class<?> sourceClass) {
super(message);
_sourceClass = sourceClass;
}
public Class<?> getSourceClass() {
return _sourceClass;
}
//note - hc and eq defined for purpose of unit testing fudge builder.
//Throwables don't normally define these.
@Override
public int hashCode() {
return new HashCodeBuilder()
.append(_sourceClass)
.append(getMessage())
.hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj == null || obj.getClass() != getClass()) {
return false;
}
ThrowableWithClass other = (ThrowableWithClass) obj;
return new EqualsBuilder()
.append(_sourceClass, other._sourceClass)
.append(getMessage(), other.getMessage())
.isEquals();
}
}
}