/* * 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.profiler; import java.lang.instrument.ClassFileTransformer; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import com.google.inject.Inject; import com.navercorp.pinpoint.bootstrap.instrument.RequestHandle; import com.navercorp.pinpoint.profiler.util.JavaAssistUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class DefaultDynamicTransformerRegistry implements DynamicTransformerRegistry { private final Logger logger = LoggerFactory.getLogger(this.getClass()); private final ConcurrentMap<TransformerKey, ClassFileTransformer> transformerMap = new ConcurrentHashMap<TransformerKey, ClassFileTransformer>(); @Inject public DefaultDynamicTransformerRegistry() { } @Override public RequestHandle onRetransformRequest(Class<?> target, final ClassFileTransformer transformer) { if (target == null) { throw new NullPointerException("target must not be null"); } if (transformer == null) { throw new NullPointerException("transformer must not be null"); } final TransformerKey key = createTransformKey(target); add(key, transformer); if (logger.isInfoEnabled()) { logger.info("added retransformer classLoader: {}, class: {}, registry size: {}", target.getClassLoader(), target.getName(), transformerMap.size()); } return new DefaultRequestHandle(key); } @Override public void onTransformRequest(ClassLoader classLoader, String targetClassName, ClassFileTransformer transformer) { // TODO fix classLoader null case // if (classLoader== null) { // boot? ext? system? // classLoader = ClassLoader.getSystemClassLoader(); // } final TransformerKey transformKey = createTransformKey(classLoader, targetClassName); add(transformKey, transformer); if (logger.isInfoEnabled()) { logger.info("added dynamic transformer classLoader: {}, className: {}, registry size: {}", classLoader, targetClassName, transformerMap.size()); } } private void add(TransformerKey key, ClassFileTransformer transformer) { final ClassFileTransformer prev = transformerMap.putIfAbsent(key, transformer); if (prev != null) { throw new ProfilerException("Transformer already exists. TransformKey: " + key + ", transformer: " + prev); } } private TransformerKey createTransformKey(ClassLoader classLoader, String targetClassName) { final String classInternName = JavaAssistUtils.javaNameToJvmName(targetClassName); return new TransformerKey(classLoader, classInternName); } private TransformerKey createTransformKey(Class<?> targetClass) { final ClassLoader classLoader = targetClass.getClassLoader(); final String targetClassName = targetClass.getName(); return createTransformKey(classLoader, targetClassName); } @Override public ClassFileTransformer getTransformer(ClassLoader classLoader, String targetClassName) { // TODO fix classLoader null case if (transformerMap.isEmpty()) { return null; } final TransformerKey key = new TransformerKey(classLoader, targetClassName); final ClassFileTransformer transformer = transformerMap.remove(key); if (transformer != null) { if (logger.isInfoEnabled()) { logger.info("removed dynamic transformer classLoader: {}, className: {}, registry size: {}", classLoader, targetClassName, transformerMap.size()); } } return transformer; } int size() { return transformerMap.size(); } private static final class TransformerKey { // TODO defense classLoader memory leak private final ClassLoader classLoader; private final String targetClassInternalName; public TransformerKey(ClassLoader classLoader, String targetClassInternalName) { if (targetClassInternalName == null) { throw new NullPointerException("targetClassInternalName must not be null"); } this.classLoader = classLoader; this.targetClassInternalName = targetClassInternalName; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; TransformerKey that = (TransformerKey) o; if (classLoader != null ? !classLoader.equals(that.classLoader) : that.classLoader != null) return false; return targetClassInternalName.equals(that.targetClassInternalName); } @Override public int hashCode() { int result = classLoader != null ? classLoader.hashCode() : 0; result = 31 * result + targetClassInternalName.hashCode(); return result; } @Override public String toString() { return "TransformerKey{" + "classLoader=" + classLoader + ", targetClassInternalName='" + targetClassInternalName + '\'' + '}'; } } private class DefaultRequestHandle implements RequestHandle { private final TransformerKey key; public DefaultRequestHandle(TransformerKey key) { if (key == null) { throw new NullPointerException("key must not be null"); } this.key = key; } @Override public boolean cancel() { final ClassFileTransformer remove = transformerMap.remove(key); if (remove == null) { return false; } return true; } } }