/*
* Copyright 2014 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.vo.callstacks;
import java.util.ArrayList;
import java.util.List;
import com.navercorp.pinpoint.common.server.bo.AnnotationBo;
import com.navercorp.pinpoint.common.server.bo.ApiMetaDataBo;
import com.navercorp.pinpoint.common.server.bo.MethodTypeEnum;
import com.navercorp.pinpoint.common.service.AnnotationKeyRegistryService;
import com.navercorp.pinpoint.common.service.ServiceTypeRegistryService;
import com.navercorp.pinpoint.common.trace.AnnotationKey;
import com.navercorp.pinpoint.common.trace.ServiceType;
import com.navercorp.pinpoint.common.server.util.AnnotationUtils;
import com.navercorp.pinpoint.common.util.ApiDescription;
import com.navercorp.pinpoint.common.server.util.ApiDescriptionParser;
import com.navercorp.pinpoint.web.calltree.span.CallTreeNode;
import com.navercorp.pinpoint.web.calltree.span.SpanAlign;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author minwoo.jung
*/
public class RecordFactory {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
// spans with id = 0 are regarded as root - start at 1
private int idGen = 1;
private ServiceTypeRegistryService registry;
private AnnotationKeyRegistryService annotationKeyRegistryService;
private final ApiDescriptionParser apiDescriptionParser = new ApiDescriptionParser();
public RecordFactory(ServiceTypeRegistryService registry, AnnotationKeyRegistryService annotationKeyRegistryService) {
this.registry = registry;
this.annotationKeyRegistryService = annotationKeyRegistryService;
}
public Record get(final CallTreeNode node, final String argument) {
final SpanAlign align = node.getValue();
align.setId(getNextId());
final int parentId = getParentId(node);
Api api = getApi(align);
final Record record = new Record(align.getDepth(),
align.getId(),
parentId,
true,
api.getTitle(),
argument,
align.getStartTime(),
align.getElapsed(),
align.getGap(),
align.getAgentId(),
align.getApplicationId(),
registry.findServiceType(align.getServiceType()),
align.getDestinationId(),
align.hasChild(),
false,
align.getTransactionId(),
align.getSpanId(),
align.getExecutionMilliseconds(),
api.getMethodTypeEnum(),
true);
record.setSimpleClassName(api.getClassName());
record.setFullApiDescription(api.getDescription());
return record;
}
public Record getFilteredRecord(final CallTreeNode node, String apiTitle) {
final SpanAlign align = node.getValue();
align.setId(getNextId());
final int parentId = getParentId(node);
// Api api = getApi(align);
final Record record = new Record(align.getDepth(),
align.getId(),
parentId,
true,
apiTitle,
"",
align.getStartTime(),
align.getElapsed(),
align.getGap(),
"UNKNOWN",
align.getApplicationId(),
ServiceType.UNKNOWN,
"",
false,
false,
align.getTransactionId(),
align.getSpanId(),
align.getExecutionMilliseconds(),
MethodTypeEnum.DEFAULT,
false);
return record;
}
public Record getException(final int depth, final int parentId, final SpanAlign align) {
if(!align.hasException()) {
return null;
}
final Record record = new Record(depth,
getNextId(),
parentId,
false,
getSimpleExceptionName(align.getExceptionClass()),
align.getExceptionMessage(),
0L, 0L, 0, null, null, null, null, false, true,
align.getTransactionId(),
align.getSpanId(),
align.getExecutionMilliseconds(),
MethodTypeEnum.DEFAULT,
true);
return record;
}
private String getSimpleExceptionName(String exceptionClass) {
if (exceptionClass == null) {
return "";
}
final int index = exceptionClass.lastIndexOf('.');
if (index != -1) {
exceptionClass = exceptionClass.substring(index + 1, exceptionClass.length());
}
return exceptionClass;
}
public List<Record> getAnnotations(final int depth, final int parentId, SpanAlign align) {
List<Record> list = new ArrayList<>();
for(AnnotationBo annotation : align.getAnnotationBoList()) {
final AnnotationKey key = findAnnotationKey(annotation.getKey());
if (key.isViewInRecordSet()) {
final Record record = new Record(depth,
getNextId(),
parentId,
false,
key.getName(),
annotation.getValue().toString(),
0L, 0L, 0, null, null, null, null, false, false, null, 0, 0,
MethodTypeEnum.DEFAULT, annotation.isAuthorized());
list.add(record);
}
}
return list;
}
public Record getParameter(final int depth, final int parentId, final String method, final String argument) {
return new Record(depth,
getNextId(),
parentId,
false,
method,
argument,
0L, 0L, 0, null, null, null, null, false, false, null, 0, 0,
MethodTypeEnum.DEFAULT, true);
}
int getParentId(final CallTreeNode node) {
final CallTreeNode parent = node.getParent();
if (parent == null) {
if (!node.getValue().isSpan()) {
throw new IllegalStateException("parent is null. node=" + node);
}
return 0;
}
return parent.getValue().getId();
}
private Api getApi(final SpanAlign align) {
final AnnotationBo annotation = AnnotationUtils.findAnnotationBo(align.getAnnotationBoList(), AnnotationKey.API_METADATA);
if (annotation != null) {
final Api api = new Api();
final ApiMetaDataBo apiMetaData = (ApiMetaDataBo) annotation.getValue();
String apiInfo = getApiInfo(apiMetaData);
api.setTitle(apiInfo);
api.setDescription(apiInfo);
if (apiMetaData.getMethodTypeEnum() == MethodTypeEnum.DEFAULT) {
try {
ApiDescription apiDescription = apiDescriptionParser.parse(api.description);
api.setTitle(apiDescription.getSimpleMethodDescription());
api.setClassName(apiDescription.getSimpleClassName());
} catch (Exception e) {
logger.debug("Failed to api parse. {}", api.description, e);
}
}
api.setMethodTypeEnum(apiMetaData.getMethodTypeEnum());
return api;
} else {
final Api api = new Api();
AnnotationKey apiMetaDataError = getApiMetaDataError(align.getAnnotationBoList());
api.setTitle(apiMetaDataError.getName());
return api;
}
}
private String getApiInfo(ApiMetaDataBo apiMetaDataBo) {
if (apiMetaDataBo.getLineNumber() != -1) {
return apiMetaDataBo.getApiInfo() + ":" + apiMetaDataBo.getLineNumber();
} else {
return apiMetaDataBo.getApiInfo();
}
}
public AnnotationKey getApiMetaDataError(List<AnnotationBo> annotationBoList) {
for (AnnotationBo bo : annotationBoList) {
AnnotationKey apiErrorCode = annotationKeyRegistryService.findApiErrorCode(bo.getKey());
if (apiErrorCode != null) {
return apiErrorCode;
}
}
// could not find a more specific error - returns generalized error
return AnnotationKey.ERROR_API_METADATA_ERROR;
}
private AnnotationKey findAnnotationKey(int key) {
return annotationKeyRegistryService.findAnnotationKey(key);
}
private int getNextId() {
return idGen++;
}
private static class Api {
private String title = "";
private String className = "";
private String description = "";
private MethodTypeEnum methodTypeEnum = MethodTypeEnum.DEFAULT;
public Api() {
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public MethodTypeEnum getMethodTypeEnum() {
return methodTypeEnum;
}
public void setMethodTypeEnum(MethodTypeEnum methodTypeEnum) {
if (methodTypeEnum == null) {
throw new NullPointerException("methodTypeEnum must not be null");
}
this.methodTypeEnum = methodTypeEnum;
}
}
}