/*
* Copyright (C) 2014 Intel Corporation
* All rights reserved.
*/
package com.intel.mtwilson.rpc.v2.resource;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.intel.dcsg.cpg.validation.Fault;
import com.intel.mtwilson.patch.PatchException;
import com.intel.mtwilson.patch.PatchUtil;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
/**
* Given an @RPC instance that implements Callable, this class organizes
* how that instance will be handled for input and output,
* how the intermediate output will
* be stored, etc.
*
* A callable instance itself is the input type - its setters and
* getters are used as the available attributes. The type of the return value
* from the call() method is the output type. This means a Callable has a
* precise specification of what are inputs and what is the output compared
* with the Runnable RPC interface.
*
* For example:
*
* Input: <add_integers><x>1</x><y>1</y></add_integers>
*
* Output: <integer>2</integer>
*
* @author jbuhacoff
*/
public class CallableRpcAdapter implements RpcAdapter<Object,Object> {
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(CallableRpcAdapter.class);
private ObjectMapper mapper;
private Callable rpcInstance;
private Object result;
private ArrayList<Fault> faults = new ArrayList<Fault>();
public CallableRpcAdapter(Callable rpcInstance) {
this.rpcInstance = rpcInstance;
mapper = new ObjectMapper();
mapper.setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
}
// these declare how we want to interpret the client's request body
// and how we want to store the intermediate output
@Override
public Class getInputClass() {
return rpcInstance.getClass();
}
@Override
public Class/*<T>*/ getOutputClass() {
try {
return rpcInstance.getClass().getMethod("call").getReturnType();
}
catch(NoSuchMethodException e) {
log.error("Cannot determine return type of Callable RPC", e); // should never happen since the rpcInstance must implement Callable
throw new RuntimeException(e);
}
}
// this method will receive the input object which was deserialized from
// the client's request body - and is the same type we returned in
// getInputClass
@Override
public void setInput(Object/*T*/ input) {
// use beanutils to copy the properties we need
try {
// Map<String,Object> diff = PatchUtil.diff(input, rpcInstance);
// log.debug("Going to copy input to rpc instance: {}", mapper.writeValueAsString(input)); // throws
// PatchUtil.apply(diff, rpcInstance); // throws JsonProcessingException
PatchUtil.copy(input, rpcInstance);
// log.debug("RPC instance is now: {}", mapper.writeValueAsString(rpcInstance));
}
catch(Exception e) {
log.error("Error while setting task input: {}", e.getMessage());
faults.add(new Fault(e, "Error while preparing task"));
}
}
// this method is called to invoke the rpc
@Override
public void invoke() {
try {
result = rpcInstance.call();
}
catch(Exception e) {
log.error("Error while executing task: {}", e.getMessage());
faults.add(new Fault(e, "Error while executing task"));
}
}
// this method is called to get the output from the rpc,
// which is also called the intermediate output because
// it will later be serialized according to the client's
// preference at delivery time
@Override
public Object getOutput() {
return result;
}
public List<Fault> getFaults() { return faults; }
}