/*
* 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.common.trace;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.navercorp.pinpoint.common.plugin.PluginLoader;
import com.navercorp.pinpoint.common.util.logger.CommonLogger;
import com.navercorp.pinpoint.common.util.logger.CommonLoggerFactory;
import com.navercorp.pinpoint.common.util.logger.StdoutCommonLoggerFactory;
/**
* @author Jongho Moon
*
*/
public class TraceMetadataLoader {
private final CommonLogger logger;
private final List<ServiceTypeInfo> serviceTypeInfos = new ArrayList<ServiceTypeInfo>();
private final ServiceTypeChecker serviceTypeChecker = new ServiceTypeChecker();
private final List<AnnotationKey> annotationKeys = new ArrayList<AnnotationKey>();
private final AnnotationKeyChecker annotationKeyChecker = new AnnotationKeyChecker();
public TraceMetadataLoader() {
this(StdoutCommonLoggerFactory.INSTANCE);
}
public TraceMetadataLoader(CommonLoggerFactory loggerFactory) {
if (loggerFactory == null) {
throw new NullPointerException("loggerFactory must not be null");
}
this.logger = loggerFactory.getLogger(TraceMetadataLoader.class.getName());
}
public void load(URL[] urls) {
if (urls == null) {
throw new NullPointerException("urls must not be null");
}
List<TraceMetadataProvider> providers = PluginLoader.load(TraceMetadataProvider.class, urls);
load(providers);
}
public void load(ClassLoader loader) {
if (loader == null) {
throw new NullPointerException("loader must not be null");
}
List<TraceMetadataProvider> providers = PluginLoader.load(TraceMetadataProvider.class, loader);
load(providers);
}
public void load(List<TraceMetadataProvider> providers) {
if (providers == null) {
throw new NullPointerException("providers must not be null");
}
logger.info("Loading TraceMetadataProviders");
for (TraceMetadataProvider provider : providers) {
if (logger.isInfoEnabled()) {
logger.info("Loading TraceMetadataProvider: " + provider.getClass().getName() + " name:" + provider.toString());
}
TraceMetadataSetupContextImpl context = new TraceMetadataSetupContextImpl(provider.getClass());
provider.setup(context);
}
this.serviceTypeChecker.logResult();
this.annotationKeyChecker.logResult();
}
public List<ServiceTypeInfo> getServiceTypeInfos() {
return serviceTypeInfos;
}
public List<AnnotationKey> getAnnotationKeys() {
return annotationKeys;
}
private class TraceMetadataSetupContextImpl implements TraceMetadataSetupContext {
private final Class<?> provider;
public TraceMetadataSetupContextImpl(Class<?> provider) {
this.provider = provider;
}
@Override
public void addServiceType(ServiceType serviceType) {
if (serviceType == null) {
throw new NullPointerException("serviceType must not be null");
}
ServiceTypeInfo type = new DefaultServiceTypeInfo(serviceType);
addType0(type);
}
@Override
public void addServiceType(ServiceType serviceType, AnnotationKeyMatcher annotationKeyMatcher) {
if (serviceType == null) {
throw new NullPointerException("serviceType must not be null");
}
if (annotationKeyMatcher == null) {
throw new NullPointerException("annotationKeyMatcher must not be null");
}
ServiceTypeInfo type = new DefaultServiceTypeInfo(serviceType, annotationKeyMatcher);
addType0(type);
}
private void addType0(ServiceTypeInfo type) {
if (type == null) {
throw new NullPointerException("type must not be null");
}
// local check
serviceTypeChecker.check(type.getServiceType(), provider);
serviceTypeInfos.add(type);
}
@Override
public void addAnnotationKey(AnnotationKey annotationKey) {
if (annotationKey == null) {
throw new NullPointerException("annotationKey must not be null");
}
// local check
annotationKeyChecker.check(annotationKey, provider);
annotationKeys.add(annotationKey);
}
}
private static String serviceTypePairToString(Pair<ServiceType> pair) {
return pair.value.getName() + "(" + pair.value.getCode() + ") from " + pair.provider.getName();
}
private static String annotationKeyPairToString(Pair<AnnotationKey> pair) {
return pair.value.getName() + "(" + pair.value.getCode() + ") from " + pair.provider.getName();
}
private static class Pair<T> {
private final T value;
private final Class<?> provider;
public Pair(T value, Class<?> provider) {
this.value = value;
this.provider = provider;
}
}
private class ServiceTypeChecker {
private final Map<String, Pair<ServiceType>> serviceTypeNameMap = new HashMap<String, Pair<ServiceType>>();
private final Map<Short, Pair<ServiceType>> serviceTypeCodeMap = new HashMap<Short, Pair<ServiceType>>();
private void check(ServiceType type, Class<?> providerClass) {
Pair<ServiceType> pair = new Pair<ServiceType>(type, providerClass);
Pair<ServiceType> prev = serviceTypeNameMap.put(type.getName(), pair);
if (prev != null) {
// TODO change exception type
throw new RuntimeException("ServiceType name of " + serviceTypePairToString(pair) + " is duplicated with " + serviceTypePairToString(prev));
}
prev = serviceTypeCodeMap.put(type.getCode(), pair);
if (prev != null) {
// TODO change exception type
throw new RuntimeException("ServiceType code of " + serviceTypePairToString(pair) + " is duplicated with " + serviceTypePairToString(prev));
}
}
private void logResult() {
logger.info("Finished loading ServiceType:");
List<Pair<ServiceType>> serviceTypes = new ArrayList<Pair<ServiceType>>(serviceTypeCodeMap.values());
Collections.sort(serviceTypes, new Comparator<Pair<ServiceType>>() {
@Override
public int compare(Pair<ServiceType> o1, Pair<ServiceType> o2) {
short code1 = o1.value.getCode();
short code2 = o2.value.getCode();
return code1 > code2 ? 1 : (code1 < code2 ? -1 : 0);
}
});
for (Pair<ServiceType> serviceType : serviceTypes) {
logger.info(serviceTypePairToString(serviceType));
}
}
}
private class AnnotationKeyChecker {
private final Map<Integer, Pair<AnnotationKey>> annotationKeyCodeMap = new HashMap<Integer, Pair<AnnotationKey>>();
private void check(AnnotationKey key, Class<?> providerClass) {
Pair<AnnotationKey> pair = new Pair<AnnotationKey>(key, providerClass);
Pair<AnnotationKey> prev = annotationKeyCodeMap.put(key.getCode(), pair);
if (prev != null) {
// TODO change exception type
throw new RuntimeException("AnnotationKey code of " + annotationKeyPairToString(pair) + " is duplicated with " + annotationKeyPairToString(prev));
}
}
private void logResult() {
logger.info("Finished loading AnnotationKeys:");
List<Pair<AnnotationKey>> annotationKeys = new ArrayList<Pair<AnnotationKey>>(annotationKeyCodeMap.values());
Collections.sort(annotationKeys, new Comparator<Pair<AnnotationKey>>() {
@Override
public int compare(Pair<AnnotationKey> o1, Pair<AnnotationKey> o2) {
int code1 = o1.value.getCode();
int code2 = o2.value.getCode();
return code1 > code2 ? 1 : (code1 < code2 ? -1 : 0);
}
});
for (Pair<AnnotationKey> annotationKey : annotationKeys) {
logger.info(annotationKeyPairToString(annotationKey));
}
}
}
}