/* * 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.instrument.classpool; import com.navercorp.pinpoint.common.util.ClassLoaderUtils; import com.navercorp.pinpoint.profiler.util.Maps; import javassist.ClassPath; import javassist.LoaderClassPath; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Collection; import java.util.Deque; import java.util.LinkedList; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicInteger; /** * @author emeroad */ public class HierarchyMultipleClassPool implements MultipleClassPool { private static final AtomicInteger ID = new AtomicInteger(); private final Logger logger = LoggerFactory.getLogger(this.getClass()); private static final ClassLoader SYSTEM = ClassLoader.getSystemClassLoader(); private final ConcurrentMap<ClassLoader, NamedClassPool> classMap; private final NamedClassPool parentClassPool; public HierarchyMultipleClassPool(NamedClassPool parentClassPool) { if (parentClassPool == null) { throw new NullPointerException("parentClassPool must not be null"); } this.classMap = Maps.newWeakConcurrentMap(); this.parentClassPool = parentClassPool; } public HierarchyMultipleClassPool() { this.classMap = Maps.newWeakConcurrentMap(); this.parentClassPool = new NamedClassPool("system"); parentClassPool.appendSystemPath(); } @Override public NamedClassPool getClassPool(ClassLoader classLoader) { if (classLoader == null) { throw new NullPointerException("classLoader must not be null"); } final NamedClassPool hit = this.classMap.get(classLoader); if (hit != null) { return hit; } // concurrent classPool create prepareHierarchyClassPool(classLoader); final NamedClassPool classPool = this.classMap.get(classLoader); if (classPool == null) { throw new IllegalStateException("unexpected condition. ClassPool not found. classLoader:" + classLoader); } return classPool; } private NamedClassPool put(ClassLoader classLoader, NamedClassPool classPool) { final NamedClassPool exist = this.classMap.putIfAbsent(classLoader, classPool); if (exist != null) { return exist; } return classPool; } private NamedClassPool createClassPool(ClassLoader classLoader, NamedClassPool parentClassPool) { String classLoaderName = classLoader.getClass().getName(); NamedClassPool newClassPool = new NamedClassPool(parentClassPool, classLoaderName + "-" + ID.getAndIncrement()); newClassPool.childFirstLookup = true; final ClassPath classPath = new LoaderClassPath(classLoader); newClassPool.appendClassPath(classPath); return newClassPool; } private void prepareHierarchyClassPool(ClassLoader findClassLoader) { final Collection<ClassLoader> classLoaderHierarchyList = findClassLoaderHierarchy(findClassLoader); logger.debug("ClassLoaderHierarchy:{}", classLoaderHierarchyList); NamedClassPool parentClassPool = this.parentClassPool; for (ClassLoader classLoader : classLoaderHierarchyList) { final NamedClassPool existClassPool = this.classMap.get(classLoader); if (existClassPool != null) { parentClassPool = existClassPool; } else { NamedClassPool classPool = createClassPool(classLoader, parentClassPool); parentClassPool = put(classLoader, classPool); } } } private Collection<ClassLoader> findClassLoaderHierarchy(ClassLoader classLoader) { if (classLoader == null) { throw new NullPointerException("classLoader must not be null"); } Deque<ClassLoader> classLoaderHierarchyList = new LinkedList<ClassLoader>(); classLoaderHierarchyList.addFirst(classLoader); // search classLoader; ClassLoader parent; while (true) { parent = classLoader.getParent(); if (ClassLoaderUtils.isJvmClassLoader(parent)) { classLoaderHierarchyList.addFirst(SYSTEM); break; } classLoaderHierarchyList.addFirst(parent); classLoader = parent; } return classLoaderHierarchyList; } public int size() { return this.classMap.size(); } public Collection<NamedClassPool> values() { return classMap.values(); } }