package io.cattle.platform.agent.impl;
import io.cattle.platform.agent.AgentRequest;
import io.cattle.platform.agent.RemoteAgent;
import io.cattle.platform.async.utils.AsyncUtils;
import io.cattle.platform.async.utils.TimeoutException;
import io.cattle.platform.core.model.Agent;
import io.cattle.platform.eventing.EventCallOptions;
import io.cattle.platform.eventing.EventService;
import io.cattle.platform.eventing.exception.AgentRemovedException;
import io.cattle.platform.eventing.exception.EventExecutionException;
import io.cattle.platform.eventing.impl.AbstractEventService;
import io.cattle.platform.eventing.model.Event;
import io.cattle.platform.eventing.model.EventVO;
import io.cattle.platform.json.JsonMapper;
import io.cattle.platform.object.ObjectManager;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import com.google.common.util.concurrent.ListenableFuture;
public class RemoteAgentImpl implements RemoteAgent {
private static final Set<String> FRIENDLY_REPLY = new HashSet<>(Arrays.asList("compute.instance.activate"));
JsonMapper jsonMapper;
ObjectManager objectManager;
EventService rawEventService;
EventService wrappedEventService;
Long agentId;
public RemoteAgentImpl(JsonMapper jsonMapper, ObjectManager objectManager, EventService rawEventService, EventService wrappedEventService, Long agentId) {
this.jsonMapper = jsonMapper;
this.objectManager = objectManager;
this.rawEventService = rawEventService;
this.wrappedEventService = wrappedEventService;
this.agentId = agentId;
}
@Override
public long getAgentId() {
return agentId;
}
protected AgentRequest createRequest(Event event) {
return new AgentRequest(agentId, event);
}
@Override
public void publish(Event event) {
wrappedEventService.publish(createRequest(event));
}
@Override
public <T extends Event> T callSync(Event event, Class<T> reply, long timeout) {
return callSync(event, reply, new EventCallOptions(AbstractEventService.DEFAULT_RETRIES.get(), timeout));
}
@Override
public <T extends Event> T callSync(Event event, Class<T> reply, EventCallOptions options) {
/*
* NOTE: Forever blocking get() used only because underlying future will
* always timeout
*/
try {
return AsyncUtils.get(call(event, reply, options));
} catch (TimeoutException e) {
Agent agent = objectManager.loadResource(Agent.class, agentId);
if (agent == null || agent.getRemoved() != null) {
throw new AgentRemovedException("Agent [" + agentId + "] is removed", event);
}
throw e;
} catch (AgentRemovedException e) {
throw e;
} catch (EventExecutionException e) {
/*
* This is done so that the exception will have a better stack
* trace. Normally the exceptions from a future will have a pretty
* sparse stack not giving too much context
*/
throw EventExecutionException.fromEvent(e.getEvent());
}
}
@Override
public <T extends Event> ListenableFuture<T> call(final Event event, final Class<T> reply, long timeout) {
return call(event, reply, new EventCallOptions(AbstractEventService.DEFAULT_RETRIES.get(), timeout));
}
@Override
public <T extends Event> ListenableFuture<T> call(final Event event, final Class<T> reply, EventCallOptions options) {
AgentRequest request = createRequest(event);
return EventCallProgressHelper.call(wrappedEventService, request, reply, options, new EventResponseMarshaller() {
@Override
public <V> V convert(Event resultEvent, Class<V> reply) {
return getReply(event, resultEvent, reply);
}
});
}
protected <T> T getReply(Event inputEvent, Event resultEvent, Class<T> reply) {
if (resultEvent.getData() == null) {
return null;
}
T commandReply = jsonMapper.convertValue(resultEvent.getData(), reply);
if (FRIENDLY_REPLY.contains(inputEvent.getName())) {
EventVO<?> publishEvent = null;
if (commandReply instanceof EventVO) {
publishEvent = (EventVO<?>) commandReply;
} else {
publishEvent = jsonMapper.convertValue(resultEvent.getData(), EventVO.class);
}
publishEvent.setName(inputEvent.getName() + Event.REPLY_SUFFIX);
rawEventService.publish(publishEvent);
}
return commandReply;
}
@Override
public Event callSync(Event event) {
return callSync(event, AbstractEventService.DEFAULT_TIMEOUT.get());
}
@Override
public Event callSync(Event event, EventCallOptions options) {
return callSync(event, EventVO.class, options);
}
@Override
public Event callSync(Event event, long timeout) {
return callSync(event, EventVO.class, timeout);
}
@Override
public ListenableFuture<? extends Event> call(Event event) {
return call(event, AbstractEventService.DEFAULT_TIMEOUT.get());
}
@Override
public ListenableFuture<? extends Event> call(Event event, EventCallOptions options) {
return call(event, EventVO.class, options);
}
@Override
public ListenableFuture<? extends Event> call(Event event, long timeout) {
return call(event, EventVO.class, timeout);
}
@Override
public <T extends Event> T callSync(Event event, Class<T> reply) {
return callSync(event, reply, AbstractEventService.DEFAULT_TIMEOUT.get());
}
@Override
public <T extends Event> ListenableFuture<T> call(Event event, Class<T> reply) {
return call(event, reply, AbstractEventService.DEFAULT_TIMEOUT.get());
}
}