/*
* 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 java.util.Collection;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
import javassist.ClassPath;
import javassist.LoaderClassPath;
import com.navercorp.pinpoint.common.util.ClassLoaderUtils;
import com.navercorp.pinpoint.profiler.util.Maps;
/**
* @author emeroad
*/
public class IsolateMultipleClassPool implements MultipleClassPool {
private static final AtomicInteger ID = new AtomicInteger();
private static final ClassLoader AGENT_CLASS_LOADER = IsolateMultipleClassPool.class.getClassLoader();
private final NamedClassPool rootClassPool;
private final ConcurrentMap<ClassLoader, NamedClassPool> classPoolMap;
private final EventListener eventListener;
public static final boolean DEFAULT_CHILD_FIRST_LOOKUP = true;
private final boolean childFirstLookup;
public static final EventListener EMPTY_EVENT_LISTENER = new EventListener() {
@Override
public void onCreateClassPool(ClassLoader classLoader, NamedClassPool classPool) {
}
};
public interface ClassPoolHandler {
void handleClassPool(NamedClassPool classPool);
}
public interface EventListener {
void onCreateClassPool(ClassLoader classLoader, NamedClassPool classPool);
}
public IsolateMultipleClassPool(EventListener eventListener, ClassPoolHandler systemClassPoolHandler) {
this(DEFAULT_CHILD_FIRST_LOOKUP, eventListener, systemClassPoolHandler);
}
public IsolateMultipleClassPool() {
this(DEFAULT_CHILD_FIRST_LOOKUP, EMPTY_EVENT_LISTENER, null);
}
public IsolateMultipleClassPool(boolean childFirstLookup, EventListener eventListener, ClassPoolHandler rootClassPoolHandler) {
if (eventListener == null) {
throw new NullPointerException("eventListener must not be null");
}
this.rootClassPool = createRootClassPool(rootClassPoolHandler);
this.classPoolMap = Maps.newWeakConcurrentMap();
this.eventListener = eventListener;
this.childFirstLookup = childFirstLookup;
}
private NamedClassPool createRootClassPool(ClassPoolHandler rootClassPoolHandler) {
NamedClassPool systemClassPool = new NamedClassPool("rootClassPool");
systemClassPool.appendSystemPath();
if (rootClassPoolHandler != null ) {
rootClassPoolHandler.handleClassPool(systemClassPool);
}
return systemClassPool;
}
@Override
public NamedClassPool getClassPool(ClassLoader classLoader) {
if (ClassLoaderUtils.isJvmClassLoader(classLoader)) {
return rootClassPool;
}
if (AGENT_CLASS_LOADER == classLoader) {
throw new IllegalArgumentException("unexpected classLoader access. classLoader:" + classLoader);
}
final NamedClassPool hit = this.classPoolMap.get(classLoader);
if (hit != null) {
return hit;
}
NamedClassPool newClassPool = createClassPool(classLoader);
return put(classLoader, newClassPool);
}
private NamedClassPool put(ClassLoader classLoader, NamedClassPool classPool) {
final NamedClassPool exist = this.classPoolMap.putIfAbsent(classLoader, classPool);
if (exist != null) {
return exist;
}
fireOnCreateClassPool(classLoader, classPool);
return classPool;
}
private void fireOnCreateClassPool(ClassLoader classLoader, NamedClassPool classPool) {
eventListener.onCreateClassPool(classLoader, classPool);
}
private NamedClassPool createClassPool(ClassLoader classLoader) {
String classLoaderName = classLoader.toString();
NamedClassPool newClassPool = new NamedClassPool(rootClassPool, classLoaderName + "-" + getNextId());
if (childFirstLookup) {
newClassPool.childFirstLookup = true;
}
final ClassPath classPath = new LoaderClassPath(classLoader);
newClassPool.appendClassPath(classPath);
return newClassPool;
}
private int getNextId() {
return ID.getAndIncrement();
}
public int size() {
return this.classPoolMap.size();
}
// for Test
Collection<NamedClassPool> values() {
return classPoolMap.values();
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("IsolateMultipleClassPool{");
sb.append("classPoolMap=").append(classPoolMap);
sb.append('}');
return sb.toString();
}
}