Merge pull request #12 from lioolli/master

Translated comments to English
This commit is contained in:
Jongho Moon
2014-12-26 18:16:03 +09:00
22 changed files with 94 additions and 115 deletions
@@ -21,14 +21,13 @@ import java.util.Map;
import com.navercorp.pinpoint.rpc.util.ClassUtils;
/**
* You must modify {@link com.navercorp.pinpoint.collector.receiver.tcp.AgentHandshakePropertyType} when you modify this enum type.
* But There is no compatibility issue if you only add some properties.
*
* @author koo.taejin
*/
public enum AgentHandshakePropertyType {
// 해당 객체는 profiler, collector 양쪽에 함꼐 있음
// 변경시 함께 변경 필요
// map으로 처리하기 때문에 이전 파라미터 제거 대신 추가할 경우 확장성에는 문제가 없음
SUPPORT_SERVER("supportServer", Boolean.class),
HOSTNAME("hostName", String.class),
@@ -64,8 +64,8 @@ public class AgentInfoSenderListener implements FutureListener<ResponseMessage>
private TBase<?, ?> deserialize(Future<ResponseMessage> future) {
final ResponseMessage responseMessage = future.getResult();
// TODO theradlocalcache로 변경해야 되는지 검토 자주 생성이 될수 있는 객체라서 life cycle이 상이함.
// caching해야 될려나?
// TODO Should we change this to thread local cache? This object's life cycle is different because it could be created many times.
// Should we cache this?
byte[] message = responseMessage.getMessage();
return SerializationUtils.deserialize(message, HeaderTBaseDeserializerFactory.DEFAULT_FACTORY, null);
@@ -41,8 +41,8 @@ public class AgentInformationFactory {
if (serverType == null) {
throw new NullPointerException("serverType must not be null");
}
// TODO 일단 임시로 호환성을 위해 agentid에 machinename을 넣도록 하자
// TODO 박스 하나에 서버 인스턴스를 여러개 실행할 때에 문제가 될 수 있음.
// For compatibility issue, use machineName as agentId when agengId is not provided.
// This could be a problem if more than one server instances run on a box.
final String machineName = NetworkUtils.getHostName();
final String hostIp = NetworkUtils.getHostIp();
final String agentId = getId("pinpoint.agentId", machineName, PinpointConstants.AGENT_NAME_MAX_LEN);
@@ -62,8 +62,9 @@ public class AgentInformationFactory {
if (id == null) {
throw new NullPointerException("id must not be null");
}
// 에러 체크 로직을 bootclass 앞단으로 이동시켜야 함.
// 아니면 여기서 체크해서 실패시 agent동작을 하지 않도록 하던가 하는 추가 동작을 해야함.
// TODO AgengId should be validated before bootclass.
// or agent should stop when validataion is failed here.
final byte[] bytes = BytesUtils.toBytes(id);
if (bytes.length > maxlen) {
logger.warn("{} is too long(1~24). value={}", idName, id);
@@ -98,10 +98,10 @@ public class ClassFileTransformerDispatcher implements ClassFileTransformer {
if (className.equals("net/spy/memcached/CacheManager")) {
logger.debug("no modifier for CacheManager");
}
// TODO : 디버그 용도로 추가함
// TODO : modifier가 중복 적용되면 어떻게 되지???
// TODO For debug
// TODO What if a modifier is duplicated?
if (this.profilerConfig.getProfilableClassFilter().filter(className)) {
// 테스트 장비에서 callstack view가 잘 보이는지 확인하려고 추가함.
// Added to see if call stack view is OK on a test machine.
findModifier = this.modifierRegistry.findModifier("*");
} else {
return null;
@@ -120,8 +120,7 @@ public class ClassFileTransformerDispatcher implements ClassFileTransformer {
try {
return findModifier.modify(classLoader, javassistClassName, protectionDomain, classFileBuffer);
} finally {
// null일 경우도 다시 원복하는게 맞음.
// getContextClass 호출시 에러가 발생하였을 경우 여기서 호출당하지 않으므로 이부분에서 원복하는게 맞음.
// The context class loader have to be recovered even if it was null.
thread.setContextClassLoader(before);
}
}
@@ -189,7 +188,7 @@ public class ClassFileTransformerDispatcher implements ClassFileTransformer {
}
/*
* for plugins. This method is not used now because plugin feature is not completed yet.
* for plugins. This method is not used yet because plugin feature is not completed.
*/
private void loadPlugins(DefaultModifierRegistry modifierRepository) {
String pluginPath = agent.getAgentPath() + File.separatorChar + "plugin";
@@ -93,11 +93,10 @@ public class DefaultAgent implements Agent {
private final AgentInformation agentInformation;
private final ServerMetaDataHolder serverMetaDataHolder;
// agent의 상태,
private volatile AgentStatus agentStatus;
static {
// rpc쪽 preload
// Preload classes related to pinpoint-rpc module.
ClassPreLoader.preload();
}
@@ -158,7 +157,6 @@ public class DefaultAgent implements Agent {
this.agentInfoSender = new AgentInfoSender(tcpDataSender, profilerConfig.getAgentInfoSendRetryInterval(), this.agentInformation, this.serverMetaDataHolder);
// JVM 통계 등을 주기적으로 수집하여 collector에 전송하는 monitor를 초기화한다.
this.agentStatMonitor = new AgentStatMonitor(this.statDataSender, this.agentInformation.getAgentId(), this.agentInformation.getStartTime());
@@ -171,8 +169,9 @@ public class DefaultAgent implements Agent {
preLoadClass();
/**
* FIXME tomcat의 경우에는 com.navercorp.pinpoint.profiler.modifier.tomcat.interceptor.CatalinaAwaitInterceptor가 org/apache/catalina/startup/Catalina/await함수가 실행되기
* 전에 실행해주나. stand alone application은 그렇지 않으므로..
* FIXME
* In case of Tomcat, com.navercorp.pinpoint.profiler.modifier.tomcat.interceptor.CatalinaAwaitInterceptor invokes start() method
* before entering await() method of org.apache.catalina.startup.Catalina. But for other applications, it must be invoked directly.
*/
if (typeResolver.isManuallyStartupRequired()) {
start();
@@ -223,9 +222,9 @@ public class DefaultAgent implements Agent {
final String binderClassName = binder.getClass().getName();
PLogger pLogger = binder.getLogger(binder.getClass().getName());
pLogger.info("PLoggerFactory.initialize() bind:{} cl:{}", binderClassName, binder.getClass().getClassLoader());
// static LoggerFactory에 binder를 붙임.
// Set binder to static LoggerFactory
// Should we unset binder at shutdonw hook or stop()?
PLoggerFactory.initialize(binder);
// shutdown hook이나 stop에 LoggerBinder의 연결을 풀어야 되는가?
}
private TraceContext createTraceContext(short serverType) {
@@ -290,10 +289,6 @@ public class DefaultAgent implements Agent {
}
protected PinpointSocket createPinpointSocket(String host, int port, PinpointSocketFactory factory) {
// 1.2 버전이 Tcp Data Command 허용하는 버전이 아니기 떄문에 true이던 false이던 무조건 SimpleLoggingMessageListener를 이용하게 함
// SimpleLoggingMessageListener.LISTENER 는 서로 통신을 하지 않게 설정되어 있음 (테스트코드는 pinpoint-rpc에 존재)
// 1.3 버전으로 할 경우 아래 분기에서 MessageListener 변경 필요
PinpointSocket socket = null;
for (int i = 0; i < 3; i++) {
try {
@@ -376,7 +371,7 @@ public class DefaultAgent implements Agent {
this.agentInfoSender.stop();
this.agentStatMonitor.stop();
// 종료 처리 필요.
// Need to process stop
this.spanDataSender.stop();
this.statDataSender.stop();
this.tcpDataSender.stop();
@@ -34,7 +34,7 @@ public class DefaultClassFileFilter implements ClassFileFilter {
@Override
public boolean doFilter(ClassLoader classLoader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classFileBuffer) {
// fast java class skip
// fast skip java classes
if (className.startsWith("java")) {
if (className.startsWith("/", 4) || className.startsWith("x/", 4)) {
return SKIP;
@@ -42,11 +42,11 @@ public class DefaultClassFileFilter implements ClassFileFilter {
}
if (classLoader == agentLoader) {
// agent clssLoader에 로드된 클래스는 스킵한다.
// skip classes loaded by agent class loader.
return SKIP;
}
// 자기 자신의 패키지도 제외
// 향후 패키지명 변경에 의해 코드 변경이 필요함.
// Skip pinpoint packages too.
if (className.startsWith("com/navercorp/pinpoint/")) {
return SKIP;
}
@@ -30,14 +30,13 @@ import java.util.Arrays;
public class CallStack {
private static final Logger logger = LoggerFactory.getLogger(CallStack.class);
// 추적 depth크기 제한을 위해서 필요. 해당 사이즈를 넘어갈경우 부드럽게 트레이스를 무시하는 로직이 필요함.
private static final int TRACE_STACK_MAX_SIZE = 64;
private static final int STACK_SIZE = 8;
private static final int STACK_INCREASE_SIZE = 8;
private final Span span;
// CallStack을 동시성 환경에서 복사해서 볼수 있는 방법이 필요함.
// We have to find some way to copy call stack in concurrent situation.
private StackFrame[] stack = new StackFrame[STACK_SIZE];
@@ -54,14 +53,15 @@ public class CallStack {
return span;
}
// without synchronization for the present.
// public synchronized int getIndex() {
public int getIndex() {
// 일단 락 안잡는 코드로 함.
return index;
}
// copy시의 락 생각할 경우 좀더 정교하게 잡을수 있을듯.
// push, pop, copy만 락을 잡아도 될거 같은 생각이 듬.
// We could handle synchronization more precisely.
// Maybe synchonizing push, pop, copy would be enough.
public synchronized StackFrame getCurrentStackFrame() {
return stack[index];
}
@@ -133,7 +133,7 @@ public class CallStack {
private PinpointException createStackException(String stackApiPoint, final int index) {
final PinpointException ex = new PinpointException("Profiler CallStack check. index:" + index + " stackApiPoint:" + stackApiPoint);
if (logger.isWarnEnabled()) {
// 자체 stack dump 필요.
// need to dump stack.
logger.warn("invalid callStack found stack dump:{}", this, ex);
}
return ex;
@@ -40,11 +40,6 @@ public class DefaultAsyncTrace implements AsyncTrace {
public static final int NON_REGIST = -1;
// private int id;
// 비동기일 경우 traceenable의 경우 애매함. span을 보내는것으로 데이터를 생성하므로 약간 이상.
// private boolean tracingEnabled;
private final AtomicInteger state = new AtomicInteger(STATE_INIT);
@@ -136,12 +131,6 @@ public class DefaultAsyncTrace implements AsyncTrace {
String drop = StringUtils.drop(th.getMessage());
recordAttribute(AnnotationKey.EXCEPTION, drop);
// TODO 비동기 api일 경우, span에 exception을 마크하기가 까다로움
// AnnotationKey span = getCallStack().getSpan();
// if (span.getErrCode() == 0) {
// span.setErrCode(1);
// }
}
}
@@ -178,7 +167,6 @@ public class DefaultAsyncTrace implements AsyncTrace {
this.spanEvent.setDestinationId(destinationId);
}
// TODO: endPoint로 받으면 합치는데 비용이 들어가 그냥 한번에 받는게 나을것 같음.
@Override
public void recordEndPoint(final String endPoint) {
this.spanEvent.setEndPoint(endPoint);
@@ -202,15 +190,13 @@ public class DefaultAsyncTrace implements AsyncTrace {
public void timeout() {
if (state.compareAndSet(STATE_INIT, STATE_TIMEOUT)) {
// TODO timeout spanEvent log 던지기.
// 뭘 어떤 내용을 던져야 되는지 아직 모르겠음????
// TODO
}
}
public boolean fire() {
if (state.compareAndSet(STATE_INIT, STATE_FIRE)) {
if (timeoutTask != null) {
// timeout이 걸려 있는 asynctrace일 경우 호출해 준다.
this.timeoutTask.cancel();
}
return true;
@@ -124,8 +124,8 @@ public final class DefaultTrace implements Trace {
private StackFrame createSpanEventStackFrame(int stackId) {
SpanEvent spanEvent = new SpanEvent(callStack.getSpan());
// Span내부의 SpanEvent로 들어가지 않을 경우 사용하기 위해 set한다.
// Set properties for the case when stackFrame is not used as part of Span.
SpanEventStackFrame stackFrame = new SpanEventStackFrame(spanEvent);
stackFrame.setStackFrameId(stackId);
stackFrame.setSequence(nextSequence());
@@ -185,7 +185,8 @@ public final class DefaultTrace implements Trace {
// metricResponseTime();
pop(ROOT_STACKID);
callStack.popRoot();
// 잘못된 stack 조작시 다음부터 그냥 nullPointerException이 발생할건데 괜찮은가?
// If the stack is not handled properly, NullPointerException will be thrown after this. Is it OK?
this.currentStackFrame = null;
}
@@ -227,7 +228,7 @@ public final class DefaultTrace implements Trace {
public void traceBlockEnd(int stackId) {
pop(stackId);
StackFrame popStackFrame = callStack.pop();
// pop 할때 frame위치를 원복해야 한다.
// When pop, current frame have to be recovered.
this.currentStackFrame = popStackFrame;
}
@@ -235,7 +236,7 @@ public final class DefaultTrace implements Trace {
final StackFrame currentStackFrame = this.currentStackFrame;
int stackFrameId = currentStackFrame.getStackFrameId();
if (stackFrameId != stackId) {
// 자체 stack dump를 하면 오류발견이 쉬울것으로 생각됨
// stack dump will make debugging easy.
if (logger.isWarnEnabled()) {
PinpointException exception = new PinpointException("Corrupted CallStack found");
logger.warn("Corrupted CallStack found. StackId not matched. expected:{} current:{}", stackId, stackFrameId, exception);
@@ -296,7 +297,7 @@ public final class DefaultTrace implements Trace {
return;
}
final String drop = StringUtils.drop(th.getMessage(), 256);
// exception class가 proxy라서 class Name이 불규칙하면 문제가 발생할수 있다.
// An exception that is an instance of a proxy class could make something wrong becuase the class name will vary.
final int exceptionId = traceContext.cacheString(th.getClass().getName());
this.currentStackFrame.setExceptionInfo(exceptionId, drop);
@@ -320,7 +321,7 @@ public final class DefaultTrace implements Trace {
@Override
public void recordApi(MethodDescriptor methodDescriptor, Object[] args) {
// API 저장 방법의 개선 필요.
// Need to improve the way of storing APIs.
recordApi(methodDescriptor);
recordArgs(args);
}
@@ -350,7 +351,7 @@ public final class DefaultTrace implements Trace {
for (int i = start; i < max; i++) {
recordAttribute(AnnotationKey.getArgs(i), args[i]);
}
// TODO MAX 사이즈를 넘는건 마크만 해줘야 하나?
// TODO How to handle if args length is greater than MAX_ARGS_SIZE?
}
}
@@ -373,7 +374,7 @@ public final class DefaultTrace implements Trace {
for (int i = 0; i < max; i++) {
recordAttribute(AnnotationKey.getArgs(i), args[i]);
}
// TODO MAX 사이즈를 넘는건 마크만 해줘야 하나?
// TODO How to handle if args length is greater than MAX_ARGS_SIZE?
}
}
@@ -468,14 +469,14 @@ public final class DefaultTrace implements Trace {
@Override
public void recordEndPoint(final String endPoint) {
// TODO API 단일화 필요.
// TODO Need to unify API
StackFrame currentStackFrame = this.currentStackFrame;
currentStackFrame.setEndPoint(endPoint);
}
@Override
public void recordRemoteAddress(final String remoteAddress) {
// TODO API 단일화 필요.
// TODO Need to unify API
StackFrame currentStackFrame = this.currentStackFrame;
if (currentStackFrame instanceof RootStackFrame) {
((RootStackFrame) currentStackFrame).setRemoteAddress(remoteAddress);
@@ -107,7 +107,7 @@ public class DefaultTraceContext implements TraceContext {
}
/**
* sampling 여부까지 체크하여 유효성을 검증한 후 Trace를 리턴한다.
* Return trace only if current transaction can be sampled.
* @return
*/
public Trace currentTraceObject() {
@@ -119,7 +119,7 @@ public class DefaultTraceContext implements TraceContext {
}
/**
* 유효성을 검증하지 않고 Trace를 리턴한다.
* Return trace without sampling check.
* @return
*/
@Override
@@ -145,7 +145,7 @@ public class DefaultTraceContext implements TraceContext {
return profilerConfig;
}
// remote 에서 샘플링 대상으로 선정된 경우.
// Will be invoked when current transaction is picked as sampling target at remote.
public Trace continueTraceObject(final TraceId traceID) {
return traceFactory.continueTraceObject(traceID);
}
@@ -239,7 +239,7 @@ public class DefaultTraceContext implements TraceContext {
if (transactionId == null) {
throw new NullPointerException("transactionId must not be null");
}
// TODO parse error 때 예외 처리 필요.
// TODO Should handle exception when parsing failed.
return DefaultTraceId.parse(transactionId, parentSpanID, spanID, flags);
}
@@ -254,13 +254,12 @@ public class DefaultTraceContext implements TraceContext {
final Result cachingResult = this.sqlCache.put(normalizedSql);
if (cachingResult.isNewValue()) {
if (isDebug) {
// TODO hit% 로그를 남겨야 문제 발생시 도움이 될듯 하다.
// TODO logging hit ratio could help debugging
logger.debug("NewSQLParsingResult:{}", parsingResult);
}
// newValue란 의미는 cache에 인입됬다는 의미이고 이는 신규 sql문일 가능성이 있다는 의미임.
// 그러므로 메타데이터를 서버로 전송해야 한다.
// isNewValue means that the value is newly cached.
// So the sql could be new one. We have to send sql metadata to collector.
final TSqlMetaData sqlMetaData = new TSqlMetaData();
sqlMetaData.setAgentId(getAgentId());
sqlMetaData.setAgentStartTime(getAgentStartTime());
@@ -268,7 +267,7 @@ public class DefaultTraceContext implements TraceContext {
sqlMetaData.setSqlId(cachingResult.getId());
sqlMetaData.setSql(normalizedSql);
// 좀더 신뢰성이 있는 tcp connection이 필요함.
// Need more reliable tcp connection
this.priorityDataSender.request(sqlMetaData);
}
parsingResult.setId(cachingResult.getId());
@@ -30,7 +30,7 @@ import com.navercorp.pinpoint.common.util.ParsingResult;
public class DisableTrace implements Trace {
public static final DisableTrace INSTANCE = new DisableTrace();
// 구지 객체를 생성하여 사용할 필요가 없을듯.
private DisableTrace() {
}
@@ -86,7 +86,7 @@ public class DisableTrace implements Trace {
@Override
public boolean canSampled() {
// sampling false를 항상 false를 리턴한다.
// always return false
return false;
}
@@ -25,8 +25,6 @@ import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 뭔가 복잡한 비동기 call trace시 객체 등록용으로 쓰자.
* 근데 손좀봐야 될듯.뭔가 좀 구림.
* @author emeroad
*/
@Deprecated
@@ -38,12 +36,9 @@ public class GlobalCallTrace {
private ConcurrentMap<Integer, AsyncTrace> trace = new ConcurrentHashMap<Integer, AsyncTrace>(32);
private AtomicInteger idGenerator = new AtomicInteger(0);
// stop을 해줘야 할듯.
private Timer timer = new Timer("Pinpoint-GlobalCallTrace-Timer-" + timerId.getAndIncrement(), true);
public int registerTraceObject(AsyncTrace asyncTrace) {
// TODO 연관관계가 전달부분이 영 별로임.
TimeoutTask timeoutTask = new TimeoutTask(trace, asyncTrace.getAsyncId());
asyncTrace.setTimeoutTask(timeoutTask);
@@ -68,7 +63,6 @@ public class GlobalCallTrace {
if (asyncTrace != null) {
boolean result = ((DefaultAsyncTrace)asyncTrace).fire();
if (!result) {
// 이미 timeout된 asyncTrace임.
return null;
}
}
@@ -107,8 +107,8 @@ public class MetricTrace implements Trace {
private StackFrame createSpanEventStackFrame(int stackId) {
SpanEvent spanEvent = new SpanEvent(callStack.getSpan());
// Span내부의 SpanEvent로 들어가지 않을 경우 사용하기 위해 set한다.
// Set properties for the case when stackFrame is not used as part of Span.
SpanEventStackFrame stackFrame = new SpanEventStackFrame(spanEvent);
stackFrame.setStackFrameId(stackId);
@@ -167,7 +167,8 @@ public class MetricTrace implements Trace {
metricResponseTime();
checkStackId(ROOT_STACKID);
callStack.popRoot();
// 잘못된 stack 조작시 다음부터 그냥 nullPointerException이 발생할건데 괜찮은가?
// If the stack is not handled properly, NullPointerException will be thrown after this. Is it OK?
this.currentStackFrame = null;
}
@@ -193,7 +194,7 @@ public class MetricTrace implements Trace {
public void traceBlockEnd(int stackId) {
checkStackId(stackId);
StackFrame popStackFrame = callStack.pop();
// pop 할때 frame위치를 원복해야 한다.
// When pop, current frame have to be recovered.
this.currentStackFrame = popStackFrame;
}
@@ -201,7 +202,7 @@ public class MetricTrace implements Trace {
final StackFrame currentStackFrame = this.currentStackFrame;
int stackFrameId = currentStackFrame.getStackFrameId();
if (stackFrameId != stackId) {
// 자체 stack dump를 하면 오류발견이 쉬울것으로 생각됨
// stack dump will make debugging easy.
if (logger.isWarnEnabled()) {
PinpointException exception = new PinpointException("Corrupted CallStack found");
logger.warn("Corrupted CallStack found. StackId not matched. expected:{} current:{}", stackId, stackFrameId, exception);
@@ -239,7 +240,7 @@ public class MetricTrace implements Trace {
if (th == null) {
return;
}
// TODO 추가적인 객체를 생성하지 않도록 MARK Exception이 있으면 좋을것 같음.
// TODO We'd better having MARK Exception to prevent more objects being created.
this.currentStackFrame.setExceptionInfo(EXCEPTION_MARK, "");
final Span span = getCallStack().getSpan();
@@ -38,7 +38,7 @@ public class SpanChunkFactory {
if (flushData == null) {
throw new NullPointerException("flushData must not be null");
}
// TODO 반드시 1개 이상이라는 조건을 충족해야 된다.
// TODO must be equals to or greater than 1
final int size = flushData.size();
if (size < 1) {
throw new IllegalArgumentException("flushData.size() < 1 size:" + size);
@@ -56,8 +56,9 @@ public class SpanEvent extends TSpanEvent {
public void markStartTime() {
// spanEvent.setStartElapsed((int) (startTime - parentSpanStartTime));
final int startElapsed = (int)(System.currentTimeMillis() - span.getStartTime());
// startElapsed이 0이면 마크를 안하는 로직은 거의 쓸모가 없다. 하지 말것.
// Span에서 최초 SpanEvent나 0이 나올 가능성이 있고, 이후 SpanEvent의 경우는 거의 그렇지 않음.
// If startElapsed is 0, logic without mark is useless. Don't do that.
// The first SpaneEvent of a Sapn could result in 0. Not likely afterwards.
this.setStartElapsed(startElapsed);
}
@@ -30,13 +30,13 @@ public class SpanId {
// private static final Random seed = new Random();
public static long newSpanId() {
// thread 마다 가능한 겹치지 않는 값이 생성되면 문제 없으므로 ThreadLocalRandom으로 변경함.
final Random random = getRandom();
return createSpanId(random);
}
// 이거 구현 바꾸면 다른 random 사용이 가능함.
// Changed to ThreadLocalRandom because unique value per thread will be enough.
// If you need to change Random implementation, modify this method.
private static Random getRandom() {
return ThreadLocalRandom.current();
}
@@ -31,7 +31,7 @@ public interface TraceFactory {
Trace disableSampling();
// remote 에서 샘플링 대상으로 선정된 경우.
// picked as sampling target at remote
Trace continueTraceObject(TraceId traceID);
Trace newTraceObject();
@@ -85,8 +85,8 @@ public class BufferedStorage implements Storage {
if (logger.isErrorEnabled()) {
logger.error("storage is null. discard spanEvent:{}", spanEvent);
}
// 이미 span이 와서 flush된 상황임.
// 비동기를 이쪽에 포함시키면 이렇게 될수 있으나. 현재 구조를 변경할 계획임.
// Already flushed. This could happen with async processing.
return;
}
storage.add(spanEvent);
@@ -31,7 +31,7 @@ public class LogStorageFactory implements StorageFactory {
@Override
public Storage createStorage() {
// 상태 없음 그냥 재활용하면 됨.
// reuse because it has no states.
return DEFAULT_STORAGE;
}
@@ -192,7 +192,7 @@ public class AspectWeaverClass {
private CtMethod copyMethod(CtClass sourceClass, CtMethod sourceMethod) throws CannotCompileException {
// id라도 더 줘야 될려나?
// need id?
String copyMethodName = methodNameReplacer.replaceMethodName(sourceMethod.getName());
@@ -56,7 +56,7 @@ public class ClassLoadChecker {
if (className == null) {
throw new NullPointerException("className must not be null");
}
// classLoader는 null일수도 있을거 같음. 몇몇 java reference의 최상위 로더의 경우 null 이 나옴.
// classLoader could be null (some jdk classes)
this.classLoader = classLoader;
this.className = className;
}
@@ -50,7 +50,8 @@ public class JavaAssistByteCodeInstrumentor implements ByteCodeInstrumentor {
private final boolean isDebug = logger.isDebugEnabled();
private final NamedClassPool rootClassPool;
// classPool의 수평적 확장이 필요할수 있음. was에 여러개의 webapp가 있을 경우 충돌방지.
// TODO Need to separate childClassPool per class space to prevent collision(ex: multiple web applications on a Tomcat server)
private final NamedClassPool childClassPool;
private Agent agent;
@@ -68,7 +69,7 @@ public class JavaAssistByteCodeInstrumentor implements ByteCodeInstrumentor {
this.rootClassPool = createClassPool(pathNames, "rootClassPool");
this.childClassPool = createChildClassPool(rootClassPool, "childClassPool");
this.agent = agent;
// agent class rootClassPool에 넣는다.
// Add Pinpoint classes to rootClassPool
checkLibrary(this.getClass().getClassLoader(), this.rootClassPool, this.getClass().getName());
}
@@ -116,11 +117,12 @@ public class JavaAssistByteCodeInstrumentor implements ByteCodeInstrumentor {
}
public void checkLibrary(ClassLoader classLoader, NamedClassPool classPool, String javassistClassName) {
// 최상위 classLoader일 경우 null이라 찾을필요가 없음.
// if it's loaded by boot class loader, classLoader is null.
if (classLoader == null) {
return;
}
// TODO Util로 뽑을까?
// TODO extract to Util?
final boolean findClass = findClass(javassistClassName, classPool);
if (findClass) {
if (isDebug) {
@@ -160,16 +162,15 @@ public class JavaAssistByteCodeInstrumentor implements ByteCodeInstrumentor {
}
try {
final NamedClassPool classPool = findClassPool(classLoader);
// classLoader로 락을 잡는게 안전함.
// 어차피 classLoader에서 락을 잡고 들어오는점도 있고. 예외 사항이 발생할수 있기 때문에.
// classLoader의 재진입 락을 잡고 들어오는게 무난함.
// It's safe to synchronize on classLoader because current thread already hold lock on classLoader.
// Without lock, maybe something could go wrong.
synchronized (classLoader) {
if (this.classLoadChecker.exist(classLoader, defineClass)) {
return classLoader.loadClass(defineClass);
} else {
final CtClass clazz = classPool.get(defineClass);
// 로그 레벨을 debug로 하니 개발때 제대로 체크 안하는 사람이 있어서 수정함.
checkTargetClassInterface(clazz);
defineAbstractSuperClass(clazz, classLoader, protectedDomain);
@@ -200,20 +201,22 @@ public class JavaAssistByteCodeInstrumentor implements ByteCodeInstrumentor {
private void defineAbstractSuperClass(CtClass clazz, ClassLoader classLoader, ProtectionDomain protectedDomain) throws NotFoundException, CannotCompileException {
final CtClass superClass = clazz.getSuperclass();
if (superClass == null) {
// java.lang.Object가 아닌 경우 null은 안나올듯.
// maybe java.lang.Object
return;
}
final int modifiers = superClass.getModifiers();
if (Modifier.isAbstract(modifiers)) {
if (this.classLoadChecker.exist(classLoader, superClass.getName())) {
// nestedClass는 자기 자신에게만 속해 있으므로 로드 여부 체크가 필요 없으나 abstractClass는 같이 사용할수 있으므로 체크해야 된다.
// We have to check if abstract super classes is already loaded because it could be used by other classes unlike nested classes.
return;
}
if (isInfo) {
logger.info("defineAbstractSuperClass class:{} cl:{}", superClass.getName(), classLoader);
}
// 좀더 정확하게 java 스펙처럼 하려면 제귀를 돌면서 추가로 super를 확인해야 되나. 구지 그래야 되나 싶다. 패스.
// 스펙상 1차원 abstractClass만 지원하는 것으로..
// If it was more strict we had to make a recursive call to check super class of super class.
// But it seems like too much. We'll check direct super class only.
superClass.toClass(classLoader, protectedDomain);
}
}
@@ -224,7 +227,7 @@ public class JavaAssistByteCodeInstrumentor implements ByteCodeInstrumentor {
return;
}
for (CtClass nested : nestedClasses) {
// 재귀하면서 최하위부터 로드
// load from inner-most to outer.
defineNestedClass(nested, classLoader, protectedDomain);
if (isInfo) {
logger.info("defineNestedClass class:{} cl:{}", nested.getName(), classLoader);
@@ -274,14 +277,14 @@ public class JavaAssistByteCodeInstrumentor implements ByteCodeInstrumentor {
private void loadClassLoaderLibraries(ClassLoader classLoader, NamedClassPool classPool) {
if (classLoader instanceof URLClassLoader) {
URLClassLoader urlClassLoader = (URLClassLoader) classLoader;
// classLoader가 가지고 있는 전체 리소스를 가능한 패스로 다 걸어야 됨
// 임의의 class가 없을 경우 class의 byte code를 classpool에 적재 할 수 없음.
// We have to add every class path URLs to classPool.
// To load bytecode of a class, ClassPool requires every classes referenced by the class.
URL[] urlList = urlClassLoader.getURLs();
for (URL tempURL : urlList) {
String filePath = tempURL.getFile();
try {
classPool.appendClassPath(filePath);
// 만약 한개만 로딩해도 된다면. return true 할것
if (isInfo) {
logger.info("Loaded cl:{} classPool:{} {} ", classLoader.getClass().getName(), classPool.getName(), filePath);
}