/* * 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.bootstrap; import java.io.File; import java.io.FileFilter; import java.io.FilenameFilter; import java.io.IOException; import java.net.MalformedURLException; import java.net.URI; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.jar.JarFile; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * @author emeroad */ public class AgentDirBaseClassPathResolver implements ClassPathResolver { private final BootLogger logger = BootLogger.getLogger(this.getClass().getName()); static final String VERSION_PATTERN = "(-[0-9]+\\.[0-9]+\\.[0-9]+((\\-SNAPSHOT)|(-RC[0-9]+))?)?"; static final Pattern DEFAULT_AGENT_PATTERN = compile("pinpoint-bootstrap" + VERSION_PATTERN + "\\.jar"); static final Pattern DEFAULT_AGENT_COMMONS_PATTERN = compile("pinpoint-commons" + VERSION_PATTERN + "\\.jar"); static final Pattern DEFAULT_AGENT_CORE_PATTERN = compile("pinpoint-bootstrap-core" + VERSION_PATTERN + "\\.jar"); static final Pattern DEFAULT_AGENT_CORE_OPTIONAL_PATTERN = compile("pinpoint-bootstrap-core-optional" + VERSION_PATTERN + "\\.jar"); static final Pattern DEFAULT_ANNOTATIONS = compile("pinpoint-annotations" + VERSION_PATTERN + "\\.jar"); private final Pattern agentPattern; private final Pattern agentCommonsPattern; private final Pattern agentCorePattern; private final Pattern agentCoreOptionalPattern; private final Pattern annotationsPattern; private String classPath; private String agentJarName; private String agentJarFullPath; private String agentDirPath; private List<String> fileExtensionList; private String pinpointCommonsJar; private String bootStrapCoreJar; private String bootStrapCoreOptionalJar; private String annotationsJar; private BootstrapJarFile bootstrapJarFile; private static Pattern compile(String regex) { return Pattern.compile(regex); } public AgentDirBaseClassPathResolver() { this(getClassPathFromSystemProperty()); } public AgentDirBaseClassPathResolver(String classPath) { this.classPath = classPath; this.agentPattern = DEFAULT_AGENT_PATTERN; this.agentCommonsPattern = DEFAULT_AGENT_COMMONS_PATTERN; this.agentCorePattern = DEFAULT_AGENT_CORE_PATTERN; this.agentCoreOptionalPattern = DEFAULT_AGENT_CORE_OPTIONAL_PATTERN; this.annotationsPattern = DEFAULT_ANNOTATIONS; this.fileExtensionList = getDefaultFileExtensionList(); } public List<String> getDefaultFileExtensionList() { List<String> extensionList = new ArrayList<String>(); extensionList.add("jar"); extensionList.add("xml"); extensionList.add("properties"); return extensionList; } public AgentDirBaseClassPathResolver(String classPath, String agentPattern) { this.classPath = classPath; this.agentPattern = Pattern.compile(agentPattern); this.agentCommonsPattern = DEFAULT_AGENT_COMMONS_PATTERN; this.agentCorePattern = DEFAULT_AGENT_CORE_PATTERN; this.agentCoreOptionalPattern = DEFAULT_AGENT_CORE_OPTIONAL_PATTERN; this.annotationsPattern = DEFAULT_ANNOTATIONS; this.fileExtensionList = getDefaultFileExtensionList(); } @Override public boolean verify() { final BootstrapJarFile bootstrapJarFile = new BootstrapJarFile(); // 1st find boot-strap.jar final boolean agentJarNotFound = this.findAgentJar(); if (!agentJarNotFound) { logger.warn("pinpoint-bootstrap-x.x.x(-SNAPSHOT).jar not found."); return false; } // 2nd find pinpoint-commons.jar final String pinpointCommonsJar = getPinpointCommonsJar(); if (pinpointCommonsJar == null) { logger.warn("pinpoint-commons-x.x.x(-SNAPSHOT).jar not found"); return false; } final JarFile pinpointCommonsJarFile = getJarFile(pinpointCommonsJar); if (pinpointCommonsJarFile == null) { logger.warn("pinpoint-commons-x.x.x(-SNAPSHOT).jar not found"); return false; } bootstrapJarFile.append(pinpointCommonsJarFile); // 3rd find bootstrap-core.jar final String bootStrapCoreJar = getBootStrapCoreJar(); if (bootStrapCoreJar == null) { logger.warn("pinpoint-bootstrap-core-x.x.x(-SNAPSHOT).jar not found"); return false; } JarFile bootStrapCoreJarFile = getJarFile(bootStrapCoreJar); if (bootStrapCoreJarFile == null) { logger.warn("pinpoint-bootstrap-core-x.x.x(-SNAPSHOT).jar not found"); return false; } bootstrapJarFile.append(bootStrapCoreJarFile); // 4th find bootstrap-core-optional.jar final String bootStrapCoreOptionalJar = getBootStrapCoreOptionalJar(); if (bootStrapCoreOptionalJar == null) { logger.info("pinpoint-bootstrap-core-optional-x.x.x(-SNAPSHOT).jar not found"); } else { JarFile bootStrapCoreOptionalJarFile = getJarFile(bootStrapCoreOptionalJar); if (bootStrapCoreOptionalJarFile == null) { logger.info("pinpoint-bootstrap-core-optional-x.x.x(-SNAPSHOT).jar not found"); } else { bootstrapJarFile.append(bootStrapCoreOptionalJarFile); } } // 5th find annotations.jar : optional dependency final String annotationsJar = getAnnotationsJar(); if (annotationsJar == null) { logger.info("pinpoint-annotations-x.x.x(-SNAPSHOT).jar not found"); } else { JarFile jarFile = getJarFile(annotationsJar); bootstrapJarFile.append(jarFile); } this.bootstrapJarFile = bootstrapJarFile; return true; } public void setClassPathFromSystemProperty() { this.classPath = getClassPathFromSystemProperty(); } @Override public BootstrapJarFile getBootstrapJarFile() { return bootstrapJarFile; } public static String getClassPathFromSystemProperty() { return System.getProperty("java.class.path"); } boolean findAgentJar() { Matcher matcher = agentPattern.matcher(classPath); if (!matcher.find()) { return false; } this.agentJarName = parseAgentJar(matcher); this.agentJarFullPath = parseAgentJarPath(classPath, agentJarName); if (agentJarFullPath == null) { return false; } this.agentDirPath = parseAgentDirPath(agentJarFullPath); if (agentDirPath == null) { return false; } logger.info("Agent original-path:" + agentDirPath); // defense alias change this.agentDirPath = toCanonicalPath(agentDirPath); logger.info("Agent canonical-path:" + agentDirPath); this.pinpointCommonsJar = findFromBootDir("pinpoint-commons.jar", agentCommonsPattern); this.bootStrapCoreJar = findFromBootDir("pinpoint-bootstrap-core.jar", agentCorePattern); this.bootStrapCoreOptionalJar = findFromBootDir("pinpoint-bootstrap-core-optional.jar", agentCoreOptionalPattern); this.annotationsJar = findFromBootDir("pinpoint-annotations.jar", annotationsPattern); return true; } private String toCanonicalPath(String path) { final File file = new File(path); return toCanonicalPath(file); } private String toCanonicalPath(File file) { try { return file.getCanonicalPath(); } catch (IOException e) { logger.warn(file.getPath() + " getCanonicalPath() error. Error:" + e.getMessage(), e); return file.getAbsolutePath(); } } private String findFromBootDir(final String name, final Pattern pattern) { String bootDirPath = agentDirPath + File.separator + "boot"; final File[] files = listFiles(name, pattern, bootDirPath); if (isEmpty(files)) { logger.info(name + " not found."); return null; } else if (files.length == 1) { File file = files[0]; return toCanonicalPath(file); } else { logger.info("too many " + name + " found. " + Arrays.toString(files)); return null; } } private boolean isEmpty(File[] files) { return files == null || files.length == 0; } private File[] listFiles(final String name, final Pattern pattern, String bootDirPath) { File bootDir = new File(bootDirPath); return bootDir.listFiles(new FilenameFilter() { @Override public boolean accept(File dir, String fileName) { Matcher matcher = pattern.matcher(fileName); if (matcher.matches()) { logger.info("found " + name + ". " + dir.getAbsolutePath() + File.separator + fileName); return true; } return false; } }); } @Override public String getPinpointCommonsJar() { return pinpointCommonsJar; } @Override public String getBootStrapCoreJar() { return bootStrapCoreJar; } @Override public String getBootStrapCoreOptionalJar() { return bootStrapCoreOptionalJar; } public String getAnnotationsJar() { return annotationsJar; } private String parseAgentJar(Matcher matcher) { int start = matcher.start(); int end = matcher.end(); return this.classPath.substring(start, end); } @Override public String getAgentJarName() { return this.agentJarName; } private String parseAgentJarPath(String classPath, String agentJar) { String[] classPathList = classPath.split(File.pathSeparator); for (String findPath : classPathList) { boolean find = findPath.contains(agentJar); if (find) { return findPath; } } return null; } @Override public String getAgentJarFullPath() { return agentJarFullPath; } @Override public String getAgentLibPath() { return this.agentDirPath + File.separator + "lib"; } @Override public String getAgentLogFilePath() { return this.agentDirPath + File.separator + "log"; } @Override public String getAgentPluginPath() { return this.agentDirPath + File.separator + "plugin"; } @Override public List<URL> resolveLib() { String agentLibPath = getAgentLibPath(); File libDir = new File(agentLibPath); if (!libDir.exists()) { logger.warn(agentLibPath + " not found"); return Collections.emptyList(); } if (!libDir.isDirectory()) { logger.warn(agentLibPath + " not Directory"); return Collections.emptyList(); } final List<URL> jarURLList = new ArrayList<URL>(); final File[] findJarList = findJar(libDir); if (findJarList != null) { for (File file : findJarList) { URL url = toURI(file); if (url != null) { jarURLList.add(url); } } } URL agentDirUri = toURI(new File(agentLibPath)); if (agentDirUri != null) { jarURLList.add(agentDirUri); } // hot fix. boot jars not found from classPool ?? jarURLList.add(toURI(new File(getPinpointCommonsJar()))); jarURLList.add(toURI(new File(getBootStrapCoreJar()))); String bootstrapCoreOptionalJar = getBootStrapCoreOptionalJar(); // bootstrap-core-optional jar is not required and is okay to be null if (bootstrapCoreOptionalJar != null) { jarURLList.add(toURI(new File(bootstrapCoreOptionalJar))); } return jarURLList; } @Override public URL[] resolvePlugins() { final File file = new File(getAgentPluginPath()); if (!file.exists()) { logger.warn(file + " not found"); return new URL[0]; } if (!file.isDirectory()) { logger.warn(file + " is not a directory"); return new URL[0]; } final File[] jars = file.listFiles(new FilenameFilter() { @Override public boolean accept(File dir, String name) { return name.endsWith(".jar"); } }); if (isEmpty(jars)) { return new URL[0]; } final URL[] urls = new URL[jars.length]; for (int i = 0; i < jars.length; i++) { try { urls[i] = jars[i].toURI().toURL(); } catch (MalformedURLException e) { // TODO have to change to PinpointException AFTER moving the exception to pinpoint-common throw new RuntimeException("Fail to load plugin jars", e); } } for (File pluginJar : jars) { logger.info("Found plugins: " + pluginJar.getPath()); } return urls; } private URL toURI(File file) { URI uri = file.toURI(); try { return uri.toURL(); } catch (MalformedURLException e) { logger.warn(file.getName() + ".toURL() failed.", e); return null; } } private File[] findJar(File libDir) { return libDir.listFiles(new FileFilter() { @Override public boolean accept(File pathname) { String path = pathname.getName(); for (String extension : fileExtensionList) { if (path.lastIndexOf("." + extension) != -1) { return true; } } return false; } }); } private String parseAgentDirPath(String agentJarFullPath) { int index1 = agentJarFullPath.lastIndexOf("/"); int index2 = agentJarFullPath.lastIndexOf("\\"); int max = Math.max(index1, index2); if (max == -1) { return null; } return agentJarFullPath.substring(0, max); } @Override public String getAgentDirPath() { return agentDirPath; } @Override public String getAgentConfigPath() { return agentDirPath + File.separator + "pinpoint.config"; } private JarFile getJarFile(String jarFilePath) { try { return new JarFile(jarFilePath); } catch (IOException ioe) { logger.warn(jarFilePath + " file not found. Error:" + ioe.getMessage(), ioe); return null; } } }