/*
* 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;
/**
* Given an @RPC instance that implements Runnable, this class organizes
* how that instance will be handled for input and output,
* how the intermediate output will
* be stored, etc.
*
* A runnable instance itself is the input and output type - its setters and
* getters are used as the available attributes. This means a Runnable cannot
* limit which attributes are "in" and which are "out" , but of course whatever
* attributes are "out" attributes can be set by the runnable so if the
* client tried to set them they would be ignored. IT also means there cannot
* be private inputs because inputs will be displayed to the user together
* with the outputs. For example:
*
* Input: <add_integers><x>1</x><y>1</y></add_integers>
*
* Output: <add_integers><x>1</x><y>1</y><result>2</result></add_integers>
*
* @author jbuhacoff
*/
public class RunnableRpcAdapter implements RpcAdapter<Object,Object> {
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(RunnableRpcAdapter.class);
private ObjectMapper mapper;
private Runnable rpcInstance;
private ArrayList<Fault> faults = new ArrayList<Fault>();
public RunnableRpcAdapter(Runnable 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() {
return rpcInstance.getClass();
}
// 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 {
rpcInstance.run();
}
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 rpcInstance;
}
@Override
public List<Fault> getFaults() { return faults; }
}