/* * Copyright 2016 ThoughtWorks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.thoughtworks.go.server.websocket; import com.thoughtworks.go.domain.AgentInstance; import com.thoughtworks.go.domain.JobIdentifier; import com.thoughtworks.go.domain.JobInstance; import com.thoughtworks.go.remote.AgentInstruction; import com.thoughtworks.go.remote.BuildRepositoryRemote; import com.thoughtworks.go.server.service.AgentRuntimeInfo; import com.thoughtworks.go.server.service.AgentService; import com.thoughtworks.go.server.service.ConsoleService; import com.thoughtworks.go.server.service.JobInstanceService; import com.thoughtworks.go.websocket.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; import java.io.File; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @Component public class AgentRemoteHandler { private static final Logger LOGGER = LoggerFactory.getLogger(AgentRemoteHandler.class); private Map<Agent, String> sessionIds = new ConcurrentHashMap<>(); private Map<Agent, String> agentCookie = new ConcurrentHashMap<>(); private Map<String, Agent> agentSessions = new ConcurrentHashMap<>(); @Qualifier("buildRepositoryMessageProducer") @Autowired private BuildRepositoryRemote buildRepositoryRemote; @Autowired private AgentService agentService; @Autowired private JobInstanceService jobInstanceService; private ConsoleService consoleService; @Autowired public AgentRemoteHandler(@Qualifier("buildRepositoryMessageProducer") BuildRepositoryRemote buildRepositoryRemote, AgentService agentService, JobInstanceService jobInstanceService, ConsoleService consoleService) { this.buildRepositoryRemote = buildRepositoryRemote; this.agentService = agentService; this.jobInstanceService = jobInstanceService; this.consoleService = consoleService; } public void process(Agent agent, Message msg) throws Exception { try { processWithoutAcknowledgement(agent, msg); } finally { agent.send(new Message(Action.acknowledge, MessageEncoding.encodeData(msg.getAcknowledgementId()))); } } public void processWithoutAcknowledgement(Agent agent, Message msg) throws Exception { switch (msg.getAction()) { case ping: AgentRuntimeInfo info = MessageEncoding.decodeData(msg.getData(), AgentRuntimeInfo.class); if (!sessionIds.containsKey(agent)) { LOGGER.info("{} is connected with websocket {}", info.getIdentifier(), agent); sessionIds.put(agent, info.getUUId()); this.agentSessions.put(info.getUUId(), agent); } if (info.getCookie() == null) { String cookie = agentCookie.get(agent); if (cookie == null) { cookie = buildRepositoryRemote.getCookie(info.getIdentifier(), info.getLocation()); agentCookie.put(agent, cookie); } info.setCookie(cookie); agent.send(new Message(Action.setCookie, MessageEncoding.encodeData(cookie))); } AgentInstruction instruction = this.buildRepositoryRemote.ping(info); if (instruction.isShouldCancelJob()) { agent.send(new Message(Action.cancelBuild)); } break; case reportCurrentStatus: Report report = MessageEncoding.decodeData(msg.getData(), Report.class); buildRepositoryRemote.reportCurrentStatus(report.getAgentRuntimeInfo(), findJobIdentifier(report), report.getJobState()); break; case reportCompleting: report = MessageEncoding.decodeData(msg.getData(), Report.class); buildRepositoryRemote.reportCompleting(report.getAgentRuntimeInfo(), findJobIdentifier(report), report.getResult()); break; case reportCompleted: report = MessageEncoding.decodeData(msg.getData(), Report.class); buildRepositoryRemote.reportCompleted(report.getAgentRuntimeInfo(), findJobIdentifier(report), report.getResult()); break; case consoleOut: ConsoleTransmission consoleTransmission = MessageEncoding.decodeData(msg.getData(), ConsoleTransmission.class); File consoleLogFile = consoleService.consoleLogFile(findJobIdentifier(consoleTransmission)); consoleService.updateConsoleLog(consoleLogFile, consoleTransmission.getLineAsStream()); break; default: throw new RuntimeException("Unknown action: " + msg.getAction()); } } private JobIdentifier findJobIdentifier(Transmission transmission) { if (transmission.getJobIdentifier() != null) { return transmission.getJobIdentifier(); } JobInstance instance = jobInstanceService.buildById(Long.valueOf(transmission.getBuildId())); return instance.getIdentifier(); } public void remove(Agent agent) { agentCookie.remove(agent); String uuid = sessionIds.remove(agent); if (uuid == null) { return; } agentSessions.remove(uuid); } public Map<String, Agent> connectedAgents() { return agentSessions; } public void sendCancelMessage(String uuid) { if (uuid == null) { return; } Agent agent = agentSessions.get(uuid); if(agent != null) { agent.send(new Message(Action.cancelBuild)); } } }