/* * 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.plugin.tomcat.interceptor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Collections; import java.util.List; import com.navercorp.pinpoint.bootstrap.context.ServerMetaDataHolder; import org.apache.catalina.Container; import org.apache.catalina.Context; import org.apache.catalina.Engine; import org.apache.catalina.Host; import org.apache.catalina.loader.WebappLoader; import com.navercorp.pinpoint.bootstrap.context.TraceContext; import com.navercorp.pinpoint.bootstrap.interceptor.AroundInterceptor; import com.navercorp.pinpoint.bootstrap.logging.PLogger; import com.navercorp.pinpoint.bootstrap.logging.PLoggerFactory; /** * @author hyungil.jeong */ public class WebappLoaderStartInterceptor implements AroundInterceptor { private final PLogger logger = PLoggerFactory.getLogger(this.getClass()); private final TraceContext traceContext; public WebappLoaderStartInterceptor(TraceContext traceContext) { this.traceContext = traceContext; } @Override public void before(Object target, Object[] args) { // Do Nothing } @Override public void after(Object target, Object[] args, Object result, Throwable throwable) { // target should be an instance of WebappLoader. if (target instanceof WebappLoader) { WebappLoader webappLoader = (WebappLoader)target; try { String contextKey = extractContextKey(webappLoader); List<String> loadedJarNames = extractLibJars(webappLoader); dispatchLibJars(contextKey, loadedJarNames); } catch (Exception e) { if (logger.isWarnEnabled()) { logger.warn(e.getMessage(), e); } } } else { logger.warn("Webapp loader is not an instance of org.apache.catalina.loader.WebappLoader. Found [{}]", target.getClass().toString()); } } private String extractContextKey(WebappLoader webappLoader) { final String defaultContextName = ""; try { Container container = extractContext(webappLoader); // WebappLoader's associated Container should be a Context. if (container instanceof Context) { Context context = (Context)container; String contextName = context.getName(); Host host = (Host)container.getParent(); Engine engine = (Engine)host.getParent(); StringBuilder sb = new StringBuilder(); sb.append(engine.getName()).append("/").append(host.getName()); if (!contextName.startsWith("/")) { sb.append('/'); } sb.append(contextName); return sb.toString(); } } catch (Exception e) { // Same action for any and all exceptions. logger.warn("Error extracting context name.", e); } return defaultContextName; } // FIXME Use reflection until we provide separate packages for instrumented libraries. // Tomcat 8's WebappLoader does not have getContainer() method. // Providing an optional package that calls WebappLoader.getContext() method could be an option. private Container extractContext(WebappLoader webappLoader) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { Method m; try { // Tomcat 6, 7 - org.apache.catalina.loader.getContainer() m = webappLoader.getClass().getDeclaredMethod("getContainer"); } catch (NoSuchMethodException e1) { try { // Tomcat 8 - org.apache.catalina.loader.getContainer() m = webappLoader.getClass().getDeclaredMethod("getContext"); } catch (NoSuchMethodException e2) { logger.warn("Webapp loader does not have access to its container."); return null; } } Object container = m.invoke(webappLoader); if (container instanceof Container) { return (Container)container; } return null; } private List<String> extractLibJars(WebappLoader webappLoader) { ClassLoader classLoader = webappLoader.getClassLoader(); if (classLoader instanceof URLClassLoader) { URLClassLoader webappClassLoader = (URLClassLoader)classLoader; URL[] urls = webappClassLoader.getURLs(); return extractLibJarNamesFromURLs(urls); } else { logger.warn("Webapp class loader is not an instance of URLClassLoader. Found [{}]", classLoader.getClass().toString()); return Collections.emptyList(); } } private List<String> extractLibJarNamesFromURLs(URL[] urls) { if (urls == null) { return Collections.emptyList(); } List<String> libJarNames = new ArrayList<String>(urls.length); for (URL url : urls) { try { URI uri = url.toURI(); String libJarName = extractLibJarName(uri); if (libJarName.length() > 0) { libJarNames.add(libJarName); } } catch (URISyntaxException e) { // ignore invalid formats logger.warn("Invalid library url found : [{}]", url, e); } catch (Exception e) { logger.warn("Error extracting library name", e); } } return libJarNames; } private String extractLibJarName(URI uri) { String jarName = uri.toString(); if (jarName == null) { return ""; } int lastIndexOfSeparator = jarName.lastIndexOf("/"); if (lastIndexOfSeparator < 0) { return jarName; } else { return jarName.substring(lastIndexOfSeparator + 1); } } private void dispatchLibJars(String contextKey, List<String> libJars) { ServerMetaDataHolder holder = this.traceContext.getServerMetaDataHolder(); holder.addServiceInfo(contextKey, libJars); holder.notifyListeners(); } }