/*
* Copyright 2016 NAVER Corp.
*
* 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.navercorp.pinpoint.web.controller;
import com.navercorp.pinpoint.thrift.dto.TResult;
import com.navercorp.pinpoint.thrift.dto.command.TActiveThreadDump;
import com.navercorp.pinpoint.thrift.dto.command.TActiveThreadLightDump;
import com.navercorp.pinpoint.thrift.dto.command.TCmdActiveThreadDump;
import com.navercorp.pinpoint.thrift.dto.command.TCmdActiveThreadDumpRes;
import com.navercorp.pinpoint.thrift.dto.command.TCmdActiveThreadLightDump;
import com.navercorp.pinpoint.thrift.dto.command.TCmdActiveThreadLightDumpRes;
import com.navercorp.pinpoint.thrift.dto.command.TRouteResult;
import com.navercorp.pinpoint.web.cluster.PinpointRouteResponse;
import com.navercorp.pinpoint.web.config.ConfigProperties;
import com.navercorp.pinpoint.web.service.AgentService;
import com.navercorp.pinpoint.web.vo.AgentActiveThreadDumpFactory;
import com.navercorp.pinpoint.web.vo.AgentActiveThreadDumpList;
import com.navercorp.pinpoint.web.vo.AgentInfo;
import org.apache.thrift.TBase;
import org.apache.thrift.TException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author Taejin Koo
*/
@Controller
@RequestMapping("/agent")
public class AgentCommandController {
@Autowired
private AgentService agentService;
@Autowired
private ConfigProperties webProperties;
@RequestMapping(value = "/activeThreadDump", method = RequestMethod.GET)
public ModelAndView getActiveThreadDump(@RequestParam(value = "applicationName") String applicationName,
@RequestParam(value = "agentId") String agentId,
@RequestParam(value = "limit", required = false, defaultValue = "-1") int limit,
@RequestParam(value = "threadName", required = false) String[] threadNameList,
@RequestParam(value = "localTraceId", required = false) Long[] localTraceIdList) throws TException {
if (!webProperties.isEnableActiveThreadDump()) {
return createResponse(false, "Disable activeThreadDump option. 'config.enable.activeThreadDump=false'");
}
AgentInfo agentInfo = agentService.getAgentInfo(applicationName, agentId);
if (agentInfo == null) {
return createResponse(false, String.format("Can't find suitable Agent(%s/%s)", applicationName, agentId));
}
TCmdActiveThreadDump threadDump = new TCmdActiveThreadDump();
if (limit > 0) {
threadDump.setLimit(limit);
}
if (threadNameList != null) {
threadDump.setThreadNameList(Arrays.asList(threadNameList));
}
if (localTraceIdList != null) {
threadDump.setLocalTraceIdList(Arrays.asList(localTraceIdList));
}
try {
PinpointRouteResponse pinpointRouteResponse = agentService.invoke(agentInfo, threadDump);
if (isSuccessResponse(pinpointRouteResponse)) {
TBase<?, ?> result = pinpointRouteResponse.getResponse();
if (result instanceof TCmdActiveThreadDumpRes) {
TCmdActiveThreadDumpRes activeThreadDumpResponse = (TCmdActiveThreadDumpRes) result;
List<TActiveThreadDump> activeThreadDumps = activeThreadDumpResponse.getThreadDumps();
AgentActiveThreadDumpFactory factory = new AgentActiveThreadDumpFactory();
AgentActiveThreadDumpList activeThreadDumpList = factory.create1(activeThreadDumps);
Map<String, Object> responseData = createResponseData(activeThreadDumpList, activeThreadDumpResponse.getType(), activeThreadDumpResponse.getSubType(), activeThreadDumpResponse.getVersion());
return createResponse(true, responseData);
}
}
return handleFailedResponse(pinpointRouteResponse);
} catch (TException e) {
return createResponse(false, e.getMessage());
}
}
private boolean isSuccessResponse(PinpointRouteResponse pinpointRouteResponse) {
if (pinpointRouteResponse == null) {
return false;
}
TRouteResult routeResult = pinpointRouteResponse.getRouteResult();
if (routeResult != TRouteResult.OK) {
return false;
}
return true;
}
private Map<String, Object> createResponseData(AgentActiveThreadDumpList activeThreadDumpList, String type, String subType, String version) {
Map<String, Object> response = new HashMap<>(4);
response.put("threadDumpData", activeThreadDumpList);
response.put("type", type);
response.put("subType", subType);
response.put("version", version);
return response;
}
@RequestMapping(value = "/activeThreadLightDump", method = RequestMethod.GET)
public ModelAndView getActiveThreadLightDump(@RequestParam(value = "applicationName") String applicationName,
@RequestParam(value = "agentId") String agentId,
@RequestParam(value = "limit", required = false, defaultValue = "-1") int limit,
@RequestParam(value = "threadName", required = false) String[] threadNameList,
@RequestParam(value = "localTraceId", required = false) Long[] localTraceIdList) throws TException {
if (!webProperties.isEnableActiveThreadDump()) {
return createResponse(false, "Disable activeThreadDump option. 'config.enable.activeThreadDump=false'");
}
AgentInfo agentInfo = agentService.getAgentInfo(applicationName, agentId);
if (agentInfo == null) {
return createResponse(false, String.format("Can't find suitable Agent(%s/%s)", applicationName, agentId));
}
TCmdActiveThreadLightDump threadDump = new TCmdActiveThreadLightDump();
if (limit > 0) {
threadDump.setLimit(limit);
}
if (threadNameList != null) {
threadDump.setThreadNameList(Arrays.asList(threadNameList));
}
if (localTraceIdList != null) {
threadDump.setLocalTraceIdList(Arrays.asList(localTraceIdList));
}
try {
PinpointRouteResponse pinpointRouteResponse = agentService.invoke(agentInfo, threadDump);
if (isSuccessResponse(pinpointRouteResponse)) {
TBase<?, ?> result = pinpointRouteResponse.getResponse();
if (result instanceof TCmdActiveThreadLightDumpRes) {
TCmdActiveThreadLightDumpRes activeThreadDumpResponse = (TCmdActiveThreadLightDumpRes) result;
List<TActiveThreadLightDump> activeThreadDumps = activeThreadDumpResponse.getThreadDumps();
AgentActiveThreadDumpFactory factory = new AgentActiveThreadDumpFactory();
AgentActiveThreadDumpList activeThreadDumpList = factory.create2(activeThreadDumps);
Map<String, Object> responseData = createResponseData(activeThreadDumpList, activeThreadDumpResponse.getType(), activeThreadDumpResponse.getSubType(), activeThreadDumpResponse.getVersion());
return createResponse(true, responseData);
}
}
return handleFailedResponse(pinpointRouteResponse);
} catch (TException e) {
return createResponse(false, e.getMessage());
}
}
private ModelAndView handleFailedResponse(PinpointRouteResponse response) {
if (response == null) {
return createResponse(false, "response is null");
}
TRouteResult routeResult = response.getRouteResult();
if (routeResult != TRouteResult.OK) {
return createResponse(false, routeResult.name());
} else {
TBase tBase = response.getResponse();
if (tBase instanceof TResult) {
return createResponse(false, ((TResult) tBase).getMessage());
} else {
return createResponse(false, "unknown");
}
}
}
private ModelAndView createResponse(boolean success, Object message) {
ModelAndView mv = new ModelAndView();
mv.setViewName("jsonView");
if (success) {
mv.addObject("code", 0);
} else {
mv.addObject("code", -1);
}
mv.addObject("message", message);
return mv;
}
}